Приложение ALSA для чтения и воспроизведения WAV-файла на Raspberry Pi

попытка изучить звуковые слои ALSA, чтобы в конечном итоге написать драйвер устройства ALSA для платформы Raspberry Pi. Начав с простого, я склеил различные образцы с сайта проекта ALSA и других онлайн-источников, чтобы сделать простейшую вещь: прочитать WAV-файл и воспроизвести его на звуковом устройстве по умолчанию. Я не могу заставить этот простой образец C работать.

Я использую libsndfile для выполнения всего чтения/декодирования заголовка wav-файла. Я проверил, что образцы, которые я прочитал в буфер, правильно (проверено первые 400k образцов того, что программа читает в приложении sndfile-to-text, которое сбрасывает значения образца в текстовый файл). Поэтому я знаю, что мой буфер содержит правильные данные, проблема должна быть в том, как я передаю его API ALSA.

при запуске он производит звук только в правом канале, а искаженный / мутный-едва узнаваемый. Кстати, приложение "aplay" отлично воспроизводит тот же wav-файл и сообщает, что файл имеет 16-битную подпись LE, 44100Hz, стерео, который соответствует тому, что сообщает мое приложение. Запуск этого на Raspberry Pi.

я сократил программу C до минимума здесь, чтобы сэкономить место, но я проверил правильные коды возврата из всех вызовов API. Почему это простое приложение ALSA не производит правильный звук?

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

#define PCM_DEVICE "default"

int main(int argc, char **argv) {

    snd_pcm_t *pcm_handle;
    snd_pcm_hw_params_t *params;
    snd_pcm_uframes_t frames;
    int dir, pcmrc;

    char *infilename = "/home/pi/shortsample.wav";
    int* buf = NULL;
    int readcount;

    SF_INFO sfinfo;
    SNDFILE *infile = NULL;

    infile = sf_open(infilename, SFM_READ, &sfinfo);
    fprintf(stderr,"Channels: %dn", sfinfo.channels);
    fprintf(stderr,"Sample rate: %dn", sfinfo.samplerate);
    fprintf(stderr,"Sections: %dn", sfinfo.sections);
    fprintf(stderr,"Format: %dn", sfinfo.format);

    /* Open the PCM device in playback mode */
    snd_pcm_open(&pcm_handle, PCM_DEVICE, SND_PCM_STREAM_PLAYBACK, 0);

    /* Allocate parameters object and fill it with default values*/
    snd_pcm_hw_params_alloca(&params);
    snd_pcm_hw_params_any(pcm_handle, params);
    /* Set parameters */
    snd_pcm_hw_params_set_access(pcm_handle, params, SND_PCM_ACCESS_RW_INTERLEAVED);
    snd_pcm_hw_params_set_format(pcm_handle, params, SND_PCM_FORMAT_S16_LE);
    snd_pcm_hw_params_set_channels(pcm_handle, params, sfinfo.channels);
    snd_pcm_hw_params_set_rate(pcm_handle, params, sfinfo.samplerate, 0);

    /* Write parameters */
    snd_pcm_hw_params(pcm_handle, params);

    /* Allocate buffer to hold single period */
    snd_pcm_hw_params_get_period_size(params, &frames, &dir);
    fprintf(stderr,"# frames in a period: %dn", frames);

    fprintf(stderr,"Starting read/write loopn");
    buf = malloc(frames * sfinfo.channels * sizeof(int));
    while ((readcount = sf_readf_int(infile, buf, frames))>0) {

        pcmrc = snd_pcm_writei(pcm_handle, buf, readcount);
        if (pcmrc == -EPIPE) {
            fprintf(stderr, "Underrun!n");
            snd_pcm_prepare(pcm_handle);
        }
        else if (pcmrc < 0) {
            fprintf(stderr, "Error writing to PCM device: %sn", snd_strerror(pcmrc));
        }
        else if (pcmrc != readcount) {
            fprintf(stderr,"PCM write difffers from PCM read.n");
        }

    }
    fprintf(stderr,"End read/write loopn");

    snd_pcm_drain(pcm_handle);
    snd_pcm_close(pcm_handle);
    free(buf);

    return 0;
}

1 ответов


вы должны проверьте возвращаемые значения всех snd_ функции, которые могут завершиться неудачей.

на S16_LE формат имеет два байта на образец, но int четыре. Использовать short и sf_readf_short.