本文共 4312 字,大约阅读时间需要 14 分钟。
在音频编程中,使用ffplay和SDL是实现音频播放的常用方法。以下将详细介绍如何通过这些工具和库来播放PCM音频文件。
s16le:PCM signed 16-bit little-endian。ffplay -ar 44100 -ac 2 -f s16le out.pcm
SDL(Simple DirectMedia Layer)是一款跨平台的多媒体开发库,广泛应用于游戏开发和音视频播放。其音频功能可以帮助开发者高效管理和播放音频内容。
brew install sdl2
安装目录位于 /usr/local/Cellar/sdl2。
#include#include int main(int argc, char *argv) { if (SDL_Init(SDL_INIT_AUDIO) != 0) { printf("SDL_Init Error: %s\n", SDL_GetError()); return -1; } // 初始化音频设备 SDL_AudioSpec spec; spec.freq = 44100; // 采样率 spec.format = AUDIO_S16LSB; // 采样格式 spec.channels = 2; // 声道数 spec.samples = 1024; // 音频缓冲区大小 spec.callback = pull_audio_data; spec.userdata = &buffer; if (SDL_OpenAudio(&spec, NULL) != 0) { printf("SDL_OpenAudio Error: %s\n", SDL_GetError()); SDL_Quit(); return -1; } // 打开文件 FILE *file = fopen("in.pcm", "rb"); if (!file) { printf("无法打开文件: %s\n", "in.pcm"); SDL_CloseAudio(); SDL_Quit(); return -1; } // 开始播放 SDL_PauseAudio(0); // 读取并播放PCM数据 while (!isInterruptionRequested()) { if (buffer.len > 0) continue; buffer.len = fread((char *)data, BUFFER_SIZE, file); if (buffer.len <= 0) { // 计算延迟并等待 int samples = buffer.pullLen / BYTES_PER_SAMPLE; int ms = samples * 1000 / 44100; SDL_Delay(ms); break; } buffer.data = data; } // 释放资源 fclose(file); SDL_CloseAudio(); SDL_Quit(); return 0;}
// 初始化Audio子系统if (SDL_Init(SDL_INIT_AUDIO)) { qDebug() << "SDL_Init Error" << SDL_GetError(); return;} // 音频参数定义#define SAMPLE_RATE 44100#define SAMPLE_FORMAT AUDIO_S16LSB#define SAMPLE_SIZE (AUDIO_S16LSB & 0xff) ? 1 : 2 // 根据采样格式确定样本大小#define CHANNELS 2#define SAMPLES 1024typedef struct { int len; // 已处理的数据长度 int pullLen; // 从文件中读取的数据长度 Uint8 *data; // 存储音频数据的缓冲区} AudioBuffer;// 初始化SDL_AudioSpecSDL_AudioSpec spec;spec.freq = SAMPLE_RATE; // 采样率spec.format = SAMPLE_FORMAT; // 采样格式spec.channels = CHANNELS; // 声道数spec.samples = SAMPLES; // 缓冲区样本数量// 回调函数定义void pull_audio_data(void *userdata, Uint8 *stream, int len) { AudioBuffer *buffer = (AudioBuffer *)userdata; if (buffer->len == 0) return; // 确保读取的数据不超过缓冲区长度 buffer->pullLen = (len > buffer->len) ? buffer->len : len; // 将音频数据混合到缓冲区 SDL_MixAudio(stream, buffer->data, buffer->pullLen, SDL_MIX_MAXVOLUME); // 更新缓冲区指针 buffer->data += buffer->pullLen; buffer->len -= buffer->pullLen;} // 定义文件路径#define FILENAME "in.pcm"// 创建QFile对象并打开文件QFile file(FILENAME);if (!file.open(QFile::ReadOnly)) { qDebug() << "文件打开失败" << FILENAME; // 关闭音频设备 SDL_CloseAudio(); // 退出SDL系统 SDL_Quit(); return;} // 开始播放SDL_PauseAudio(0);// 读取音频数据Uint8 data[BUFFER_LEN];while (!isInterruptionRequested()) { if (buffer.len > 0) continue; buffer.len = file.read((char *)data, BUFFER_SIZE); if (buffer.len <= 0) { // 计算剩余样本的时间延迟 int samples = buffer.pullLen / BYTES_PER_SAMPLE; int ms = samples * 1000 / SAMPLE_RATE; SDL_Delay(ms); break; } buffer.data = data;} // 回调函数填充音频数据void pull_audio_data(void *userdata, Uint8 *stream, int len) { AudioBuffer *buffer = (AudioBuffer *)userdata; if (buffer->len == 0) return; // 确保读取的数据不超过缓冲区长度 buffer->pullLen = (len > buffer->len) ? buffer->len : len; // 将音频数据混合到缓冲区 SDL_MixAudio(stream, buffer->data, buffer->pullLen, SDL_MIX_MAXVOLUME); // 更新缓冲区指针 buffer->data += buffer->pullLen; buffer->len -= buffer->pullLen;} // 关闭文件file.close();// 关闭音频设备SDL_CloseAudio();// 退出SDL系统SDL_Quit();
通过以上步骤,我们成功实现了使用ffplay和SDL来播放PCM音频文件。这个过程涵盖了从初始化音频系统到实际播放的各个环节,确保了音频数据能够正确读取和播放。这是一个基础但重要的技能,对于后续的多媒体开发工作至关重要。
转载地址:http://vxhyz.baihongyu.com/