2018-12-21 02:16:54 +01:00
|
|
|
|
#include "stdafx.h"
|
|
|
|
|
|
|
|
|
|
|
|
#include "OpenALBackend.h"
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef _MSC_VER
|
|
|
|
|
|
#pragma comment(lib, "OpenAL32.lib")
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
2020-01-31 13:05:35 +01:00
|
|
|
|
LOG_CHANNEL(OpenAL);
|
2018-12-21 02:16:54 +01:00
|
|
|
|
|
2020-01-31 13:05:35 +01:00
|
|
|
|
#define checkForAlError(sit) do { ALenum g_last_al_error = alGetError(); if(g_last_al_error != AL_NO_ERROR) OpenAL.error("%s: OpenAL error 0x%04x", sit, g_last_al_error); } while(0)
|
2020-01-31 21:15:41 +01:00
|
|
|
|
#define checkForAlcError(sit) do { ALCenum g_last_alc_error = alcGetError(m_device); if(g_last_alc_error != ALC_NO_ERROR) { OpenAL.error("%s: OpenALC error 0x%04x", sit, g_last_alc_error); return; }} while(0)
|
2018-12-21 02:16:54 +01:00
|
|
|
|
|
|
|
|
|
|
OpenALBackend::OpenALBackend()
|
2020-06-20 02:44:32 +02:00
|
|
|
|
: AudioBackend()
|
2018-12-21 02:16:54 +01:00
|
|
|
|
{
|
|
|
|
|
|
ALCdevice* m_device = alcOpenDevice(nullptr);
|
2020-01-31 13:05:35 +01:00
|
|
|
|
checkForAlcError("alcOpenDevice");
|
2018-12-21 02:16:54 +01:00
|
|
|
|
|
|
|
|
|
|
ALCcontext* m_context = alcCreateContext(m_device, nullptr);
|
2020-01-31 13:05:35 +01:00
|
|
|
|
checkForAlcError("alcCreateContext");
|
2018-12-21 02:16:54 +01:00
|
|
|
|
|
|
|
|
|
|
alcMakeContextCurrent(m_context);
|
2020-01-31 13:05:35 +01:00
|
|
|
|
checkForAlcError("alcMakeContextCurrent");
|
2018-12-21 02:16:54 +01:00
|
|
|
|
|
2020-06-20 02:44:32 +02:00
|
|
|
|
switch (m_channels)
|
2018-12-21 02:16:54 +01:00
|
|
|
|
{
|
2020-06-01 20:36:45 +02:00
|
|
|
|
case 2:
|
2018-12-21 02:16:54 +01:00
|
|
|
|
m_format = (m_sample_size == 2) ? AL_FORMAT_STEREO16 : AL_FORMAT_STEREO_FLOAT32;
|
2020-06-01 20:36:45 +02:00
|
|
|
|
break;
|
|
|
|
|
|
case 6:
|
|
|
|
|
|
m_format = (m_sample_size == 2) ? AL_FORMAT_51CHN16 : AL_FORMAT_51CHN32;
|
|
|
|
|
|
break;
|
|
|
|
|
|
default:
|
2018-12-21 02:16:54 +01:00
|
|
|
|
m_format = (m_sample_size == 2) ? AL_FORMAT_71CHN16 : AL_FORMAT_71CHN32;
|
2020-06-01 20:36:45 +02:00
|
|
|
|
break;
|
2018-12-21 02:16:54 +01:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
OpenALBackend::~OpenALBackend()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (alIsSource(m_source))
|
|
|
|
|
|
{
|
|
|
|
|
|
Close();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (ALCcontext* m_context = alcGetCurrentContext())
|
|
|
|
|
|
{
|
|
|
|
|
|
ALCdevice* m_device = alcGetContextsDevice(m_context);
|
|
|
|
|
|
alcMakeContextCurrent(nullptr);
|
|
|
|
|
|
alcDestroyContext(m_context);
|
|
|
|
|
|
alcCloseDevice(m_device);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void OpenALBackend::Play()
|
|
|
|
|
|
{
|
|
|
|
|
|
AUDIT(alIsSource(m_source));
|
|
|
|
|
|
|
|
|
|
|
|
ALint state;
|
|
|
|
|
|
alGetSourcei(m_source, AL_SOURCE_STATE, &state);
|
|
|
|
|
|
checkForAlError("Play->alGetSourcei(AL_SOURCE_STATE)");
|
|
|
|
|
|
|
|
|
|
|
|
if (state != AL_PLAYING)
|
|
|
|
|
|
{
|
|
|
|
|
|
alSourcePlay(m_source);
|
|
|
|
|
|
checkForAlError("Play->alSourcePlay");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void OpenALBackend::Pause()
|
|
|
|
|
|
{
|
|
|
|
|
|
AUDIT(alIsSource(m_source));
|
|
|
|
|
|
|
|
|
|
|
|
alSourcePause(m_source);
|
|
|
|
|
|
checkForAlError("Pause->alSourcePause");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool OpenALBackend::IsPlaying()
|
|
|
|
|
|
{
|
|
|
|
|
|
AUDIT(alIsSource(m_source));
|
|
|
|
|
|
|
|
|
|
|
|
ALint state;
|
|
|
|
|
|
alGetSourcei(m_source, AL_SOURCE_STATE, &state);
|
|
|
|
|
|
checkForAlError("IsPlaying->alGetSourcei(AL_SOURCE_STATE)");
|
|
|
|
|
|
|
|
|
|
|
|
return state == AL_PLAYING;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void OpenALBackend::Open(u32 num_buffers)
|
|
|
|
|
|
{
|
|
|
|
|
|
AUDIT(!alIsSource(m_source));
|
|
|
|
|
|
|
|
|
|
|
|
// Initialize Source
|
|
|
|
|
|
alGenSources(1, &m_source);
|
|
|
|
|
|
checkForAlError("Open->alGenSources");
|
|
|
|
|
|
|
|
|
|
|
|
alSourcei(m_source, AL_LOOPING, AL_FALSE);
|
|
|
|
|
|
checkForAlError("Open->alSourcei");
|
|
|
|
|
|
|
|
|
|
|
|
// Initialize Buffers
|
|
|
|
|
|
alGenBuffers(num_buffers, m_buffers);
|
|
|
|
|
|
checkForAlError("Open->alGenBuffers");
|
|
|
|
|
|
|
|
|
|
|
|
m_num_buffers = num_buffers;
|
|
|
|
|
|
m_num_unqueued = num_buffers;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void OpenALBackend::Close()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (alIsSource(m_source))
|
|
|
|
|
|
{
|
|
|
|
|
|
// Stop & Kill Source
|
|
|
|
|
|
Pause();
|
|
|
|
|
|
alDeleteSources(1, &m_source);
|
|
|
|
|
|
|
|
|
|
|
|
// Free Buffers
|
|
|
|
|
|
alDeleteBuffers(m_num_buffers, m_buffers);
|
|
|
|
|
|
checkForAlError("alDeleteBuffers");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-12-21 03:13:22 +01:00
|
|
|
|
bool OpenALBackend::AddData(const void* src, u32 num_samples)
|
2018-12-21 02:16:54 +01:00
|
|
|
|
{
|
|
|
|
|
|
AUDIT(alIsSource(m_source));
|
|
|
|
|
|
|
|
|
|
|
|
// Unqueue processed buffers, if any
|
|
|
|
|
|
unqueue_processed();
|
|
|
|
|
|
|
|
|
|
|
|
// Fail if there are no free buffers remaining
|
|
|
|
|
|
if (m_num_unqueued == 0)
|
|
|
|
|
|
{
|
2020-01-31 13:05:35 +01:00
|
|
|
|
OpenAL.warning("No unqueued buffers remaining");
|
2018-12-21 02:16:54 +01:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Copy data to the next available buffer
|
2018-12-21 03:13:22 +01:00
|
|
|
|
alBufferData(m_buffers[m_next_buffer], m_format, src, num_samples * m_sample_size, m_sampling_rate);
|
2018-12-21 02:16:54 +01:00
|
|
|
|
checkForAlError("AddData->alBufferData");
|
|
|
|
|
|
|
|
|
|
|
|
// Enqueue buffer
|
|
|
|
|
|
alSourceQueueBuffers(m_source, 1, &m_buffers[m_next_buffer]);
|
|
|
|
|
|
checkForAlError("AddData->alSourceQueueBuffers");
|
|
|
|
|
|
|
|
|
|
|
|
m_num_unqueued--;
|
|
|
|
|
|
m_next_buffer = (m_next_buffer + 1) % m_num_buffers;
|
|
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void OpenALBackend::Flush()
|
|
|
|
|
|
{
|
|
|
|
|
|
AUDIT(alIsSource(m_source));
|
|
|
|
|
|
|
|
|
|
|
|
// Stop source first
|
|
|
|
|
|
alSourceStop(m_source);
|
|
|
|
|
|
checkForAlError("Flush->alSourceStop");
|
|
|
|
|
|
|
|
|
|
|
|
// Unqueue processed buffers (should now be all of them)
|
|
|
|
|
|
unqueue_processed();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void OpenALBackend::unqueue_processed()
|
|
|
|
|
|
{
|
|
|
|
|
|
AUDIT(alIsSource(m_source));
|
|
|
|
|
|
|
|
|
|
|
|
// Get number of buffers
|
|
|
|
|
|
ALint num_processed;
|
|
|
|
|
|
alGetSourcei(m_source, AL_BUFFERS_PROCESSED, &num_processed);
|
|
|
|
|
|
checkForAlError("Flush->alGetSourcei(AL_BUFFERS_PROCESSED)");
|
|
|
|
|
|
|
|
|
|
|
|
if (num_processed > 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Unqueue all buffers
|
|
|
|
|
|
ALuint x[MAX_AUDIO_BUFFERS];
|
|
|
|
|
|
alSourceUnqueueBuffers(m_source, num_processed, x);
|
|
|
|
|
|
checkForAlError("Flush->alSourceUnqueueBuffers");
|
|
|
|
|
|
|
|
|
|
|
|
m_num_unqueued += num_processed;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
u64 OpenALBackend::GetNumEnqueuedSamples()
|
|
|
|
|
|
{
|
|
|
|
|
|
AUDIT(alIsSource(m_source));
|
|
|
|
|
|
|
|
|
|
|
|
// Get number of buffers queued
|
|
|
|
|
|
ALint num_queued;
|
|
|
|
|
|
alGetSourcei(m_source, AL_BUFFERS_QUEUED, &num_queued);
|
|
|
|
|
|
checkForAlError("GetNumEnqueuedSamples->alGetSourcei(AL_BUFFERS_QUEUED)");
|
2018-12-21 03:13:22 +01:00
|
|
|
|
AUDIT(static_cast<u32>(num_queued) <= m_num_buffers - m_num_unqueued);
|
2018-12-21 02:16:54 +01:00
|
|
|
|
|
|
|
|
|
|
// Get sample position
|
|
|
|
|
|
ALint sample_pos;
|
|
|
|
|
|
alGetSourcei(m_source, AL_SAMPLE_OFFSET, &sample_pos);
|
|
|
|
|
|
checkForAlError("GetNumEnqueuedSamples->alGetSourcei(AL_SAMPLE_OFFSET)");
|
|
|
|
|
|
|
|
|
|
|
|
// Return
|
|
|
|
|
|
return (num_queued * AUDIO_BUFFER_SAMPLES) + (sample_pos % AUDIO_BUFFER_SAMPLES);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
f32 OpenALBackend::SetFrequencyRatio(f32 new_ratio)
|
|
|
|
|
|
{
|
|
|
|
|
|
new_ratio = std::clamp(new_ratio, 0.5f, 2.0f);
|
|
|
|
|
|
|
|
|
|
|
|
alSourcef(m_source, AL_PITCH, new_ratio);
|
|
|
|
|
|
checkForAlError("SetFrequencyRatio->alSourcei(AL_PITCH)");
|
|
|
|
|
|
|
|
|
|
|
|
return new_ratio;
|
|
|
|
|
|
}
|