方案一
使用回调的方式,也就是pull模式
SDL_AudioSpec结构
typedef struct SDL_AudioSpec
{
// 采样率
int freq; /**< DSP frequency -- samples per second */
// 音频数据格式
SDL_AudioFormat format; /**< Audio data format */
// 声道数
Uint8 channels; /**< Number of channels: 1 mono, 2 stereo */
// 音频缓冲区静音值
Uint8 silence; /**< Audio buffer silence value (calculated) */
// 采样帧大小
Uint16 samples; /**< Audio buffer size in sample FRAMES (total samples divided by channel count) */
// 兼容性参数
Uint16 padding; /**< Necessary for some compile environments */
// 音频缓冲区大小
Uint32 size; /**< Audio buffer size in bytes (calculated) */
// 填充音频缓冲区回调函数
SDL_AudioCallback callback; /**< Callback that feeds the audio device (NULL to use SDL_QueueAudio()). */
// 用户自定义数据,
void *userdata; /**< Userdata passed to callback (ignored for NULL callbacks). */
} SDL_AudioSpec;
完整代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "sdl.h"
static Uint8* audio_chunk;
static Uint32 audio_len;
static Uint8* audio_pos;
void fill_audio(void* udata, Uint8* stream, int len) {
//SDL 2.0
SDL_memset(stream, 0, len);
if (audio_len == 0)
return;
len = (len > audio_len ? audio_len : len);
SDL_MixAudio(stream, audio_pos, len, SDL_MIX_MAXVOLUME);
audio_len -= len;
}
int main2(int argc, char* argv[])
{
if (SDL_Init(SDL_INIT_AUDIO || SDL_INIT_TIMER))
{
printf("SDL init error\n");
return -1;
}
//SDL_AudioSpec
SDL_AudioSpec wanted_spec;
wanted_spec.freq = 44100;
wanted_spec.format = AUDIO_S16LSB;
wanted_spec.channels = 2;
wanted_spec.silence = 0;
wanted_spec.samples = 512;
wanted_spec.callback = fill_audio;
if (SDL_OpenAudio(&wanted_spec, NULL) < 0) {
printf("can't open audio.\n");
return -1;
}
FILE* fp = nullptr;
fopen_s(&fp, "D:/工程/音视频分析/source/s16.pcm", "rb+");
if (fp == NULL) {
printf("cannot open this file\n");
return -1;
}
int pcm_buffer_size = 2048;
char* pcm_buffer = (char*)malloc(pcm_buffer_size);
int data_count = 0;
int readcount = 0;
//Play
SDL_PauseAudio(0);
while (!feof(fp))
{
readcount = fread(pcm_buffer, 1, pcm_buffer_size, fp);
printf(" Now Playing %10d KBytes data. %d \n", data_count / 1024, readcount);
data_count += readcount;
//Set audio buffer (PCM data)
audio_chunk = (Uint8*)pcm_buffer;
//Audio buffer length
audio_len = readcount;
audio_pos = audio_chunk;
while (audio_len > 0)//Wait until finish
SDL_Delay(1);
}
free(pcm_buffer);
SDL_Quit();
return 0;
}
注意事项:
每次读取的数据 是 ( 通道数 x 样本数 x 样本个数字节数) 整数倍
例如:通道数为2 ,每次样本数 1024, 样本深度 f32le(float 32 小端)
那么 pcm_buffer_size 至少是 2 x 1024 x 4 = 8192 个字节,或其他整数倍
方案二
主动添加音频数据到播放数据队列中,push模式
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "sdl.h"
int main(int argc, char* argv[])
{
if (SDL_Init(SDL_INIT_AUDIO))
{
printf("SDL init error\n");
return -1;
}
//SDL_AudioSpec
SDL_AudioSpec wanted_spec;
wanted_spec.freq = 44100;
wanted_spec.format = AUDIO_S16LSB;
wanted_spec.channels = 2;
wanted_spec.silence = 0;
wanted_spec.samples = 512;
wanted_spec.callback = nullptr;
SDL_AudioDeviceID deviceID = 0;
// 打开设备
if ((deviceID = SDL_OpenAudioDevice(NULL, 0, &wanted_spec, NULL, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE)) < 2)
{
printf("could not open audio device: %s\n", SDL_GetError());
// 清除所有的子系统
SDL_Quit();
return 0;
}
SDL_PauseAudioDevice(deviceID, 0);
FILE* fp = nullptr;
fopen_s(&fp, "D:/工程/音视频分析/source/s16.pcm", "rb+");
if (fp == NULL) {
printf("cannot open this file\n");
return -1;
}
if (fp == NULL) {
printf("error \n");
}
Uint32 buffer_size = 4096;
char* buffer = (char*)malloc(buffer_size);
while (true)
{
if (fread(buffer, 1, buffer_size, fp) != buffer_size)
{
printf("end of file\n");
break;
}
SDL_QueueAudio(deviceID, buffer, buffer_size);
}
Uint32 residueAudioLen = 0;
/*while (true)
{
residueAudioLen = SDL_GetQueuedAudioSize(deviceID);
printf("%10d\n", residueAudioLen);
if (residueAudioLen <= 0)
break;
SDL_Delay(1);
}*/
while (true)
{
printf("1 暂停 2 继续 3 退出 \n");
int flag = 0;
scanf_s("%d", &flag);
if (flag == 1)
SDL_PauseAudioDevice(deviceID, 1);
else if (flag == 2)
SDL_PauseAudioDevice(deviceID, 0);
else if(flag == 3)
break;
}
SDL_CloseAudio();
SDL_Quit();
fclose(fp);
return 0;