我有一个程序通过套接字接收消息并根据消息开始或停止播放某个声音文件。为了让“停止”消息起作用,我需要从单独的线程播放声音。我的解决方案是使用我使用 pthread_create() 调用的函数中的 alsa 播放声音,并在收到停止消息后使用 pthread_cancel() 结束线程。播放声音的函数称为 play_sound(void *args);
这是有效的:
struct args *args;
args->fp = fopen("path/to/soundfile.wav", "r");
args->volume = 1;
play_sound((void *) args);
但是一旦我尝试从一个新线程中运行该函数,我的两个线程上都没有声音和 100% 的 CPU 使用率:
struct args *args;
int sound_thread;
args->fp = fopen("path/to/soundfile.wav", "r");
args->volume = 1;
pthread_create(&sound_thread, NULL, (void *) play_sound, (void *) args);
我什至不知道从哪里开始故障排除。
我的代码如下所示:
#include <alsa/asoundlib.h>
#include <alsa/mixer.h>
#include <stdbool.h>
#include <pthread.h>
#include "server.h"
#include "sound.h"
//#include "log.h"
int sound_thread;
struct args {
FILE *fp;
float volume;
};
void init_sound ()
{
sound_thread = -1;
}
void stop_sound ()
{
if (sound_thread != -1) {
pthread_cancel(sound_thread);
keep_playing = false;
sound_thread = -1;
}
}
void dispatch_sound (FILE *fp, float volume)
{
// this function serves to create a new thread for the
// sound to be played from. This is what's giving me
// headaches.
if (sound_thread != -1) {
stop_sound();
}
struct args *args = (struct args *) malloc(sizeof(struct args));
args->fp = fp;
args->volume = volume;
if (pthread_create(&sound_thread, NULL, (void *) play_sound, args) != 0)
sound_thread = -1;
}
}
bool play_sound (void *args)
{
// This function actually plays the sound using functions
// from the alsa lib. it works when invoked regularly without
// threading.
keep_playing = true;
FILE *fp;
int volume;
bool success;
unsigned int samplerate;
int bufsz;
char *buf;
snd_pcm_t *pcm;
snd_pcm_hw_params_t *params;
snd_pcm_uframes_t frames;
samplerate = SAMPLERATE;
fp = ((struct args *) args)->fp;
volume = ((struct args *) args)->volume;
// volume is not actually used right now, since I took out
// the function that sets the volume before playing the
// audio in order to make it easier to pinpoint the issue.
if (snd_pcm_open(&pcm, PCM_DEVICE, SND_PCM_STREAM_PLAYBACK, 0) < 0) {
success = false;
}
snd_pcm_hw_params_alloca(¶ms);
snd_pcm_hw_params_any(pcm, params);
if (snd_pcm_hw_params_set_access(pcm, params, SND_PCM_ACCESS_RW_INTERLEAVED) < 0) {
success = false;
}
if (snd_pcm_hw_params_set_format(pcm, params, SND_PCM_FORMAT_S16_LE) < 0) {
success = false;
}
if (snd_pcm_hw_params_set_channels(pcm, params, CHANNELS) < 0) {
success = false;
}
if (snd_pcm_hw_params_set_rate_near(pcm, params, &samplerate, 0) < 0) {
success = false;
}´
if (snd_pcm_hw_params(pcm, params) < 0) {
success = false;
}
snd_pcm_hw_params_get_period_size(params, &frames, 0);
bufsz = frames * CHANNELS * SAMPLE_SIZE;
buf = (char *) malloc(bufsz);
while (keep_playing) {
while (fread(buf, bufsz, 1, fp) != 0 && keep_playing) {
int err;
if ((err = snd_pcm_writei(pcm, buf, frames)) == -EPIPE) {
snd_pcm_prepare(pcm);
}
}
rewind(fp);
}
snd_pcm_drain(pcm);
snd_pcm_close(pcm);
free(buf);
return success;
}