Потоковая передача байтов на устройство воспроизведения ALSA

У меня много проблем с записью случайных байтов на устройство воспроизведения ALSA с помощью libasound, В конечном итоге моя цель состоит в том, чтобы иметь возможность направлять поток воспроизведения по сети и воспроизводить его на удаленном устройстве.

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

Адаптируя приведенный выше пример кода под свои нужды, я получаю следующее:

#include <stdio.h>
#include <unistd.h>
#include <alsa/asoundlib.h>

static snd_pcm_t *PlaybackHandle;

int init_playback(const char *device, int samplerate, int channels) {
    int err;

    printf("Init parameters: %s %d %d\n", device, samplerate, channels);

    if((err = snd_pcm_open(&PlaybackHandle, device, SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
        printf("Can't open audio %s: %s\n", device, snd_strerror(err));
        return -1; 
    }

    if ((err = snd_pcm_set_params(PlaybackHandle, SND_PCM_FORMAT_S16, SND_PCM_ACCESS_RW_INTERLEAVED, channels, samplerate, 1, 500000)) < 0) {
        printf("Can't set sound parameters: %s\n", snd_strerror(err));
        return -1; 
    }
    return 0;
}

int play_bytes(const void *bytes, int len) {
    snd_pcm_uframes_t frames, count;
    snd_pcm_uframes_t bufsize, period_size;
    frames = 0;
    count = 0;

    snd_pcm_prepare(PlaybackHandle);

    snd_pcm_get_params(PlaybackHandle, &bufsize, &period_size);
    printf("bufsize=%d\n", (int) bufsize);

    do {
        int remaining = len - count;
        int buflen = remaining < bufsize ? remaining : bufsize;
        frames = snd_pcm_writei(PlaybackHandle, bytes + count, buflen);
        // If an error, try to recover from it
        if (frames == -EPIPE) {
            printf("EPIPE\n");
            snd_pcm_prepare(PlaybackHandle);
        }
        if (frames < 0) {
            printf("Recovering\n");
            frames = snd_pcm_recover(PlaybackHandle, frames, 0);
        }
        if (frames < 0)
        {
            printf("Error playing wave: %s\n", snd_strerror(frames));
            break;
        }

        // Update our pointer
        count += frames;
        //printf("count=%d len=%d\n", (int)count, len);

    } while (count < len);

    // Wait for playback to completely finish
    if (count == len)
        snd_pcm_drain(PlaybackHandle);
    return 0;
}

int close_playback() {
    snd_pcm_close(PlaybackHandle);
    return 0;
}

int main(int argc, char **argv) {
    if(argc < 1) {
        printf("Usage: %s <WAV-file>\n", argv[0]);
        return -1;
    }

    int fd;
    unsigned long long len;

    fd = open(argv[1], O_RDONLY);
    // Find the length
    len = lseek(fd, 0, SEEK_END);

    // Skip the first 44 bytes (header)
    lseek(fd, 44, SEEK_SET);
    len -= 44;

    char *data = malloc(len);
    read(fd, data, len);

    init_playback("default", 44100, 2);
    play_bytes(data, len);
    close_playback();
    return 0;
}

Этот код может быть скомпилирован с gcc playback.c -o playback -lasound, Файл WAV, который я использую, можно найти здесь.

Когда я запускаю этот фрагмент кода, где я собираю входящие данные на основе bufsize есть фрагменты аудио, которые повторяются при воспроизведении в зависимости от размера фрагмента. Большой размер чанка дает меньше повторений, чем маленький размер чанка. В сочетании с тем, как звучит звук, я считаю, что небольшой фрагмент в конце каждого фрагмента повторяется.

Параметры, которые я использовал, были:

  • частота дискретизации: 44100
  • каналы: 2

Почему отправка всего файла WAV за один прием работает, а отправка его не работает? Как отправить куски аудиоданных в драйвер и правильно их воспроизвести?

1 ответ

Решение
    frames = snd_pcm_writei(PlaybackHandle, bytes + count, buflen);
    count += frames;

snd_pcm_writei() измеряет размер в кадрах, но вы рассматриваете его как байты. Таким образом, вы пропускаете только одну четвертую часть только что воспроизведенных данных.

Другие вопросы по тегам