我正在尝试用 openal 实现空间声音,但是无论我如何改变源的位置,声音的音量都不会改变。这是一些示例代码:
#include <chrono>
#include <AL/al.h>
#include <AL/alc.h>
#include "stb_vorbis.c"
double timeDiff(std::chrono::steady_clock::time_point start, std::chrono::steady_clock::time_point stop) {
auto dur = std::chrono::duration_cast<std::chrono::duration<double>>(stop - start);
return dur.count();
}
int main(void) {
auto audioDevice = alcOpenDevice(nullptr);
auto alContext = alcCreateContext(audioDevice, nullptr);
alcMakeContextCurrent(alContext);
alDistanceModel(AL_LINEAR_DISTANCE_CLAMPED);
ALuint source = 0;
alGenSources(1, &source);
alSourcei(source, AL_SOURCE_RELATIVE, AL_TRUE);
// Added max distance and reference distance, thanks to paddy's comment
alSourcef(source, AL_MAX_DISTANCE, 50.f);
alSourcef(source, AL_REFERENCE_DISTANCE, 5.f);
ALuint buffer = 0;
alGenBuffers(1, &buffer);
{
int spacialAudioChannels = 0, sampleRate = 0;
short* hData = nullptr;
int samples = stb_vorbis_decode_filename("Cipher2.ogg", &spacialAudioChannels, &sampleRate, &hData);
if (hData == nullptr) {
printf("Failed to open file Cipher2.ogg");
exit(EXIT_FAILURE);
}
auto alFormat = spacialAudioChannels == 1 ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16;
alBufferData(buffer, alFormat, hData, samples * sizeof(short), sampleRate);
free(hData);
}
alSourcei(source, AL_BUFFER, buffer);
alSourcePlay(source);
auto startTime = std::chrono::steady_clock::now();
while (timeDiff(startTime, std::chrono::steady_clock::now()) < 180.0) {
double percentElasped = timeDiff(startTime, std::chrono::steady_clock::now()) / 180.0;
float pos[] = { percentElasped * 100.f, 0.f, 0.f };
alSourcefv(source, AL_POSITION, pos);
}
}
我正在使用 stb vorbis 加载 ogg 文件。然后我计算已经过去的时间百分比,最后将源位置设置为该百分比。 这应该会产生声源远离听者的效果,并使声音随着时间的推移变得更安静。问题是音量保持不变。
我下载并安装了最新的 OpenAL SDK (Windows) 并复制了您的代码。我没有乱搞音频文件,而是生成了 5 秒单声道 440Hz 波形。这工作正常。
所以,我决定尝试立体声,发现它根本没有衰减。在阅读 alBufferData
的
documentation时,我在备注部分看到以下评论:
包含多个数据通道的缓冲区 将在没有 3D 空间化的情况下播放。
我想说这可能是您问题的症结所在。您可能有一个立体声音频文件。尝试将其转换为单声道。
这是我的测试程序,它将音频源从最左移动到最右:
#include <cmath>
#include <chrono>
#include <vector>
#include "al.h"
#include "alc.h"
template <typename T, typename TimePoint>
T timeDiff(TimePoint start, TimePoint stop)
{
auto dur = std::chrono::duration_cast<std::chrono::duration<T>>(stop - start);
return dur.count();
}
std::vector<short> generateWave(int sampleRate, float durationSeconds, float frequencyHz, bool stereo)
{
std::vector<short> samples;
int numChannels = stereo + 1;
int numSamples = static_cast<int>(sampleRate * durationSeconds) * numChannels;
samples.reserve(numSamples);
for (int t = 0; t < numSamples; t++)
{
double amplitude = sin(3.1415927 * 2.0 * frequencyHz * t / sampleRate / numChannels);
samples.push_back(static_cast<short>(amplitude * 32767));
}
return samples;
}
int main()
{
using Clock = std::chrono::steady_clock;
const bool stereo = false;
const int sampleRate = 44100;
const float waveFrequencyHz = 440.f;
const float playTimeSeconds = 5.f;
const float maxDistance = 50.f;
const float refDistance = 5.f;
// Init device context
auto audioDevice = alcOpenDevice(nullptr);
auto alContext = alcCreateContext(audioDevice, nullptr);
alcMakeContextCurrent(alContext);
alDistanceModel(AL_LINEAR_DISTANCE_CLAMPED);
// Create audio source
ALuint source = 0;
alGenSources(1, &source);
alSourcei(source, AL_SOURCE_RELATIVE, AL_TRUE);
// Added max distance and reference distance, thanks to paddy's comment
alSourcef(source, AL_MAX_DISTANCE, maxDistance);
alSourcef(source, AL_REFERENCE_DISTANCE, refDistance);
// Create audio buffer
ALuint buffer = 0;
alGenBuffers(1, &buffer);
std::vector<short> samples = generateWave(sampleRate, playTimeSeconds, waveFrequencyHz, stereo);
alBufferData(buffer, stereo ? AL_FORMAT_STEREO16 : AL_FORMAT_MONO16, samples.data(), (ALsizei)samples.size() * sizeof(short), sampleRate);
// Play audio source
alSourcei(source, AL_BUFFER, buffer);
alSourcePlay(source);
// Animate the audio source from far-left to far-right
auto startTime = Clock::now();
for (float elapsed = 0; elapsed < playTimeSeconds; elapsed = timeDiff<float>(startTime, Clock::now()))
{
float t = elapsed / playTimeSeconds;
float x = (t * 2.f - 1.f) * maxDistance;
float pos[] = { x, 0, refDistance }; // offset z to avoid artifacts transitioning through origin
alSourcefv(source, AL_POSITION, pos);
}
}