用QT使用SDL播放pcm文件

主要程序片断:

#include "playthread.h"
#include <QDebug>
#include <SDL2/SDL.h>
#include <QDebug>
#include <QFile>

#define FILENAME   "../../haha/02_09_21_46_02.pcm"
#define SAMPLE_RATE 44100
#define SAMPLE_SIZE AUDIO_S16LSB
#define CHANNELS 2

#define BUFFER_SIZE 4096    //当缓存区小于音频缓存区时播放会出错,例如此处宏设为2048


PlayThread::PlayThread(QObject *parent):QThread(parent)
{
    connect(this, &PlayThread::finished, this, &PlayThread::deleteLater);       //线程结束后自动释放内存
}

PlayThread::~PlayThread()
{
    disconnect();
    requestInterruption();
    quit();
    wait();
    qDebug()<< this <<"析构" << endl;
}

char *bufferData;
int bufferLen;

//等待音频设备回调(会回调多次);回调函数会在子线程(自己创建的),说明存在两个子线程
void pull_audio_data (void *userdata, Uint8 * stream, int len)  //需要往stream中填充pcm数据;希望填充len这么大(samples*format*channels);本例中为4096个字节
{
    qDebug() << "pull_audio_data" << len << endl;       //len = samples*format*channels

    //清空stream
    SDL_memset(stream, 0,len);
    //文件数据还没准备好
    if(bufferLen <= 0) return;
    //取bufferLen和len的最小值
    len = (len > bufferLen) ? bufferLen : len;  //主要是为了处理最后剩下的一点点数据,两个缓存区大小不一样时也需要判断(本例中是一样的)

    //填充数据
    SDL_MixAudio(stream, (const Uint8*)bufferData, len, SDL_MIX_MAXVOLUME);
    bufferData += len;  //指针向前移
    bufferLen -= len;
}


void PlayThread::run()
{
    //初始化Audio子系统
    if(SDL_Init(SDL_INIT_AUDIO) < 0)
    {
        qDebug()<<"SDL_Init error" << SDL_GetError();
        return;
    }

    //音频参数
    SDL_AudioSpec spec;
    //采样率
    spec.freq = SAMPLE_RATE;
    //通道数
    spec.channels = CHANNELS;
    //采样格式
    spec.format = SAMPLE_SIZE;
    //回调
    spec.callback = pull_audio_data;
    //音频缓冲区的样本数(这个值必须是2的幂)
    spec.samples = 1024;    //缓冲区实际字节数:1024X通道数2X采样格式16/8=4096个字节

    //打开设备
    if(SDL_OpenAudio(&spec, nullptr))
    {
        qDebug()<<"SDL_OpenAudio error" << SDL_GetError();
        //清除所有子系统
        SDL_Quit();
    }

    //打开文件
    QFile file(FILENAME);
    if(!file.open(QFile::ReadOnly))
    {
        qDebug() << "file open error" << endl;
        SDL_CloseAudio();
        //清除所有子系统
        SDL_Quit();
    }

    //开始播放
    SDL_PauseAudio(0);

    //存放从文件中读取的数据
    char data[BUFFER_SIZE];

    while(!isInterruptionRequested())
    {
        bufferLen = file.read(data, BUFFER_SIZE);

        if(bufferLen <=0)   break;

        bufferData = data;

        while (bufferLen > 0)
        {
            //大于零,说明缓存区还有数据, 在此死循环,bufferLen会在子线程中被修改
        }
    }

    file.close();
    //关闭设备
    SDL_CloseAudio();

    //清除所有子系统
    SDL_Quit();
}

代码中数据走向如下图所示:

但是此项目还存在一个问题,就是当数据缓存区小于音频缓存区时播放会出错,例如当数据大小宏设为2048时

[项目源码]项目名为sdl_test 链接:https://pan.baidu.com/s/1gMactd9alFY4PAhhAapY4Q?pwd=8888

posted @ 2023-02-11 23:36  一颗蘋果  阅读(78)  评论(0编辑  收藏  举报