Search code examples
c++audioopenal

OpenAL source sound is not changing even when I change the position


I'm trying to implement spatial sound with openal, however no matter how much I change the position of the source, the volume of the sound does not change. Here's some sample code:



#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);
  }
}

I'm using stb vorbis to load the ogg file. Then I'm calculating the percent of time that has elasped and finally I'm setting the source postion to that percent. This should create the effect of the source moving away from the listener and make the sound quieter as time goes on. The problem is that the sounds volume remains the same.


Solution

  • I downloaded and installed the latest OpenAL SDK (Windows) and copied your code. Instead of messing around with audio files, I generated a 5-second mono 440Hz waveform. This worked correctly.

    So, I decided to try stereo, and found that it did not attenuate at all. On reading the documentation for alBufferData, I see the following comment in the Remarks section:

    Buffers containing more than one channel of data will be played without 3D spatialization.

    I would say this is probably the crux of your issue. It's likely that you have a stereo audio file. Try converting it to mono.

    Here is my test program which moves the audio source from far-left to far-right:

    #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);
        }
    }