miniaudio
是一个轻量级的音频库,以单个头文件的形式提供,方便在 C++ 项目中集成。它提供了简单易用的 API 来处理音频播放。本教程将详细介绍如何引入 miniaudio
,并通过面向对象的封装方式创建一个 AudioPlayer
类,使音频播放功能更易于使用。我们还将提供一个完整的示例程序,展示如何加载、播放和控制音频。
miniaudio
首先,你需要从 miniaudio 的 GitHub 仓库 下载 miniaudio.h
文件。将该头文件放入你的项目目录中,并在需要使用音频播放功能的文件中引入它。
在代码中引入 miniaudio.h
的方式如下:
#define MINIAUDIO_IMPLEMENTATION
#include "miniaudio.h"
MINIAUDIO_IMPLEMENTATION
宏:必须在 #include "miniaudio.h"
之前定义。这个宏确保 miniaudio
的实现代码被包含到你的项目中。如果不定义该宏,仅包含头文件将不会有实际的功能实现。完成这一步后,你就可以开始使用 miniaudio
的功能了。接下来,我们将通过封装一个 AudioPlayer
类来简化音频播放的操作。
AudioPlayer
类为了更方便地管理音频播放,我们将 miniaudio
的核心功能封装到一个 AudioPlayer
类中。这个类提供了加载音频文件、播放、暂停、恢复、停止以及音量控制等常用功能。
以下是完整的 AudioPlayer
类代码,建议将其保存为 AudioPlayer.hpp
:
#pragma once
#define MINIAUDIO_IMPLEMENTATION
#include "miniaudio.h"
#include
#include
class AudioPlayer
{
public:
AudioPlayer() : m_initialized(false), m_volume(1.0f), m_isPaused(false) {}
~AudioPlayer() {
stop(); // 析构时释放资源
}
// 加载音频文件,自动释放旧资源
bool load(const std::string& filePath, bool loop = true)
{
// 自动释放旧资源
if (m_initialized) {
stop();
}
// 初始化解码器
ma_result result = ma_decoder_init_file(filePath.c_str(), NULL, &m_decoder);
if (result != MA_SUCCESS) {
std::cerr << "Failed to initialize decoder for file: " << filePath << std::endl;
m_initialized = false;
return false;
}
// 循环设置
ma_data_source_set_looping(&m_decoder, loop ? MA_TRUE : MA_FALSE);
// 设备配置
m_deviceConfig = ma_device_config_init(ma_device_type_playback);
m_deviceConfig.playback.format = m_decoder.outputFormat;
m_deviceConfig.playback.channels = m_decoder.outputChannels;
m_deviceConfig.sampleRate = m_decoder.outputSampleRate;
m_deviceConfig.dataCallback = data_callback;
m_deviceConfig.pUserData = &m_decoder;
// 初始化设备
if (ma_device_init(NULL, &m_deviceConfig, &m_device) != MA_SUCCESS) {
std::cerr << "Failed to initialize playback device." << std::endl;
ma_decoder_uninit(&m_decoder);
m_initialized = false;
return false;
}
// 设置音量
ma_device_set_master_volume(&m_device, m_volume);
m_initialized = true;
m_isPaused = false;
return true;
}
// 开始播放
bool play()
{
if (!m_initialized) {
std::cerr << "Device is not initialized.\n";
return false;
}
if (ma_device_start(&m_device) != MA_SUCCESS) {
std::cerr << "Failed to start playback device.\n";
return false;
}
m_isPaused = false;
return true;
}
// 停止播放并释放资源
void stop()
{
if (m_initialized) {
ma_device_uninit(&m_device);
ma_decoder_uninit(&m_decoder);
m_initialized = false;
m_isPaused = false;
}
}
// 暂停播放
void pause()
{
if (m_initialized && !m_isPaused) {
ma_device_stop(&m_device);
m_isPaused = true;
}
}
// 恢复播放
void resume()
{
if (m_initialized && m_isPaused) {
ma_device_start(&m_device);
m_isPaused = false;
}
}
// 设置音量(0.0 ~ 1.0)
void setVolume(float volume)
{
if (!m_initialized) return;
m_volume = (volume < 0.0f) ? 0.0f : (volume > 1.0f) ? 1.0f : volume; // 限制范围
ma_device_set_master_volume(&m_device, m_volume);
}
// 获取音量
float getVolume() const { return m_volume; }
bool isPlaying() const { return m_initialized && !m_isPaused; }
bool isPaused() const { return m_initialized && m_isPaused; }
private:
static void data_callback(ma_device* pDevice, void* pOutput, const void* /*pInput*/, ma_uint32 frameCount)
{
ma_decoder* pDecoder = (ma_decoder*)pDevice->pUserData;
if (pDecoder == nullptr) {
return;
}
ma_data_source_read_pcm_frames(pDecoder, pOutput, frameCount, nullptr);
}
private:
ma_decoder m_decoder;
ma_device_config m_deviceConfig;
ma_device m_device;
bool m_initialized;
bool m_isPaused;
float m_volume;
};
以下是 AudioPlayer
类中各个成员函数的详细说明:
构造函数和析构函数:
m_initialized
为 false
(表示未初始化)、m_volume
为 1.0(默认最大音量)、m_isPaused
为 false
(未暂停)。stop()
,确保在对象销毁时释放所有资源。load(const std::string& filePath, bool loop = true)
:
loop
控制是否循环播放,默认值为 true
。true
表示加载成功,否则返回 false
并输出错误信息。play()
:
false
并输出错误信息。stop()
:
pause()
:
resume()
:
setVolume(float volume)
:
getVolume()
:
isPlaying()
和 isPaused()
:
data_callback
:
miniaudio
调用,用于从解码器读取音频数据并输出到播放设备。以下是一个使用 AudioPlayer
类的完整示例程序,展示如何加载音频文件、循环播放,并通过命令行控制音量和播放状态。
将以下代码保存为 main.cpp
:
#include "AudioPlayer.hpp"
#include
int main() {
AudioPlayer player;
// 加载并播放
if (!player.load("./input.mp3", true)) {
std::cerr << "Failed to load audio file." << std::endl;
return 1;
}
player.play();
std::cout << "Playing input.mp3 in loop." << std::endl;
std::cout << "Commands: [+] Volume Up | [-] Volume Down | [p] Pause | [r] Resume | [q] Quit" << std::endl;
char cmd;
while (std::cin >> cmd) {
if (cmd == '+') {
float newVolume = player.getVolume() + 0.1f;
player.setVolume(newVolume);
std::cout << "Volume: " << player.getVolume() << std::endl;
} else if (cmd == '-') {
float newVolume = player.getVolume() - 0.1f;
player.setVolume(newVolume);
std::cout << "Volume: " << player.getVolume() << std::endl;
} else if (cmd == 'p') {
player.pause();
std::cout << "Paused." << std::endl;
} else if (cmd == 'r') {
player.resume();
std::cout << "Resumed." << std::endl;
} else if (cmd == 'q') {
break;
}
}
player.stop(); // 最终释放资源
return 0;
}
miniaudio.h
和 AudioPlayer.hpp
已正确引入项目。input.mp3
)放置在程序运行目录下。使用支持 C++ 的编译器(例如 g++)编译程序:
g++ main.cpp -o audio_player
运行程序:
./audio_player
+
:增加音量(每次增加 0.1)。-
:减小音量(每次减小 0.1)。p
:暂停播放。r
:恢复播放。q
:退出程序。程序启动后,音频将循环播放,并通过命令行显示当前状态和音量。
资源管理:
AudioPlayer
类在加载新音频文件时会自动释放旧资源,避免内存泄漏。音量范围:
错误处理:
false
,便于调试。线程安全:
AudioPlayer
,需自行添加线程同步机制。通过本教程,你可以在 C++ 项目中快速集成音频播放功能,并使用封装好的 AudioPlayer
类轻松控制播放行为。无论是简单的音频播放需求,还是更复杂的应用场景,这一轻量封装都能提供良好的支持。