1. 播放原理
- 设置音频选项,如采样率,通道数等,设置回调函数
- 当我们播放音频数据时,
SDL
会不断调用这个回调函数,并要求它填充一定数量的字节的音频数据到音频缓冲区 - 当调用
SDL_OpenAudio
时,将打开音频设备并将其返回给另一个AudioSpec
结构体
2. 流程
- 初始化
- 初始化
SDL
- 根据参数(
SDL_AudioSpec
)打开音频设施
- 初始化
- 循环播放数据
- 播放音频数据
- 延时等待播放完成
3. API
-
打开音频设施
int SDLCALL SDL_OpenAudio(SDL_AudioSpec * desired, SDL_AudioSpec * obtained); // desired:期望的参数。 // obtained:实际音频设备的参数,一般情况下设置为NULL即可。
-
SDL_AudioSpec
结构体//在这个结构体中包含了音频的各种参数 typedef struct SDL_AudioSpec { int freq; // 音频采样率 SDL_AudioFormat format; // 音频数据格式 Uint8 channels; // 声道数: 1 单声道, 2 立体声 Uint8 silence; // 设置静音的值 Uint16 samples; // 音频缓冲区中的采样个数,如441000 Uint16 padding; // 考虑到兼容性的一个参数 Uint32 size; // 音频缓冲区的大小,以字节为单位 SDL_AudioCallback callback; // 填充音频缓冲区的回调函数 void *userdata; // 用户自定义的数据 } SDL_AudioSpec; format:音频数据的格式。举例几种格式: AUDIO_U16SYS:Unsigned 16-bit samples AUDIO_S16SYS:Signed 16-bit samples AUDIO_S32SYS:32-bit integer samples AUDIO_F32SYS:32-bit floating point samples
-
SDL_AudioCallback
向音频设备写入数据void (SDLCALL * SDL_AudioCallback) (void *userdata, Uint8 *stream, int len); userdata 用户自定义数据 stream 指向要填充的音频缓冲区 len 音频缓冲区大小
-
SDL_PauseAudio
播放音频数据void SDLCALL SDL_PauseAudio(int pause_on) pause_on: 0-可以开始播放音频数据,1-静音
4. 完整示例
#include "SDLHeader.h"
#include <algorithm>
#include <fstream>
#include <string>
#include <iostream>
Uint8* audio_chunk;
Uint32 audio_len;
Uint8* audio_pos;
const static uint32_t PCM_BUFFER_SIZE = 4096;
//ffplay.exe -ar 44100 -ac 2 -f s16le -i NocturneNo2inEflat_44.1k_s16le.pcm
const static std::string PCM_PATH = "D:\\vod\\testvideo\\NocturneNo2inEflat_44.1k_s16le.pcm";
//数据到来的回调函数
void read_audio_data_cb(void* udata, Uint8* stream, int len)
{
SDL_memset(stream, 0, len);
if (audio_len == 0)
return;
len = std::min(len, static_cast<int>(audio_len));
SDL_MixAudio(stream, audio_pos, len, SDL_MIX_MAXVOLUME);
audio_pos += len;
audio_len -= len;
}
int main(int argc, char** argv)
{
SDL_Init(SDL_INIT_AUDIO); //init
SDL_AudioSpec spec;
spec.freq = 44100;
spec.format = AUDIO_S16SYS;
spec.channels = 2;
spec.samples = 1024;
spec.callback = read_audio_data_cb;
spec.userdata = NULL;
//根据参数,打开音频设备
if (SDL_OpenAudio(&spec, NULL) < 0)
return -1;
//打开文件
std::ifstream pcm(PCM_PATH.c_str(), std::ios::in | std::ios::binary);
if (!pcm.is_open())
return -1;
char* pcm_buffer = (char*)malloc(PCM_BUFFER_SIZE);
//开始播放
SDL_PauseAudio(0);
while (!pcm.eof())
{
pcm.read(pcm_buffer, PCM_BUFFER_SIZE);
if (pcm.bad())
break;
audio_chunk = reinterpret_cast<Uint8*>(pcm_buffer);
audio_len = pcm.gcount(); //读取到的字节数
audio_pos = audio_chunk;
std::cout << "play " << audio_len << " data" << std::endl;
while (audio_len > 0) //等待audio_len长度的数据播放完成
SDL_Delay(1);
}
free(pcm_buffer);
SDL_Quit();
return 0;
}