Функция вывода звука через alsa не работает при вызове через pthread create: нет звука, 100% загрузка процессора - PullRequest
0 голосов
/ 14 июля 2020

У меня есть программа, которая принимает сообщения через сокет и запускает или останавливает воспроизведение определенного звукового файла в зависимости от сообщения. Чтобы сообщение "стоп" работало, мне нужно, чтобы звук проигрывался из отдельного потока. Мое решение - воспроизвести звук с помощью alsa из функции, которую я вызываю с помощью pthread_create (), и после получения сообщения остановки я завершаю поток с помощью 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% загрузка ЦП в обоих моих потоках:

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;
}

1 Ответ

1 голос
/ 15 июля 2020

Из справочной страницы pthread_cancel:

На Linux отмена осуществляется с помощью сигналов. В реализации потоковой передачи NPTL для этой цели используется первый сигнал реального времени (т. Е. Сигнал 32). В LinuxThreads используется второй сигнал реального времени, если сигналы реального времени доступны, в противном случае используется SIGUSR2.

В вашем while (keep_playing) l oop вы не уступаете поток, достаточный для обработки сигнала отмены; в вашем основном потоке; вы не ждете результата запроса на отмену, er go оба потока загружают процессор.

Небольшая задержка перед перезапуском воспроизведения звука и pthread_join () после вызова pthread_cancel должна решить вашу проблему .

...