OpenAL
OpenAL是跨平台3D音频库,一般使用完全遵循其API的软件实现OpenAL-Soft。
OpenAL Soft - Software 3D Audio
作者已经提供预编译二进制动态链接库(.dll),但静态库(.lib)仍需要从源代码手动编译。
编译静态链接库
需要在CMake中单独手动添加生成参数LIBTYPE,值为STATIC。
确保生成大小约为30兆的静态库,而不是动态库的导入库。
注意:通过静态链接引入OpenAL soft,需要在include头文件之前添加预处理宏AL_LIBTYPE_STATIC。同时,Windows平台下需要一同链接winmm.lib。
初始化
类似OpenGL,OpenAL需要创建并绑定上下文。
1 2 3 4 5 6 7 8 9
| ALCdevice* pDevice = alcOpenDevice(NULL); ALCcontext* pContext; if (pDevice){ pContext = alcCreateContext(pDevice, NULL); alcMakeContextCurrent(pContext); } else{ std::cerr << "ERROR: DEVICE OPEN FAILED" << std::endl; }
|
创建AL对象
AL对象包括buffer,source,listener。其中每个Context包含一个默认listener,不需要手动创建。
- Buffer(缓冲区):用于存储音频数据。
- Source(音源):播放音频的对象,与缓冲区绑定。
- Listener(监听者):模拟耳朵,决定音频的空间定位。
1 2 3
| ALuint buffer, source; alGenBuffers(1, &buffer); alGenSources(1, &source);
|
函数返回无号整型id,为后续传参函数使用。
参数传递
OpenAL的传参函数类似于OpenGL。函数尾缀f/i代表传入浮点/整型,尾缀v代表传入指针。第一参数为对象id,第二参数为OpenAL内建宏定义,这些宏定义以 AL_ 或 ALC_ 开头。
音源的宏用于设置 音量、位置、速度、音调、循环播放 等。
| 宏定义 |
作用 |
默认值 |
使用的 API |
AL_GAIN |
设置音量 (0.0 ~ 1.0,>1.0 可增益) |
1.0 |
alSourcef |
AL_PITCH |
设置音调 (1.0 = 正常, 0.5 = 低八度, 2.0 = 高八度) |
1.0 |
alSourcef |
AL_LOOPING |
是否循环播放 (AL_TRUE / AL_FALSE) |
AL_FALSE |
alSourcei |
AL_POSITION |
设置音源位置 (x, y, z) |
{0,0,0} |
alSourcefv |
AL_VELOCITY |
设置音源速度 (x, y, z) |
{0,0,0} |
alSourcefv |
AL_DIRECTION |
设置音源方向 (x, y, z) |
{0,0,0} |
alSourcefv |
AL_SOURCE_RELATIVE |
是否相对监听者定位 (AL_TRUE / AL_FALSE) |
AL_FALSE |
alSourcei |
AL_SOURCE_STATE |
获取音源状态 (AL_PLAYING, AL_PAUSED, AL_STOPPED) |
- |
alGetSourcei |
AL_BUFFER |
绑定缓冲区 (Buffer ID) |
0 |
alSourcei |
AL_REFERENCE_DISTANCE |
3D 音频的衰减起点 |
1.0 |
alSourcef |
AL_ROLLOFF_FACTOR |
3D 音频衰减速率 (0.0 表示无衰减) |
1.0 |
alSourcef |
示例:设置音源属性
1 2 3 4 5
| alSourcef(source, AL_GAIN, 0.8f); alSourcef(source, AL_PITCH, 1.2f); alSourcei(source, AL_LOOPING, AL_TRUE); ALfloat pos[] = {0.0f, 0.0f, -3.0f}; alSourcefv(source, AL_POSITION, pos);
|
监听者代表“耳朵”,影响 3D 声音的方向、位置等。
| 宏定义 |
作用 |
默认值 |
使用 API |
AL_POSITION |
监听者位置 (x, y, z) |
{0,0,0} |
alListenerfv |
AL_VELOCITY |
监听者速度 (x, y, z) |
{0,0,0} |
alListenerfv |
AL_ORIENTATION |
监听者朝向 (前向向量 + 上向量) |
{0,0,-1, 0,1,0} |
alListenerfv |
AL_GAIN |
监听者整体音量 |
1.0 |
alListenerf |
示例:设置监听者位置和方向。
1 2 3 4 5
| ALfloat listenerPos[] = {0.0f, 0.0f, 0.0f}; ALfloat listenerOri[] = {0.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f}; alListenerfv(AL_POSITION, listenerPos); alListenerfv(AL_ORIENTATION, listenerOri);
|
参数查询
这些宏用于查询 音源、缓冲区、监听者 的当前状态。
| 宏定义 |
作用 |
返回值 |
使用 API |
AL_SOURCE_STATE |
获取音源当前状态 |
AL_PLAYING / AL_STOPPED / AL_PAUSED |
alGetSourcei |
AL_PLAYING |
表示音源正在播放 |
0x1012 |
alGetSourcei |
AL_STOPPED |
表示音源已停止 |
0x1014 |
alGetSourcei |
AL_PAUSED |
表示音源已暂停 |
0x1013 |
alGetSourcei |
AL_INITIAL |
表示音源未播放过 |
0x1011 |
alGetSourcei |
1 2 3 4 5
| ALint state; alGetSourcei(source, AL_SOURCE_STATE, &state); if (state == AL_PLAYING) { std::cout << "音源正在播放\n"; }
|
音频播放
缓冲区 (Buffer) 存储音频数据,需要先填充缓冲区才能播放音频。
1
| alBufferData(buffer, AL_FORMAT_MONO16, pcmData, dataSize, sampleRate);
|
参数解析:
buffer:缓冲区 ID
AL_FORMAT_MONO16:音频格式(单声道 16-bit)
pcmData:音频数据指针
dataSize:数据大小(字节)
sampleRate:采样率,一般为44100Hz
此处提供加载音频数据指针的函数。
1 2 3 4 5 6 7 8 9 10 11
| void loadPCM(std::string audioPath, unsigned char** audioBuffer,int* audioSize) { std::ifstream audioFile(audioPath, std::ios::binary | std::ios::in); audioFile.seekg(0, std::ios::beg); std::ifstream file(audioPath, std::ios::binary | std::ios::ate); std::streampos endPos = file.tellg(); *audioSize = int(endPos); file.close(); *audioBuffer = new unsigned char[audioSize]; audioFile.read((char*)audioBuffer, audioSize); audioFile.close(); }
|
绑定缓冲区至声源,并播放音频。
1 2 3 4
| alSourcei(source, AL_BUFFER, buffer);
alSourcePlay(source);
|
销毁
运行结束前,需要手动释放每一个创建过的AL对象,销毁上下文和设备。
1 2 3 4 5
| alDeleteSources(1, &source); alDeleteBuffers(1, &buffer); alcMakeContextCurrent(NULL); alcDestroyContext(context); alcCloseDevice(device);
|
示例代码
此处提供音频加载,循环播放的完整实现代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
| #define AL_LIBTYPE_STATIC #include <stdlib.h> #include <AL/al.h> #include <AL/alc.h> #include <iostream> #include <fstream> #include <string>
void loadAudioSource(ALuint* m_source, ALuint* m_buffer, std::string audioPath) { std::ifstream audioFile(audioPath, std::ios::binary | std::ios::in); audioFile.seekg(100, std::ios::beg); std::ifstream file(audioPath, std::ios::binary | std::ios::ate); std::streampos endPos = file.tellg(); int audioSize = int(endPos); file.close(); unsigned char* audioBuffer = new unsigned char[audioSize]; audioFile.read((char*)audioBuffer, audioSize); audioFile.close();
ALuint buffer, source; alGenBuffers(1, &buffer); alBufferData(buffer, AL_FORMAT_STEREO16, audioBuffer, audioSize, 44100); alGenSources(1, &source); alSourcei(source, AL_BUFFER, buffer); *m_source = source; *m_buffer = buffer; delete[] audioBuffer; }
int main() { ALCdevice* device = alcOpenDevice(NULL); ALCcontext* context; if (device) { context = alcCreateContext(device, NULL); alcMakeContextCurrent(context); } else { std::cout << "device open error" << std::endl; }
ALuint source, buffer; loadAudioSource(&source, &buffer, "../audio/test_audio.wav"); alSourcei(source, AL_LOOPING, AL_TRUE); alSourcePlay(source);
while (1) {
}
alDeleteSources(1, &source); alDeleteBuffers(1, &buffer); alcMakeContextCurrent(NULL); alcDestroyContext(context); alcCloseDevice(device);
return 0; }
|
Author:
Floraison
Permalink:
https://floraison.io/2025/03/14/
License:
Copyright (c) 2019 CC-BY-NC-4.0 LICENSE