Воспроизведение аудио на Raspberry Pi 3 через libalsa очень агрессивно - PullRequest
0 голосов
/ 23 марта 2019

У меня есть сервер, который обрабатывает входящие данные с микрофона (1 канал) и отправляет обратно данные динамиков (2 канала). Opus Float 32 требуется со стороны сервера, и вокруг него делается несколько простых кадров. У меня есть клиент на базе Mac (простая командная строка), который работает хорошо (с небольшим дрожанием, но с достаточными допусками), но я не могу заставить звук правильно воспроизводиться с пи на динамик USB.

Похоже, что пакеты opus декодируются правильно, и я убедился, что потоки одинаковы с обеих сторон, поэтому я предполагаю, что каким-то образом неправильно управляю аудиооборудованием.

Пример кода прилагается, который не показывает кадрирование и тому подобное - а также кольцевой буфер, который я использую для передачи данных между потоками - но это охватывает декодирование опуса, а также всю конфигурацию alsa PCM и воспроизведение. Определения в верхней части являются общими для сервера и клиента.

Любая помощь была бы очень признательна, я бы несколько дней боролся с этим!

Кажется, что это работает декодирование с Mac с использованием AVFoundation и т. Д. - но я не могу воспроизвести звук без всплывающих окон на пи.

#define BUFFER_LEN 48000

#define MAXDATASIZE 1000  // max number of bytes we can get at once

#define TAG_TYPE 1
#define TAG_LENGTH 2
#define TAG_DATA 3

#define TYPE_DATA 1
#define TYPE_SAMPLE_RATE 2

#define OPUS_PACKET_TIME 20

#define INPUT_SAMPLE_RATE 16000
#define INPUT_CHANNELS 1
#define INPUT_MAX_PACKET_SIZE 64
#define INPUT_FRAMES_PER_PACKET (INPUT_SAMPLE_RATE / 1000) * OPUS_PACKET_TIME

#define OUTPUT_SAMPLE_RATE 48000
#define OUTPUT_CHANNELS 2
#define OUTPUT_MAX_PACKET_SIZE 256
#define OUTPUT_FRAMES_PER_PACKET (OUTPUT_SAMPLE_RATE / 1000) * OPUS_PACKET_TIME

#define MAX_FRAME_SIZE 6 * 960

#define OUTPUT_BUFFER_SIZE 960
#define OUTPUT_PERIOD_SIZE 480

static char *device = "default";  // soundcard

static int currentPacketType = TYPE_SAMPLE_RATE;
static int currentReadState = 0;
static int nextDataLengthToRead = 0;

static OpusDecoder *opusDecoder;

void readData(int socket, int32_t length, int tag);
void readNextPacket(int socket);

struct sample_holder {
    float *ref;
};

CIRC_GBUF_DEF(struct sample_holder, playback_buffer, 8192);

#define NON_BLOCKING_PLAYBACK 0

void *playbackThread(void *sock_desc) {
    plog("playback", "Playback thread started");
    snd_pcm_t *handle = NULL;

    int err;
    if ((err = snd_pcm_open(&handle, device, SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
        plog("playback", "Playback open error: %s", snd_strerror(err));
        exit(-1);
    } else {
        plog("playback", "Opened device: %s", device);
    }

    if ((err = snd_pcm_set_params(handle, SND_PCM_FORMAT_FLOAT_LE, SND_PCM_ACCESS_RW_INTERLEAVED, 2, OUTPUT_SAMPLE_RATE, 0, 100000)) < 0) {
        plog("playback", "Playback open error: %s", snd_strerror(err));
    } else {
        plog("playback", " - Configured device: %s", device);
    }

    unsigned int tmp = 0;
    snd_pcm_hw_params_t *params;
    snd_pcm_uframes_t frames;
    int rate;
    unsigned int rrate = OUTPUT_SAMPLE_RATE;
    snd_pcm_uframes_t buffer_size = OUTPUT_BUFFER_SIZE;
    snd_pcm_uframes_t period_size = OUTPUT_PERIOD_SIZE;


    snd_pcm_hw_params_alloca(&params);
    snd_pcm_hw_params_any(handle, params);

    snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED);
    snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_FLOAT_LE);
    snd_pcm_hw_params_set_rate_near(handle, params, &rrate, NULL);
    snd_pcm_hw_params_set_channels(handle, params, 2);

    snd_pcm_hw_params_set_buffer_size_near(handle, params, &buffer_size);
    snd_pcm_hw_params_set_period_size_near(handle, params, &period_size, NULL);

    snd_pcm_hw_params_get_rate(params, &tmp, 0);
    plog("playback", "rate: %d bps", tmp);

    snd_pcm_hw_params_get_period_size(params, &frames, 0);
    plog("playback", "frames: %d", frames);

    snd_pcm_hw_params_get_period_time(params, &tmp, NULL);
    plog("playback", "time: %d", tmp);

#if NON_BLOCKING_PLAYBACK
    if ((err = snd_pcm_nonblock(handle, 1) < 0))
    {
        plog("playback", "Could not set non blocking mode: %s",
        snd_strerror(err));
    }
    else
    {
        plog("playback", " - Non blocking: %s", device);
    }
#endif

    snd_pcm_prepare(handle);

    while (1) {
        struct sample_holder container;

        if (CIRC_GBUF_POP(playback_buffer, &container)) {
            float *outPCM = container.ref;
            float *buffer = outPCM;
            if (outPCM != NULL) {
                // plog("playback", "Pulled a buffer, free space is now: %d",
                // CIRC_GBUF_FS(playback_buffer));
                snd_pcm_sframes_t frames = -1;
                int length = (OUTPUT_FRAMES_PER_PACKET);
                while (frames != 0 && length != 0) {
                    snd_pcm_sframes_t avail = snd_pcm_avail_update(handle);
                    plog("playback", " - avail: %d", avail);
                    plog("playback", " - writing frames: %d", length);
                    snd_pcm_sframes_t availp;
                    snd_pcm_sframes_t delayp;
                    int result = snd_pcm_avail_delay(handle, &availp, &delayp);
                    if (availp < ((OUTPUT_FRAMES_PER_PACKET) / 3)) {
                        plog("playback", " - no space, waiting");
                        msleep(2);
                        continue;
                    }
                    plog("playback", " - availp: %d", availp);
                    plog("playback", " - delayp: %d", delayp);
                    for (int i = 0; i < length; i++) {
                        ;  // buffer[i] /= 1.4;
                    }
                    frames = snd_pcm_writei(
                        handle, buffer,
                        length);  // sending values to sound driver

                    if (frames > 0) {
                        length -= frames;
                    } else if (frames == -EAGAIN) {
                        continue;
                    } else if (frames == -EPIPE) {
                        snd_pcm_prepare(handle);
                    } else {
                        plog("playback", "bad * wrote %d frames", frames);
                    }
                }
            }
        }
    }
}

void receiveSampleData(uint8_t *data, int32_t length) {
    plog("decoding", "decoding %d sample bytes", length);
    float *outPCM = malloc(sizeof(float) * (MAX_FRAME_SIZE * OUTPUT_CHANNELS));

    int decodedSamples = opus_decode_float(opusDecoder, data, length, outPCM, (OUTPUT_FRAMES_PER_PACKET), 0);

    if (decodedSamples != OUTPUT_FRAMES_PER_PACKET) {
        plog("decoding", "Could not decode samples, error: %d", decodedSamples);
    }

    struct sample_holder container;
    container.ref = outPCM;

    if (CIRC_GBUF_PUSH(playback_buffer, &container)) {
        plog("decoding", "Out of space in playback_buffer");
    }
}

Звук звучит довольно агрессивно, поэтому его трудно услышать.

...