共计 6530 个字符,预计需要花费 17 分钟才能阅读完成。
若该文为原创文章,未经容许不得转载
原博主博客地址:https://blog.csdn.net/qq21497936
原博主博客导航:https://blog.csdn.net/qq21497936/article/details/102478062
本文章博客地址:https://blog.csdn.net/qq21497936/article/details/108596396
红瘦子 (红模拟) 的博文大全:开发技术汇合(蕴含 Qt 实用技术、树莓派、三维、OpenCV、OpenGL、ffmpeg、OSG、单片机、软硬联合等等)继续更新中…(点击传送门)
Qt 开发专栏:三方库开发技术(点击传送门)
上一篇:《SDL 开发笔记 (一):SDL 介绍、编译应用以及工程模板》
下一篇:敬请期待
前言
对于 Qt 利用来说,为了更大的跨平台通用性,应用 SDL 播放音频,同时也能做更多的裁减操作。
声波
声音是通过空气流传的一种间断的波,简称声波。声音的强弱体现在声波压力的大小上,音调的音调体现在声音的频率上。
声音信号由两个基本参数是频率和复读。信号的频率指的是信号每秒变动的次数,用 Hz 示意。
频率范畴为 20Hz~20Khz 的信号成为音频信号。该范畴内的音频声音幅度在 0~120dB 之间,可被人感知到。
声音转换为数字信号,则成为音频信号。
音频信号
音频信号(acoustic signals)是带有语音、音乐和音效的有法则的声波的频率、幅度变动信息载体。依据声波的特色,可把音频信息分类为规定音频和不规则声音。其中规定音频又能够分为语音、音乐和音效。规定音频是一种间断变动的模拟信号,可用一条间断的曲线来示意,称为声波。
声音的三个因素是音调、音强和音色。声波或正弦波有三个重要参数:频率 ω0、幅度 An 和相位 ψn,这也就决定了音频信号的特色。
对音频信号进行采样,模拟信号数字化后,就是数字音频信号了。
数字音频信号
数字音频计算机数据的存储是以 0、1 的模式存取的,那么数字音频就是首先将音频文件转化,接着再将这些电平信号转化成二进制数据保留,播放的时候就把这些数据转换为模仿的电平信号再送到喇叭播出,数字声音和个别磁带、播送、电视中的声音就存储播放形式而言有着本质区别。相比而言,它具备存储不便、存储老本低廉、存储和传输的过程中没有声音的失真、编辑和解决十分不便等特点。
数字音频信号,就是咱们最终解决的音频数据。
音频数字信号信号具备几个特色:
量化级
简略地说就是形容声音波形的数据是多少位的二进制数据,通常用 bit 做单位,如 16bit、24bit。16bit 量化级记录声音的数据是用 16 位的二进制数,因而,量化级也是数字声音品质的重要指标。咱们形容数字声音的品质,通常就形容为 24bit(量化级)、48KHz 采样,比方规范 CD 音乐的品质就是 16bit、44.1KHz 采样。
声道
能够简略的了解为通过一个振膜采样到的音频数据就是一个声道,两个振膜就是两个声道,以此类推。振膜个别有大、中、小三种尺寸,尺寸越大,对声波越敏感,老本也越高。一个麦克风外面有的有一个振膜,有的有两个振膜。一个振膜的麦克风进行的是 Mono 单声道录音,两个振膜的麦克风进行的是 Stereo 双声道立体声录音。五声道盘绕立体声录音就是麦克风 1 录取东北方向的声音,麦克风 2 录取东南方向的声音,麦克风 3 录取东北方向的声音,麦克风 4 录取西北方向的声音,麦克风 5 录取正前方的声音。另外还有四声道盘绕立体声录音和七声道盘绕立体声录音。
采样率
简略地说就是通过波形采样的办法记录 1 秒钟长度的声音,须要多少个数据。44KHz 采样率的声音就是要花费 44000 个数据来形容 1 秒钟的声音波形。原则上采样率越高,声音的品质越好。
比特率
一种数字音乐压缩效率的参考性指标,示意记录音频数据每秒钟所须要的均匀比特值(比特是电脑中最小的数据单位,指一个 0 或者 1 的数),通常咱们应用 Kbps(艰深地讲就是每秒钟 1024 比特)作为单位。CD 中的数字音乐比特率为 1411.2Kbps(也就是记录 1 秒钟的 CD 音乐,须要 1411.2×1024 比特的数据),近乎于 CD 音质的 MP3 数字音乐须要的比特率大概是 112Kbps~128Kbps。
压缩率
通常指音乐文件压缩前和压缩后大小的比值,用来简略形容数字声音的压缩效率。
SDL 音频播放流程解析
根本流程如下:
步骤一:初始化子系统
初始化音频系统,其余多余的零碎不必初始化。
步骤二:依据音频信息关上音频设备
填充好 SDL_AudioSpec 音频信息,关上音频设备,此时会返回最靠近的音频设备,若没有靠近的则第二个参数返回 0,此时咱们间接第二个参数如 0,无需返回。
步骤三:开始播放
应用 SDL_PauseAudio(0)进行播放。
步骤四:循环补充数据
依据缓冲区数据长度和文件残余的数据长度进行补充,若缓冲区数据没了,就补充一次,应用 SDL_Delay 进行 1ms 的提早,用以后缓存区残余未播放的长度大于 0 联合后面的提早进行期待。
步骤四(附加):回调函数
开始播放后,会有音频其余子线程来调用回调函数,进行音频数据的补充,通过测试每次补充 4096 个字节。
步骤五:敞开音频设别
步骤六:退出 SDL 零碎
SDL 播放音频相干变量
struct SDL_AudioSpec
SDL_AudioSpec 是蕴含音频输入格局的构造体,同时它也蕴含当音频设备须要更多数据时调用的回调函数,此构造体是要害。
typedef struct SDL_AudioSpec
{
int freq; // DSP 频率—每秒采样数
SDL_AudioFormat format; // 音频数据格式
Uint8 channels; // 通道数 1 - 单声道,2- 立体声
Uint8 silence; // 音频缓冲静音值(计算)Uint16 samples; // 根本是 512、1024 设置不适合可能会导致卡顿’Uint16 padding; // 对于某些编译环境是必须的
Uint32 size; // 音频缓冲区大小(字节)(计算)SDL_AudioCallback callback; // 为音频设备提供数据回调 (空值应用 SDL 本身事后定义的 SDL_QueueAudio () 回调函数)
void *userdata; // 传递给回调的 Userdata(对于空回调疏忽)} SDL_AudioSpec;
举例:播放 pcm 音频“匆匆那年 -44100-16 位 - 双通道.pcm”
// 音频构造体设置
SDL_AudioSpec sdlAudioSpec;
sdlAudioSpec.freq = 44100;
sdlAudioSpec.format = AUDIO_S16SYS;
sdlAudioSpec.channels = 1;
sdlAudioSpec.silence = 0;
sdlAudioSpec.samples = 1024;
sdlAudioSpec.callback = callBack_fillAudioData;
sdlAudioSpec.userdata = 0;
SDL 播放音频相干原型
SDL_Init()
int SDLCALL SDL_Init(Uint32 flags);
应用此函数初始化 SDL 库,必须在应用大多数其余 SDL 函数之前调用它,初始化的时候尽量做到“够用就好”,而不要用 SDL_INIT_EVERYTHING。会呈现一些不可预知的问题。
- 参数一:输出初始化的设施
SDL_OpenAudio()
int SDL_OpenAudio(SDL_AudioSpec * desired,
SDL_AudioSpec * obtained);
此函数应用所需参数关上音频设备,而后如果胜利,则返回 0,将理论硬件参数放入已取得指向的构造。如果取得的为空,则音频传递给回调函数的数据将被保障在申请的格局,并将主动转换为硬件音频格式(如有必要)。如果失败,此函数返回 -1,则无奈关上音频设备,或无奈设置音频线程。
- 参数一:输出须要关上的音频设备参数;
- 参数二:返回关上胜利的音频设备参数;
SDL_PauseAudio()
extern DECLSPEC void SDLCALL SDL_PauseAudio(int pause_on);
暂停音频性能。函数暂停和勾销暂停音频回调解决。
关上音频后,应应用参数 0 调用它们开始播放声音的设施。这样就能够在关上音频设备后平安地初始化回调函数的数据。
暂停期间,静音将写入音频设备。
SDL_MixAudio:混音播放函数
void SDL_MixAudio(Uint8 * dst,
const Uint8 * src,
Uint32 len,
int volume);
这须要播放音频格式和混音的两个音频缓冲区它们执行加法、音量调节和溢出剪辑。音量的范畴从 0 到 128,应设置为 SDL_MIX_MAXVOLUME 全音频音量。留神这不会扭转硬件的音量。
这是为了不便起见,能够混合音频数据。
- 参数一:指标数据,这个是回调函数外面的 stream 指针指向的,间接应用回调的 stream 指针即可。
- 参数二:音频数据,这个是将须要播放的音频数据混到 stream 外面去,那么这里就是咱们须要填充的播放的数据。
- 参数三:音频数据的长度,这个是咱们填充过来的长度。
- 参数四:音量,0~128 范畴,SAL_MIX_MAXVOLUME 为 128,设置的是软音量,不是硬件的音响。
SDL_Delay()
void SDL_Delay(Uint32 ms);
在返回之前期待指定的毫秒数。
SDL_Quit()
void SDLCALL SDL_Quit(void);
此函数用于革除所有初始化的子系统。在所有退出条件后调用它。
Demo 源码
void SDLManager::testPlayPCM()
{
int ret = 0;
// 音频构造体
SDL_AudioSpec sdlAudioSpec;
// sdlAudioSpec.freq = 44100;
sdlAudioSpec.freq = 22050;
// sdlAudioSpec.format = AUDIO_U8; // x
// sdlAudioSpec.format = AUDIO_S8; // x
// sdlAudioSpec.format = AUDIO_U16LSB; // x
// sdlAudioSpec.format = AUDIO_S16LSB; // √
// sdlAudioSpec.format = AUDIO_U16MSB; // x
// sdlAudioSpec.format = AUDIO_U16LSB; // x
// sdlAudioSpec.format = AUDIO_S16MSB; // x
// sdlAudioSpec.format = AUDIO_U16; // x
sdlAudioSpec.format = AUDIO_S16; // √
// sdlAudioSpec.format = AUDIO_S16SYS; // x
// sdlAudioSpec.format = AUDIO_S32SYS; // x
// sdlAudioSpec.format = AUDIO_F32SYS; // x
// sdlAudioSpec.format = AUDIO_F32MSB; // x
sdlAudioSpec.channels = 1;
sdlAudioSpec.silence = 0;
sdlAudioSpec.samples = 1024; // 导致谬误 512~1024 之间
sdlAudioSpec.callback = callBack_fillAudioData;
sdlAudioSpec.userdata = 0;
QString fileName;
#if 0
fileName = "testPCM/ 王妃 -22050-16 位 - 单通道.pcm";
sdlAudioSpec.freq = 22050;
sdlAudioSpec.channels = 1;
sdlAudioSpec.format = AUDIO_S16;
#endif
#if 1
fileName = "testPCM/ 匆匆那年 -44100-16 位 - 双通道.pcm";
sdlAudioSpec.freq = 44100;
sdlAudioSpec.channels = 2;
sdlAudioSpec.format = AUDIO_S16;
#endif
#if 0
fileName = "testPCM/ 北京北京 8k16bits 单声道.pcm";
sdlAudioSpec.freq = 8000;
sdlAudioSpec.channels = 1;
sdlAudioSpec.format = AUDIO_S16;
#endif
#if 0
fileName = "testPCM/ 冰雨片段 48k16bit 单声道.pcm";
sdlAudioSpec.freq = 48000;
sdlAudioSpec.channels = 1;
sdlAudioSpec.format = AUDIO_S16;
#endif
#if 0
fileName = "testPCM/ 浪花一朵朵片段 48k16bit 单声道.pcm";
sdlAudioSpec.freq = 48000;
sdlAudioSpec.channels = 1;
sdlAudioSpec.format = AUDIO_S16;
#endif
QFile file(fileName);
if(!file.open(QIODevice::ReadOnly))
{LOG << "Failed" << file.exists();
return;
}
// 步骤一:初始化音频子系统
ret = SDL_Init(SDL_INIT_AUDIO);
if(ret)
{
LOG << "Failed";
return;
}
// 步骤二:关上音频设备
ret = SDL_OpenAudio(&sdlAudioSpec, 0);
if(ret)
{
LOG << "Failed";
return;
}
// 步骤三:开始播放
SDL_PauseAudio(0);
#if 1
// 步骤四:一次性读取所有的数据
QByteArray data = file.readAll();
int pos = 0;
_audioPos = (uint8_t *)data.data();
_audioLen = data.size();
pos += data.size();
while(_audioLen > 0)
{SDL_Delay(1);
}
#else
// 步骤四:一次性读取 4096
int readSize = 4096;
while(true)
{_audioPos = (uint8_t *)file.read(readSize).data();
_audioLen = readSize;
while(_audioLen > 0)
{SDL_Delay(1);
}
}
#endif
// 步骤:播放结束
SDL_CloseAudio();
// 步骤:开释 SDL
SDL_Quit();
if(file.isOpen())
{file.close();
return;
}
}
void SDLManager::callBack_fillAudioData(void *userdata, uint8_t *stream, int len)
{SDL_memset(stream, 0, len);
if(_audioLen == 0)
{return;}
len = (len > _audioLen ? _audioLen : len);
SDL_MixAudio(stream, _audioPos, len, SDL_MIX_MAXVOLUME);
_audioPos += len;
_audioLen -= len;
// 每次加载 4096
LOG << len;
}
工程模板:对应版本号 v1.1.0
对应版本号 v1.1.0:播放裸 PCM 数据。
上一篇:《SDL 开发笔记 (一):SDL 介绍、编译应用以及工程模板》
下一篇:敬请期待
原博主博客地址:https://blog.csdn.net/qq21497936
原博主博客导航:https://blog.csdn.net/qq21497936/article/details/102478062
本文章博客地址:https://blog.csdn.net/qq21497936/article/details/108596396