mirror of
https://github.com/RPCSX/rpcsx.git
synced 2025-12-06 07:12:14 +01:00
208 lines
4.9 KiB
C++
208 lines
4.9 KiB
C++
#include "stdafx.h"
|
|
#include "Emu/System.h"
|
|
|
|
#include "OpenALBackend.h"
|
|
|
|
#ifdef _MSC_VER
|
|
#pragma comment(lib, "OpenAL32.lib")
|
|
#endif
|
|
|
|
|
|
#define checkForAlError(sit) do { ALenum g_last_al_error = alGetError(); if(g_last_al_error != AL_NO_ERROR) fmt::throw_exception("%s: OpenAL error 0x%04x", sit, g_last_al_error); } while(0)
|
|
#define checkForAlcError(sit) do { ALCenum g_last_alc_error = alcGetError(m_device); if(g_last_alc_error != ALC_NO_ERROR) fmt::throw_exception("%s: OpenALC error 0x%04x", sit, g_last_alc_error); } while(0)
|
|
|
|
|
|
OpenALBackend::OpenALBackend()
|
|
: m_sampling_rate(get_sampling_rate())
|
|
, m_sample_size(get_sample_size())
|
|
{
|
|
ALCdevice* m_device = alcOpenDevice(nullptr);
|
|
checkForAlcError("OpenALBackend->alcOpenDevice");
|
|
|
|
ALCcontext* m_context = alcCreateContext(m_device, nullptr);
|
|
checkForAlcError("OpenALBackend->alcCreateContext");
|
|
|
|
alcMakeContextCurrent(m_context);
|
|
checkForAlcError("OpenALBackend->alcMakeContextCurrent");
|
|
|
|
if (get_channels() == 2)
|
|
{
|
|
m_format = (m_sample_size == 2) ? AL_FORMAT_STEREO16 : AL_FORMAT_STEREO_FLOAT32;
|
|
}
|
|
else
|
|
{
|
|
m_format = (m_sample_size == 2) ? AL_FORMAT_71CHN16 : AL_FORMAT_71CHN32;
|
|
}
|
|
}
|
|
|
|
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");
|
|
}
|
|
}
|
|
|
|
bool OpenALBackend::AddData(const void* src, u32 num_samples)
|
|
{
|
|
AUDIT(alIsSource(m_source));
|
|
|
|
// Unqueue processed buffers, if any
|
|
unqueue_processed();
|
|
|
|
// Fail if there are no free buffers remaining
|
|
if (m_num_unqueued == 0)
|
|
{
|
|
LOG_WARNING(GENERAL, "XAudio2Backend : no unqueued buffers remaining");
|
|
return false;
|
|
}
|
|
|
|
// Copy data to the next available buffer
|
|
alBufferData(m_buffers[m_next_buffer], m_format, src, num_samples * m_sample_size, m_sampling_rate);
|
|
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)");
|
|
AUDIT(static_cast<u32>(num_queued) <= m_num_buffers - m_num_unqueued);
|
|
|
|
// 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;
|
|
} |