2

我正在尝试在 Qt 的 C 代码中使用 alsa 库调用在 RHEL6 中播放波形文件。我正在读取缓冲区(wave_buffer)中的波形文件(“t15.wav”)。由于 alsa 库需要播放原始 PCM 样本,因此已去除波头。此外,我还使用 'snd_pcm_hw_params(PCM, params)' & 'snd_pcm_sw_params_current(PCM, swparams)' 和许多其他调用设置了 PCM 硬件和软件参数。我正在使用“snd_pcm_writei”命令在 PCM 句柄上编写 PCM 样本。为此,我正在从 wave_buffer 读取数据块(32 或 1024 或 2048 或 4096 或 8192 字节),并使用 snd_pcm_writei 命令将其发送以进行播放。如果我选择一小块音频质量会下降,但播放不会中断。如果我使用更大的块(大于 4096 即 8192)我获得了完美的音频质量,但它被中断(当播放需要下一个数据块时)。我的限制是我只能访问块中的数据,而不是文件或整个缓冲区。任何人都可以帮助我消除播放波形数据的中断,以便我可以不间断地播放音频。以下是我的代码: 两个变量 buffer_time 和 period_time 返回周期大小,即块的大小。if buffer_time = 5000 & period_time=1000 alsa 库返回的 period_size 为 32 字节 //音频质量下降但没有中断 如果 buffer_time = 500000 & period_time=100000 alsa 库返回的 period_size 为 8192 字节 //音频质量好但中断了 Tuning这些参数似乎没用,因为我在这方面浪费了很多时间。

波形文件结构:采样率:44100 位/采样:16 通道:2

mainwindow.h----

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <alsa/asoundlib.h>
#define BLOCKSIZE 44100 * 2 * 2 // Sample Rate * Channels * Byte per Sample(Bits per sample / 8)
namespace Ui {
    class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    int init_alsa();
    int play_snd();
    ~MainWindow();
    snd_pcm_t *PCM;
    snd_pcm_sframes_t delayp;
    snd_pcm_sframes_t availp;
    snd_pcm_sw_params_t *swparams;
    snd_pcm_hw_params_t *params;
    static snd_pcm_sframes_t period_size;
    static snd_pcm_sframes_t buffer_size;
    unsigned char wave_buffer[900000];
    unsigned char play_buffer[BLOCKSIZE];
    int filesize;
    FILE *fp;

private:
    Ui::MainWindow *ui;
};


#endif // MAINWINDOW_H


mainwindow.cpp---

#include "mainwindow.h"
#include "ui_mainwindow.h"

snd_pcm_sframes_t MainWindow::period_size;
snd_pcm_sframes_t MainWindow::buffer_size;
MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    if((fp = fopen("t15.wav","rb"))==NULL)
        printf("Error Opening File");
    fseek(fp,0L,SEEK_END);
    filesize = ftell(fp)-44;
    fseek(fp,0L,SEEK_SET);
    fseek(fp,44,SEEK_SET);
    fread(wave_buffer,filesize,1,fp);
    fclose(fp);
    delayp = 0;
    init_alsa();
    play_snd();
}


MainWindow::~MainWindow()
{
    delete ui;
}


int MainWindow::init_alsa()
{
    unsigned int rate = 44100;
    int err,dir;

    unsigned int rrate  = 44100;
    snd_pcm_uframes_t size;
    static unsigned int buffer_time = 500000;
    static unsigned int period_time = 100000;

    static int period_event = 0;

    if ((err=snd_pcm_open(&PCM,"plughw:0,0",SND_PCM_STREAM_PLAYBACK, 0)) < 0)
    {
        fprintf(stderr, "Can't use sound: %s\n", snd_strerror(err));
        return err;
    }


            snd_pcm_hw_params_alloca(&params);
            snd_pcm_sw_params_alloca(&swparams);
            //snd_pcm_nonblock(PCM,0);
        /* choose all parameters */
        err = snd_pcm_hw_params_any(PCM, params);
        if (err < 0) {
                printf("Broken configuration for playback: no configurations available: %s\n", snd_strerror(err));
                return err;
        }
        /* set hardware resampling */
        err = snd_pcm_hw_params_set_rate_resample(PCM, params, 1);
        if (err < 0) {
                printf("Resampling setup failed for playback: %s\n", snd_strerror(err));
                return err;
        }
        /* set the interleaved read/write format */
        err = snd_pcm_hw_params_set_access(PCM, params, SND_PCM_ACCESS_RW_INTERLEAVED);
        if (err < 0) {
                printf("Access type not available for playback: %s\n", snd_strerror(err));
                return err;
        }
        /* set the sample format */
        err = snd_pcm_hw_params_set_format(PCM, params, SND_PCM_FORMAT_S16_LE);
        if (err < 0) {
                printf("Sample format not available for playback: %s\n", snd_strerror(err));
                return err;
        }
        /* set the count of channels */
        err = snd_pcm_hw_params_set_channels(PCM, params, 2);
        if (err < 0) {
                printf("Channels count (%i) not available for playbacks: %s\n", 2, snd_strerror(err));
                return err;
        }
        /* set the stream rate */
        rrate = rate;
        err = snd_pcm_hw_params_set_rate_near(PCM, params, &rrate, 0);
        if (err < 0) {
                printf("Rate %iHz not available for playback: %s\n", 44100, snd_strerror(err));
                return err;
        }
        if (rrate != 44100) {
                printf("Rate doesn't match (requested %iHz, get %iHz)\n", rrate, err);
                return -EINVAL;
        }
        /* set the buffer time */
        err = snd_pcm_hw_params_set_buffer_time_near(PCM, params, &buffer_time, &dir);
        if (err < 0) {
                printf("Unable to set buffer time %i for playback: %s\n", buffer_time, snd_strerror(err));
                return err;
        }
        err = snd_pcm_hw_params_get_buffer_size(params, &size);
        if (err < 0) {
                printf("Unable to get buffer size for playback: %s\n", snd_strerror(err));
                return err;
        }
        buffer_size = size;

        /* set the period time */
        err = snd_pcm_hw_params_set_period_time_near(PCM, params, &period_time, &dir);
        if (err < 0) {
                printf("Unable to set period time %i for playback: %s\n", period_time, snd_strerror(err));
                return err;
        }
        err = snd_pcm_hw_params_get_period_size(params, &size, &dir);
        if (err < 0) {
                printf("Unable to get period size for playback: %s\n", snd_strerror(err));
                return err;
        }
        period_size = size;
        /* write the parameters to device */
        err = snd_pcm_hw_params(PCM, params);
        if (err < 0) {
                printf("Unable to set hw params for playback: %s\n", snd_strerror(err));
                return err;
        }
        printf("Size = %ld",period_size);

        snd_pcm_sw_params_current(PCM, swparams);                /* get the current swparams */

                                    /* start the transfer when the buffer is almost full: */
                                    /* (buffer_size / avail_min) * avail_min */
        snd_pcm_sw_params_set_start_threshold(PCM, swparams, (buffer_size / period_size) * period_size);

                                    /* allow the transfer when at least period_size samples can be processed */
                                    /* or disable this mechanism when period event is enabled (aka interrupt like style processing) */
        snd_pcm_sw_params_set_avail_min(PCM, swparams, period_event ? buffer_size : period_size);
        snd_pcm_sw_params(PCM, swparams);/* write the parameters to the playback device */
        return 1;
}

int MainWindow::play_snd()
{
int curr_pos = 0;
int buff_size = 0;
long val = 0;

while(curr_pos < filesize)
{
    if(filesize-curr_pos >= period_size)
    {
        memcpy(play_buffer,wave_buffer+curr_pos,period_size);

        buff_size = period_size;
        curr_pos += buff_size;
    }
    else
    {
        memcpy(play_buffer,wave_buffer+curr_pos,filesize-curr_pos);

        buff_size = filesize - curr_pos;
        curr_pos += buff_size;
    }

    int i=1;
    unsigned char *ptr = play_buffer;
    while(buff_size > 0)
    {
        val = snd_pcm_writei(PCM,&play_buffer,buff_size);
        if (val == -EAGAIN)
           continue;
        ptr += val * 2;
        buff_size -= val;
    }
}
return 0;
}

我有一个类似的 alsa 库的 C 代码,它在运行时生成正弦波样本并使用相同的 snd_pcm_writei 命令播放它们,它可以完美播放而没有任何中断......这是 alsa 库代码---

/*
 *  This small demo sends a simple sinusoidal wave to your speakers.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sched.h>
#include <errno.h>
#include <getopt.h>
#include "alsa/asoundlib.h"
#include <sys/time.h>
#include <math.h>

static char *device = "plughw:0,0";                     /* playback device */
static snd_pcm_format_t format = SND_PCM_FORMAT_S16_LE; /* sample format */
static unsigned int rate = 44100;                       /* stream rate */
static unsigned int channels = 2;                       /* count of channels */
static unsigned int buffer_time = 5000;               /* ring buffer length in us */
static unsigned int period_time = 1000;               /* period time in us */
static double freq = 440;                               /* sinusoidal wave frequency in Hz */
static int resample = 1;                                /* enable alsa-lib resampling */
static int period_event = 0;                            /* produce poll event after each period */

static snd_pcm_sframes_t buffer_size;
static snd_pcm_sframes_t period_size;
static snd_output_t *output = NULL;
snd_pcm_sframes_t delayp;
snd_pcm_sframes_t availp;

static void generate_sine(const snd_pcm_channel_area_t *areas, 
                          snd_pcm_uframes_t offset,
                          int count, double *_phase)
{
        static double max_phase = 2. * M_PI;
        double phase = *_phase;
        double step = max_phase*freq/(double)rate;
        unsigned char *samples[channels];
        int steps[channels];
        unsigned int chn;
        int format_bits = snd_pcm_format_width(format);
        unsigned int maxval = (1 << (format_bits - 1)) - 1;
        int bps = format_bits / 8;                              /* bytes per sample */
        int phys_bps = snd_pcm_format_physical_width(format) / 8;
        int big_endian = snd_pcm_format_big_endian(format) == 1;
        int to_unsigned = snd_pcm_format_unsigned(format) == 1;
        int is_float = (format == SND_PCM_FORMAT_FLOAT_LE ||
                        format == SND_PCM_FORMAT_FLOAT_BE);

                                                                /* verify and prepare the contents of areas */
        for (chn = 0; chn < channels; chn++) {
                samples[chn] = /*(signed short *)*/(((unsigned char *)areas[chn].addr) + (areas[chn].first / 8));
                steps[chn] = areas[chn].step / 8;
                samples[chn] += offset * steps[chn];
        }
                                                                 /* fill the channel areas */
        while (count-- > 0) {
                union {
                        float f;
                        int i;
                      } fval;
                int res, i;
                if (is_float)
                {
                        fval.f = sin(phase) * maxval;
                        res = fval.i;
                }
                else
                    res = sin(phase) * maxval;
                if (to_unsigned)
                        res ^= 1U << (format_bits - 1);
                for (chn = 0; chn < channels; chn++) {
                                                                  /* Generate data in native endian format */
                        if (big_endian) {
                                for (i = 0; i < bps; i++)
                                        *(samples[chn] + phys_bps - 1 - i) = (res >> i * 8) & 0xff;
                        } else {
                                for (i = 0; i < bps; i++)
                                        *(samples[chn] + i) = (res >>  i * 8) & 0xff;
                        }
                        samples[chn] += steps[chn];
                }
                phase += step;
                if (phase >= max_phase)
                        phase -= max_phase;
        }
        *_phase = phase;
}

static int set_hwparams(snd_pcm_t *handle, snd_pcm_hw_params_t *params, snd_pcm_access_t access)
{
        unsigned int rrate;
        snd_pcm_uframes_t size;
        int dir;
        snd_pcm_hw_params_any(handle, params);                        /* choose all parameters */
        snd_pcm_hw_params_set_rate_resample(handle, params, resample);/* set hardware resampling */
        snd_pcm_hw_params_set_access(handle, params, access);         /* set the interleaved read/write format */
        snd_pcm_hw_params_set_format(handle, params, format);         /* set the sample format */
        snd_pcm_hw_params_set_channels(handle, params, channels);     /* set the count of channels */
        rrate = rate;                                                 /* set the stream rate */
        snd_pcm_hw_params_set_rate_near(handle, params, &rrate, 0);
        snd_pcm_hw_params_set_buffer_time_near(handle, params, &buffer_time, &dir);/* set the buffer time */
        snd_pcm_hw_params_get_buffer_size(params, &size);
        buffer_size = size;
        snd_pcm_hw_params_set_period_time_near(handle, params, &period_time, &dir);/* set the period time */
        snd_pcm_hw_params_get_period_size(params, &size, &dir);
        period_size = size;
        snd_pcm_hw_params(handle, params);                            /* write the parameters to device */
        return 0;
}

static int set_swparams(snd_pcm_t *handle, snd_pcm_sw_params_t *swparams)
{
        snd_pcm_sw_params_current(handle, swparams);                /* get the current swparams */

                                            /* start the transfer when the buffer is almost full: */
                                            /* (buffer_size / avail_min) * avail_min */
        snd_pcm_sw_params_set_start_threshold(handle, swparams, (buffer_size / period_size) * period_size);

                                            /* allow the transfer when at least period_size samples can be processed */
                                            /* or disable this mechanism when period event is enabled (aka interrupt like style processing) */
        snd_pcm_sw_params_set_avail_min(handle, swparams, period_event ? buffer_size : period_size);
        snd_pcm_sw_params(handle, swparams);/* write the parameters to the playback device */
        return 0;
}


/*
 *   Transfer method - write only
 */

static int write_loop(snd_pcm_t *handle, signed short *samples, snd_pcm_channel_area_t *areas)
{
        double phase = 0;
        signed short *ptr;
        int err, cptr;
        int i=0;
        printf("Period Size = %ld",period_size);
        while (1) {
        fflush(stdout);
                generate_sine(areas, 0, period_size, &phase);
                ptr = samples;
                cptr = period_size;
            i=1;
                while (cptr > 0) {

                    err = snd_pcm_writei(handle, ptr, cptr);
                snd_pcm_avail_delay(handle,&availp,&delayp);
               printf("available frames =%ld  delay = %ld  i = %d\n",availp,delayp,i);
                        if (err == -EAGAIN)
                                continue;
                        ptr += err * channels;
                        cptr -= err;
            i++;
                }
        }
}


/*
 *   Transfer method - asynchronous notification
 */

int main()
{
        snd_pcm_t *handle;
        snd_pcm_hw_params_t *hwparams;
        snd_pcm_sw_params_t *swparams;
        signed short *samples;
        unsigned int chn;
        snd_pcm_channel_area_t *areas;

        snd_pcm_hw_params_alloca(&hwparams);
        snd_pcm_sw_params_alloca(&swparams);

        snd_output_stdio_attach(&output, stdout, 0);

        printf("Playback device is %s\n", device);
        printf("Stream parameters are %iHz, %s, %i channels\n", rate, snd_pcm_format_name(format), channels);
        printf("Sine wave rate is %.4fHz\n", freq);

        snd_pcm_open(&handle, device, SND_PCM_STREAM_PLAYBACK, 0);
        set_hwparams(handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED);
        set_swparams(handle, swparams);
        samples = malloc((period_size * channels * snd_pcm_format_physical_width(format)) / 8);
        areas = calloc(channels, sizeof(snd_pcm_channel_area_t));

        for (chn = 0; chn < channels; chn++) {
                areas[chn].addr = samples;
                areas[chn].first = chn * snd_pcm_format_physical_width(format);
                areas[chn].step = channels * snd_pcm_format_physical_width(format);
        }

        write_loop(handle, samples, areas);
        free(areas);
        free(samples);
        snd_pcm_close(handle);
        return 0;
}
4

1 回答 1

0

我通过改变 snd_pcm_writei 的长度参数解决了这个问题......以前我给它的数据等于 play_buffer 中包含的数据......现在我把它改成了“buff_size/4”,音频播放完美而没有中断。实际上,根据我的理解,它是系统应该开始缓冲新 pcm 样本的大小。以前它在播放整个长度 buff_size 后进行缓冲,导致音频输出中断......

于 2013-10-17T10:10:22.027 回答