首页 > 解决方案 > 通过 alsa 输出声音的函数在通过 pthread create 调用时不起作用:没有声音,100% CPU 使用率

问题描述

我有一个程序通过套接字接收消息并根据消息开始或停止播放某个声音文件。为了让“停止”消息起作用,我需要从单独的线程播放声音。我的解决方案是使用我使用 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(&params);
    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;
}

标签: clinuxaudiopthreadslibalsa

解决方案


从 pthread_cancel 的手册页:

在 Linux 上,取消是使用信号实现的。在 NPTL 线程实现下,第一个实时信号(即信号 32)用于此目的。在 LinuxThreads 上,如果实时信号可用,则使用第二个实时信号,否则使用 SIGUSR2。

在您的 while(keep_playing) 循环中,您产生的线程不足以处理取消信号;在你的主线程中;您不是在等待取消请求的结果,因此两个线程都占用了 cpu。

在您调用 pthread_cancel 之后重新开始播放声音和 pthread_join() 之前的一小段延迟应该可以解决您的问题。


推荐阅读