2

Qt の C コードで alsa ライブラリ呼び出しを使用して、RHEL6 で wave ファイルを再生しようとしています。バッファー (wave_buffer) 内の wave ファイル ("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) 完全なオーディオ品質が得られますが、中断されます (再生に次のデータのチャンクが必要な場合)。私の制約は、ファイルまたはバッファー全体としてではなく、チャンクでのみデータにアクセスできることです。途切れることなくオーディオを再生できるように、Wave データの再生中の中断を取り除くのを手伝ってくれる人はいますか? 以下は私のコードです: 2 つの変数 buffer_time & period_time は、チャンクのサイズである期間サイズを返します。buffer_time = 5000 & period_time=1000 の場合、alsa ライブラリによって返される period_size は 32 バイトです //オーディオ品質は低下しますが、中断はありませんこれを行うのに多くの時間を無駄にしたため、これらのパラメーターは役に立たないようです。

Wave ファイルの構造 : サンプル レート : 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;
}

実行時に正弦波サンプルを生成し、同じ snd_pcm_writei コマンドを使用してそれらを再生する alsa ライブラリの同様の C コードがあり、中断することなく完全に再生されます....これは 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