软件开发中遇到需要播放音频文件时,可以使用操作系统提供的API也可以依赖于第三方库,通常第三方库多因其简单易用的接口而成为首选。miniaudio便是游戏的音频开源库之一。
miniaudio 是一个轻量级的音频播放、采集、播放+采集的库,专注于提供简单易用的 API 和跨平台的音频播放功能。它具有以下特点:
下载和安装
下载链接见(https://github.com/mackron/miniaudio /tree/0.11.21),
miniaudio作为header-only的开源库,只需将miniaudio.h头文件集成到项目中即可。在使用时,需要在包含头文件前定义宏MINIAUDIO_IMPLEMENTATION,形如:
#define MINIAUDIO_IMPLEMENTATION
#include"miniaudio/miniaudio.h"
使用
miniaudio分为上层(High Low)接口和底层(Low Level)接口两种,高层接口做了封装,使用起来更加方便,对于开发者来讲更像一个黑盒子。=反而底层接口,开发者可以获得操作音频原始数据的机会。
接下来将分别使用上层接口、底层接口来播放本地文件以及录制声音。
#define MINIAUDIO_IMPLEMENTATION
#include"miniaudio/miniaudio.h"
//上层接口播放本地文件
void using_high_level_playback()
{
ma_result result;
ma_engine engine;
result = ma_engine_init(NULL, &engine);
if (result != MA_SUCCESS) {
return ;
}
//"d://22.mp3"本地文件,需替换为自己的文件
ma_engine_play_sound(&engine, "d://22.mp3", NULL);
printf("Press Enter to quit...");
getchar();
ma_engine_uninit(&engine);
}
//底层接口播放本地文件
void data_callback_playback(ma_device*pDevice, void*pOutput,
constvoid*pInput, ma_uint32frameCount)
{
ma_decoder* pDecoder = (ma_decoder*)pDevice->pUserData;
if (pDecoder == NULL) {
return;
}
ma_decoder_read_pcm_frames(pDecoder, pOutput, frameCount, NULL);
(void)pInput;
}
void using_low_level_playback()
{
ma_result result;
ma_decoder decoder;
ma_device_config deviceConfig;
ma_device device;
result = ma_decoder_init_file("d://22.mp3", NULL, &decoder);
if (result != MA_SUCCESS) {
return;
}
deviceConfig = ma_device_config_init(ma_device_type_playback);
deviceConfig.playback.format = decoder.outputFormat;
deviceConfig.playback.channels = decoder.outputChannels;
deviceConfig.sampleRate = decoder.outputSampleRate;
deviceConfig.dataCallback = data_callback_playback;
deviceConfig.pUserData = &decoder;
if (ma_device_init(NULL, &deviceConfig, &device) != MA_SUCCESS) {
printf("Failed to open playback device.\n");
ma_decoder_uninit(&decoder);
return ;
}
if (ma_device_start(&device) != MA_SUCCESS) {
printf("Failed to start playback device.\n");
ma_device_uninit(&device);
ma_decoder_uninit(&decoder);
return ;
}
printf("Press Enter to quit...");
getchar();
ma_device_uninit(&device);
ma_decoder_uninit(&decoder);
}
//采集声音
void data_callback_capture(ma_device*pDevice, void*pOutput,
constvoid*pInput, ma_uint32frameCount)
{
ma_encoder* pEncoder = (ma_encoder*)pDevice->pUserData;
MA_ASSERT(pEncoder != NULL);
ma_encoder_write_pcm_frames(pEncoder, pInput, frameCount, NULL);
(void)pOutput;
}
int using_capture()
{
ma_result result;
ma_encoder_config encoderConfig;
ma_encoder encoder;
ma_device_config deviceConfig;
ma_device device;
encoderConfig = ma_encoder_config_init(ma_encoding_format_wav, ma_format_f32, 2, 48000);
if (ma_encoder_init_file("d://capture.wav", &encoderConfig, &encoder) != MA_SUCCESS) {
printf("Failed to initialize output file.\n");
return -1;
}
deviceConfig = ma_device_config_init(ma_device_type_duplex);
deviceConfig.capture.format = encoder.config.format;
deviceConfig.capture.channels = encoder.config.channels;
deviceConfig.sampleRate = encoder.config.sampleRate;
deviceConfig.dataCallback = data_callback_capture;
deviceConfig.pUserData = &encoder;
result = ma_device_init(NULL, &deviceConfig, &device);
if (result != MA_SUCCESS) {
printf("Failed to initialize capture device.\n");
return -2;
}
result = ma_device_start(&device);
if (result != MA_SUCCESS) {
ma_device_uninit(&device);
printf("Failed to start device.\n");
return -3;
}
printf("Press Enter to stop recording...\n");
getchar();
ma_device_uninit(&device);
ma_encoder_uninit(&encoder);
return0;
}
对比播放本地文件部分代码,扎心的发现,上层接口使用极其少量的代码实现了和底层接口相同的功能。但是底层接口,使得我们获得了在回调函数data_callback操作pcm数据的可能,当然若没有修改pcm的需求时,可以直接使用上层的接口。
总结
miniaudio作为一个header-only且MIT协议的开源库,极大地方便了在项目中的集成。同时,miniaudio支持播放、采集、采集同时播放的功能,可视为音频开源库的首选。