考虑到跨平台,在Linux上播放音频我们一般都会使用SDL库来进行开发。这里就介绍一下如何在Linux下使用SDL库来进行音频应用的开发。
编译SDL
在编译SDL库之前先安装对应的依赖库,否则在使用SDL应用打开音频设备的时候会报no such audio device的错误。安装命令如下:
sudo apt-get install libasound2-dev libpulse-dev
这里最好使用Ubuntu 16.04来进行开发,在使用一些国产操作系统的时候会报源找不到的错误不知道为啥。
SDL库源码文件的下载地址如下:
SDL下载地址
SDL编译命令如下:
#解压代码包
tar xvf SDL2-2.0.14.tar.gz
#安装修复
sudo apt --fix-broken install
#进入源码目录
cd SDL2-2.0.14/
#指定安装目录
./configure --prefix=$PWD/_install
#清理
make clean
#安装
make && make install
安装完成之后在对应的目录下就会有相应的库文件和头文件了
项目集成SDL
将编译好的SDL库拷贝到对应的工程目录下进行使用,这里以一个QT项目为例说明一下在pro文件中如何调用SDL库
#包含对应的静态库
unix
{
LIBS += -L$$PWD/3rd/SDL2/ -lSDL2
LIBS += -L$$PWD/3rd/SDL2/ -lSDL2main
}
#添加对应的依赖目录
INCLUDEPATH += $$PWD/3rd/SDL2
DEPENDPATH += $$PWD/3rd/SDL2
代码调用
在项目中导入了SDL库之后 我们就可以使用SDL库来进行开发使用了,这里介绍一下如何使用SDL库来播放WAV音频数据
#include "SDL2/SDL.h"
//音频数据
struct AudioData
{
Uint8 *idx; //数据索引
Uint32 len; //数据长度
};
//buf为数据 size为数据长度 uRepeat为重复次数 vol为音量(0~1)
void RaiseVolume(char* buf, int size, int uRepeat, double vol)
{
if (!size)
{
return;
}
for (int i = 0; i < size; i += 2)
{
short wData;
wData = MAKEWORD(buf[i], buf[i + 1]);
long dwData = wData;
for (int j = 0; j < uRepeat; j++)
{
dwData = dwData * vol;
if (dwData < -0x8000)
{
dwData = -0x8000;
}
else if (dwData > 0x7FFF)
{
dwData = 0x7FFF;
}
}
wData = LOWORD(dwData);
buf[i] = LOBYTE(wData);
buf[i + 1] = HIBYTE(wData);
}
}
//全局音量控制
float gvolume = 1.0;
//音频输出的回调函数
void callbackfun(void *data, Uint8 *buffer, int buffer_len)
{
//调用的时候清空数据
SDL_memset(buffer, 0, buffer_len);
AudioData *audio = (AudioData*)data;
if( audio->len <= 0)
{
return;
}
Uint32 length = (Uint32)buffer_len;
length = (length > audio->len ? audio->len : length);
RaiseVolume((char*)(audio->idx), length, 1, gvolume);
SDL_memcpy(buffer, audio->idx, length);
audio->idx += length;
audio->len -= length;
}
int main()
{
SDL_Init(SDL_INIT_AUDIO);
SDL_AudioSpec aud_wav_spec;
Uint8 *wav_start;
Uint32 wav_len;
if(SDL_LoadWAV("/home/username/test.wav", &aud_wav_spec, &wav_start, &wav_len) == NULL)
{
std::cout << "load wav error";
return;
}
AudioData audio;
audio.idx = wav_start;
audio.len = wav_len;
aud_wav_spec.callback = callbackfun;
aud_wav_spec.userdata = &audio;
//查找设备 检查设备
int num = SDL_GetNumAudioDevices(0);
for (int i=0;i<num;i++)
{
const char* device_name = SDL_GetAudioDeviceName(i, 0);
printf("play device:%s", device_name);
}
if (SDL_OpenAudio(&aud_wav_spec, nullptr)) {
const char* error_msg = SDL_GetError();
std::cout << "打开音频设备失败";
SDL_FreeWAV(wav_start);
SDL_Quit();
return;
}
//启动设备
SDL_PauseAudio(0);
while(audio.len > 0)
{
SDL_Delay(300);
}
SDL_Delay(300);
SDL_PauseAudio(1);
SDL_CloseAudio();
SDL_FreeWAV(wav_start);
SDL_Quit();
}
要点说明
1.在音频回调函数中一定要调用SDL_memset(buffer, 0, buffer_len)清空缓存,否则在列表播放切换文件的时候会出现杂音。
2.在列表切换播放文件的时候一定要做延时操作 否则会出现文件结尾播放不完整的问题。