Reformat all the things. Have fun with merge conflicts.

This commit is contained in:
Pierre Bourdon
2016-06-24 10:43:46 +02:00
parent 2115e8a4a6
commit 3570c7f03a
1116 changed files with 187405 additions and 180344 deletions

View File

@ -6,77 +6,77 @@
#include "AudioCommon/AOSoundStream.h"
#include "AudioCommon/Mixer.h"
#include "Common/MsgHandler.h"
#include "Common/Logging/Log.h"
#include "Common/MsgHandler.h"
#if defined(HAVE_AO) && HAVE_AO
void AOSound::SoundLoop()
{
Common::SetCurrentThreadName("Audio thread - ao");
Common::SetCurrentThreadName("Audio thread - ao");
uint_32 numBytesToRender = 256;
ao_initialize();
default_driver = ao_default_driver_id();
format.bits = 16;
format.channels = 2;
format.rate = m_mixer->GetSampleRate();
format.byte_format = AO_FMT_LITTLE;
uint_32 numBytesToRender = 256;
ao_initialize();
default_driver = ao_default_driver_id();
format.bits = 16;
format.channels = 2;
format.rate = m_mixer->GetSampleRate();
format.byte_format = AO_FMT_LITTLE;
device = ao_open_live(default_driver, &format, nullptr /* no options */);
if (!device)
{
PanicAlertT("AudioCommon: Error opening AO device.\n");
ao_shutdown();
Stop();
return;
}
device = ao_open_live(default_driver, &format, nullptr /* no options */);
if (!device)
{
PanicAlertT("AudioCommon: Error opening AO device.\n");
ao_shutdown();
Stop();
return;
}
buf_size = format.bits/8 * format.channels * format.rate;
buf_size = format.bits / 8 * format.channels * format.rate;
while (m_run_thread.load())
{
m_mixer->Mix(realtimeBuffer, numBytesToRender >> 2);
while (m_run_thread.load())
{
m_mixer->Mix(realtimeBuffer, numBytesToRender >> 2);
{
std::lock_guard<std::mutex> lk(soundCriticalSection);
ao_play(device, (char*)realtimeBuffer, numBytesToRender);
}
{
std::lock_guard<std::mutex> lk(soundCriticalSection);
ao_play(device, (char*)realtimeBuffer, numBytesToRender);
}
soundSyncEvent.Wait();
}
soundSyncEvent.Wait();
}
}
bool AOSound::Start()
{
m_run_thread.store(true);
memset(realtimeBuffer, 0, sizeof(realtimeBuffer));
m_run_thread.store(true);
memset(realtimeBuffer, 0, sizeof(realtimeBuffer));
thread = std::thread(&AOSound::SoundLoop, this);
return true;
thread = std::thread(&AOSound::SoundLoop, this);
return true;
}
void AOSound::Update()
{
soundSyncEvent.Set();
soundSyncEvent.Set();
}
void AOSound::Stop()
{
m_run_thread.store(false);
soundSyncEvent.Set();
m_run_thread.store(false);
soundSyncEvent.Set();
{
std::lock_guard<std::mutex> lk(soundCriticalSection);
thread.join();
{
std::lock_guard<std::mutex> lk(soundCriticalSection);
thread.join();
if (device)
ao_close(device);
if (device)
ao_close(device);
ao_shutdown();
ao_shutdown();
device = nullptr;
}
device = nullptr;
}
}
#endif

View File

@ -19,29 +19,25 @@
class AOSound final : public SoundStream
{
#if defined(HAVE_AO) && HAVE_AO
std::thread thread;
std::atomic<bool> m_run_thread;
std::mutex soundCriticalSection;
Common::Event soundSyncEvent;
std::thread thread;
std::atomic<bool> m_run_thread;
std::mutex soundCriticalSection;
Common::Event soundSyncEvent;
int buf_size;
int buf_size;
ao_device *device;
ao_sample_format format;
int default_driver;
ao_device* device;
ao_sample_format format;
int default_driver;
short realtimeBuffer[1024 * 1024];
short realtimeBuffer[1024 * 1024];
public:
bool Start() override;
void SoundLoop() override;
void Stop() override;
void Update() override;
static bool isValid()
{
return true;
}
bool Start() override;
void SoundLoop() override;
void Stop() override;
void Update() override;
static bool isValid() { return true; }
#endif
};

View File

@ -6,230 +6,229 @@
#include "AudioCommon/AlsaSoundStream.h"
#include "Common/CommonTypes.h"
#include "Common/Thread.h"
#include "Common/Logging/Log.h"
#include "Common/Thread.h"
AlsaSound::AlsaSound()
: m_thread_status(ALSAThreadStatus::STOPPED)
, handle(nullptr)
, frames_to_deliver(FRAME_COUNT_MIN)
: m_thread_status(ALSAThreadStatus::STOPPED), handle(nullptr),
frames_to_deliver(FRAME_COUNT_MIN)
{
}
bool AlsaSound::Start()
{
m_thread_status.store(ALSAThreadStatus::RUNNING);
if (!AlsaInit())
{
m_thread_status.store(ALSAThreadStatus::STOPPED);
return false;
}
m_thread_status.store(ALSAThreadStatus::RUNNING);
if (!AlsaInit())
{
m_thread_status.store(ALSAThreadStatus::STOPPED);
return false;
}
thread = std::thread(&AlsaSound::SoundLoop, this);
return true;
thread = std::thread(&AlsaSound::SoundLoop, this);
return true;
}
void AlsaSound::Stop()
{
m_thread_status.store(ALSAThreadStatus::STOPPING);
m_thread_status.store(ALSAThreadStatus::STOPPING);
//Give the opportunity to the audio thread
//to realize we are stopping the emulation
cv.notify_one();
thread.join();
// Give the opportunity to the audio thread
// to realize we are stopping the emulation
cv.notify_one();
thread.join();
}
void AlsaSound::Update()
{
// don't need to do anything here.
// don't need to do anything here.
}
// Called on audio thread.
void AlsaSound::SoundLoop()
{
Common::SetCurrentThreadName("Audio thread - alsa");
while (m_thread_status.load() != ALSAThreadStatus::STOPPING)
{
while (m_thread_status.load() == ALSAThreadStatus::RUNNING)
{
m_mixer->Mix(mix_buffer, frames_to_deliver);
int rc = snd_pcm_writei(handle, mix_buffer, frames_to_deliver);
if (rc == -EPIPE)
{
// Underrun
snd_pcm_prepare(handle);
}
else if (rc < 0)
{
ERROR_LOG(AUDIO, "writei fail: %s", snd_strerror(rc));
}
}
if (m_thread_status.load() == ALSAThreadStatus::PAUSED)
{
snd_pcm_drop(handle); // Stop sound output
Common::SetCurrentThreadName("Audio thread - alsa");
while (m_thread_status.load() != ALSAThreadStatus::STOPPING)
{
while (m_thread_status.load() == ALSAThreadStatus::RUNNING)
{
m_mixer->Mix(mix_buffer, frames_to_deliver);
int rc = snd_pcm_writei(handle, mix_buffer, frames_to_deliver);
if (rc == -EPIPE)
{
// Underrun
snd_pcm_prepare(handle);
}
else if (rc < 0)
{
ERROR_LOG(AUDIO, "writei fail: %s", snd_strerror(rc));
}
}
if (m_thread_status.load() == ALSAThreadStatus::PAUSED)
{
snd_pcm_drop(handle); // Stop sound output
// Block until thread status changes.
std::unique_lock<std::mutex> lock(cv_m);
cv.wait(lock, [this]{ return m_thread_status.load() != ALSAThreadStatus::PAUSED; });
// Block until thread status changes.
std::unique_lock<std::mutex> lock(cv_m);
cv.wait(lock, [this] { return m_thread_status.load() != ALSAThreadStatus::PAUSED; });
snd_pcm_prepare(handle); // resume sound output
}
}
AlsaShutdown();
m_thread_status.store(ALSAThreadStatus::STOPPED);
snd_pcm_prepare(handle); // resume sound output
}
}
AlsaShutdown();
m_thread_status.store(ALSAThreadStatus::STOPPED);
}
void AlsaSound::Clear(bool muted)
{
m_muted = muted;
m_thread_status.store(muted ? ALSAThreadStatus::PAUSED : ALSAThreadStatus::RUNNING);
cv.notify_one(); // Notify thread that status has changed
m_muted = muted;
m_thread_status.store(muted ? ALSAThreadStatus::PAUSED : ALSAThreadStatus::RUNNING);
cv.notify_one(); // Notify thread that status has changed
}
bool AlsaSound::AlsaInit()
{
unsigned int sample_rate = m_mixer->GetSampleRate();
int err;
int dir;
snd_pcm_sw_params_t *swparams;
snd_pcm_hw_params_t *hwparams;
snd_pcm_uframes_t buffer_size,buffer_size_max;
unsigned int periods;
unsigned int sample_rate = m_mixer->GetSampleRate();
int err;
int dir;
snd_pcm_sw_params_t* swparams;
snd_pcm_hw_params_t* hwparams;
snd_pcm_uframes_t buffer_size, buffer_size_max;
unsigned int periods;
err = snd_pcm_open(&handle, "default", SND_PCM_STREAM_PLAYBACK, 0);
if (err < 0)
{
ERROR_LOG(AUDIO, "Audio open error: %s\n", snd_strerror(err));
return false;
}
err = snd_pcm_open(&handle, "default", SND_PCM_STREAM_PLAYBACK, 0);
if (err < 0)
{
ERROR_LOG(AUDIO, "Audio open error: %s\n", snd_strerror(err));
return false;
}
snd_pcm_hw_params_alloca(&hwparams);
snd_pcm_hw_params_alloca(&hwparams);
err = snd_pcm_hw_params_any(handle, hwparams);
if (err < 0)
{
ERROR_LOG(AUDIO, "Broken configuration for this PCM: %s\n", snd_strerror(err));
return false;
}
err = snd_pcm_hw_params_any(handle, hwparams);
if (err < 0)
{
ERROR_LOG(AUDIO, "Broken configuration for this PCM: %s\n", snd_strerror(err));
return false;
}
err = snd_pcm_hw_params_set_access(handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED);
if (err < 0)
{
ERROR_LOG(AUDIO, "Access type not available: %s\n", snd_strerror(err));
return false;
}
err = snd_pcm_hw_params_set_access(handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED);
if (err < 0)
{
ERROR_LOG(AUDIO, "Access type not available: %s\n", snd_strerror(err));
return false;
}
err = snd_pcm_hw_params_set_format(handle, hwparams, SND_PCM_FORMAT_S16_LE);
if (err < 0)
{
ERROR_LOG(AUDIO, "Sample format not available: %s\n", snd_strerror(err));
return false;
}
err = snd_pcm_hw_params_set_format(handle, hwparams, SND_PCM_FORMAT_S16_LE);
if (err < 0)
{
ERROR_LOG(AUDIO, "Sample format not available: %s\n", snd_strerror(err));
return false;
}
dir = 0;
err = snd_pcm_hw_params_set_rate_near(handle, hwparams, &sample_rate, &dir);
if (err < 0)
{
ERROR_LOG(AUDIO, "Rate not available: %s\n", snd_strerror(err));
return false;
}
dir = 0;
err = snd_pcm_hw_params_set_rate_near(handle, hwparams, &sample_rate, &dir);
if (err < 0)
{
ERROR_LOG(AUDIO, "Rate not available: %s\n", snd_strerror(err));
return false;
}
err = snd_pcm_hw_params_set_channels(handle, hwparams, CHANNEL_COUNT);
if (err < 0)
{
ERROR_LOG(AUDIO, "Channels count not available: %s\n", snd_strerror(err));
return false;
}
err = snd_pcm_hw_params_set_channels(handle, hwparams, CHANNEL_COUNT);
if (err < 0)
{
ERROR_LOG(AUDIO, "Channels count not available: %s\n", snd_strerror(err));
return false;
}
periods = BUFFER_SIZE_MAX / FRAME_COUNT_MIN;
err = snd_pcm_hw_params_set_periods_max(handle, hwparams, &periods, &dir);
if (err < 0)
{
ERROR_LOG(AUDIO, "Cannot set maximum periods per buffer: %s\n", snd_strerror(err));
return false;
}
periods = BUFFER_SIZE_MAX / FRAME_COUNT_MIN;
err = snd_pcm_hw_params_set_periods_max(handle, hwparams, &periods, &dir);
if (err < 0)
{
ERROR_LOG(AUDIO, "Cannot set maximum periods per buffer: %s\n", snd_strerror(err));
return false;
}
buffer_size_max = BUFFER_SIZE_MAX;
err = snd_pcm_hw_params_set_buffer_size_max(handle, hwparams, &buffer_size_max);
if (err < 0)
{
ERROR_LOG(AUDIO, "Cannot set maximum buffer size: %s\n", snd_strerror(err));
return false;
}
buffer_size_max = BUFFER_SIZE_MAX;
err = snd_pcm_hw_params_set_buffer_size_max(handle, hwparams, &buffer_size_max);
if (err < 0)
{
ERROR_LOG(AUDIO, "Cannot set maximum buffer size: %s\n", snd_strerror(err));
return false;
}
err = snd_pcm_hw_params(handle, hwparams);
if (err < 0)
{
ERROR_LOG(AUDIO, "Unable to install hw params: %s\n", snd_strerror(err));
return false;
}
err = snd_pcm_hw_params(handle, hwparams);
if (err < 0)
{
ERROR_LOG(AUDIO, "Unable to install hw params: %s\n", snd_strerror(err));
return false;
}
err = snd_pcm_hw_params_get_buffer_size(hwparams, &buffer_size);
if (err < 0)
{
ERROR_LOG(AUDIO, "Cannot get buffer size: %s\n", snd_strerror(err));
return false;
}
err = snd_pcm_hw_params_get_buffer_size(hwparams, &buffer_size);
if (err < 0)
{
ERROR_LOG(AUDIO, "Cannot get buffer size: %s\n", snd_strerror(err));
return false;
}
err = snd_pcm_hw_params_get_periods_max(hwparams, &periods, &dir);
if (err < 0)
{
ERROR_LOG(AUDIO, "Cannot get periods: %s\n", snd_strerror(err));
return false;
}
err = snd_pcm_hw_params_get_periods_max(hwparams, &periods, &dir);
if (err < 0)
{
ERROR_LOG(AUDIO, "Cannot get periods: %s\n", snd_strerror(err));
return false;
}
//periods is the number of fragments alsa can wait for during one
//buffer_size
frames_to_deliver = buffer_size / periods;
//limit the minimum size. pulseaudio advertises a minimum of 32 samples.
if (frames_to_deliver < FRAME_COUNT_MIN)
frames_to_deliver = FRAME_COUNT_MIN;
//it is probably a bad idea to try to send more than one buffer of data
if ((unsigned int)frames_to_deliver > buffer_size)
frames_to_deliver = buffer_size;
NOTICE_LOG(AUDIO, "ALSA gave us a %ld sample \"hardware\" buffer with %d periods. Will send %d samples per fragments.\n", buffer_size, periods, frames_to_deliver);
// periods is the number of fragments alsa can wait for during one
// buffer_size
frames_to_deliver = buffer_size / periods;
// limit the minimum size. pulseaudio advertises a minimum of 32 samples.
if (frames_to_deliver < FRAME_COUNT_MIN)
frames_to_deliver = FRAME_COUNT_MIN;
// it is probably a bad idea to try to send more than one buffer of data
if ((unsigned int)frames_to_deliver > buffer_size)
frames_to_deliver = buffer_size;
NOTICE_LOG(AUDIO, "ALSA gave us a %ld sample \"hardware\" buffer with %d periods. Will send %d "
"samples per fragments.\n",
buffer_size, periods, frames_to_deliver);
snd_pcm_sw_params_alloca(&swparams);
snd_pcm_sw_params_alloca(&swparams);
err = snd_pcm_sw_params_current(handle, swparams);
if (err < 0)
{
ERROR_LOG(AUDIO, "cannot init sw params: %s\n", snd_strerror(err));
return false;
}
err = snd_pcm_sw_params_current(handle, swparams);
if (err < 0)
{
ERROR_LOG(AUDIO, "cannot init sw params: %s\n", snd_strerror(err));
return false;
}
err = snd_pcm_sw_params_set_start_threshold(handle, swparams, 0U);
if (err < 0)
{
ERROR_LOG(AUDIO, "cannot set start thresh: %s\n", snd_strerror(err));
return false;
}
err = snd_pcm_sw_params_set_start_threshold(handle, swparams, 0U);
if (err < 0)
{
ERROR_LOG(AUDIO, "cannot set start thresh: %s\n", snd_strerror(err));
return false;
}
err = snd_pcm_sw_params(handle, swparams);
if (err < 0)
{
ERROR_LOG(AUDIO, "cannot set sw params: %s\n", snd_strerror(err));
return false;
}
err = snd_pcm_sw_params(handle, swparams);
if (err < 0)
{
ERROR_LOG(AUDIO, "cannot set sw params: %s\n", snd_strerror(err));
return false;
}
err = snd_pcm_prepare(handle);
if (err < 0)
{
ERROR_LOG(AUDIO, "Unable to prepare: %s\n", snd_strerror(err));
return false;
}
NOTICE_LOG(AUDIO, "ALSA successfully initialized.\n");
return true;
err = snd_pcm_prepare(handle);
if (err < 0)
{
ERROR_LOG(AUDIO, "Unable to prepare: %s\n", snd_strerror(err));
return false;
}
NOTICE_LOG(AUDIO, "ALSA successfully initialized.\n");
return true;
}
void AlsaSound::AlsaShutdown()
{
if (handle != nullptr)
{
snd_pcm_drop(handle);
snd_pcm_close(handle);
handle = nullptr;
}
if (handle != nullptr)
{
snd_pcm_drop(handle);
snd_pcm_close(handle);
handle = nullptr;
}
}

View File

@ -20,47 +20,43 @@ class AlsaSound final : public SoundStream
{
#if defined(HAVE_ALSA) && HAVE_ALSA
public:
AlsaSound();
AlsaSound();
bool Start() override;
void SoundLoop() override;
void Stop() override;
void Update() override;
void Clear(bool) override;
static bool isValid()
{
return true;
}
bool Start() override;
void SoundLoop() override;
void Stop() override;
void Update() override;
void Clear(bool) override;
static bool isValid() { return true; }
private:
// maximum number of frames the buffer can hold
static constexpr size_t BUFFER_SIZE_MAX = 8192;
// maximum number of frames the buffer can hold
static constexpr size_t BUFFER_SIZE_MAX = 8192;
// minimum number of frames to deliver in one transfer
static constexpr u32 FRAME_COUNT_MIN = 256;
// minimum number of frames to deliver in one transfer
static constexpr u32 FRAME_COUNT_MIN = 256;
// number of channels per frame
static constexpr u32 CHANNEL_COUNT = 2;
// number of channels per frame
static constexpr u32 CHANNEL_COUNT = 2;
enum class ALSAThreadStatus
{
RUNNING,
PAUSED,
STOPPING,
STOPPED,
};
enum class ALSAThreadStatus
{
RUNNING,
PAUSED,
STOPPING,
STOPPED,
};
bool AlsaInit();
void AlsaShutdown();
bool AlsaInit();
void AlsaShutdown();
s16 mix_buffer[BUFFER_SIZE_MAX * CHANNEL_COUNT];
std::thread thread;
std::atomic<ALSAThreadStatus> m_thread_status;
std::condition_variable cv;
std::mutex cv_m;
s16 mix_buffer[BUFFER_SIZE_MAX * CHANNEL_COUNT];
std::thread thread;
std::atomic<ALSAThreadStatus> m_thread_status;
std::condition_variable cv;
std::mutex cv_m;
snd_pcm_t *handle;
unsigned int frames_to_deliver;
snd_pcm_t* handle;
unsigned int frames_to_deliver;
#endif
};

View File

@ -2,22 +2,21 @@
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include "AudioCommon/AlsaSoundStream.h"
#include "AudioCommon/AOSoundStream.h"
#include "AudioCommon/AudioCommon.h"
#include "AudioCommon/AOSoundStream.h"
#include "AudioCommon/AlsaSoundStream.h"
#include "AudioCommon/CoreAudioSoundStream.h"
#include "AudioCommon/Mixer.h"
#include "AudioCommon/NullSoundStream.h"
#include "AudioCommon/OpenALStream.h"
#include "AudioCommon/OpenSLESStream.h"
#include "AudioCommon/PulseAudioStream.h"
#include "AudioCommon/XAudio2_7Stream.h"
#include "AudioCommon/XAudio2Stream.h"
#include "AudioCommon/XAudio2_7Stream.h"
#include "Common/Common.h"
#include "Common/FileUtil.h"
#include "Common/MsgHandler.h"
#include "Common/Logging/Log.h"
#include "Common/MsgHandler.h"
#include "Core/ConfigManager.h"
#include "Core/Movie.h"
@ -28,182 +27,182 @@ static bool s_audio_dump_start = false;
namespace AudioCommon
{
static const int AUDIO_VOLUME_MIN = 0;
static const int AUDIO_VOLUME_MAX = 100;
static const int AUDIO_VOLUME_MIN = 0;
static const int AUDIO_VOLUME_MAX = 100;
SoundStream* InitSoundStream()
{
std::string backend = SConfig::GetInstance().sBackend;
if (backend == BACKEND_OPENAL && OpenALStream::isValid())
g_sound_stream = new OpenALStream();
else if (backend == BACKEND_NULLSOUND && NullSound::isValid())
g_sound_stream = new NullSound();
else if (backend == BACKEND_XAUDIO2)
{
if (XAudio2::isValid())
g_sound_stream = new XAudio2();
else if (XAudio2_7::isValid())
g_sound_stream = new XAudio2_7();
}
else if (backend == BACKEND_AOSOUND && AOSound::isValid())
g_sound_stream = new AOSound();
else if (backend == BACKEND_ALSA && AlsaSound::isValid())
g_sound_stream = new AlsaSound();
else if (backend == BACKEND_COREAUDIO && CoreAudioSound::isValid())
g_sound_stream = new CoreAudioSound();
else if (backend == BACKEND_PULSEAUDIO && PulseAudio::isValid())
g_sound_stream = new PulseAudio();
else if (backend == BACKEND_OPENSLES && OpenSLESStream::isValid())
g_sound_stream = new OpenSLESStream();
SoundStream* InitSoundStream()
{
std::string backend = SConfig::GetInstance().sBackend;
if (backend == BACKEND_OPENAL && OpenALStream::isValid())
g_sound_stream = new OpenALStream();
else if (backend == BACKEND_NULLSOUND && NullSound::isValid())
g_sound_stream = new NullSound();
else if (backend == BACKEND_XAUDIO2)
{
if (XAudio2::isValid())
g_sound_stream = new XAudio2();
else if (XAudio2_7::isValid())
g_sound_stream = new XAudio2_7();
}
else if (backend == BACKEND_AOSOUND && AOSound::isValid())
g_sound_stream = new AOSound();
else if (backend == BACKEND_ALSA && AlsaSound::isValid())
g_sound_stream = new AlsaSound();
else if (backend == BACKEND_COREAUDIO && CoreAudioSound::isValid())
g_sound_stream = new CoreAudioSound();
else if (backend == BACKEND_PULSEAUDIO && PulseAudio::isValid())
g_sound_stream = new PulseAudio();
else if (backend == BACKEND_OPENSLES && OpenSLESStream::isValid())
g_sound_stream = new OpenSLESStream();
if (!g_sound_stream && NullSound::isValid())
{
WARN_LOG(AUDIO, "Could not initialize backend %s, using %s instead.",
backend.c_str(), BACKEND_NULLSOUND);
g_sound_stream = new NullSound();
}
if (!g_sound_stream && NullSound::isValid())
{
WARN_LOG(AUDIO, "Could not initialize backend %s, using %s instead.", backend.c_str(),
BACKEND_NULLSOUND);
g_sound_stream = new NullSound();
}
if (g_sound_stream)
{
UpdateSoundStream();
if (!g_sound_stream->Start())
{
ERROR_LOG(AUDIO, "Could not start backend %s, using %s instead",
backend.c_str(), BACKEND_NULLSOUND);
delete g_sound_stream;
g_sound_stream = new NullSound();
g_sound_stream->Start();
}
if (g_sound_stream)
{
UpdateSoundStream();
if (!g_sound_stream->Start())
{
ERROR_LOG(AUDIO, "Could not start backend %s, using %s instead", backend.c_str(),
BACKEND_NULLSOUND);
delete g_sound_stream;
g_sound_stream = new NullSound();
g_sound_stream->Start();
}
if (SConfig::GetInstance().m_DumpAudio && !s_audio_dump_start)
StartAudioDump();
if (SConfig::GetInstance().m_DumpAudio && !s_audio_dump_start)
StartAudioDump();
return g_sound_stream;
}
return g_sound_stream;
}
PanicAlertT("Sound backend %s is not valid.", backend.c_str());
PanicAlertT("Sound backend %s is not valid.", backend.c_str());
delete g_sound_stream;
g_sound_stream = nullptr;
return nullptr;
}
void ShutdownSoundStream()
{
INFO_LOG(AUDIO, "Shutting down sound stream");
if (g_sound_stream)
{
g_sound_stream->Stop();
if (SConfig::GetInstance().m_DumpAudio && s_audio_dump_start)
StopAudioDump();
delete g_sound_stream;
g_sound_stream = nullptr;
}
INFO_LOG(AUDIO, "Done shutting down sound stream");
}
std::vector<std::string> GetSoundBackends()
{
std::vector<std::string> backends;
if (NullSound::isValid())
backends.push_back(BACKEND_NULLSOUND);
if (XAudio2_7::isValid() || XAudio2::isValid())
backends.push_back(BACKEND_XAUDIO2);
if (AOSound::isValid())
backends.push_back(BACKEND_AOSOUND);
if (AlsaSound::isValid())
backends.push_back(BACKEND_ALSA);
if (CoreAudioSound::isValid())
backends.push_back(BACKEND_COREAUDIO);
if (PulseAudio::isValid())
backends.push_back(BACKEND_PULSEAUDIO);
if (OpenALStream::isValid())
backends.push_back(BACKEND_OPENAL);
if (OpenSLESStream::isValid())
backends.push_back(BACKEND_OPENSLES);
return backends;
}
void UpdateSoundStream()
{
if (g_sound_stream)
{
int volume = SConfig::GetInstance().m_IsMuted ? 0 : SConfig::GetInstance().m_Volume;
g_sound_stream->SetVolume(volume);
}
}
void ClearAudioBuffer(bool mute)
{
if (g_sound_stream)
g_sound_stream->Clear(mute);
}
void SendAIBuffer(short *samples, unsigned int num_samples)
{
if (!g_sound_stream)
return;
if (SConfig::GetInstance().m_DumpAudio && !s_audio_dump_start)
StartAudioDump();
else if (!SConfig::GetInstance().m_DumpAudio && s_audio_dump_start)
StopAudioDump();
CMixer* pMixer = g_sound_stream->GetMixer();
if (pMixer && samples)
{
pMixer->PushSamples(samples, num_samples);
}
g_sound_stream->Update();
}
void StartAudioDump()
{
std::string audio_file_name_dtk = File::GetUserPath(D_DUMPAUDIO_IDX) + "dtkdump.wav";
std::string audio_file_name_dsp = File::GetUserPath(D_DUMPAUDIO_IDX) + "dspdump.wav";
File::CreateFullPath(audio_file_name_dtk);
File::CreateFullPath(audio_file_name_dsp);
g_sound_stream->GetMixer()->StartLogDTKAudio(audio_file_name_dtk);
g_sound_stream->GetMixer()->StartLogDSPAudio(audio_file_name_dsp);
s_audio_dump_start = true;
}
void StopAudioDump()
{
g_sound_stream->GetMixer()->StopLogDTKAudio();
g_sound_stream->GetMixer()->StopLogDSPAudio();
s_audio_dump_start = false;
}
void IncreaseVolume(unsigned short offset)
{
SConfig::GetInstance().m_IsMuted = false;
int& currentVolume = SConfig::GetInstance().m_Volume;
currentVolume += offset;
if (currentVolume > AUDIO_VOLUME_MAX)
currentVolume = AUDIO_VOLUME_MAX;
UpdateSoundStream();
}
void DecreaseVolume(unsigned short offset)
{
SConfig::GetInstance().m_IsMuted = false;
int& currentVolume = SConfig::GetInstance().m_Volume;
currentVolume -= offset;
if (currentVolume < AUDIO_VOLUME_MIN)
currentVolume = AUDIO_VOLUME_MIN;
UpdateSoundStream();
}
void ToggleMuteVolume()
{
bool& isMuted = SConfig::GetInstance().m_IsMuted;
isMuted = !isMuted;
UpdateSoundStream();
}
delete g_sound_stream;
g_sound_stream = nullptr;
return nullptr;
}
void ShutdownSoundStream()
{
INFO_LOG(AUDIO, "Shutting down sound stream");
if (g_sound_stream)
{
g_sound_stream->Stop();
if (SConfig::GetInstance().m_DumpAudio && s_audio_dump_start)
StopAudioDump();
delete g_sound_stream;
g_sound_stream = nullptr;
}
INFO_LOG(AUDIO, "Done shutting down sound stream");
}
std::vector<std::string> GetSoundBackends()
{
std::vector<std::string> backends;
if (NullSound::isValid())
backends.push_back(BACKEND_NULLSOUND);
if (XAudio2_7::isValid() || XAudio2::isValid())
backends.push_back(BACKEND_XAUDIO2);
if (AOSound::isValid())
backends.push_back(BACKEND_AOSOUND);
if (AlsaSound::isValid())
backends.push_back(BACKEND_ALSA);
if (CoreAudioSound::isValid())
backends.push_back(BACKEND_COREAUDIO);
if (PulseAudio::isValid())
backends.push_back(BACKEND_PULSEAUDIO);
if (OpenALStream::isValid())
backends.push_back(BACKEND_OPENAL);
if (OpenSLESStream::isValid())
backends.push_back(BACKEND_OPENSLES);
return backends;
}
void UpdateSoundStream()
{
if (g_sound_stream)
{
int volume = SConfig::GetInstance().m_IsMuted ? 0 : SConfig::GetInstance().m_Volume;
g_sound_stream->SetVolume(volume);
}
}
void ClearAudioBuffer(bool mute)
{
if (g_sound_stream)
g_sound_stream->Clear(mute);
}
void SendAIBuffer(short* samples, unsigned int num_samples)
{
if (!g_sound_stream)
return;
if (SConfig::GetInstance().m_DumpAudio && !s_audio_dump_start)
StartAudioDump();
else if (!SConfig::GetInstance().m_DumpAudio && s_audio_dump_start)
StopAudioDump();
CMixer* pMixer = g_sound_stream->GetMixer();
if (pMixer && samples)
{
pMixer->PushSamples(samples, num_samples);
}
g_sound_stream->Update();
}
void StartAudioDump()
{
std::string audio_file_name_dtk = File::GetUserPath(D_DUMPAUDIO_IDX) + "dtkdump.wav";
std::string audio_file_name_dsp = File::GetUserPath(D_DUMPAUDIO_IDX) + "dspdump.wav";
File::CreateFullPath(audio_file_name_dtk);
File::CreateFullPath(audio_file_name_dsp);
g_sound_stream->GetMixer()->StartLogDTKAudio(audio_file_name_dtk);
g_sound_stream->GetMixer()->StartLogDSPAudio(audio_file_name_dsp);
s_audio_dump_start = true;
}
void StopAudioDump()
{
g_sound_stream->GetMixer()->StopLogDTKAudio();
g_sound_stream->GetMixer()->StopLogDSPAudio();
s_audio_dump_start = false;
}
void IncreaseVolume(unsigned short offset)
{
SConfig::GetInstance().m_IsMuted = false;
int& currentVolume = SConfig::GetInstance().m_Volume;
currentVolume += offset;
if (currentVolume > AUDIO_VOLUME_MAX)
currentVolume = AUDIO_VOLUME_MAX;
UpdateSoundStream();
}
void DecreaseVolume(unsigned short offset)
{
SConfig::GetInstance().m_IsMuted = false;
int& currentVolume = SConfig::GetInstance().m_Volume;
currentVolume -= offset;
if (currentVolume < AUDIO_VOLUME_MIN)
currentVolume = AUDIO_VOLUME_MIN;
UpdateSoundStream();
}
void ToggleMuteVolume()
{
bool& isMuted = SConfig::GetInstance().m_IsMuted;
isMuted = !isMuted;
UpdateSoundStream();
}
}

View File

@ -7,22 +7,21 @@
#include "AudioCommon/SoundStream.h"
#include "Common/CommonTypes.h"
class CMixer;
extern SoundStream *g_sound_stream;
extern SoundStream* g_sound_stream;
namespace AudioCommon
{
SoundStream* InitSoundStream();
void ShutdownSoundStream();
std::vector<std::string> GetSoundBackends();
void UpdateSoundStream();
void ClearAudioBuffer(bool mute);
void SendAIBuffer(short* samples, unsigned int num_samples);
void StartAudioDump();
void StopAudioDump();
void IncreaseVolume(unsigned short offset);
void DecreaseVolume(unsigned short offset);
void ToggleMuteVolume();
SoundStream* InitSoundStream();
void ShutdownSoundStream();
std::vector<std::string> GetSoundBackends();
void UpdateSoundStream();
void ClearAudioBuffer(bool mute);
void SendAIBuffer(short* samples, unsigned int num_samples);
void StartAudioDump();
void StopAudioDump();
void IncreaseVolume(unsigned short offset);
void DecreaseVolume(unsigned short offset);
void ToggleMuteVolume();
}

View File

@ -7,105 +7,94 @@
#include "AudioCommon/CoreAudioSoundStream.h"
#include "Common/Logging/Log.h"
OSStatus CoreAudioSound::callback(void *inRefCon,
AudioUnitRenderActionFlags *ioActionFlags,
const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber,
UInt32 inNumberFrames, AudioBufferList *ioData)
OSStatus CoreAudioSound::callback(void* inRefCon, AudioUnitRenderActionFlags* ioActionFlags,
const AudioTimeStamp* inTimeStamp, UInt32 inBusNumber,
UInt32 inNumberFrames, AudioBufferList* ioData)
{
for (UInt32 i = 0; i < ioData->mNumberBuffers; i++)
((CoreAudioSound *)inRefCon)->m_mixer->
Mix((short *)ioData->mBuffers[i].mData,
ioData->mBuffers[i].mDataByteSize / 4);
for (UInt32 i = 0; i < ioData->mNumberBuffers; i++)
((CoreAudioSound*)inRefCon)
->m_mixer->Mix((short*)ioData->mBuffers[i].mData, ioData->mBuffers[i].mDataByteSize / 4);
return noErr;
return noErr;
}
bool CoreAudioSound::Start()
{
OSStatus err;
AURenderCallbackStruct callback_struct;
AudioStreamBasicDescription format;
AudioComponentDescription desc;
AudioComponent component;
OSStatus err;
AURenderCallbackStruct callback_struct;
AudioStreamBasicDescription format;
AudioComponentDescription desc;
AudioComponent component;
desc.componentType = kAudioUnitType_Output;
desc.componentSubType = kAudioUnitSubType_DefaultOutput;
desc.componentFlags = 0;
desc.componentFlagsMask = 0;
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
component = AudioComponentFindNext(nullptr, &desc);
if (component == nullptr)
{
ERROR_LOG(AUDIO, "error finding audio component");
return false;
}
desc.componentType = kAudioUnitType_Output;
desc.componentSubType = kAudioUnitSubType_DefaultOutput;
desc.componentFlags = 0;
desc.componentFlagsMask = 0;
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
component = AudioComponentFindNext(nullptr, &desc);
if (component == nullptr)
{
ERROR_LOG(AUDIO, "error finding audio component");
return false;
}
err = AudioComponentInstanceNew(component, &audioUnit);
if (err != noErr)
{
ERROR_LOG(AUDIO, "error opening audio component");
return false;
}
err = AudioComponentInstanceNew(component, &audioUnit);
if (err != noErr)
{
ERROR_LOG(AUDIO, "error opening audio component");
return false;
}
FillOutASBDForLPCM(format, m_mixer->GetSampleRate(),
2, 16, 16, false, false, false);
err = AudioUnitSetProperty(audioUnit,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Input, 0, &format,
sizeof(AudioStreamBasicDescription));
if (err != noErr)
{
ERROR_LOG(AUDIO, "error setting audio format");
return false;
}
FillOutASBDForLPCM(format, m_mixer->GetSampleRate(), 2, 16, 16, false, false, false);
err = AudioUnitSetProperty(audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0,
&format, sizeof(AudioStreamBasicDescription));
if (err != noErr)
{
ERROR_LOG(AUDIO, "error setting audio format");
return false;
}
callback_struct.inputProc = callback;
callback_struct.inputProcRefCon = this;
err = AudioUnitSetProperty(audioUnit,
kAudioUnitProperty_SetRenderCallback,
kAudioUnitScope_Input, 0, &callback_struct,
sizeof callback_struct);
if (err != noErr)
{
ERROR_LOG(AUDIO, "error setting audio callback");
return false;
}
callback_struct.inputProc = callback;
callback_struct.inputProcRefCon = this;
err = AudioUnitSetProperty(audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input,
0, &callback_struct, sizeof callback_struct);
if (err != noErr)
{
ERROR_LOG(AUDIO, "error setting audio callback");
return false;
}
err = AudioUnitSetParameter(audioUnit,
kHALOutputParam_Volume,
kAudioUnitScope_Output, 0,
m_volume / 100., 0);
if (err != noErr)
ERROR_LOG(AUDIO, "error setting volume");
err = AudioUnitSetParameter(audioUnit, kHALOutputParam_Volume, kAudioUnitScope_Output, 0,
m_volume / 100., 0);
if (err != noErr)
ERROR_LOG(AUDIO, "error setting volume");
err = AudioUnitInitialize(audioUnit);
if (err != noErr)
{
ERROR_LOG(AUDIO, "error initializing audiounit");
return false;
}
err = AudioUnitInitialize(audioUnit);
if (err != noErr)
{
ERROR_LOG(AUDIO, "error initializing audiounit");
return false;
}
err = AudioOutputUnitStart(audioUnit);
if (err != noErr)
{
ERROR_LOG(AUDIO, "error starting audiounit");
return false;
}
err = AudioOutputUnitStart(audioUnit);
if (err != noErr)
{
ERROR_LOG(AUDIO, "error starting audiounit");
return false;
}
return true;
return true;
}
void CoreAudioSound::SetVolume(int volume)
{
OSStatus err;
m_volume = volume;
OSStatus err;
m_volume = volume;
err = AudioUnitSetParameter(audioUnit,
kHALOutputParam_Volume,
kAudioUnitScope_Output, 0,
volume / 100., 0);
if (err != noErr)
ERROR_LOG(AUDIO, "error setting volume");
err = AudioUnitSetParameter(audioUnit, kHALOutputParam_Volume, kAudioUnitScope_Output, 0,
volume / 100., 0);
if (err != noErr)
ERROR_LOG(AUDIO, "error setting volume");
}
void CoreAudioSound::SoundLoop()
@ -114,19 +103,19 @@ void CoreAudioSound::SoundLoop()
void CoreAudioSound::Stop()
{
OSStatus err;
OSStatus err;
err = AudioOutputUnitStop(audioUnit);
if (err != noErr)
ERROR_LOG(AUDIO, "error stopping audiounit");
err = AudioOutputUnitStop(audioUnit);
if (err != noErr)
ERROR_LOG(AUDIO, "error stopping audiounit");
err = AudioUnitUninitialize(audioUnit);
if (err != noErr)
ERROR_LOG(AUDIO, "error uninitializing audiounit");
err = AudioUnitUninitialize(audioUnit);
if (err != noErr)
ERROR_LOG(AUDIO, "error uninitializing audiounit");
err = AudioComponentInstanceDispose(audioUnit);
if (err != noErr)
ERROR_LOG(AUDIO, "error closing audio component");
err = AudioComponentInstanceDispose(audioUnit);
if (err != noErr)
ERROR_LOG(AUDIO, "error closing audio component");
}
void CoreAudioSound::Update()

View File

@ -14,25 +14,19 @@ class CoreAudioSound final : public SoundStream
{
#ifdef __APPLE__
public:
bool Start() override;
void SetVolume(int volume) override;
void SoundLoop() override;
void Stop() override;
void Update() override;
static bool isValid()
{
return true;
}
bool Start() override;
void SetVolume(int volume) override;
void SoundLoop() override;
void Stop() override;
void Update() override;
static bool isValid() { return true; }
private:
AudioUnit audioUnit;
int m_volume;
AudioUnit audioUnit;
int m_volume;
static OSStatus callback(void *inRefCon,
AudioUnitRenderActionFlags *ioActionFlags,
const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber, UInt32 inNumberFrames,
AudioBufferList *ioData);
static OSStatus callback(void* inRefCon, AudioUnitRenderActionFlags* ioActionFlags,
const AudioTimeStamp* inTimeStamp, UInt32 inBusNumber,
UInt32 inNumberFrames, AudioBufferList* ioData);
#endif
};

View File

@ -35,54 +35,56 @@ static float adapt_l_gain, adapt_r_gain, adapt_lpr_gain, adapt_lmr_gain;
static std::vector<float> lf, rf, lr, rr, cf, cr;
static float LFE_buf[256];
static unsigned int lfe_pos;
static float *filter_coefs_lfe;
static float* filter_coefs_lfe;
static unsigned int len125;
template<class T, class _ftype_t>
static _ftype_t DotProduct(int count, const T *buf, const _ftype_t *coefficients)
template <class T, class _ftype_t>
static _ftype_t DotProduct(int count, const T* buf, const _ftype_t* coefficients)
{
int i;
float sum0 = 0.0f, sum1 = 0.0f, sum2 = 0.0f, sum3 = 0.0f;
int i;
float sum0 = 0.0f, sum1 = 0.0f, sum2 = 0.0f, sum3 = 0.0f;
// Unrolled loop
for (i = 0; (i + 3) < count; i += 4)
{
sum0 += buf[i + 0] * coefficients[i + 0];
sum1 += buf[i + 1] * coefficients[i + 1];
sum2 += buf[i + 2] * coefficients[i + 2];
sum3 += buf[i + 3] * coefficients[i + 3];
}
// Unrolled loop
for (i = 0; (i + 3) < count; i += 4)
{
sum0 += buf[i + 0] * coefficients[i + 0];
sum1 += buf[i + 1] * coefficients[i + 1];
sum2 += buf[i + 2] * coefficients[i + 2];
sum3 += buf[i + 3] * coefficients[i + 3];
}
// Epilogue of unrolled loop
for (; i < count; i++)
sum0 += buf[i] * coefficients[i];
// Epilogue of unrolled loop
for (; i < count; i++)
sum0 += buf[i] * coefficients[i];
return sum0 + sum1 + sum2 + sum3;
return sum0 + sum1 + sum2 + sum3;
}
template<class T>
static T FIRFilter(const T *buf, int pos, int len, int count, const float *coefficients)
template <class T>
static T FIRFilter(const T* buf, int pos, int len, int count, const float* coefficients)
{
int count1, count2;
int count1, count2;
if (pos >= count)
{
pos -= count;
count1 = count; count2 = 0;
}
else
{
count2 = pos;
count1 = count - pos;
pos = len - count1;
}
if (pos >= count)
{
pos -= count;
count1 = count;
count2 = 0;
}
else
{
count2 = pos;
count1 = count - pos;
pos = len - count1;
}
// high part of window
const T *ptr = &buf[pos];
// high part of window
const T* ptr = &buf[pos];
float r1 = DotProduct(count1, ptr, coefficients); coefficients += count1;
float r2 = DotProduct(count2, buf, coefficients);
return T(r1 + r2);
float r1 = DotProduct(count1, ptr, coefficients);
coefficients += count1;
float r2 = DotProduct(count2, buf, coefficients);
return T(r1 + r2);
}
/*
@ -96,11 +98,11 @@ static T FIRFilter(const T *buf, int pos, int len, int count, const float *coeff
*/
static void Hamming(int n, float* w)
{
float k = float(2*M_PI/((float)(n - 1))); // 2*pi/(N-1)
float k = float(2 * M_PI / ((float)(n - 1))); // 2*pi/(N-1)
// Calculate window coefficients
for (int i = 0; i < n; i++)
*w++ = float(0.54 - 0.46*cos(k*(float)i));
// Calculate window coefficients
for (int i = 0; i < n; i++)
*w++ = float(0.54 - 0.46 * cos(k * (float)i));
}
/******************************************************************************
@ -120,276 +122,270 @@ opt beta constant used only when designing using kaiser windows
returns 0 if OK, -1 if fail
*/
static float* DesignFIR(unsigned int *n, float* fc, float opt)
static float* DesignFIR(unsigned int* n, float* fc, float opt)
{
unsigned int o = *n & 1; // Indicator for odd filter length
unsigned int end = ((*n + 1) >> 1) - o; // Loop end
unsigned int o = *n & 1; // Indicator for odd filter length
unsigned int end = ((*n + 1) >> 1) - o; // Loop end
float k1 = 2 * float(M_PI); // 2*pi*fc1
float k2 = 0.5f * (float)(1 - o); // Constant used if the filter has even length
float g = 0.0f; // Gain
float t1; // Temporary variables
float fc1; // Cutoff frequencies
float k1 = 2 * float(M_PI); // 2*pi*fc1
float k2 = 0.5f * (float)(1 - o); // Constant used if the filter has even length
float g = 0.0f; // Gain
float t1; // Temporary variables
float fc1; // Cutoff frequencies
// Sanity check
if (*n == 0)
return nullptr;
// Sanity check
if (*n == 0)
return nullptr;
fc[0] = MathUtil::Clamp(fc[0], 0.001f, 1.0f);
fc[0] = MathUtil::Clamp(fc[0], 0.001f, 1.0f);
float *w = (float*)calloc(sizeof(float), *n);
float* w = (float*)calloc(sizeof(float), *n);
// Get window coefficients
Hamming(*n, w);
// Get window coefficients
Hamming(*n, w);
fc1 = *fc;
// Cutoff frequency must be < 0.5 where 0.5 <=> Fs/2
fc1 = ((fc1 <= 1.0) && (fc1 > 0.0)) ? fc1 / 2 : 0.25f;
k1 *= fc1;
fc1 = *fc;
// Cutoff frequency must be < 0.5 where 0.5 <=> Fs/2
fc1 = ((fc1 <= 1.0) && (fc1 > 0.0)) ? fc1 / 2 : 0.25f;
k1 *= fc1;
// Low pass filter
// Low pass filter
// If the filter length is odd, there is one point which is exactly
// in the middle. The value at this point is 2*fCutoff*sin(x)/x,
// where x is zero. To make sure nothing strange happens, we set this
// value separately.
if (o)
{
w[end] = fc1 * w[end] * 2.0f;
g = w[end];
}
// If the filter length is odd, there is one point which is exactly
// in the middle. The value at this point is 2*fCutoff*sin(x)/x,
// where x is zero. To make sure nothing strange happens, we set this
// value separately.
if (o)
{
w[end] = fc1 * w[end] * 2.0f;
g = w[end];
}
// Create filter
for (u32 i = 0; i < end; i++)
{
t1 = (float)(i + 1) - k2;
w[end - i - 1] = w[*n - end + i] = float(w[end - i - 1] * sin(k1 * t1)/(M_PI * t1)); // Sinc
g += 2*w[end - i - 1]; // Total gain in filter
}
// Create filter
for (u32 i = 0; i < end; i++)
{
t1 = (float)(i + 1) - k2;
w[end - i - 1] = w[*n - end + i] = float(w[end - i - 1] * sin(k1 * t1) / (M_PI * t1)); // Sinc
g += 2 * w[end - i - 1]; // Total gain in filter
}
// Normalize gain
g = 1 / g;
for (u32 i = 0; i < *n; i++)
w[i] *= g;
// Normalize gain
g = 1/g;
for (u32 i = 0; i < *n; i++)
w[i] *= g;
return w;
return w;
}
static void OnSeek()
{
l_fwr = r_fwr = lpr_fwr = lmr_fwr = 0;
std::fill(fwrbuf_l.begin(), fwrbuf_l.end(), 0.0f);
std::fill(fwrbuf_r.begin(), fwrbuf_r.end(), 0.0f);
adapt_l_gain = adapt_r_gain = adapt_lpr_gain = adapt_lmr_gain = 0;
std::fill(lf.begin(), lf.end(), 0.0f);
std::fill(rf.begin(), rf.end(), 0.0f);
std::fill(lr.begin(), lr.end(), 0.0f);
std::fill(rr.begin(), rr.end(), 0.0f);
std::fill(cf.begin(), cf.end(), 0.0f);
std::fill(cr.begin(), cr.end(), 0.0f);
lfe_pos = 0;
memset(LFE_buf, 0, sizeof(LFE_buf));
l_fwr = r_fwr = lpr_fwr = lmr_fwr = 0;
std::fill(fwrbuf_l.begin(), fwrbuf_l.end(), 0.0f);
std::fill(fwrbuf_r.begin(), fwrbuf_r.end(), 0.0f);
adapt_l_gain = adapt_r_gain = adapt_lpr_gain = adapt_lmr_gain = 0;
std::fill(lf.begin(), lf.end(), 0.0f);
std::fill(rf.begin(), rf.end(), 0.0f);
std::fill(lr.begin(), lr.end(), 0.0f);
std::fill(rr.begin(), rr.end(), 0.0f);
std::fill(cf.begin(), cf.end(), 0.0f);
std::fill(cr.begin(), cr.end(), 0.0f);
lfe_pos = 0;
memset(LFE_buf, 0, sizeof(LFE_buf));
}
static void Done()
{
OnSeek();
OnSeek();
if (filter_coefs_lfe)
{
free(filter_coefs_lfe);
}
if (filter_coefs_lfe)
{
free(filter_coefs_lfe);
}
filter_coefs_lfe = nullptr;
filter_coefs_lfe = nullptr;
}
static float* CalculateCoefficients125HzLowpass(int rate)
{
len125 = 256;
float f = 125.0f / (rate / 2);
float *coeffs = DesignFIR(&len125, &f, 0);
static const float M3_01DB = 0.7071067812f;
for (unsigned int i = 0; i < len125; i++)
{
coeffs[i] *= M3_01DB;
}
return coeffs;
len125 = 256;
float f = 125.0f / (rate / 2);
float* coeffs = DesignFIR(&len125, &f, 0);
static const float M3_01DB = 0.7071067812f;
for (unsigned int i = 0; i < len125; i++)
{
coeffs[i] *= M3_01DB;
}
return coeffs;
}
static float PassiveLock(float x)
{
static const float MATAGCLOCK = 0.2f; /* AGC range (around 1) where the matrix behaves passively */
const float x1 = x - 1;
const float ax1s = fabs(x - 1) * (1.0f / MATAGCLOCK);
return x1 - x1 / (1 + ax1s * ax1s) + 1;
static const float MATAGCLOCK =
0.2f; /* AGC range (around 1) where the matrix behaves passively */
const float x1 = x - 1;
const float ax1s = fabs(x - 1) * (1.0f / MATAGCLOCK);
return x1 - x1 / (1 + ax1s * ax1s) + 1;
}
static void MatrixDecode(const float *in, const int k, const int il,
const int ir, bool decode_rear,
const int _dlbuflen,
float _l_fwr, float _r_fwr,
float _lpr_fwr, float _lmr_fwr,
float *_adapt_l_gain, float *_adapt_r_gain,
float *_adapt_lpr_gain, float *_adapt_lmr_gain,
float *_lf, float *_rf, float *_lr,
float *_rr, float *_cf)
static void MatrixDecode(const float* in, const int k, const int il, const int ir, bool decode_rear,
const int _dlbuflen, float _l_fwr, float _r_fwr, float _lpr_fwr,
float _lmr_fwr, float* _adapt_l_gain, float* _adapt_r_gain,
float* _adapt_lpr_gain, float* _adapt_lmr_gain, float* _lf, float* _rf,
float* _lr, float* _rr, float* _cf)
{
static const float M9_03DB = 0.3535533906f;
static const float MATAGCTRIG = 8.0f; /* (Fuzzy) AGC trigger */
static const float MATAGCDECAY = 1.0f; /* AGC baseline decay rate (1/samp.) */
static const float MATCOMPGAIN = 0.37f; /* Cross talk compensation gain, 0.50 - 0.55 is full cancellation. */
static const float M9_03DB = 0.3535533906f;
static const float MATAGCTRIG = 8.0f; /* (Fuzzy) AGC trigger */
static const float MATAGCDECAY = 1.0f; /* AGC baseline decay rate (1/samp.) */
static const float MATCOMPGAIN =
0.37f; /* Cross talk compensation gain, 0.50 - 0.55 is full cancellation. */
const int kr = (k + olddelay) % _dlbuflen;
float l_gain = (_l_fwr + _r_fwr) / (1 + _l_fwr + _l_fwr);
float r_gain = (_l_fwr + _r_fwr) / (1 + _r_fwr + _r_fwr);
// The 2nd axis has strong gain fluctuations, and therefore require
// limits. The factor corresponds to the 1 / amplification of (Lt
// - Rt) when (Lt, Rt) is strongly correlated. (e.g. during
// dialogues). It should be bigger than -12 dB to prevent
// distortion.
float lmr_lim_fwr = _lmr_fwr > M9_03DB * _lpr_fwr ? _lmr_fwr : M9_03DB * _lpr_fwr;
float lpr_gain = (_lpr_fwr + lmr_lim_fwr) / (1 + _lpr_fwr + _lpr_fwr);
float lmr_gain = (_lpr_fwr + lmr_lim_fwr) / (1 + lmr_lim_fwr + lmr_lim_fwr);
float lmr_unlim_gain = (_lpr_fwr + _lmr_fwr) / (1 + _lmr_fwr + _lmr_fwr);
float lpr, lmr;
float l_agc, r_agc, lpr_agc, lmr_agc;
float f, d_gain, c_gain, c_agc_cfk;
const int kr = (k + olddelay) % _dlbuflen;
float l_gain = (_l_fwr + _r_fwr) / (1 + _l_fwr + _l_fwr);
float r_gain = (_l_fwr + _r_fwr) / (1 + _r_fwr + _r_fwr);
// The 2nd axis has strong gain fluctuations, and therefore require
// limits. The factor corresponds to the 1 / amplification of (Lt
// - Rt) when (Lt, Rt) is strongly correlated. (e.g. during
// dialogues). It should be bigger than -12 dB to prevent
// distortion.
float lmr_lim_fwr = _lmr_fwr > M9_03DB * _lpr_fwr ? _lmr_fwr : M9_03DB * _lpr_fwr;
float lpr_gain = (_lpr_fwr + lmr_lim_fwr) / (1 + _lpr_fwr + _lpr_fwr);
float lmr_gain = (_lpr_fwr + lmr_lim_fwr) / (1 + lmr_lim_fwr + lmr_lim_fwr);
float lmr_unlim_gain = (_lpr_fwr + _lmr_fwr) / (1 + _lmr_fwr + _lmr_fwr);
float lpr, lmr;
float l_agc, r_agc, lpr_agc, lmr_agc;
float f, d_gain, c_gain, c_agc_cfk;
/*** AXIS NO. 1: (Lt, Rt) -> (C, Ls, Rs) ***/
/* AGC adaption */
d_gain = (fabs(l_gain - *_adapt_l_gain) + fabs(r_gain - *_adapt_r_gain)) * 0.5f;
f = d_gain * (1.0f / MATAGCTRIG);
f = MATAGCDECAY - MATAGCDECAY / (1 + f * f);
*_adapt_l_gain = (1 - f) * *_adapt_l_gain + f * l_gain;
*_adapt_r_gain = (1 - f) * *_adapt_r_gain + f * r_gain;
/* Matrix */
l_agc = in[il] * PassiveLock(*_adapt_l_gain);
r_agc = in[ir] * PassiveLock(*_adapt_r_gain);
_cf[k] = (l_agc + r_agc) * (float)M_SQRT1_2;
if (decode_rear)
{
_lr[kr] = _rr[kr] = (l_agc - r_agc) * (float)M_SQRT1_2;
// Stereo rear channel is steered with the same AGC steering as
// the decoding matrix. Note this requires a fast updating AGC
// at the order of 20 ms (which is the case here).
_lr[kr] *= (_l_fwr + _l_fwr) / (1 + _l_fwr + _r_fwr);
_rr[kr] *= (_r_fwr + _r_fwr) / (1 + _l_fwr + _r_fwr);
}
/*** AXIS NO. 1: (Lt, Rt) -> (C, Ls, Rs) ***/
/* AGC adaption */
d_gain = (fabs(l_gain - *_adapt_l_gain) + fabs(r_gain - *_adapt_r_gain)) * 0.5f;
f = d_gain * (1.0f / MATAGCTRIG);
f = MATAGCDECAY - MATAGCDECAY / (1 + f * f);
*_adapt_l_gain = (1 - f) * *_adapt_l_gain + f * l_gain;
*_adapt_r_gain = (1 - f) * *_adapt_r_gain + f * r_gain;
/* Matrix */
l_agc = in[il] * PassiveLock(*_adapt_l_gain);
r_agc = in[ir] * PassiveLock(*_adapt_r_gain);
_cf[k] = (l_agc + r_agc) * (float)M_SQRT1_2;
if (decode_rear)
{
_lr[kr] = _rr[kr] = (l_agc - r_agc) * (float)M_SQRT1_2;
// Stereo rear channel is steered with the same AGC steering as
// the decoding matrix. Note this requires a fast updating AGC
// at the order of 20 ms (which is the case here).
_lr[kr] *= (_l_fwr + _l_fwr) / (1 + _l_fwr + _r_fwr);
_rr[kr] *= (_r_fwr + _r_fwr) / (1 + _l_fwr + _r_fwr);
}
/*** AXIS NO. 2: (Lt + Rt, Lt - Rt) -> (L, R) ***/
lpr = (in[il] + in[ir]) * (float)M_SQRT1_2;
lmr = (in[il] - in[ir]) * (float)M_SQRT1_2;
/* AGC adaption */
d_gain = fabs(lmr_unlim_gain - *_adapt_lmr_gain);
f = d_gain * (1.0f / MATAGCTRIG);
f = MATAGCDECAY - MATAGCDECAY / (1 + f * f);
*_adapt_lpr_gain = (1 - f) * *_adapt_lpr_gain + f * lpr_gain;
*_adapt_lmr_gain = (1 - f) * *_adapt_lmr_gain + f * lmr_gain;
/* Matrix */
lpr_agc = lpr * PassiveLock(*_adapt_lpr_gain);
lmr_agc = lmr * PassiveLock(*_adapt_lmr_gain);
_lf[k] = (lpr_agc + lmr_agc) * (float)M_SQRT1_2;
_rf[k] = (lpr_agc - lmr_agc) * (float)M_SQRT1_2;
/*** AXIS NO. 2: (Lt + Rt, Lt - Rt) -> (L, R) ***/
lpr = (in[il] + in[ir]) * (float)M_SQRT1_2;
lmr = (in[il] - in[ir]) * (float)M_SQRT1_2;
/* AGC adaption */
d_gain = fabs(lmr_unlim_gain - *_adapt_lmr_gain);
f = d_gain * (1.0f / MATAGCTRIG);
f = MATAGCDECAY - MATAGCDECAY / (1 + f * f);
*_adapt_lpr_gain = (1 - f) * *_adapt_lpr_gain + f * lpr_gain;
*_adapt_lmr_gain = (1 - f) * *_adapt_lmr_gain + f * lmr_gain;
/* Matrix */
lpr_agc = lpr * PassiveLock(*_adapt_lpr_gain);
lmr_agc = lmr * PassiveLock(*_adapt_lmr_gain);
_lf[k] = (lpr_agc + lmr_agc) * (float)M_SQRT1_2;
_rf[k] = (lpr_agc - lmr_agc) * (float)M_SQRT1_2;
/*** CENTER FRONT CANCELLATION ***/
// A heuristic approach exploits that Lt + Rt gain contains the
// information about Lt, Rt correlation. This effectively reshapes
// the front and rear "cones" to concentrate Lt + Rt to C and
// introduce Lt - Rt in L, R.
/* 0.67677 is the empirical lower bound for lpr_gain. */
c_gain = 8 * (*_adapt_lpr_gain - 0.67677f);
c_gain = c_gain > 0 ? c_gain : 0;
// c_gain should not be too high, not even reaching full
// cancellation (~ 0.50 - 0.55 at current AGC implementation), or
// the center will sound too narrow. */
c_gain = MATCOMPGAIN / (1 + c_gain * c_gain);
c_agc_cfk = c_gain * _cf[k];
_lf[k] -= c_agc_cfk;
_rf[k] -= c_agc_cfk;
_cf[k] += c_agc_cfk + c_agc_cfk;
/*** CENTER FRONT CANCELLATION ***/
// A heuristic approach exploits that Lt + Rt gain contains the
// information about Lt, Rt correlation. This effectively reshapes
// the front and rear "cones" to concentrate Lt + Rt to C and
// introduce Lt - Rt in L, R.
/* 0.67677 is the empirical lower bound for lpr_gain. */
c_gain = 8 * (*_adapt_lpr_gain - 0.67677f);
c_gain = c_gain > 0 ? c_gain : 0;
// c_gain should not be too high, not even reaching full
// cancellation (~ 0.50 - 0.55 at current AGC implementation), or
// the center will sound too narrow. */
c_gain = MATCOMPGAIN / (1 + c_gain * c_gain);
c_agc_cfk = c_gain * _cf[k];
_lf[k] -= c_agc_cfk;
_rf[k] -= c_agc_cfk;
_cf[k] += c_agc_cfk + c_agc_cfk;
}
void DPL2Decode(float *samples, int numsamples, float *out)
void DPL2Decode(float* samples, int numsamples, float* out)
{
static const unsigned int FWRDURATION = 240; // FWR average duration (samples)
static const int cfg_delay = 0;
static const unsigned int fmt_freq = 48000;
static const unsigned int fmt_nchannels = 2; // input channels
static const unsigned int FWRDURATION = 240; // FWR average duration (samples)
static const int cfg_delay = 0;
static const unsigned int fmt_freq = 48000;
static const unsigned int fmt_nchannels = 2; // input channels
int cur = 0;
int cur = 0;
if (olddelay != cfg_delay || oldfreq != fmt_freq)
{
Done();
olddelay = cfg_delay;
oldfreq = fmt_freq;
dlbuflen = std::max(FWRDURATION, (fmt_freq * cfg_delay / 1000)); //+(len7000-1);
cyc_pos = dlbuflen - 1;
fwrbuf_l.resize(dlbuflen);
fwrbuf_r.resize(dlbuflen);
lf.resize(dlbuflen);
rf.resize(dlbuflen);
lr.resize(dlbuflen);
rr.resize(dlbuflen);
cf.resize(dlbuflen);
cr.resize(dlbuflen);
filter_coefs_lfe = CalculateCoefficients125HzLowpass(fmt_freq);
lfe_pos = 0;
memset(LFE_buf, 0, sizeof(LFE_buf));
}
if (olddelay != cfg_delay || oldfreq != fmt_freq)
{
Done();
olddelay = cfg_delay;
oldfreq = fmt_freq;
dlbuflen = std::max(FWRDURATION, (fmt_freq * cfg_delay / 1000)); //+(len7000-1);
cyc_pos = dlbuflen - 1;
fwrbuf_l.resize(dlbuflen);
fwrbuf_r.resize(dlbuflen);
lf.resize(dlbuflen);
rf.resize(dlbuflen);
lr.resize(dlbuflen);
rr.resize(dlbuflen);
cf.resize(dlbuflen);
cr.resize(dlbuflen);
filter_coefs_lfe = CalculateCoefficients125HzLowpass(fmt_freq);
lfe_pos = 0;
memset(LFE_buf, 0, sizeof(LFE_buf));
}
float *in = samples; // Input audio data
float *end = in + numsamples * fmt_nchannels; // Loop end
float* in = samples; // Input audio data
float* end = in + numsamples * fmt_nchannels; // Loop end
while (in < end)
{
const int k = cyc_pos;
while (in < end)
{
const int k = cyc_pos;
const int fwr_pos = (k + FWRDURATION) % dlbuflen;
/* Update the full wave rectified total amplitude */
/* Input matrix decoder */
l_fwr += fabs(in[0]) - fabs(fwrbuf_l[fwr_pos]);
r_fwr += fabs(in[1]) - fabs(fwrbuf_r[fwr_pos]);
lpr_fwr += fabs(in[0] + in[1]) - fabs(fwrbuf_l[fwr_pos] + fwrbuf_r[fwr_pos]);
lmr_fwr += fabs(in[0] - in[1]) - fabs(fwrbuf_l[fwr_pos] - fwrbuf_r[fwr_pos]);
const int fwr_pos = (k + FWRDURATION) % dlbuflen;
/* Update the full wave rectified total amplitude */
/* Input matrix decoder */
l_fwr += fabs(in[0]) - fabs(fwrbuf_l[fwr_pos]);
r_fwr += fabs(in[1]) - fabs(fwrbuf_r[fwr_pos]);
lpr_fwr += fabs(in[0] + in[1]) - fabs(fwrbuf_l[fwr_pos] + fwrbuf_r[fwr_pos]);
lmr_fwr += fabs(in[0] - in[1]) - fabs(fwrbuf_l[fwr_pos] - fwrbuf_r[fwr_pos]);
/* Matrix encoded 2 channel sources */
fwrbuf_l[k] = in[0];
fwrbuf_r[k] = in[1];
MatrixDecode(in, k, 0, 1, true, dlbuflen,
l_fwr, r_fwr,
lpr_fwr, lmr_fwr,
&adapt_l_gain, &adapt_r_gain,
&adapt_lpr_gain, &adapt_lmr_gain,
&lf[0], &rf[0], &lr[0], &rr[0], &cf[0]);
/* Matrix encoded 2 channel sources */
fwrbuf_l[k] = in[0];
fwrbuf_r[k] = in[1];
MatrixDecode(in, k, 0, 1, true, dlbuflen, l_fwr, r_fwr, lpr_fwr, lmr_fwr, &adapt_l_gain,
&adapt_r_gain, &adapt_lpr_gain, &adapt_lmr_gain, &lf[0], &rf[0], &lr[0], &rr[0],
&cf[0]);
out[cur + 0] = lf[k];
out[cur + 1] = rf[k];
out[cur + 2] = cf[k];
LFE_buf[lfe_pos] = (lf[k] + rf[k] + 2.0f * cf[k] + lr[k] + rr[k]) / 2.0f;
out[cur + 3] = FIRFilter(LFE_buf, lfe_pos, len125, len125, filter_coefs_lfe);
lfe_pos++;
if (lfe_pos == len125)
{
lfe_pos = 0;
}
out[cur + 4] = lr[k];
out[cur + 5] = rr[k];
// Next sample...
in += 2;
cur += 6;
cyc_pos--;
if (cyc_pos < 0)
{
cyc_pos += dlbuflen;
}
}
out[cur + 0] = lf[k];
out[cur + 1] = rf[k];
out[cur + 2] = cf[k];
LFE_buf[lfe_pos] = (lf[k] + rf[k] + 2.0f * cf[k] + lr[k] + rr[k]) / 2.0f;
out[cur + 3] = FIRFilter(LFE_buf, lfe_pos, len125, len125, filter_coefs_lfe);
lfe_pos++;
if (lfe_pos == len125)
{
lfe_pos = 0;
}
out[cur + 4] = lr[k];
out[cur + 5] = rr[k];
// Next sample...
in += 2;
cur += 6;
cyc_pos--;
if (cyc_pos < 0)
{
cyc_pos += dlbuflen;
}
}
}
void DPL2Reset()
{
olddelay = -1;
oldfreq = 0;
filter_coefs_lfe = nullptr;
olddelay = -1;
oldfreq = 0;
filter_coefs_lfe = nullptr;
}

View File

@ -4,5 +4,5 @@
#pragma once
void DPL2Decode(float *samples, int numsamples, float *out);
void DPL2Decode(float* samples, int numsamples, float* out);
void DPL2Reset();

View File

@ -8,18 +8,17 @@
#include "AudioCommon/Mixer.h"
#include "Common/CommonFuncs.h"
#include "Common/CommonTypes.h"
#include "Common/MathUtil.h"
#include "Common/Logging/Log.h"
#include "Common/MathUtil.h"
#include "Core/ConfigManager.h"
#if _M_SSE >= 0x301 && !(defined __GNUC__ && !defined __SSSE3__)
#include <tmmintrin.h>
#endif
CMixer::CMixer(unsigned int BackendSampleRate)
: m_sampleRate(BackendSampleRate)
CMixer::CMixer(unsigned int BackendSampleRate) : m_sampleRate(BackendSampleRate)
{
INFO_LOG(AUDIO_INTERFACE, "Mixer is initialized");
INFO_LOG(AUDIO_INTERFACE, "Mixer is initialized");
}
CMixer::~CMixer()
@ -27,249 +26,253 @@ CMixer::~CMixer()
}
// Executed from sound stream thread
unsigned int CMixer::MixerFifo::Mix(short* samples, unsigned int numSamples, bool consider_framelimit)
unsigned int CMixer::MixerFifo::Mix(short* samples, unsigned int numSamples,
bool consider_framelimit)
{
unsigned int currentSample = 0;
unsigned int currentSample = 0;
// Cache access in non-volatile variable
// This is the only function changing the read value, so it's safe to
// cache it locally although it's written here.
// The writing pointer will be modified outside, but it will only increase,
// so we will just ignore new written data while interpolating.
// Without this cache, the compiler wouldn't be allowed to optimize the
// interpolation loop.
u32 indexR = m_indexR.load();
u32 indexW = m_indexW.load();
// Cache access in non-volatile variable
// This is the only function changing the read value, so it's safe to
// cache it locally although it's written here.
// The writing pointer will be modified outside, but it will only increase,
// so we will just ignore new written data while interpolating.
// Without this cache, the compiler wouldn't be allowed to optimize the
// interpolation loop.
u32 indexR = m_indexR.load();
u32 indexW = m_indexW.load();
u32 low_waterwark = m_input_sample_rate * SConfig::GetInstance().iTimingVariance / 1000;
low_waterwark = std::min(low_waterwark, MAX_SAMPLES / 2);
u32 low_waterwark = m_input_sample_rate * SConfig::GetInstance().iTimingVariance / 1000;
low_waterwark = std::min(low_waterwark, MAX_SAMPLES / 2);
float numLeft = (float)(((indexW - indexR) & INDEX_MASK) / 2);
m_numLeftI = (numLeft + m_numLeftI*(CONTROL_AVG-1)) / CONTROL_AVG;
float offset = (m_numLeftI - low_waterwark) * CONTROL_FACTOR;
if (offset > MAX_FREQ_SHIFT) offset = MAX_FREQ_SHIFT;
if (offset < -MAX_FREQ_SHIFT) offset = -MAX_FREQ_SHIFT;
float numLeft = (float)(((indexW - indexR) & INDEX_MASK) / 2);
m_numLeftI = (numLeft + m_numLeftI * (CONTROL_AVG - 1)) / CONTROL_AVG;
float offset = (m_numLeftI - low_waterwark) * CONTROL_FACTOR;
if (offset > MAX_FREQ_SHIFT)
offset = MAX_FREQ_SHIFT;
if (offset < -MAX_FREQ_SHIFT)
offset = -MAX_FREQ_SHIFT;
//render numleft sample pairs to samples[]
//advance indexR with sample position
//remember fractional offset
// render numleft sample pairs to samples[]
// advance indexR with sample position
// remember fractional offset
float emulationspeed = SConfig::GetInstance().m_EmulationSpeed;
float aid_sample_rate = m_input_sample_rate + offset;
if (consider_framelimit && emulationspeed > 0.0f)
{
aid_sample_rate = aid_sample_rate * emulationspeed;
}
float emulationspeed = SConfig::GetInstance().m_EmulationSpeed;
float aid_sample_rate = m_input_sample_rate + offset;
if (consider_framelimit && emulationspeed > 0.0f)
{
aid_sample_rate = aid_sample_rate * emulationspeed;
}
const u32 ratio = (u32)(65536.0f * aid_sample_rate / (float)m_mixer->m_sampleRate);
const u32 ratio = (u32)(65536.0f * aid_sample_rate / (float)m_mixer->m_sampleRate);
s32 lvolume = m_LVolume.load();
s32 rvolume = m_RVolume.load();
s32 lvolume = m_LVolume.load();
s32 rvolume = m_RVolume.load();
// TODO: consider a higher-quality resampling algorithm.
for (; currentSample < numSamples * 2 && ((indexW-indexR) & INDEX_MASK) > 2; currentSample += 2)
{
u32 indexR2 = indexR + 2; //next sample
// TODO: consider a higher-quality resampling algorithm.
for (; currentSample < numSamples * 2 && ((indexW - indexR) & INDEX_MASK) > 2; currentSample += 2)
{
u32 indexR2 = indexR + 2; // next sample
s16 l1 = Common::swap16(m_buffer[indexR & INDEX_MASK]); //current
s16 l2 = Common::swap16(m_buffer[indexR2 & INDEX_MASK]); //next
int sampleL = ((l1 << 16) + (l2 - l1) * (u16)m_frac) >> 16;
sampleL = (sampleL * lvolume) >> 8;
sampleL += samples[currentSample + 1];
samples[currentSample + 1] = MathUtil::Clamp(sampleL, -32767, 32767);
s16 l1 = Common::swap16(m_buffer[indexR & INDEX_MASK]); // current
s16 l2 = Common::swap16(m_buffer[indexR2 & INDEX_MASK]); // next
int sampleL = ((l1 << 16) + (l2 - l1) * (u16)m_frac) >> 16;
sampleL = (sampleL * lvolume) >> 8;
sampleL += samples[currentSample + 1];
samples[currentSample + 1] = MathUtil::Clamp(sampleL, -32767, 32767);
s16 r1 = Common::swap16(m_buffer[(indexR + 1) & INDEX_MASK]); //current
s16 r2 = Common::swap16(m_buffer[(indexR2 + 1) & INDEX_MASK]); //next
int sampleR = ((r1 << 16) + (r2 - r1) * (u16)m_frac) >> 16;
sampleR = (sampleR * rvolume) >> 8;
sampleR += samples[currentSample];
samples[currentSample] = MathUtil::Clamp(sampleR, -32767, 32767);
s16 r1 = Common::swap16(m_buffer[(indexR + 1) & INDEX_MASK]); // current
s16 r2 = Common::swap16(m_buffer[(indexR2 + 1) & INDEX_MASK]); // next
int sampleR = ((r1 << 16) + (r2 - r1) * (u16)m_frac) >> 16;
sampleR = (sampleR * rvolume) >> 8;
sampleR += samples[currentSample];
samples[currentSample] = MathUtil::Clamp(sampleR, -32767, 32767);
m_frac += ratio;
indexR += 2 * (u16)(m_frac >> 16);
m_frac &= 0xffff;
}
m_frac += ratio;
indexR += 2 * (u16)(m_frac >> 16);
m_frac &= 0xffff;
}
// Padding
short s[2];
s[0] = Common::swap16(m_buffer[(indexR - 1) & INDEX_MASK]);
s[1] = Common::swap16(m_buffer[(indexR - 2) & INDEX_MASK]);
s[0] = (s[0] * rvolume) >> 8;
s[1] = (s[1] * lvolume) >> 8;
for (; currentSample < numSamples * 2; currentSample += 2)
{
int sampleR = MathUtil::Clamp(s[0] + samples[currentSample + 0], -32767, 32767);
int sampleL = MathUtil::Clamp(s[1] + samples[currentSample + 1], -32767, 32767);
// Padding
short s[2];
s[0] = Common::swap16(m_buffer[(indexR - 1) & INDEX_MASK]);
s[1] = Common::swap16(m_buffer[(indexR - 2) & INDEX_MASK]);
s[0] = (s[0] * rvolume) >> 8;
s[1] = (s[1] * lvolume) >> 8;
for (; currentSample < numSamples * 2; currentSample += 2)
{
int sampleR = MathUtil::Clamp(s[0] + samples[currentSample + 0], -32767, 32767);
int sampleL = MathUtil::Clamp(s[1] + samples[currentSample + 1], -32767, 32767);
samples[currentSample + 0] = sampleR;
samples[currentSample + 1] = sampleL;
}
samples[currentSample + 0] = sampleR;
samples[currentSample + 1] = sampleL;
}
// Flush cached variable
m_indexR.store(indexR);
// Flush cached variable
m_indexR.store(indexR);
return numSamples;
return numSamples;
}
unsigned int CMixer::Mix(short* samples, unsigned int num_samples, bool consider_framelimit)
{
if (!samples)
return 0;
if (!samples)
return 0;
memset(samples, 0, num_samples * 2 * sizeof(short));
memset(samples, 0, num_samples * 2 * sizeof(short));
m_dma_mixer.Mix(samples, num_samples, consider_framelimit);
m_streaming_mixer.Mix(samples, num_samples, consider_framelimit);
m_wiimote_speaker_mixer.Mix(samples, num_samples, consider_framelimit);
return num_samples;
m_dma_mixer.Mix(samples, num_samples, consider_framelimit);
m_streaming_mixer.Mix(samples, num_samples, consider_framelimit);
m_wiimote_speaker_mixer.Mix(samples, num_samples, consider_framelimit);
return num_samples;
}
void CMixer::MixerFifo::PushSamples(const short *samples, unsigned int num_samples)
void CMixer::MixerFifo::PushSamples(const short* samples, unsigned int num_samples)
{
// Cache access in non-volatile variable
// indexR isn't allowed to cache in the audio throttling loop as it
// needs to get updates to not deadlock.
u32 indexW = m_indexW.load();
// Cache access in non-volatile variable
// indexR isn't allowed to cache in the audio throttling loop as it
// needs to get updates to not deadlock.
u32 indexW = m_indexW.load();
// Check if we have enough free space
// indexW == m_indexR results in empty buffer, so indexR must always be smaller than indexW
if (num_samples * 2 + ((indexW - m_indexR.load()) & INDEX_MASK) >= MAX_SAMPLES * 2)
return;
// Check if we have enough free space
// indexW == m_indexR results in empty buffer, so indexR must always be smaller than indexW
if (num_samples * 2 + ((indexW - m_indexR.load()) & INDEX_MASK) >= MAX_SAMPLES * 2)
return;
// AyuanX: Actual re-sampling work has been moved to sound thread
// to alleviate the workload on main thread
// and we simply store raw data here to make fast mem copy
int over_bytes = num_samples * 4 - (MAX_SAMPLES * 2 - (indexW & INDEX_MASK)) * sizeof(short);
if (over_bytes > 0)
{
memcpy(&m_buffer[indexW & INDEX_MASK], samples, num_samples * 4 - over_bytes);
memcpy(&m_buffer[0], samples + (num_samples * 4 - over_bytes) / sizeof(short), over_bytes);
}
else
{
memcpy(&m_buffer[indexW & INDEX_MASK], samples, num_samples * 4);
}
// AyuanX: Actual re-sampling work has been moved to sound thread
// to alleviate the workload on main thread
// and we simply store raw data here to make fast mem copy
int over_bytes = num_samples * 4 - (MAX_SAMPLES * 2 - (indexW & INDEX_MASK)) * sizeof(short);
if (over_bytes > 0)
{
memcpy(&m_buffer[indexW & INDEX_MASK], samples, num_samples * 4 - over_bytes);
memcpy(&m_buffer[0], samples + (num_samples * 4 - over_bytes) / sizeof(short), over_bytes);
}
else
{
memcpy(&m_buffer[indexW & INDEX_MASK], samples, num_samples * 4);
}
m_indexW.fetch_add(num_samples * 2);
m_indexW.fetch_add(num_samples * 2);
}
void CMixer::PushSamples(const short *samples, unsigned int num_samples)
void CMixer::PushSamples(const short* samples, unsigned int num_samples)
{
m_dma_mixer.PushSamples(samples, num_samples);
if (m_log_dsp_audio)
m_wave_writer_dsp.AddStereoSamplesBE(samples, num_samples);
m_dma_mixer.PushSamples(samples, num_samples);
if (m_log_dsp_audio)
m_wave_writer_dsp.AddStereoSamplesBE(samples, num_samples);
}
void CMixer::PushStreamingSamples(const short *samples, unsigned int num_samples)
void CMixer::PushStreamingSamples(const short* samples, unsigned int num_samples)
{
m_streaming_mixer.PushSamples(samples, num_samples);
if (m_log_dtk_audio)
m_wave_writer_dtk.AddStereoSamplesBE(samples, num_samples);
m_streaming_mixer.PushSamples(samples, num_samples);
if (m_log_dtk_audio)
m_wave_writer_dtk.AddStereoSamplesBE(samples, num_samples);
}
void CMixer::PushWiimoteSpeakerSamples(const short *samples, unsigned int num_samples, unsigned int sample_rate)
void CMixer::PushWiimoteSpeakerSamples(const short* samples, unsigned int num_samples,
unsigned int sample_rate)
{
short samples_stereo[MAX_SAMPLES * 2];
short samples_stereo[MAX_SAMPLES * 2];
if (num_samples < MAX_SAMPLES)
{
m_wiimote_speaker_mixer.SetInputSampleRate(sample_rate);
if (num_samples < MAX_SAMPLES)
{
m_wiimote_speaker_mixer.SetInputSampleRate(sample_rate);
for (unsigned int i = 0; i < num_samples; ++i)
{
samples_stereo[i * 2] = Common::swap16(samples[i]);
samples_stereo[i * 2 + 1] = Common::swap16(samples[i]);
}
for (unsigned int i = 0; i < num_samples; ++i)
{
samples_stereo[i * 2] = Common::swap16(samples[i]);
samples_stereo[i * 2 + 1] = Common::swap16(samples[i]);
}
m_wiimote_speaker_mixer.PushSamples(samples_stereo, num_samples);
}
m_wiimote_speaker_mixer.PushSamples(samples_stereo, num_samples);
}
}
void CMixer::SetDMAInputSampleRate(unsigned int rate)
{
m_dma_mixer.SetInputSampleRate(rate);
m_dma_mixer.SetInputSampleRate(rate);
}
void CMixer::SetStreamInputSampleRate(unsigned int rate)
{
m_streaming_mixer.SetInputSampleRate(rate);
m_streaming_mixer.SetInputSampleRate(rate);
}
void CMixer::SetStreamingVolume(unsigned int lvolume, unsigned int rvolume)
{
m_streaming_mixer.SetVolume(lvolume, rvolume);
m_streaming_mixer.SetVolume(lvolume, rvolume);
}
void CMixer::SetWiimoteSpeakerVolume(unsigned int lvolume, unsigned int rvolume)
{
m_wiimote_speaker_mixer.SetVolume(lvolume, rvolume);
m_wiimote_speaker_mixer.SetVolume(lvolume, rvolume);
}
void CMixer::StartLogDTKAudio(const std::string& filename)
{
if (!m_log_dtk_audio)
{
m_log_dtk_audio = true;
m_wave_writer_dtk.Start(filename, 48000);
m_wave_writer_dtk.SetSkipSilence(false);
NOTICE_LOG(AUDIO, "Starting DTK Audio logging");
}
else
{
WARN_LOG(AUDIO, "DTK Audio logging has already been started");
}
if (!m_log_dtk_audio)
{
m_log_dtk_audio = true;
m_wave_writer_dtk.Start(filename, 48000);
m_wave_writer_dtk.SetSkipSilence(false);
NOTICE_LOG(AUDIO, "Starting DTK Audio logging");
}
else
{
WARN_LOG(AUDIO, "DTK Audio logging has already been started");
}
}
void CMixer::StopLogDTKAudio()
{
if (m_log_dtk_audio)
{
m_log_dtk_audio = false;
m_wave_writer_dtk.Stop();
NOTICE_LOG(AUDIO, "Stopping DTK Audio logging");
}
else
{
WARN_LOG(AUDIO, "DTK Audio logging has already been stopped");
}
if (m_log_dtk_audio)
{
m_log_dtk_audio = false;
m_wave_writer_dtk.Stop();
NOTICE_LOG(AUDIO, "Stopping DTK Audio logging");
}
else
{
WARN_LOG(AUDIO, "DTK Audio logging has already been stopped");
}
}
void CMixer::StartLogDSPAudio(const std::string& filename)
{
if (!m_log_dsp_audio)
{
m_log_dsp_audio = true;
m_wave_writer_dsp.Start(filename, 32000);
m_wave_writer_dsp.SetSkipSilence(false);
NOTICE_LOG(AUDIO, "Starting DSP Audio logging");
}
else
{
WARN_LOG(AUDIO, "DSP Audio logging has already been started");
}
if (!m_log_dsp_audio)
{
m_log_dsp_audio = true;
m_wave_writer_dsp.Start(filename, 32000);
m_wave_writer_dsp.SetSkipSilence(false);
NOTICE_LOG(AUDIO, "Starting DSP Audio logging");
}
else
{
WARN_LOG(AUDIO, "DSP Audio logging has already been started");
}
}
void CMixer::StopLogDSPAudio()
{
if (m_log_dsp_audio)
{
m_log_dsp_audio = false;
m_wave_writer_dsp.Stop();
NOTICE_LOG(AUDIO, "Stopping DSP Audio logging");
}
else
{
WARN_LOG(AUDIO, "DSP Audio logging has already been stopped");
}
if (m_log_dsp_audio)
{
m_log_dsp_audio = false;
m_wave_writer_dsp.Stop();
NOTICE_LOG(AUDIO, "Stopping DSP Audio logging");
}
else
{
WARN_LOG(AUDIO, "DSP Audio logging has already been stopped");
}
}
void CMixer::MixerFifo::SetInputSampleRate(unsigned int rate)
{
m_input_sample_rate = rate;
m_input_sample_rate = rate;
}
void CMixer::MixerFifo::SetVolume(unsigned int lvolume, unsigned int rvolume)
{
m_LVolume.store(lvolume + (lvolume >> 7));
m_RVolume.store(rvolume + (rvolume >> 7));
m_LVolume.store(lvolume + (lvolume >> 7));
m_RVolume.store(rvolume + (rvolume >> 7));
}

View File

@ -13,74 +13,73 @@
class CMixer final
{
public:
explicit CMixer(unsigned int BackendSampleRate);
~CMixer();
explicit CMixer(unsigned int BackendSampleRate);
~CMixer();
// Called from audio threads
unsigned int Mix(short* samples, unsigned int numSamples, bool consider_framelimit = true);
// Called from audio threads
unsigned int Mix(short* samples, unsigned int numSamples, bool consider_framelimit = true);
// Called from main thread
void PushSamples(const short* samples, unsigned int num_samples);
void PushStreamingSamples(const short* samples, unsigned int num_samples);
void PushWiimoteSpeakerSamples(const short* samples, unsigned int num_samples, unsigned int sample_rate);
unsigned int GetSampleRate() const { return m_sampleRate; }
// Called from main thread
void PushSamples(const short* samples, unsigned int num_samples);
void PushStreamingSamples(const short* samples, unsigned int num_samples);
void PushWiimoteSpeakerSamples(const short* samples, unsigned int num_samples,
unsigned int sample_rate);
unsigned int GetSampleRate() const { return m_sampleRate; }
void SetDMAInputSampleRate(unsigned int rate);
void SetStreamInputSampleRate(unsigned int rate);
void SetStreamingVolume(unsigned int lvolume, unsigned int rvolume);
void SetWiimoteSpeakerVolume(unsigned int lvolume, unsigned int rvolume);
void SetDMAInputSampleRate(unsigned int rate);
void SetStreamInputSampleRate(unsigned int rate);
void SetStreamingVolume(unsigned int lvolume, unsigned int rvolume);
void SetWiimoteSpeakerVolume(unsigned int lvolume, unsigned int rvolume);
void StartLogDTKAudio(const std::string& filename);
void StopLogDTKAudio();
void StartLogDTKAudio(const std::string& filename);
void StopLogDTKAudio();
void StartLogDSPAudio(const std::string& filename);
void StopLogDSPAudio();
float GetCurrentSpeed() const { return m_speed.load(); }
void UpdateSpeed(float val) { m_speed.store(val); }
void StartLogDSPAudio(const std::string& filename);
void StopLogDSPAudio();
float GetCurrentSpeed() const { return m_speed.load(); }
void UpdateSpeed(float val) { m_speed.store(val); }
private:
static constexpr u32 MAX_SAMPLES = 1024 * 4; // 128 ms
static constexpr u32 INDEX_MASK = MAX_SAMPLES * 2 - 1;
static constexpr int MAX_FREQ_SHIFT = 200; // Per 32000 Hz
static constexpr float CONTROL_FACTOR = 0.2f;
static constexpr u32 CONTROL_AVG = 32; // In freq_shift per FIFO size offset
static constexpr u32 MAX_SAMPLES = 1024 * 4; // 128 ms
static constexpr u32 INDEX_MASK = MAX_SAMPLES * 2 - 1;
static constexpr int MAX_FREQ_SHIFT = 200; // Per 32000 Hz
static constexpr float CONTROL_FACTOR = 0.2f;
static constexpr u32 CONTROL_AVG = 32; // In freq_shift per FIFO size offset
class MixerFifo final
{
public:
MixerFifo(CMixer* mixer, unsigned sample_rate)
: m_mixer(mixer)
, m_input_sample_rate(sample_rate)
{
}
void PushSamples(const short* samples, unsigned int num_samples);
unsigned int Mix(short* samples, unsigned int numSamples, bool consider_framelimit = true);
void SetInputSampleRate(unsigned int rate);
void SetVolume(unsigned int lvolume, unsigned int rvolume);
private:
CMixer* m_mixer;
unsigned m_input_sample_rate;
std::array<short, MAX_SAMPLES * 2> m_buffer{};
std::atomic<u32> m_indexW{0};
std::atomic<u32> m_indexR{0};
// Volume ranges from 0-256
std::atomic<s32> m_LVolume{256};
std::atomic<s32> m_RVolume{256};
float m_numLeftI = 0.0f;
u32 m_frac = 0;
};
MixerFifo m_dma_mixer{this, 32000};
MixerFifo m_streaming_mixer{this, 48000};
MixerFifo m_wiimote_speaker_mixer{this, 3000};
unsigned int m_sampleRate;
class MixerFifo final
{
public:
MixerFifo(CMixer* mixer, unsigned sample_rate)
: m_mixer(mixer), m_input_sample_rate(sample_rate)
{
}
void PushSamples(const short* samples, unsigned int num_samples);
unsigned int Mix(short* samples, unsigned int numSamples, bool consider_framelimit = true);
void SetInputSampleRate(unsigned int rate);
void SetVolume(unsigned int lvolume, unsigned int rvolume);
WaveFileWriter m_wave_writer_dtk;
WaveFileWriter m_wave_writer_dsp;
private:
CMixer* m_mixer;
unsigned m_input_sample_rate;
std::array<short, MAX_SAMPLES * 2> m_buffer{};
std::atomic<u32> m_indexW{0};
std::atomic<u32> m_indexR{0};
// Volume ranges from 0-256
std::atomic<s32> m_LVolume{256};
std::atomic<s32> m_RVolume{256};
float m_numLeftI = 0.0f;
u32 m_frac = 0;
};
MixerFifo m_dma_mixer{this, 32000};
MixerFifo m_streaming_mixer{this, 48000};
MixerFifo m_wiimote_speaker_mixer{this, 3000};
unsigned int m_sampleRate;
bool m_log_dtk_audio = false;
bool m_log_dsp_audio = false;
WaveFileWriter m_wave_writer_dtk;
WaveFileWriter m_wave_writer_dsp;
// Current rate of emulation (1.0 = 100% speed)
std::atomic<float> m_speed{0.0f};
bool m_log_dtk_audio = false;
bool m_log_dsp_audio = false;
// Current rate of emulation (1.0 = 100% speed)
std::atomic<float> m_speed{0.0f};
};

View File

@ -13,7 +13,7 @@ void NullSound::SoundLoop()
bool NullSound::Start()
{
return true;
return true;
}
void NullSound::SetVolume(int volume)
@ -22,19 +22,22 @@ void NullSound::SetVolume(int volume)
void NullSound::Update()
{
// num_samples_to_render in this update - depends on SystemTimers::AUDIO_DMA_PERIOD.
constexpr u32 stereo_16_bit_size = 4;
constexpr u32 dma_length = 32;
const u64 audio_dma_period = SystemTimers::GetTicksPerSecond() / (AudioInterface::GetAIDSampleRate() * stereo_16_bit_size / dma_length);
const u64 ais_samples_per_second = 48000 * stereo_16_bit_size;
const u64 num_samples_to_render = (audio_dma_period * ais_samples_per_second) / SystemTimers::GetTicksPerSecond();
// num_samples_to_render in this update - depends on SystemTimers::AUDIO_DMA_PERIOD.
constexpr u32 stereo_16_bit_size = 4;
constexpr u32 dma_length = 32;
const u64 audio_dma_period =
SystemTimers::GetTicksPerSecond() /
(AudioInterface::GetAIDSampleRate() * stereo_16_bit_size / dma_length);
const u64 ais_samples_per_second = 48000 * stereo_16_bit_size;
const u64 num_samples_to_render =
(audio_dma_period * ais_samples_per_second) / SystemTimers::GetTicksPerSecond();
m_mixer->Mix(m_realtime_buffer.data(), (unsigned int)num_samples_to_render);
m_mixer->Mix(m_realtime_buffer.data(), (unsigned int)num_samples_to_render);
}
void NullSound::Clear(bool mute)
{
m_muted = mute;
m_muted = mute;
}
void NullSound::Stop()

View File

@ -10,18 +10,17 @@
class NullSound final : public SoundStream
{
public:
bool Start() override;
void SoundLoop() override;
void SetVolume(int volume) override;
void Stop() override;
void Clear(bool mute) override;
void Update() override;
static bool isValid() { return true; }
bool Start() override;
void SoundLoop() override;
void SetVolume(int volume) override;
void Stop() override;
void Clear(bool mute) override;
void Update() override;
static bool isValid() { return true; }
private:
static constexpr size_t BUFFER_SIZE = 48000 * 4 / 32;
static constexpr size_t BUFFER_SIZE = 48000 * 4 / 32;
// Playback position
std::array<short, BUFFER_SIZE / sizeof(short)> m_realtime_buffer;
// Playback position
std::array<short, BUFFER_SIZE / sizeof(short)> m_realtime_buffer;
};

View File

@ -5,11 +5,11 @@
#include <cstring>
#include <thread>
#include "AudioCommon/aldlist.h"
#include "AudioCommon/DPL2Decoder.h"
#include "AudioCommon/OpenALStream.h"
#include "Common/Thread.h"
#include "AudioCommon/aldlist.h"
#include "Common/Logging/Log.h"
#include "Common/Thread.h"
#include "Core/ConfigManager.h"
#if defined HAVE_OPENAL && HAVE_OPENAL
@ -25,351 +25,363 @@ static soundtouch::SoundTouch soundTouch;
//
bool OpenALStream::Start()
{
m_run_thread.store(true);
bool bReturn = false;
m_run_thread.store(true);
bool bReturn = false;
ALDeviceList pDeviceList;
if (pDeviceList.GetNumDevices())
{
char *defDevName = pDeviceList.GetDeviceName(pDeviceList.GetDefaultDevice());
ALDeviceList pDeviceList;
if (pDeviceList.GetNumDevices())
{
char* defDevName = pDeviceList.GetDeviceName(pDeviceList.GetDefaultDevice());
WARN_LOG(AUDIO, "Found OpenAL device %s", defDevName);
WARN_LOG(AUDIO, "Found OpenAL device %s", defDevName);
ALCdevice *pDevice = alcOpenDevice(defDevName);
if (pDevice)
{
ALCcontext *pContext = alcCreateContext(pDevice, nullptr);
if (pContext)
{
// Used to determine an appropriate period size (2x period = total buffer size)
//ALCint refresh;
//alcGetIntegerv(pDevice, ALC_REFRESH, 1, &refresh);
//period_size_in_millisec = 1000 / refresh;
ALCdevice* pDevice = alcOpenDevice(defDevName);
if (pDevice)
{
ALCcontext* pContext = alcCreateContext(pDevice, nullptr);
if (pContext)
{
// Used to determine an appropriate period size (2x period = total buffer size)
// ALCint refresh;
// alcGetIntegerv(pDevice, ALC_REFRESH, 1, &refresh);
// period_size_in_millisec = 1000 / refresh;
alcMakeContextCurrent(pContext);
thread = std::thread(&OpenALStream::SoundLoop, this);
bReturn = true;
}
else
{
alcCloseDevice(pDevice);
PanicAlertT("OpenAL: can't create context for device %s", defDevName);
}
}
else
{
PanicAlertT("OpenAL: can't open device %s", defDevName);
}
}
else
{
PanicAlertT("OpenAL: can't find sound devices");
}
alcMakeContextCurrent(pContext);
thread = std::thread(&OpenALStream::SoundLoop, this);
bReturn = true;
}
else
{
alcCloseDevice(pDevice);
PanicAlertT("OpenAL: can't create context for device %s", defDevName);
}
}
else
{
PanicAlertT("OpenAL: can't open device %s", defDevName);
}
}
else
{
PanicAlertT("OpenAL: can't find sound devices");
}
// Initialize DPL2 parameters
DPL2Reset();
// Initialize DPL2 parameters
DPL2Reset();
soundTouch.clear();
return bReturn;
soundTouch.clear();
return bReturn;
}
void OpenALStream::Stop()
{
m_run_thread.store(false);
// kick the thread if it's waiting
soundSyncEvent.Set();
m_run_thread.store(false);
// kick the thread if it's waiting
soundSyncEvent.Set();
soundTouch.clear();
soundTouch.clear();
thread.join();
thread.join();
alSourceStop(uiSource);
alSourcei(uiSource, AL_BUFFER, 0);
alSourceStop(uiSource);
alSourcei(uiSource, AL_BUFFER, 0);
// Clean up buffers and sources
alDeleteSources(1, &uiSource);
uiSource = 0;
alDeleteBuffers(numBuffers, uiBuffers);
// Clean up buffers and sources
alDeleteSources(1, &uiSource);
uiSource = 0;
alDeleteBuffers(numBuffers, uiBuffers);
ALCcontext *pContext = alcGetCurrentContext();
ALCdevice *pDevice = alcGetContextsDevice(pContext);
ALCcontext* pContext = alcGetCurrentContext();
ALCdevice* pDevice = alcGetContextsDevice(pContext);
alcMakeContextCurrent(nullptr);
alcDestroyContext(pContext);
alcCloseDevice(pDevice);
alcMakeContextCurrent(nullptr);
alcDestroyContext(pContext);
alcCloseDevice(pDevice);
}
void OpenALStream::SetVolume(int volume)
{
fVolume = (float)volume / 100.0f;
fVolume = (float)volume / 100.0f;
if (uiSource)
alSourcef(uiSource, AL_GAIN, fVolume);
if (uiSource)
alSourcef(uiSource, AL_GAIN, fVolume);
}
void OpenALStream::Update()
{
soundSyncEvent.Set();
soundSyncEvent.Set();
}
void OpenALStream::Clear(bool mute)
{
m_muted = mute;
m_muted = mute;
if (m_muted)
{
soundTouch.clear();
alSourceStop(uiSource);
}
else
{
alSourcePlay(uiSource);
}
if (m_muted)
{
soundTouch.clear();
alSourceStop(uiSource);
}
else
{
alSourcePlay(uiSource);
}
}
void OpenALStream::SoundLoop()
{
Common::SetCurrentThreadName("Audio thread - openal");
Common::SetCurrentThreadName("Audio thread - openal");
bool surround_capable = SConfig::GetInstance().bDPL2Decoder;
bool surround_capable = SConfig::GetInstance().bDPL2Decoder;
#if defined(__APPLE__)
bool float32_capable = false;
const ALenum AL_FORMAT_STEREO_FLOAT32 = 0;
// OS X does not have the alext AL_FORMAT_51CHN32 yet.
surround_capable = false;
const ALenum AL_FORMAT_51CHN32 = 0;
const ALenum AL_FORMAT_51CHN16 = 0;
bool float32_capable = false;
const ALenum AL_FORMAT_STEREO_FLOAT32 = 0;
// OS X does not have the alext AL_FORMAT_51CHN32 yet.
surround_capable = false;
const ALenum AL_FORMAT_51CHN32 = 0;
const ALenum AL_FORMAT_51CHN16 = 0;
#else
bool float32_capable = true;
bool float32_capable = true;
#endif
u32 ulFrequency = m_mixer->GetSampleRate();
numBuffers = SConfig::GetInstance().iLatency + 2; // OpenAL requires a minimum of two buffers
u32 ulFrequency = m_mixer->GetSampleRate();
numBuffers = SConfig::GetInstance().iLatency + 2; // OpenAL requires a minimum of two buffers
memset(uiBuffers, 0, numBuffers * sizeof(ALuint));
uiSource = 0;
memset(uiBuffers, 0, numBuffers * sizeof(ALuint));
uiSource = 0;
// Checks if a X-Fi is being used. If it is, disable FLOAT32 support as this sound card has no support for it even though it reports it does.
if (strstr(alGetString(AL_RENDERER), "X-Fi"))
float32_capable = false;
// Checks if a X-Fi is being used. If it is, disable FLOAT32 support as this sound card has no
// support for it even though it reports it does.
if (strstr(alGetString(AL_RENDERER), "X-Fi"))
float32_capable = false;
// Generate some AL Buffers for streaming
alGenBuffers(numBuffers, (ALuint *)uiBuffers);
// Generate a Source to playback the Buffers
alGenSources(1, &uiSource);
// Generate some AL Buffers for streaming
alGenBuffers(numBuffers, (ALuint*)uiBuffers);
// Generate a Source to playback the Buffers
alGenSources(1, &uiSource);
// Short Silence
if (float32_capable)
memset(sampleBuffer, 0, OAL_MAX_SAMPLES * numBuffers * FRAME_SURROUND_FLOAT);
else
memset(sampleBuffer, 0, OAL_MAX_SAMPLES * numBuffers * FRAME_SURROUND_SHORT);
// Short Silence
if (float32_capable)
memset(sampleBuffer, 0, OAL_MAX_SAMPLES * numBuffers * FRAME_SURROUND_FLOAT);
else
memset(sampleBuffer, 0, OAL_MAX_SAMPLES * numBuffers * FRAME_SURROUND_SHORT);
memset(realtimeBuffer, 0, OAL_MAX_SAMPLES * FRAME_STEREO_SHORT);
memset(realtimeBuffer, 0, OAL_MAX_SAMPLES * FRAME_STEREO_SHORT);
for (int i = 0; i < numBuffers; i++)
{
if (surround_capable)
{
if (float32_capable)
alBufferData(uiBuffers[i], AL_FORMAT_51CHN32, sampleBuffer, 4 * FRAME_SURROUND_FLOAT, ulFrequency);
else
alBufferData(uiBuffers[i], AL_FORMAT_51CHN16, sampleBuffer, 4 * FRAME_SURROUND_SHORT, ulFrequency);
}
else
{
alBufferData(uiBuffers[i], AL_FORMAT_STEREO16, realtimeBuffer, 4 * FRAME_STEREO_SHORT, ulFrequency);
}
}
alSourceQueueBuffers(uiSource, numBuffers, uiBuffers);
alSourcePlay(uiSource);
for (int i = 0; i < numBuffers; i++)
{
if (surround_capable)
{
if (float32_capable)
alBufferData(uiBuffers[i], AL_FORMAT_51CHN32, sampleBuffer, 4 * FRAME_SURROUND_FLOAT,
ulFrequency);
else
alBufferData(uiBuffers[i], AL_FORMAT_51CHN16, sampleBuffer, 4 * FRAME_SURROUND_SHORT,
ulFrequency);
}
else
{
alBufferData(uiBuffers[i], AL_FORMAT_STEREO16, realtimeBuffer, 4 * FRAME_STEREO_SHORT,
ulFrequency);
}
}
alSourceQueueBuffers(uiSource, numBuffers, uiBuffers);
alSourcePlay(uiSource);
// Set the default sound volume as saved in the config file.
alSourcef(uiSource, AL_GAIN, fVolume);
// Set the default sound volume as saved in the config file.
alSourcef(uiSource, AL_GAIN, fVolume);
// TODO: Error handling
//ALenum err = alGetError();
// TODO: Error handling
// ALenum err = alGetError();
ALint iBuffersFilled = 0;
ALint iBuffersProcessed = 0;
ALint iState = 0;
ALuint uiBufferTemp[OAL_MAX_BUFFERS] = { 0 };
ALint iBuffersFilled = 0;
ALint iBuffersProcessed = 0;
ALint iState = 0;
ALuint uiBufferTemp[OAL_MAX_BUFFERS] = {0};
soundTouch.setChannels(2);
soundTouch.setSampleRate(ulFrequency);
soundTouch.setTempo(1.0);
soundTouch.setSetting(SETTING_USE_QUICKSEEK, 0);
soundTouch.setSetting(SETTING_USE_AA_FILTER, 0);
soundTouch.setSetting(SETTING_SEQUENCE_MS, 1);
soundTouch.setSetting(SETTING_SEEKWINDOW_MS, 28);
soundTouch.setSetting(SETTING_OVERLAP_MS, 12);
soundTouch.setChannels(2);
soundTouch.setSampleRate(ulFrequency);
soundTouch.setTempo(1.0);
soundTouch.setSetting(SETTING_USE_QUICKSEEK, 0);
soundTouch.setSetting(SETTING_USE_AA_FILTER, 0);
soundTouch.setSetting(SETTING_SEQUENCE_MS, 1);
soundTouch.setSetting(SETTING_SEEKWINDOW_MS, 28);
soundTouch.setSetting(SETTING_OVERLAP_MS, 12);
while (m_run_thread.load())
{
// num_samples_to_render in this update - depends on SystemTimers::AUDIO_DMA_PERIOD.
const u32 stereo_16_bit_size = 4;
const u32 dma_length = 32;
const u64 ais_samples_per_second = 48000 * stereo_16_bit_size;
u64 audio_dma_period = SystemTimers::GetTicksPerSecond() / (AudioInterface::GetAIDSampleRate() * stereo_16_bit_size / dma_length);
u64 num_samples_to_render = (audio_dma_period * ais_samples_per_second) / SystemTimers::GetTicksPerSecond();
while (m_run_thread.load())
{
// num_samples_to_render in this update - depends on SystemTimers::AUDIO_DMA_PERIOD.
const u32 stereo_16_bit_size = 4;
const u32 dma_length = 32;
const u64 ais_samples_per_second = 48000 * stereo_16_bit_size;
u64 audio_dma_period = SystemTimers::GetTicksPerSecond() /
(AudioInterface::GetAIDSampleRate() * stereo_16_bit_size / dma_length);
u64 num_samples_to_render =
(audio_dma_period * ais_samples_per_second) / SystemTimers::GetTicksPerSecond();
unsigned int numSamples = (unsigned int)num_samples_to_render;
unsigned int minSamples = surround_capable ? 240 : 0; // DPL2 accepts 240 samples minimum (FWRDURATION)
unsigned int numSamples = (unsigned int)num_samples_to_render;
unsigned int minSamples =
surround_capable ? 240 : 0; // DPL2 accepts 240 samples minimum (FWRDURATION)
numSamples = (numSamples > OAL_MAX_SAMPLES) ? OAL_MAX_SAMPLES : numSamples;
numSamples = m_mixer->Mix(realtimeBuffer, numSamples, false);
numSamples = (numSamples > OAL_MAX_SAMPLES) ? OAL_MAX_SAMPLES : numSamples;
numSamples = m_mixer->Mix(realtimeBuffer, numSamples, false);
// Convert the samples from short to float
float dest[OAL_MAX_SAMPLES * STEREO_CHANNELS];
for (u32 i = 0; i < numSamples * STEREO_CHANNELS; ++i)
dest[i] = (float)realtimeBuffer[i] / (1 << 15);
// Convert the samples from short to float
float dest[OAL_MAX_SAMPLES * STEREO_CHANNELS];
for (u32 i = 0; i < numSamples * STEREO_CHANNELS; ++i)
dest[i] = (float)realtimeBuffer[i] / (1 << 15);
soundTouch.putSamples(dest, numSamples);
soundTouch.putSamples(dest, numSamples);
if (iBuffersProcessed == iBuffersFilled)
{
alGetSourcei(uiSource, AL_BUFFERS_PROCESSED, &iBuffersProcessed);
iBuffersFilled = 0;
}
if (iBuffersProcessed == iBuffersFilled)
{
alGetSourcei(uiSource, AL_BUFFERS_PROCESSED, &iBuffersProcessed);
iBuffersFilled = 0;
}
if (iBuffersProcessed)
{
double rate = (double)m_mixer->GetCurrentSpeed();
if (rate <= 0)
{
Core::RequestRefreshInfo();
rate = (double)m_mixer->GetCurrentSpeed();
}
if (iBuffersProcessed)
{
double rate = (double)m_mixer->GetCurrentSpeed();
if (rate <= 0)
{
Core::RequestRefreshInfo();
rate = (double)m_mixer->GetCurrentSpeed();
}
// Place a lower limit of 10% speed. When a game boots up, there will be
// many silence samples. These do not need to be timestretched.
if (rate > 0.10)
{
soundTouch.setTempo(rate);
if (rate > 10)
{
soundTouch.clear();
}
}
// Place a lower limit of 10% speed. When a game boots up, there will be
// many silence samples. These do not need to be timestretched.
if (rate > 0.10)
{
soundTouch.setTempo(rate);
if (rate > 10)
{
soundTouch.clear();
}
}
unsigned int nSamples = soundTouch.receiveSamples(sampleBuffer, OAL_MAX_SAMPLES * numBuffers);
unsigned int nSamples = soundTouch.receiveSamples(sampleBuffer, OAL_MAX_SAMPLES * numBuffers);
if (nSamples <= minSamples)
continue;
if (nSamples <= minSamples)
continue;
// Remove the Buffer from the Queue. (uiBuffer contains the Buffer ID for the unqueued Buffer)
if (iBuffersFilled == 0)
{
alSourceUnqueueBuffers(uiSource, iBuffersProcessed, uiBufferTemp);
ALenum err = alGetError();
if (err != 0)
{
ERROR_LOG(AUDIO, "Error unqueuing buffers: %08x", err);
}
}
// Remove the Buffer from the Queue. (uiBuffer contains the Buffer ID for the unqueued
// Buffer)
if (iBuffersFilled == 0)
{
alSourceUnqueueBuffers(uiSource, iBuffersProcessed, uiBufferTemp);
ALenum err = alGetError();
if (err != 0)
{
ERROR_LOG(AUDIO, "Error unqueuing buffers: %08x", err);
}
}
if (surround_capable)
{
float dpl2[OAL_MAX_SAMPLES * OAL_MAX_BUFFERS * SURROUND_CHANNELS];
DPL2Decode(sampleBuffer, nSamples, dpl2);
if (surround_capable)
{
float dpl2[OAL_MAX_SAMPLES * OAL_MAX_BUFFERS * SURROUND_CHANNELS];
DPL2Decode(sampleBuffer, nSamples, dpl2);
// zero-out the subwoofer channel - DPL2Decode generates a pretty
// good 5.0 but not a good 5.1 output. Sadly there is not a 5.0
// AL_FORMAT_50CHN32 to make this super-explicit.
// DPL2Decode output: LEFTFRONT, RIGHTFRONT, CENTREFRONT, (sub), LEFTREAR, RIGHTREAR
for (u32 i = 0; i < nSamples; ++i)
{
dpl2[i*SURROUND_CHANNELS + 3 /*sub/lfe*/] = 0.0f;
}
// zero-out the subwoofer channel - DPL2Decode generates a pretty
// good 5.0 but not a good 5.1 output. Sadly there is not a 5.0
// AL_FORMAT_50CHN32 to make this super-explicit.
// DPL2Decode output: LEFTFRONT, RIGHTFRONT, CENTREFRONT, (sub), LEFTREAR, RIGHTREAR
for (u32 i = 0; i < nSamples; ++i)
{
dpl2[i * SURROUND_CHANNELS + 3 /*sub/lfe*/] = 0.0f;
}
if (float32_capable)
{
alBufferData(uiBufferTemp[iBuffersFilled], AL_FORMAT_51CHN32, dpl2, nSamples * FRAME_SURROUND_FLOAT, ulFrequency);
}
else
{
short surround_short[OAL_MAX_SAMPLES * SURROUND_CHANNELS * OAL_MAX_BUFFERS];
for (u32 i = 0; i < nSamples * SURROUND_CHANNELS; ++i)
surround_short[i] = (short)((float)dpl2[i] * (1 << 15));
if (float32_capable)
{
alBufferData(uiBufferTemp[iBuffersFilled], AL_FORMAT_51CHN32, dpl2,
nSamples * FRAME_SURROUND_FLOAT, ulFrequency);
}
else
{
short surround_short[OAL_MAX_SAMPLES * SURROUND_CHANNELS * OAL_MAX_BUFFERS];
for (u32 i = 0; i < nSamples * SURROUND_CHANNELS; ++i)
surround_short[i] = (short)((float)dpl2[i] * (1 << 15));
alBufferData(uiBufferTemp[iBuffersFilled], AL_FORMAT_51CHN16, surround_short, nSamples * FRAME_SURROUND_SHORT, ulFrequency);
}
alBufferData(uiBufferTemp[iBuffersFilled], AL_FORMAT_51CHN16, surround_short,
nSamples * FRAME_SURROUND_SHORT, ulFrequency);
}
ALenum err = alGetError();
if (err == AL_INVALID_ENUM)
{
// 5.1 is not supported by the host, fallback to stereo
WARN_LOG(AUDIO, "Unable to set 5.1 surround mode. Updating OpenAL Soft might fix this issue.");
surround_capable = false;
}
else if (err != 0)
{
ERROR_LOG(AUDIO, "Error occurred while buffering data: %08x", err);
}
}
ALenum err = alGetError();
if (err == AL_INVALID_ENUM)
{
// 5.1 is not supported by the host, fallback to stereo
WARN_LOG(AUDIO,
"Unable to set 5.1 surround mode. Updating OpenAL Soft might fix this issue.");
surround_capable = false;
}
else if (err != 0)
{
ERROR_LOG(AUDIO, "Error occurred while buffering data: %08x", err);
}
}
else
{
if (float32_capable)
{
alBufferData(uiBufferTemp[iBuffersFilled], AL_FORMAT_STEREO_FLOAT32, sampleBuffer, nSamples * FRAME_STEREO_FLOAT, ulFrequency);
ALenum err = alGetError();
if (err == AL_INVALID_ENUM)
{
float32_capable = false;
}
else if (err != 0)
{
ERROR_LOG(AUDIO, "Error occurred while buffering float32 data: %08x", err);
}
}
else
{
if (float32_capable)
{
alBufferData(uiBufferTemp[iBuffersFilled], AL_FORMAT_STEREO_FLOAT32, sampleBuffer,
nSamples * FRAME_STEREO_FLOAT, ulFrequency);
ALenum err = alGetError();
if (err == AL_INVALID_ENUM)
{
float32_capable = false;
}
else if (err != 0)
{
ERROR_LOG(AUDIO, "Error occurred while buffering float32 data: %08x", err);
}
}
else
{
// Convert the samples from float to short
short stereo[OAL_MAX_SAMPLES * STEREO_CHANNELS * OAL_MAX_BUFFERS];
for (u32 i = 0; i < nSamples * STEREO_CHANNELS; ++i)
stereo[i] = (short)((float)sampleBuffer[i] * (1 << 15));
else
{
// Convert the samples from float to short
short stereo[OAL_MAX_SAMPLES * STEREO_CHANNELS * OAL_MAX_BUFFERS];
for (u32 i = 0; i < nSamples * STEREO_CHANNELS; ++i)
stereo[i] = (short)((float)sampleBuffer[i] * (1 << 15));
alBufferData(uiBufferTemp[iBuffersFilled], AL_FORMAT_STEREO16, stereo, nSamples * FRAME_STEREO_SHORT, ulFrequency);
}
}
alBufferData(uiBufferTemp[iBuffersFilled], AL_FORMAT_STEREO16, stereo,
nSamples * FRAME_STEREO_SHORT, ulFrequency);
}
}
alSourceQueueBuffers(uiSource, 1, &uiBufferTemp[iBuffersFilled]);
ALenum err = alGetError();
if (err != 0)
{
ERROR_LOG(AUDIO, "Error queuing buffers: %08x", err);
}
iBuffersFilled++;
alSourceQueueBuffers(uiSource, 1, &uiBufferTemp[iBuffersFilled]);
ALenum err = alGetError();
if (err != 0)
{
ERROR_LOG(AUDIO, "Error queuing buffers: %08x", err);
}
iBuffersFilled++;
if (iBuffersFilled == numBuffers)
{
alSourcePlay(uiSource);
err = alGetError();
if (err != 0)
{
ERROR_LOG(AUDIO, "Error occurred during playback: %08x", err);
}
}
if (iBuffersFilled == numBuffers)
{
alSourcePlay(uiSource);
err = alGetError();
if (err != 0)
{
ERROR_LOG(AUDIO, "Error occurred during playback: %08x", err);
}
}
alGetSourcei(uiSource, AL_SOURCE_STATE, &iState);
if (iState != AL_PLAYING)
{
// Buffer underrun occurred, resume playback
alSourcePlay(uiSource);
err = alGetError();
if (err != 0)
{
ERROR_LOG(AUDIO, "Error occurred resuming playback: %08x", err);
}
}
}
else
{
soundSyncEvent.Wait();
}
}
alGetSourcei(uiSource, AL_SOURCE_STATE, &iState);
if (iState != AL_PLAYING)
{
// Buffer underrun occurred, resume playback
alSourcePlay(uiSource);
err = alGetError();
if (err != 0)
{
ERROR_LOG(AUDIO, "Error occurred resuming playback: %08x", err);
}
}
}
else
{
soundSyncEvent.Wait();
}
}
}
#endif //HAVE_OPENAL
#endif // HAVE_OPENAL

View File

@ -32,56 +32,52 @@
#define BOOL SoundTouch_BOOL
#endif
#include <soundtouch/SoundTouch.h>
#include <soundtouch/STTypes.h>
#include <soundtouch/SoundTouch.h>
#ifdef __APPLE__
#undef BOOL
#endif
// 16 bit Stereo
#define SFX_MAX_SOURCE 1
#define OAL_MAX_BUFFERS 32
#define OAL_MAX_SAMPLES 256
#define STEREO_CHANNELS 2
#define SURROUND_CHANNELS 6 // number of channels in surround mode
#define SIZE_SHORT 2
#define SIZE_FLOAT 4 // size of a float in bytes
#define FRAME_STEREO_SHORT STEREO_CHANNELS * SIZE_SHORT
#define FRAME_STEREO_FLOAT STEREO_CHANNELS * SIZE_FLOAT
#define FRAME_SURROUND_FLOAT SURROUND_CHANNELS * SIZE_FLOAT
#define FRAME_SURROUND_SHORT SURROUND_CHANNELS * SIZE_SHORT
#define SFX_MAX_SOURCE 1
#define OAL_MAX_BUFFERS 32
#define OAL_MAX_SAMPLES 256
#define STEREO_CHANNELS 2
#define SURROUND_CHANNELS 6 // number of channels in surround mode
#define SIZE_SHORT 2
#define SIZE_FLOAT 4 // size of a float in bytes
#define FRAME_STEREO_SHORT STEREO_CHANNELS* SIZE_SHORT
#define FRAME_STEREO_FLOAT STEREO_CHANNELS* SIZE_FLOAT
#define FRAME_SURROUND_FLOAT SURROUND_CHANNELS* SIZE_FLOAT
#define FRAME_SURROUND_SHORT SURROUND_CHANNELS* SIZE_SHORT
#endif
class OpenALStream final : public SoundStream
{
#if defined HAVE_OPENAL && HAVE_OPENAL
public:
OpenALStream() : uiSource(0)
{
}
bool Start() override;
void SoundLoop() override;
void SetVolume(int volume) override;
void Stop() override;
void Clear(bool mute) override;
void Update() override;
static bool isValid() { return true; }
OpenALStream() : uiSource(0) {}
bool Start() override;
void SoundLoop() override;
void SetVolume(int volume) override;
void Stop() override;
void Clear(bool mute) override;
void Update() override;
static bool isValid() { return true; }
private:
std::thread thread;
std::atomic<bool> m_run_thread;
std::thread thread;
std::atomic<bool> m_run_thread;
Common::Event soundSyncEvent;
Common::Event soundSyncEvent;
short realtimeBuffer[OAL_MAX_SAMPLES * STEREO_CHANNELS];
soundtouch::SAMPLETYPE sampleBuffer[OAL_MAX_SAMPLES * SURROUND_CHANNELS * OAL_MAX_BUFFERS];
ALuint uiBuffers[OAL_MAX_BUFFERS];
ALuint uiSource;
ALfloat fVolume;
short realtimeBuffer[OAL_MAX_SAMPLES * STEREO_CHANNELS];
soundtouch::SAMPLETYPE sampleBuffer[OAL_MAX_SAMPLES * SURROUND_CHANNELS * OAL_MAX_BUFFERS];
ALuint uiBuffers[OAL_MAX_BUFFERS];
ALuint uiSource;
ALfloat fVolume;
u8 numBuffers;
#endif // HAVE_OPENAL
u8 numBuffers;
#endif // HAVE_OPENAL
};

View File

@ -24,7 +24,7 @@ static SLPlayItf bqPlayerPlay;
static SLAndroidSimpleBufferQueueItf bqPlayerBufferQueue;
static SLMuteSoloItf bqPlayerMuteSolo;
static SLVolumeItf bqPlayerVolume;
static CMixer *g_mixer;
static CMixer* g_mixer;
#define BUFFER_SIZE 512
#define BUFFER_SIZE_IN_SAMPLES (BUFFER_SIZE / 2)
@ -32,106 +32,107 @@ static CMixer *g_mixer;
static short buffer[2][BUFFER_SIZE];
static int curBuffer = 0;
static void bqPlayerCallback(SLAndroidSimpleBufferQueueItf bq, void *context)
static void bqPlayerCallback(SLAndroidSimpleBufferQueueItf bq, void* context)
{
assert(bq == bqPlayerBufferQueue);
assert(nullptr == context);
assert(bq == bqPlayerBufferQueue);
assert(nullptr == context);
// Render to the fresh buffer
g_mixer->Mix(reinterpret_cast<short *>(buffer[curBuffer]), BUFFER_SIZE_IN_SAMPLES);
SLresult result = (*bqPlayerBufferQueue)->Enqueue(bqPlayerBufferQueue, buffer[curBuffer], sizeof(buffer[0]));
curBuffer ^= 1; // Switch buffer
// Render to the fresh buffer
g_mixer->Mix(reinterpret_cast<short*>(buffer[curBuffer]), BUFFER_SIZE_IN_SAMPLES);
SLresult result =
(*bqPlayerBufferQueue)->Enqueue(bqPlayerBufferQueue, buffer[curBuffer], sizeof(buffer[0]));
curBuffer ^= 1; // Switch buffer
// Comment from sample code:
// the most likely other result is SL_RESULT_BUFFER_INSUFFICIENT,
// which for this code example would indicate a programming error
_assert_msg_(AUDIO, SL_RESULT_SUCCESS == result, "Couldn't enqueue audio stream.");
// Comment from sample code:
// the most likely other result is SL_RESULT_BUFFER_INSUFFICIENT,
// which for this code example would indicate a programming error
_assert_msg_(AUDIO, SL_RESULT_SUCCESS == result, "Couldn't enqueue audio stream.");
}
bool OpenSLESStream::Start()
{
SLresult result;
// create engine
result = slCreateEngine(&engineObject, 0, nullptr, 0, nullptr, nullptr);
assert(SL_RESULT_SUCCESS == result);
result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);
assert(SL_RESULT_SUCCESS == result);
result = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine);
assert(SL_RESULT_SUCCESS == result);
result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 0, 0, 0);
assert(SL_RESULT_SUCCESS == result);
result = (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE);
assert(SL_RESULT_SUCCESS == result);
SLresult result;
// create engine
result = slCreateEngine(&engineObject, 0, nullptr, 0, nullptr, nullptr);
assert(SL_RESULT_SUCCESS == result);
result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);
assert(SL_RESULT_SUCCESS == result);
result = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine);
assert(SL_RESULT_SUCCESS == result);
result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 0, 0, 0);
assert(SL_RESULT_SUCCESS == result);
result = (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE);
assert(SL_RESULT_SUCCESS == result);
SLDataLocator_AndroidSimpleBufferQueue loc_bufq = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 2};
SLDataFormat_PCM format_pcm = {
SL_DATAFORMAT_PCM,
2,
m_mixer->GetSampleRate() * 1000,
SL_PCMSAMPLEFORMAT_FIXED_16,
SL_PCMSAMPLEFORMAT_FIXED_16,
SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT,
SL_BYTEORDER_LITTLEENDIAN
};
SLDataLocator_AndroidSimpleBufferQueue loc_bufq = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 2};
SLDataFormat_PCM format_pcm = {SL_DATAFORMAT_PCM,
2,
m_mixer->GetSampleRate() * 1000,
SL_PCMSAMPLEFORMAT_FIXED_16,
SL_PCMSAMPLEFORMAT_FIXED_16,
SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT,
SL_BYTEORDER_LITTLEENDIAN};
SLDataSource audioSrc = {&loc_bufq, &format_pcm};
SLDataSource audioSrc = {&loc_bufq, &format_pcm};
// configure audio sink
SLDataLocator_OutputMix loc_outmix = {SL_DATALOCATOR_OUTPUTMIX, outputMixObject};
SLDataSink audioSnk = {&loc_outmix, nullptr};
// configure audio sink
SLDataLocator_OutputMix loc_outmix = {SL_DATALOCATOR_OUTPUTMIX, outputMixObject};
SLDataSink audioSnk = {&loc_outmix, nullptr};
// create audio player
const SLInterfaceID ids[2] = {SL_IID_BUFFERQUEUE, SL_IID_VOLUME};
const SLboolean req[2] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};
result = (*engineEngine)->CreateAudioPlayer(engineEngine, &bqPlayerObject, &audioSrc, &audioSnk, 2, ids, req);
assert(SL_RESULT_SUCCESS == result);
// create audio player
const SLInterfaceID ids[2] = {SL_IID_BUFFERQUEUE, SL_IID_VOLUME};
const SLboolean req[2] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};
result =
(*engineEngine)
->CreateAudioPlayer(engineEngine, &bqPlayerObject, &audioSrc, &audioSnk, 2, ids, req);
assert(SL_RESULT_SUCCESS == result);
result = (*bqPlayerObject)->Realize(bqPlayerObject, SL_BOOLEAN_FALSE);
assert(SL_RESULT_SUCCESS == result);
result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_PLAY, &bqPlayerPlay);
assert(SL_RESULT_SUCCESS == result);
result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_BUFFERQUEUE,
&bqPlayerBufferQueue);
assert(SL_RESULT_SUCCESS == result);
result = (*bqPlayerBufferQueue)->RegisterCallback(bqPlayerBufferQueue, bqPlayerCallback, nullptr);
assert(SL_RESULT_SUCCESS == result);
result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_PLAYING);
assert(SL_RESULT_SUCCESS == result);
result = (*bqPlayerObject)->Realize(bqPlayerObject, SL_BOOLEAN_FALSE);
assert(SL_RESULT_SUCCESS == result);
result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_PLAY, &bqPlayerPlay);
assert(SL_RESULT_SUCCESS == result);
result =
(*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_BUFFERQUEUE, &bqPlayerBufferQueue);
assert(SL_RESULT_SUCCESS == result);
result = (*bqPlayerBufferQueue)->RegisterCallback(bqPlayerBufferQueue, bqPlayerCallback, nullptr);
assert(SL_RESULT_SUCCESS == result);
result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_PLAYING);
assert(SL_RESULT_SUCCESS == result);
// Render and enqueue a first buffer.
curBuffer ^= 1;
g_mixer = m_mixer.get();
// Render and enqueue a first buffer.
curBuffer ^= 1;
g_mixer = m_mixer.get();
result = (*bqPlayerBufferQueue)->Enqueue(bqPlayerBufferQueue, buffer[0], sizeof(buffer[0]));
if (SL_RESULT_SUCCESS != result)
return false;
result = (*bqPlayerBufferQueue)->Enqueue(bqPlayerBufferQueue, buffer[0], sizeof(buffer[0]));
if (SL_RESULT_SUCCESS != result)
return false;
return true;
return true;
}
void OpenSLESStream::Stop()
{
if (bqPlayerObject != nullptr)
{
(*bqPlayerObject)->Destroy(bqPlayerObject);
bqPlayerObject = nullptr;
bqPlayerPlay = nullptr;
bqPlayerBufferQueue = nullptr;
bqPlayerMuteSolo = nullptr;
bqPlayerVolume = nullptr;
}
if (bqPlayerObject != nullptr)
{
(*bqPlayerObject)->Destroy(bqPlayerObject);
bqPlayerObject = nullptr;
bqPlayerPlay = nullptr;
bqPlayerBufferQueue = nullptr;
bqPlayerMuteSolo = nullptr;
bqPlayerVolume = nullptr;
}
if (outputMixObject != nullptr)
{
(*outputMixObject)->Destroy(outputMixObject);
outputMixObject = nullptr;
}
if (outputMixObject != nullptr)
{
(*outputMixObject)->Destroy(outputMixObject);
outputMixObject = nullptr;
}
if (engineObject != nullptr)
{
(*engineObject)->Destroy(engineObject);
engineObject = nullptr;
engineEngine = nullptr;
}
if (engineObject != nullptr)
{
(*engineObject)->Destroy(engineObject);
engineObject = nullptr;
engineEngine = nullptr;
}
}
#endif

View File

@ -13,12 +13,11 @@ class OpenSLESStream final : public SoundStream
{
#ifdef ANDROID
public:
bool Start() override;
void Stop() override;
static bool isValid() { return true; }
bool Start() override;
void Stop() override;
static bool isValid() { return true; }
private:
std::thread thread;
Common::Event soundSyncEvent;
#endif // HAVE_OPENSL
std::thread thread;
Common::Event soundSyncEvent;
#endif // HAVE_OPENSL
};

View File

@ -7,245 +7,247 @@
#include "AudioCommon/DPL2Decoder.h"
#include "AudioCommon/PulseAudioStream.h"
#include "Common/CommonTypes.h"
#include "Common/Thread.h"
#include "Common/Logging/Log.h"
#include "Common/Thread.h"
#include "Core/ConfigManager.h"
namespace
{
const size_t BUFFER_SAMPLES = 512; // ~10 ms - needs to be at least 240 for surround
const size_t BUFFER_SAMPLES = 512; // ~10 ms - needs to be at least 240 for surround
}
PulseAudio::PulseAudio()
: m_thread()
, m_run_thread()
PulseAudio::PulseAudio() : m_thread(), m_run_thread()
{
}
bool PulseAudio::Start()
{
m_stereo = !SConfig::GetInstance().bDPL2Decoder;
m_channels = m_stereo ? 2 : 5; // will tell PA we use a Stereo or 5.0 channel setup
m_stereo = !SConfig::GetInstance().bDPL2Decoder;
m_channels = m_stereo ? 2 : 5; // will tell PA we use a Stereo or 5.0 channel setup
NOTICE_LOG(AUDIO, "PulseAudio backend using %d channels", m_channels);
NOTICE_LOG(AUDIO, "PulseAudio backend using %d channels", m_channels);
m_run_thread = true;
m_thread = std::thread(&PulseAudio::SoundLoop, this);
m_run_thread = true;
m_thread = std::thread(&PulseAudio::SoundLoop, this);
// Initialize DPL2 parameters
DPL2Reset();
// Initialize DPL2 parameters
DPL2Reset();
return true;
return true;
}
void PulseAudio::Stop()
{
m_run_thread = false;
m_thread.join();
m_run_thread = false;
m_thread.join();
}
void PulseAudio::Update()
{
// don't need to do anything here.
// don't need to do anything here.
}
// Called on audio thread.
void PulseAudio::SoundLoop()
{
Common::SetCurrentThreadName("Audio thread - pulse");
Common::SetCurrentThreadName("Audio thread - pulse");
if (PulseInit())
{
while (m_run_thread.load() && m_pa_connected == 1 && m_pa_error >= 0)
m_pa_error = pa_mainloop_iterate(m_pa_ml, 1, nullptr);
if (PulseInit())
{
while (m_run_thread.load() && m_pa_connected == 1 && m_pa_error >= 0)
m_pa_error = pa_mainloop_iterate(m_pa_ml, 1, nullptr);
if (m_pa_error < 0)
ERROR_LOG(AUDIO, "PulseAudio error: %s", pa_strerror(m_pa_error));
if (m_pa_error < 0)
ERROR_LOG(AUDIO, "PulseAudio error: %s", pa_strerror(m_pa_error));
PulseShutdown();
}
PulseShutdown();
}
}
bool PulseAudio::PulseInit()
{
m_pa_error = 0;
m_pa_connected = 0;
m_pa_error = 0;
m_pa_connected = 0;
// create pulseaudio main loop and context
// also register the async state callback which is called when the connection to the pa server has changed
m_pa_ml = pa_mainloop_new();
m_pa_mlapi = pa_mainloop_get_api(m_pa_ml);
m_pa_ctx = pa_context_new(m_pa_mlapi, "dolphin-emu");
m_pa_error = pa_context_connect(m_pa_ctx, nullptr, PA_CONTEXT_NOFLAGS, nullptr);
pa_context_set_state_callback(m_pa_ctx, StateCallback, this);
// create pulseaudio main loop and context
// also register the async state callback which is called when the connection to the pa server has
// changed
m_pa_ml = pa_mainloop_new();
m_pa_mlapi = pa_mainloop_get_api(m_pa_ml);
m_pa_ctx = pa_context_new(m_pa_mlapi, "dolphin-emu");
m_pa_error = pa_context_connect(m_pa_ctx, nullptr, PA_CONTEXT_NOFLAGS, nullptr);
pa_context_set_state_callback(m_pa_ctx, StateCallback, this);
// wait until we're connected to the pulseaudio server
while (m_pa_connected == 0 && m_pa_error >= 0)
m_pa_error = pa_mainloop_iterate(m_pa_ml, 1, nullptr);
// wait until we're connected to the pulseaudio server
while (m_pa_connected == 0 && m_pa_error >= 0)
m_pa_error = pa_mainloop_iterate(m_pa_ml, 1, nullptr);
if (m_pa_connected == 2 || m_pa_error < 0)
{
ERROR_LOG(AUDIO, "PulseAudio failed to initialize: %s", pa_strerror(m_pa_error));
return false;
}
if (m_pa_connected == 2 || m_pa_error < 0)
{
ERROR_LOG(AUDIO, "PulseAudio failed to initialize: %s", pa_strerror(m_pa_error));
return false;
}
// create a new audio stream with our sample format
// also connect the callbacks for this stream
pa_sample_spec ss;
pa_channel_map channel_map;
pa_channel_map* channel_map_p = nullptr; // auto channel map
if (m_stereo)
{
ss.format = PA_SAMPLE_S16LE;
m_bytespersample = sizeof(s16);
}
else
{
// surround is remixed in floats, use a float PA buffer to save another conversion
ss.format = PA_SAMPLE_FLOAT32NE;
m_bytespersample = sizeof(float);
// create a new audio stream with our sample format
// also connect the callbacks for this stream
pa_sample_spec ss;
pa_channel_map channel_map;
pa_channel_map* channel_map_p = nullptr; // auto channel map
if (m_stereo)
{
ss.format = PA_SAMPLE_S16LE;
m_bytespersample = sizeof(s16);
}
else
{
// surround is remixed in floats, use a float PA buffer to save another conversion
ss.format = PA_SAMPLE_FLOAT32NE;
m_bytespersample = sizeof(float);
channel_map_p = &channel_map; // explicit channel map:
channel_map.channels = 5;
channel_map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
channel_map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
channel_map.map[2] = PA_CHANNEL_POSITION_FRONT_CENTER;
channel_map.map[3] = PA_CHANNEL_POSITION_REAR_LEFT;
channel_map.map[4] = PA_CHANNEL_POSITION_REAR_RIGHT;
}
ss.channels = m_channels;
ss.rate = m_mixer->GetSampleRate();
assert(pa_sample_spec_valid(&ss));
m_pa_s = pa_stream_new(m_pa_ctx, "Playback", &ss, channel_map_p);
pa_stream_set_write_callback(m_pa_s, WriteCallback, this);
pa_stream_set_underflow_callback(m_pa_s, UnderflowCallback, this);
channel_map_p = &channel_map; // explicit channel map:
channel_map.channels = 5;
channel_map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
channel_map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
channel_map.map[2] = PA_CHANNEL_POSITION_FRONT_CENTER;
channel_map.map[3] = PA_CHANNEL_POSITION_REAR_LEFT;
channel_map.map[4] = PA_CHANNEL_POSITION_REAR_RIGHT;
}
ss.channels = m_channels;
ss.rate = m_mixer->GetSampleRate();
assert(pa_sample_spec_valid(&ss));
m_pa_s = pa_stream_new(m_pa_ctx, "Playback", &ss, channel_map_p);
pa_stream_set_write_callback(m_pa_s, WriteCallback, this);
pa_stream_set_underflow_callback(m_pa_s, UnderflowCallback, this);
// connect this audio stream to the default audio playback
// limit buffersize to reduce latency
m_pa_ba.fragsize = -1;
m_pa_ba.maxlength = -1; // max buffer, so also max latency
m_pa_ba.minreq = -1; // don't read every byte, try to group them _a bit_
m_pa_ba.prebuf = -1; // start as early as possible
m_pa_ba.tlength = BUFFER_SAMPLES * m_channels * m_bytespersample; // designed latency, only change this flag for low latency output
pa_stream_flags flags = pa_stream_flags(PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_ADJUST_LATENCY | PA_STREAM_AUTO_TIMING_UPDATE);
m_pa_error = pa_stream_connect_playback(m_pa_s, nullptr, &m_pa_ba, flags, nullptr, nullptr);
if (m_pa_error < 0)
{
ERROR_LOG(AUDIO, "PulseAudio failed to initialize: %s", pa_strerror(m_pa_error));
return false;
}
// connect this audio stream to the default audio playback
// limit buffersize to reduce latency
m_pa_ba.fragsize = -1;
m_pa_ba.maxlength = -1; // max buffer, so also max latency
m_pa_ba.minreq = -1; // don't read every byte, try to group them _a bit_
m_pa_ba.prebuf = -1; // start as early as possible
m_pa_ba.tlength =
BUFFER_SAMPLES * m_channels *
m_bytespersample; // designed latency, only change this flag for low latency output
pa_stream_flags flags = pa_stream_flags(PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_ADJUST_LATENCY |
PA_STREAM_AUTO_TIMING_UPDATE);
m_pa_error = pa_stream_connect_playback(m_pa_s, nullptr, &m_pa_ba, flags, nullptr, nullptr);
if (m_pa_error < 0)
{
ERROR_LOG(AUDIO, "PulseAudio failed to initialize: %s", pa_strerror(m_pa_error));
return false;
}
INFO_LOG(AUDIO, "Pulse successfully initialized");
return true;
INFO_LOG(AUDIO, "Pulse successfully initialized");
return true;
}
void PulseAudio::PulseShutdown()
{
pa_context_disconnect(m_pa_ctx);
pa_context_unref(m_pa_ctx);
pa_mainloop_free(m_pa_ml);
pa_context_disconnect(m_pa_ctx);
pa_context_unref(m_pa_ctx);
pa_mainloop_free(m_pa_ml);
}
void PulseAudio::StateCallback(pa_context* c)
{
pa_context_state_t state = pa_context_get_state(c);
switch (state)
{
case PA_CONTEXT_FAILED:
case PA_CONTEXT_TERMINATED:
m_pa_connected = 2;
break;
case PA_CONTEXT_READY:
m_pa_connected = 1;
break;
default:
break;
}
pa_context_state_t state = pa_context_get_state(c);
switch (state)
{
case PA_CONTEXT_FAILED:
case PA_CONTEXT_TERMINATED:
m_pa_connected = 2;
break;
case PA_CONTEXT_READY:
m_pa_connected = 1;
break;
default:
break;
}
}
// on underflow, increase pulseaudio latency in ~10ms steps
void PulseAudio::UnderflowCallback(pa_stream* s)
{
m_pa_ba.tlength += BUFFER_SAMPLES * m_channels * m_bytespersample;
pa_operation* op = pa_stream_set_buffer_attr(s, &m_pa_ba, nullptr, nullptr);
pa_operation_unref(op);
m_pa_ba.tlength += BUFFER_SAMPLES * m_channels * m_bytespersample;
pa_operation* op = pa_stream_set_buffer_attr(s, &m_pa_ba, nullptr, nullptr);
pa_operation_unref(op);
WARN_LOG(AUDIO, "pulseaudio underflow, new latency: %d bytes", m_pa_ba.tlength);
WARN_LOG(AUDIO, "pulseaudio underflow, new latency: %d bytes", m_pa_ba.tlength);
}
void PulseAudio::WriteCallback(pa_stream* s, size_t length)
{
int bytes_per_frame = m_channels * m_bytespersample;
int frames = (length / bytes_per_frame);
size_t trunc_length = frames * bytes_per_frame;
int bytes_per_frame = m_channels * m_bytespersample;
int frames = (length / bytes_per_frame);
size_t trunc_length = frames * bytes_per_frame;
// fetch dst buffer directly from pulseaudio, so no memcpy is needed
void* buffer;
m_pa_error = pa_stream_begin_write(s, &buffer, &trunc_length);
// fetch dst buffer directly from pulseaudio, so no memcpy is needed
void* buffer;
m_pa_error = pa_stream_begin_write(s, &buffer, &trunc_length);
if (!buffer || m_pa_error < 0)
return; // error will be printed from main loop
if (!buffer || m_pa_error < 0)
return; // error will be printed from main loop
if (m_stereo)
{
// use the raw s16 stereo mix
m_mixer->Mix((s16*) buffer, frames);
}
else
{
// get a floating point mix
s16 s16buffer_stereo[frames * 2];
m_mixer->Mix(s16buffer_stereo, frames); // implicitly mixes to 16-bit stereo
if (m_stereo)
{
// use the raw s16 stereo mix
m_mixer->Mix((s16*)buffer, frames);
}
else
{
// get a floating point mix
s16 s16buffer_stereo[frames * 2];
m_mixer->Mix(s16buffer_stereo, frames); // implicitly mixes to 16-bit stereo
float floatbuffer_stereo[frames * 2];
// s16 to float
for (int i=0; i < frames * 2; ++i)
{
floatbuffer_stereo[i] = s16buffer_stereo[i] / float(1 << 15);
}
float floatbuffer_stereo[frames * 2];
// s16 to float
for (int i = 0; i < frames * 2; ++i)
{
floatbuffer_stereo[i] = s16buffer_stereo[i] / float(1 << 15);
}
if (m_channels == 5) // Extract dpl2/5.0 Surround
{
float floatbuffer_6chan[frames * 6];
// DPL2Decode output: LEFTFRONT, RIGHTFRONT, CENTREFRONT, (sub), LEFTREAR, RIGHTREAR
DPL2Decode(floatbuffer_stereo, frames, floatbuffer_6chan);
if (m_channels == 5) // Extract dpl2/5.0 Surround
{
float floatbuffer_6chan[frames * 6];
// DPL2Decode output: LEFTFRONT, RIGHTFRONT, CENTREFRONT, (sub), LEFTREAR, RIGHTREAR
DPL2Decode(floatbuffer_stereo, frames, floatbuffer_6chan);
// Discard the subwoofer channel - DPL2Decode generates a pretty
// good 5.0 but not a good 5.1 output.
const int dpl2_to_5chan[] = {0,1,2,4,5};
for (int i=0; i < frames; ++i)
{
for (int j=0; j < m_channels; ++j)
{
((float*)buffer)[m_channels * i + j] = floatbuffer_6chan[6 * i + dpl2_to_5chan[j]];
}
}
}
else
{
ERROR_LOG(AUDIO, "Unsupported number of PA channels requested: %d", (int)m_channels);
return;
}
}
// Discard the subwoofer channel - DPL2Decode generates a pretty
// good 5.0 but not a good 5.1 output.
const int dpl2_to_5chan[] = {0, 1, 2, 4, 5};
for (int i = 0; i < frames; ++i)
{
for (int j = 0; j < m_channels; ++j)
{
((float*)buffer)[m_channels * i + j] = floatbuffer_6chan[6 * i + dpl2_to_5chan[j]];
}
}
}
else
{
ERROR_LOG(AUDIO, "Unsupported number of PA channels requested: %d", (int)m_channels);
return;
}
}
m_pa_error = pa_stream_write(s, buffer, trunc_length, nullptr, 0, PA_SEEK_RELATIVE);
m_pa_error = pa_stream_write(s, buffer, trunc_length, nullptr, 0, PA_SEEK_RELATIVE);
}
// Callbacks that forward to internal methods (required because PulseAudio is a C API).
void PulseAudio::StateCallback(pa_context* c, void* userdata)
{
PulseAudio* p = (PulseAudio*) userdata;
p->StateCallback(c);
PulseAudio* p = (PulseAudio*)userdata;
p->StateCallback(c);
}
void PulseAudio::UnderflowCallback(pa_stream* s, void* userdata)
{
PulseAudio* p = (PulseAudio*) userdata;
p->UnderflowCallback(s);
PulseAudio* p = (PulseAudio*)userdata;
p->UnderflowCallback(s);
}
void PulseAudio::WriteCallback(pa_stream* s, size_t length, void* userdata)
{
PulseAudio* p = (PulseAudio*) userdata;
p->WriteCallback(s, length);
PulseAudio* p = (PulseAudio*)userdata;
p->WriteCallback(s, length);
}

View File

@ -18,42 +18,41 @@ class PulseAudio final : public SoundStream
{
#if defined(HAVE_PULSEAUDIO) && HAVE_PULSEAUDIO
public:
PulseAudio();
PulseAudio();
bool Start() override;
void Stop() override;
void Update() override;
bool Start() override;
void Stop() override;
void Update() override;
static bool isValid() { return true; }
void StateCallback(pa_context *c);
void WriteCallback(pa_stream *s, size_t length);
void UnderflowCallback(pa_stream *s);
static bool isValid() { return true; }
void StateCallback(pa_context* c);
void WriteCallback(pa_stream* s, size_t length);
void UnderflowCallback(pa_stream* s);
private:
void SoundLoop() override;
void SoundLoop() override;
bool PulseInit();
void PulseShutdown();
bool PulseInit();
void PulseShutdown();
// wrapper callback functions, last parameter _must_ be PulseAudio*
static void StateCallback(pa_context *c, void *userdata);
static void WriteCallback(pa_stream *s, size_t length, void *userdata);
static void UnderflowCallback(pa_stream *s, void *userdata);
// wrapper callback functions, last parameter _must_ be PulseAudio*
static void StateCallback(pa_context* c, void* userdata);
static void WriteCallback(pa_stream* s, size_t length, void* userdata);
static void UnderflowCallback(pa_stream* s, void* userdata);
std::thread m_thread;
std::atomic<bool> m_run_thread;
std::thread m_thread;
std::atomic<bool> m_run_thread;
bool m_stereo; // stereo, else surround
int m_bytespersample;
int m_channels;
bool m_stereo; // stereo, else surround
int m_bytespersample;
int m_channels;
int m_pa_error;
int m_pa_connected;
pa_mainloop *m_pa_ml;
pa_mainloop_api *m_pa_mlapi;
pa_context *m_pa_ctx;
pa_stream *m_pa_s;
pa_buffer_attr m_pa_ba;
int m_pa_error;
int m_pa_connected;
pa_mainloop* m_pa_ml;
pa_mainloop_api* m_pa_mlapi;
pa_context* m_pa_ctx;
pa_stream* m_pa_s;
pa_buffer_attr m_pa_ba;
#endif
};

View File

@ -14,51 +14,49 @@
class SoundStream
{
protected:
std::unique_ptr<CMixer> m_mixer;
bool m_logAudio;
WaveFileWriter g_wave_writer;
bool m_muted;
std::unique_ptr<CMixer> m_mixer;
bool m_logAudio;
WaveFileWriter g_wave_writer;
bool m_muted;
public:
SoundStream() : m_mixer(new CMixer(48000)), m_logAudio(false), m_muted(false) {}
virtual ~SoundStream() { }
SoundStream() : m_mixer(new CMixer(48000)), m_logAudio(false), m_muted(false) {}
virtual ~SoundStream() {}
static bool isValid() { return false; }
CMixer* GetMixer() const { return m_mixer.get(); }
virtual bool Start() { return false; }
virtual void SetVolume(int) {}
virtual void SoundLoop() {}
virtual void Stop() {}
virtual void Update() {}
virtual void Clear(bool mute) { m_muted = mute; }
bool IsMuted() const { return m_muted; }
void StartLogAudio(const std::string& filename)
{
if (!m_logAudio)
{
m_logAudio = true;
g_wave_writer.Start(filename, m_mixer->GetSampleRate());
g_wave_writer.SetSkipSilence(false);
NOTICE_LOG(AUDIO, "Starting Audio logging");
}
else
{
WARN_LOG(AUDIO, "Audio logging already started");
}
}
static bool isValid() { return false; }
CMixer* GetMixer() const { return m_mixer.get(); }
virtual bool Start() { return false; }
virtual void SetVolume(int) {}
virtual void SoundLoop() {}
virtual void Stop() {}
virtual void Update() {}
virtual void Clear(bool mute) { m_muted = mute; }
bool IsMuted() const { return m_muted; }
void StartLogAudio(const std::string& filename)
{
if (!m_logAudio)
{
m_logAudio = true;
g_wave_writer.Start(filename, m_mixer->GetSampleRate());
g_wave_writer.SetSkipSilence(false);
NOTICE_LOG(AUDIO, "Starting Audio logging");
}
else
{
WARN_LOG(AUDIO, "Audio logging already started");
}
}
void StopLogAudio()
{
if (m_logAudio)
{
m_logAudio = false;
g_wave_writer.Stop();
NOTICE_LOG(AUDIO, "Stopping Audio logging");
}
else
{
WARN_LOG(AUDIO, "Audio logging already stopped");
}
}
void StopLogAudio()
{
if (m_logAudio)
{
m_logAudio = false;
g_wave_writer.Stop();
NOTICE_LOG(AUDIO, "Stopping Audio logging");
}
else
{
WARN_LOG(AUDIO, "Audio logging already stopped");
}
}
};

View File

@ -17,127 +17,130 @@ WaveFileWriter::WaveFileWriter()
WaveFileWriter::~WaveFileWriter()
{
Stop();
Stop();
}
bool WaveFileWriter::Start(const std::string& filename, unsigned int HLESampleRate)
{
// Check if the file is already open
if (file)
{
PanicAlertT("The file %s was already open, the file header will not be written.", filename.c_str());
return false;
}
// Check if the file is already open
if (file)
{
PanicAlertT("The file %s was already open, the file header will not be written.",
filename.c_str());
return false;
}
file.Open(filename, "wb");
if (!file)
{
PanicAlertT("The file %s could not be opened for writing. Please check if it's already opened by another program.", filename.c_str());
return false;
}
file.Open(filename, "wb");
if (!file)
{
PanicAlertT("The file %s could not be opened for writing. Please check if it's already opened "
"by another program.",
filename.c_str());
return false;
}
audio_size = 0;
audio_size = 0;
// -----------------
// Write file header
// -----------------
Write4("RIFF");
Write(100 * 1000 * 1000); // write big value in case the file gets truncated
Write4("WAVE");
Write4("fmt ");
// -----------------
// Write file header
// -----------------
Write4("RIFF");
Write(100 * 1000 * 1000); // write big value in case the file gets truncated
Write4("WAVE");
Write4("fmt ");
Write(16); // size of fmt block
Write(0x00020001); //two channels, uncompressed
Write(16); // size of fmt block
Write(0x00020001); // two channels, uncompressed
const u32 sample_rate = HLESampleRate;
Write(sample_rate);
Write(sample_rate * 2 * 2); //two channels, 16bit
const u32 sample_rate = HLESampleRate;
Write(sample_rate);
Write(sample_rate * 2 * 2); // two channels, 16bit
Write(0x00100004);
Write4("data");
Write(100 * 1000 * 1000 - 32);
Write(0x00100004);
Write4("data");
Write(100 * 1000 * 1000 - 32);
// We are now at offset 44
if (file.Tell() != 44)
PanicAlert("Wrong offset: %lld", (long long)file.Tell());
// We are now at offset 44
if (file.Tell() != 44)
PanicAlert("Wrong offset: %lld", (long long)file.Tell());
return true;
return true;
}
void WaveFileWriter::Stop()
{
// u32 file_size = (u32)ftello(file);
file.Seek(4, SEEK_SET);
Write(audio_size + 36);
// u32 file_size = (u32)ftello(file);
file.Seek(4, SEEK_SET);
Write(audio_size + 36);
file.Seek(40, SEEK_SET);
Write(audio_size);
file.Seek(40, SEEK_SET);
Write(audio_size);
file.Close();
file.Close();
}
void WaveFileWriter::Write(u32 value)
{
file.WriteArray(&value, 1);
file.WriteArray(&value, 1);
}
void WaveFileWriter::Write4(const char *ptr)
void WaveFileWriter::Write4(const char* ptr)
{
file.WriteBytes(ptr, 4);
file.WriteBytes(ptr, 4);
}
void WaveFileWriter::AddStereoSamples(const short *sample_data, u32 count)
void WaveFileWriter::AddStereoSamples(const short* sample_data, u32 count)
{
if (!file)
PanicAlertT("WaveFileWriter - file not open.");
if (!file)
PanicAlertT("WaveFileWriter - file not open.");
if (skip_silence)
{
bool all_zero = true;
if (skip_silence)
{
bool all_zero = true;
for (u32 i = 0; i < count * 2; i++)
{
if (sample_data[i])
all_zero = false;
}
for (u32 i = 0; i < count * 2; i++)
{
if (sample_data[i])
all_zero = false;
}
if (all_zero)
return;
}
if (all_zero)
return;
}
file.WriteBytes(sample_data, count * 4);
audio_size += count * 4;
file.WriteBytes(sample_data, count * 4);
audio_size += count * 4;
}
void WaveFileWriter::AddStereoSamplesBE(const short *sample_data, u32 count)
void WaveFileWriter::AddStereoSamplesBE(const short* sample_data, u32 count)
{
if (!file)
PanicAlertT("WaveFileWriter - file not open.");
if (!file)
PanicAlertT("WaveFileWriter - file not open.");
if (count > BUFFER_SIZE * 2)
PanicAlert("WaveFileWriter - buffer too small (count = %u).", count);
if (count > BUFFER_SIZE * 2)
PanicAlert("WaveFileWriter - buffer too small (count = %u).", count);
if (skip_silence)
{
bool all_zero = true;
if (skip_silence)
{
bool all_zero = true;
for (u32 i = 0; i < count * 2; i++)
{
if (sample_data[i])
all_zero = false;
}
for (u32 i = 0; i < count * 2; i++)
{
if (sample_data[i])
all_zero = false;
}
if (all_zero)
return;
}
if (all_zero)
return;
}
for (u32 i = 0; i < count; i++)
{
//Flip the audio channels from RL to LR
conv_buffer[2 * i] = Common::swap16((u16)sample_data[2 * i + 1]);
conv_buffer[2 * i + 1] = Common::swap16((u16)sample_data[2 * i]);
}
for (u32 i = 0; i < count; i++)
{
// Flip the audio channels from RL to LR
conv_buffer[2 * i] = Common::swap16((u16)sample_data[2 * i + 1]);
conv_buffer[2 * i + 1] = Common::swap16((u16)sample_data[2 * i]);
}
file.WriteBytes(conv_buffer.data(), count * 4);
audio_size += count * 4;
file.WriteBytes(conv_buffer.data(), count * 4);
audio_size += count * 4;
}

View File

@ -23,25 +23,23 @@
class WaveFileWriter : NonCopyable
{
public:
WaveFileWriter();
~WaveFileWriter();
WaveFileWriter();
~WaveFileWriter();
bool Start(const std::string& filename, unsigned int HLESampleRate);
void Stop();
void SetSkipSilence(bool skip) { skip_silence = skip; }
void AddStereoSamples(const short *sample_data, u32 count);
void AddStereoSamplesBE(const short *sample_data, u32 count); // big endian
u32 GetAudioSize() const { return audio_size; }
bool Start(const std::string& filename, unsigned int HLESampleRate);
void Stop();
void SetSkipSilence(bool skip) { skip_silence = skip; }
void AddStereoSamples(const short* sample_data, u32 count);
void AddStereoSamplesBE(const short* sample_data, u32 count); // big endian
u32 GetAudioSize() const { return audio_size; }
private:
static constexpr size_t BUFFER_SIZE = 32 * 1024;
static constexpr size_t BUFFER_SIZE = 32 * 1024;
File::IOFile file;
bool skip_silence = false;
u32 audio_size = 0;
std::array<short, BUFFER_SIZE> conv_buffer{};
void Write(u32 value);
void Write4(const char* ptr);
File::IOFile file;
bool skip_silence = false;
u32 audio_size = 0;
std::array<short, BUFFER_SIZE> conv_buffer{};
void Write(u32 value);
void Write4(const char* ptr);
};

View File

@ -2,12 +2,12 @@
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include "AudioCommon/XAudio2Stream.h"
#include <xaudio2.h>
#include "AudioCommon/AudioCommon.h"
#include "AudioCommon/XAudio2Stream.h"
#include "Common/Event.h"
#include "Common/MsgHandler.h"
#include "Common/Logging/Log.h"
#include "Common/MsgHandler.h"
#ifndef XAUDIO2_DLL
#error You are building this module against the wrong version of DirectX. You probably need to remove DXSDK_DIR from your include path.
@ -16,29 +16,28 @@
struct StreamingVoiceContext : public IXAudio2VoiceCallback
{
private:
CMixer* const m_mixer;
Common::Event& m_sound_sync_event;
IXAudio2SourceVoice* m_source_voice;
std::unique_ptr<BYTE[]> xaudio_buffer;
CMixer* const m_mixer;
Common::Event& m_sound_sync_event;
IXAudio2SourceVoice* m_source_voice;
std::unique_ptr<BYTE[]> xaudio_buffer;
void SubmitBuffer(PBYTE buf_data);
void SubmitBuffer(PBYTE buf_data);
public:
StreamingVoiceContext(IXAudio2 *pXAudio2, CMixer *pMixer, Common::Event& pSyncEvent);
StreamingVoiceContext(IXAudio2* pXAudio2, CMixer* pMixer, Common::Event& pSyncEvent);
~StreamingVoiceContext();
~StreamingVoiceContext();
void StreamingVoiceContext::Stop();
void StreamingVoiceContext::Play();
void StreamingVoiceContext::Stop();
void StreamingVoiceContext::Play();
STDMETHOD_(void, OnVoiceError) (THIS_ void* pBufferContext, HRESULT Error) {}
STDMETHOD_(void, OnVoiceProcessingPassStart) (UINT32) {}
STDMETHOD_(void, OnVoiceProcessingPassEnd) () {}
STDMETHOD_(void, OnBufferStart) (void*) {}
STDMETHOD_(void, OnLoopEnd) (void*) {}
STDMETHOD_(void, OnStreamEnd) () {}
STDMETHOD_(void, OnBufferEnd) (void* context);
STDMETHOD_(void, OnVoiceError)(THIS_ void* pBufferContext, HRESULT Error) {}
STDMETHOD_(void, OnVoiceProcessingPassStart)(UINT32) {}
STDMETHOD_(void, OnVoiceProcessingPassEnd)() {}
STDMETHOD_(void, OnBufferStart)(void*) {}
STDMETHOD_(void, OnLoopEnd)(void*) {}
STDMETHOD_(void, OnStreamEnd)() {}
STDMETHOD_(void, OnBufferEnd)(void* context);
};
const int NUM_BUFFERS = 3;
@ -50,199 +49,199 @@ const int BUFFER_SIZE_BYTES = BUFFER_SIZE * sizeof(s16);
void StreamingVoiceContext::SubmitBuffer(PBYTE buf_data)
{
XAUDIO2_BUFFER buf = {};
buf.AudioBytes = BUFFER_SIZE_BYTES;
buf.pContext = buf_data;
buf.pAudioData = buf_data;
XAUDIO2_BUFFER buf = {};
buf.AudioBytes = BUFFER_SIZE_BYTES;
buf.pContext = buf_data;
buf.pAudioData = buf_data;
m_source_voice->SubmitSourceBuffer(&buf);
m_source_voice->SubmitSourceBuffer(&buf);
}
StreamingVoiceContext::StreamingVoiceContext(IXAudio2 *pXAudio2, CMixer *pMixer, Common::Event& pSyncEvent)
: m_mixer(pMixer)
, m_sound_sync_event(pSyncEvent)
, xaudio_buffer(new BYTE[NUM_BUFFERS * BUFFER_SIZE_BYTES]())
StreamingVoiceContext::StreamingVoiceContext(IXAudio2* pXAudio2, CMixer* pMixer,
Common::Event& pSyncEvent)
: m_mixer(pMixer), m_sound_sync_event(pSyncEvent),
xaudio_buffer(new BYTE[NUM_BUFFERS * BUFFER_SIZE_BYTES]())
{
WAVEFORMATEXTENSIBLE wfx = {};
WAVEFORMATEXTENSIBLE wfx = {};
wfx.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
wfx.Format.nSamplesPerSec = m_mixer->GetSampleRate();
wfx.Format.nChannels = 2;
wfx.Format.wBitsPerSample = 16;
wfx.Format.nBlockAlign = wfx.Format.nChannels*wfx.Format.wBitsPerSample / 8;
wfx.Format.nAvgBytesPerSec = wfx.Format.nSamplesPerSec * wfx.Format.nBlockAlign;
wfx.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
wfx.Samples.wValidBitsPerSample = 16;
wfx.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
wfx.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
wfx.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
wfx.Format.nSamplesPerSec = m_mixer->GetSampleRate();
wfx.Format.nChannels = 2;
wfx.Format.wBitsPerSample = 16;
wfx.Format.nBlockAlign = wfx.Format.nChannels * wfx.Format.wBitsPerSample / 8;
wfx.Format.nAvgBytesPerSec = wfx.Format.nSamplesPerSec * wfx.Format.nBlockAlign;
wfx.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
wfx.Samples.wValidBitsPerSample = 16;
wfx.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
wfx.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
// create source voice
HRESULT hr;
if (FAILED(hr = pXAudio2->CreateSourceVoice(&m_source_voice, &wfx.Format, XAUDIO2_VOICE_NOSRC, 1.0f, this)))
{
PanicAlert("XAudio2 CreateSourceVoice failed: %#X", hr);
return;
}
// create source voice
HRESULT hr;
if (FAILED(hr = pXAudio2->CreateSourceVoice(&m_source_voice, &wfx.Format, XAUDIO2_VOICE_NOSRC,
1.0f, this)))
{
PanicAlert("XAudio2 CreateSourceVoice failed: %#X", hr);
return;
}
m_source_voice->Start();
m_source_voice->Start();
// start buffers with silence
for (int i = 0; i != NUM_BUFFERS; ++i)
SubmitBuffer(xaudio_buffer.get() + (i * BUFFER_SIZE_BYTES));
// start buffers with silence
for (int i = 0; i != NUM_BUFFERS; ++i)
SubmitBuffer(xaudio_buffer.get() + (i * BUFFER_SIZE_BYTES));
}
StreamingVoiceContext::~StreamingVoiceContext()
{
if (m_source_voice)
{
m_source_voice->Stop();
m_source_voice->DestroyVoice();
}
if (m_source_voice)
{
m_source_voice->Stop();
m_source_voice->DestroyVoice();
}
}
void StreamingVoiceContext::Stop()
{
if (m_source_voice)
m_source_voice->Stop();
if (m_source_voice)
m_source_voice->Stop();
}
void StreamingVoiceContext::Play()
{
if (m_source_voice)
m_source_voice->Start();
if (m_source_voice)
m_source_voice->Start();
}
void StreamingVoiceContext::OnBufferEnd(void* context)
{
// buffer end callback; gets SAMPLES_PER_BUFFER samples for a new buffer
// buffer end callback; gets SAMPLES_PER_BUFFER samples for a new buffer
if (!m_source_voice || !context)
return;
if (!m_source_voice || !context)
return;
//m_sound_sync_event->Wait(); // sync
//m_sound_sync_event->Spin(); // or tight sync
// m_sound_sync_event->Wait(); // sync
// m_sound_sync_event->Spin(); // or tight sync
m_mixer->Mix(static_cast<short*>(context), SAMPLES_PER_BUFFER);
SubmitBuffer(static_cast<BYTE*>(context));
m_mixer->Mix(static_cast<short*>(context), SAMPLES_PER_BUFFER);
SubmitBuffer(static_cast<BYTE*>(context));
}
HMODULE XAudio2::m_xaudio2_dll = nullptr;
typedef decltype(&XAudio2Create) XAudio2Create_t;
void *XAudio2::PXAudio2Create = nullptr;
void* XAudio2::PXAudio2Create = nullptr;
bool XAudio2::InitLibrary()
{
if (m_xaudio2_dll)
{
return true;
}
if (m_xaudio2_dll)
{
return true;
}
m_xaudio2_dll = ::LoadLibrary(XAUDIO2_DLL);
if (!m_xaudio2_dll)
{
return false;
}
m_xaudio2_dll = ::LoadLibrary(XAUDIO2_DLL);
if (!m_xaudio2_dll)
{
return false;
}
if (!PXAudio2Create)
{
PXAudio2Create = (XAudio2Create_t)::GetProcAddress(m_xaudio2_dll, "XAudio2Create");
if (!PXAudio2Create)
{
::FreeLibrary(m_xaudio2_dll);
m_xaudio2_dll = nullptr;
return false;
}
}
if (!PXAudio2Create)
{
PXAudio2Create = (XAudio2Create_t)::GetProcAddress(m_xaudio2_dll, "XAudio2Create");
if (!PXAudio2Create)
{
::FreeLibrary(m_xaudio2_dll);
m_xaudio2_dll = nullptr;
return false;
}
}
return true;
return true;
}
XAudio2::XAudio2()
: m_mastering_voice(nullptr)
, m_volume(1.0f)
, m_cleanup_com(SUCCEEDED(CoInitializeEx(nullptr, COINIT_MULTITHREADED)))
: m_mastering_voice(nullptr), m_volume(1.0f),
m_cleanup_com(SUCCEEDED(CoInitializeEx(nullptr, COINIT_MULTITHREADED)))
{
}
XAudio2::~XAudio2()
{
Stop();
if (m_cleanup_com)
CoUninitialize();
Stop();
if (m_cleanup_com)
CoUninitialize();
}
bool XAudio2::Start()
{
HRESULT hr;
HRESULT hr;
// callback doesn't seem to run on a specific CPU anyways
IXAudio2* xaudptr;
if (FAILED(hr = ((XAudio2Create_t)PXAudio2Create)(&xaudptr, 0, XAUDIO2_DEFAULT_PROCESSOR)))
{
PanicAlert("XAudio2 init failed: %#X", hr);
Stop();
return false;
}
m_xaudio2 = std::unique_ptr<IXAudio2, Releaser>(xaudptr);
// callback doesn't seem to run on a specific CPU anyways
IXAudio2* xaudptr;
if (FAILED(hr = ((XAudio2Create_t)PXAudio2Create)(&xaudptr, 0, XAUDIO2_DEFAULT_PROCESSOR)))
{
PanicAlert("XAudio2 init failed: %#X", hr);
Stop();
return false;
}
m_xaudio2 = std::unique_ptr<IXAudio2, Releaser>(xaudptr);
// XAudio2 master voice
// XAUDIO2_DEFAULT_CHANNELS instead of 2 for expansion?
if (FAILED(hr = m_xaudio2->CreateMasteringVoice(&m_mastering_voice, 2, m_mixer->GetSampleRate())))
{
PanicAlert("XAudio2 master voice creation failed: %#X", hr);
Stop();
return false;
}
// XAudio2 master voice
// XAUDIO2_DEFAULT_CHANNELS instead of 2 for expansion?
if (FAILED(hr = m_xaudio2->CreateMasteringVoice(&m_mastering_voice, 2, m_mixer->GetSampleRate())))
{
PanicAlert("XAudio2 master voice creation failed: %#X", hr);
Stop();
return false;
}
// Volume
m_mastering_voice->SetVolume(m_volume);
// Volume
m_mastering_voice->SetVolume(m_volume);
m_voice_context = std::unique_ptr<StreamingVoiceContext>
(new StreamingVoiceContext(m_xaudio2.get(), m_mixer.get(), m_sound_sync_event));
m_voice_context = std::unique_ptr<StreamingVoiceContext>(
new StreamingVoiceContext(m_xaudio2.get(), m_mixer.get(), m_sound_sync_event));
return true;
return true;
}
void XAudio2::SetVolume(int volume)
{
//linear 1- .01
m_volume = (float)volume / 100.f;
// linear 1- .01
m_volume = (float)volume / 100.f;
if (m_mastering_voice)
m_mastering_voice->SetVolume(m_volume);
if (m_mastering_voice)
m_mastering_voice->SetVolume(m_volume);
}
void XAudio2::Clear(bool mute)
{
m_muted = mute;
m_muted = mute;
if (m_voice_context)
{
if (m_muted)
m_voice_context->Stop();
else
m_voice_context->Play();
}
if (m_voice_context)
{
if (m_muted)
m_voice_context->Stop();
else
m_voice_context->Play();
}
}
void XAudio2::Stop()
{
//m_sound_sync_event.Set();
// m_sound_sync_event.Set();
m_voice_context.reset();
m_voice_context.reset();
if (m_mastering_voice)
{
m_mastering_voice->DestroyVoice();
m_mastering_voice = nullptr;
}
if (m_mastering_voice)
{
m_mastering_voice->DestroyVoice();
m_mastering_voice = nullptr;
}
m_xaudio2.reset(); // release interface
m_xaudio2.reset(); // release interface
if (m_xaudio2_dll)
{
::FreeLibrary(m_xaudio2_dll);
m_xaudio2_dll = nullptr;
PXAudio2Create = nullptr;
}
if (m_xaudio2_dll)
{
::FreeLibrary(m_xaudio2_dll);
m_xaudio2_dll = nullptr;
PXAudio2Create = nullptr;
}
}

View File

@ -26,40 +26,40 @@ class XAudio2 final : public SoundStream
#ifdef _WIN32
private:
class Releaser
{
public:
template <typename R>
void operator()(R* ptr)
{
ptr->Release();
}
};
class Releaser
{
public:
template <typename R>
void operator()(R* ptr)
{
ptr->Release();
}
};
std::unique_ptr<IXAudio2, Releaser> m_xaudio2;
std::unique_ptr<StreamingVoiceContext> m_voice_context;
IXAudio2MasteringVoice *m_mastering_voice;
std::unique_ptr<IXAudio2, Releaser> m_xaudio2;
std::unique_ptr<StreamingVoiceContext> m_voice_context;
IXAudio2MasteringVoice* m_mastering_voice;
Common::Event m_sound_sync_event;
float m_volume;
Common::Event m_sound_sync_event;
float m_volume;
const bool m_cleanup_com;
const bool m_cleanup_com;
static HMODULE m_xaudio2_dll;
static void *PXAudio2Create;
static HMODULE m_xaudio2_dll;
static void* PXAudio2Create;
static bool InitLibrary();
static bool InitLibrary();
public:
XAudio2();
virtual ~XAudio2();
XAudio2();
virtual ~XAudio2();
bool Start() override;
void Stop() override;
bool Start() override;
void Stop() override;
void Clear(bool mute) override;
void SetVolume(int volume) override;
void Clear(bool mute) override;
void SetVolume(int volume) override;
static bool isValid() { return InitLibrary(); }
static bool isValid() { return InitLibrary(); }
#endif
};

View File

@ -10,35 +10,34 @@
#include "AudioCommon/AudioCommon.h"
#include "AudioCommon/XAudio2_7Stream.h"
#include "Common/Event.h"
#include "Common/MsgHandler.h"
#include "Common/Logging/Log.h"
#include "Common/MsgHandler.h"
struct StreamingVoiceContext2_7 : public IXAudio2VoiceCallback
{
private:
CMixer* const m_mixer;
Common::Event& m_sound_sync_event;
IXAudio2SourceVoice* m_source_voice;
std::unique_ptr<BYTE[]> xaudio_buffer;
CMixer* const m_mixer;
Common::Event& m_sound_sync_event;
IXAudio2SourceVoice* m_source_voice;
std::unique_ptr<BYTE[]> xaudio_buffer;
void SubmitBuffer(PBYTE buf_data);
void SubmitBuffer(PBYTE buf_data);
public:
StreamingVoiceContext2_7(IXAudio2 *pXAudio2, CMixer *pMixer, Common::Event& pSyncEvent);
StreamingVoiceContext2_7(IXAudio2* pXAudio2, CMixer* pMixer, Common::Event& pSyncEvent);
~StreamingVoiceContext2_7();
~StreamingVoiceContext2_7();
void StreamingVoiceContext2_7::Stop();
void StreamingVoiceContext2_7::Play();
void StreamingVoiceContext2_7::Stop();
void StreamingVoiceContext2_7::Play();
STDMETHOD_(void, OnVoiceError) (THIS_ void* pBufferContext, HRESULT Error) {}
STDMETHOD_(void, OnVoiceProcessingPassStart) (UINT32) {}
STDMETHOD_(void, OnVoiceProcessingPassEnd) () {}
STDMETHOD_(void, OnBufferStart) (void*) {}
STDMETHOD_(void, OnLoopEnd) (void*) {}
STDMETHOD_(void, OnStreamEnd) () {}
STDMETHOD_(void, OnBufferEnd) (void* context);
STDMETHOD_(void, OnVoiceError)(THIS_ void* pBufferContext, HRESULT Error) {}
STDMETHOD_(void, OnVoiceProcessingPassStart)(UINT32) {}
STDMETHOD_(void, OnVoiceProcessingPassEnd)() {}
STDMETHOD_(void, OnBufferStart)(void*) {}
STDMETHOD_(void, OnLoopEnd)(void*) {}
STDMETHOD_(void, OnStreamEnd)() {}
STDMETHOD_(void, OnBufferEnd)(void* context);
};
const int NUM_BUFFERS = 3;
@ -50,186 +49,186 @@ const int BUFFER_SIZE_BYTES = BUFFER_SIZE * sizeof(s16);
void StreamingVoiceContext2_7::SubmitBuffer(PBYTE buf_data)
{
XAUDIO2_BUFFER buf = {};
buf.AudioBytes = BUFFER_SIZE_BYTES;
buf.pContext = buf_data;
buf.pAudioData = buf_data;
XAUDIO2_BUFFER buf = {};
buf.AudioBytes = BUFFER_SIZE_BYTES;
buf.pContext = buf_data;
buf.pAudioData = buf_data;
m_source_voice->SubmitSourceBuffer(&buf);
m_source_voice->SubmitSourceBuffer(&buf);
}
StreamingVoiceContext2_7::StreamingVoiceContext2_7(IXAudio2 *pXAudio2, CMixer *pMixer, Common::Event& pSyncEvent)
: m_mixer(pMixer)
, m_sound_sync_event(pSyncEvent)
, xaudio_buffer(new BYTE[NUM_BUFFERS * BUFFER_SIZE_BYTES]())
StreamingVoiceContext2_7::StreamingVoiceContext2_7(IXAudio2* pXAudio2, CMixer* pMixer,
Common::Event& pSyncEvent)
: m_mixer(pMixer), m_sound_sync_event(pSyncEvent),
xaudio_buffer(new BYTE[NUM_BUFFERS * BUFFER_SIZE_BYTES]())
{
WAVEFORMATEXTENSIBLE wfx = {};
WAVEFORMATEXTENSIBLE wfx = {};
wfx.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
wfx.Format.nSamplesPerSec = m_mixer->GetSampleRate();
wfx.Format.nChannels = 2;
wfx.Format.wBitsPerSample = 16;
wfx.Format.nBlockAlign = wfx.Format.nChannels*wfx.Format.wBitsPerSample / 8;
wfx.Format.nAvgBytesPerSec = wfx.Format.nSamplesPerSec * wfx.Format.nBlockAlign;
wfx.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
wfx.Samples.wValidBitsPerSample = 16;
wfx.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
wfx.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
wfx.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
wfx.Format.nSamplesPerSec = m_mixer->GetSampleRate();
wfx.Format.nChannels = 2;
wfx.Format.wBitsPerSample = 16;
wfx.Format.nBlockAlign = wfx.Format.nChannels * wfx.Format.wBitsPerSample / 8;
wfx.Format.nAvgBytesPerSec = wfx.Format.nSamplesPerSec * wfx.Format.nBlockAlign;
wfx.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
wfx.Samples.wValidBitsPerSample = 16;
wfx.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
wfx.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
// create source voice
HRESULT hr;
if (FAILED(hr = pXAudio2->CreateSourceVoice(&m_source_voice, &wfx.Format, XAUDIO2_VOICE_NOSRC, 1.0f, this)))
{
PanicAlert("XAudio2_7 CreateSourceVoice failed: %#X", hr);
return;
}
// create source voice
HRESULT hr;
if (FAILED(hr = pXAudio2->CreateSourceVoice(&m_source_voice, &wfx.Format, XAUDIO2_VOICE_NOSRC,
1.0f, this)))
{
PanicAlert("XAudio2_7 CreateSourceVoice failed: %#X", hr);
return;
}
m_source_voice->Start();
m_source_voice->Start();
// start buffers with silence
for (int i = 0; i != NUM_BUFFERS; ++i)
SubmitBuffer(xaudio_buffer.get() + (i * BUFFER_SIZE_BYTES));
// start buffers with silence
for (int i = 0; i != NUM_BUFFERS; ++i)
SubmitBuffer(xaudio_buffer.get() + (i * BUFFER_SIZE_BYTES));
}
StreamingVoiceContext2_7::~StreamingVoiceContext2_7()
{
if (m_source_voice)
{
m_source_voice->Stop();
m_source_voice->DestroyVoice();
}
if (m_source_voice)
{
m_source_voice->Stop();
m_source_voice->DestroyVoice();
}
}
void StreamingVoiceContext2_7::Stop()
{
if (m_source_voice)
m_source_voice->Stop();
if (m_source_voice)
m_source_voice->Stop();
}
void StreamingVoiceContext2_7::Play()
{
if (m_source_voice)
m_source_voice->Start();
if (m_source_voice)
m_source_voice->Start();
}
void StreamingVoiceContext2_7::OnBufferEnd(void* context)
{
// buffer end callback; gets SAMPLES_PER_BUFFER samples for a new buffer
// buffer end callback; gets SAMPLES_PER_BUFFER samples for a new buffer
if (!m_source_voice || !context)
return;
if (!m_source_voice || !context)
return;
//m_sound_sync_event->Wait(); // sync
//m_sound_sync_event->Spin(); // or tight sync
// m_sound_sync_event->Wait(); // sync
// m_sound_sync_event->Spin(); // or tight sync
m_mixer->Mix(static_cast<short*>(context), SAMPLES_PER_BUFFER);
SubmitBuffer(static_cast<BYTE*>(context));
m_mixer->Mix(static_cast<short*>(context), SAMPLES_PER_BUFFER);
SubmitBuffer(static_cast<BYTE*>(context));
}
HMODULE XAudio2_7::m_xaudio2_dll = nullptr;
void XAudio2_7::ReleaseIXAudio2(IXAudio2* ptr)
{
ptr->Release();
ptr->Release();
}
bool XAudio2_7::InitLibrary()
{
if (m_xaudio2_dll)
{
return true;
}
if (m_xaudio2_dll)
{
return true;
}
m_xaudio2_dll = ::LoadLibrary(TEXT("xaudio2_7.dll"));
m_xaudio2_dll = ::LoadLibrary(TEXT("xaudio2_7.dll"));
return m_xaudio2_dll != nullptr;
return m_xaudio2_dll != nullptr;
}
XAudio2_7::XAudio2_7()
: m_mastering_voice(nullptr)
, m_volume(1.0f)
, m_cleanup_com(SUCCEEDED(CoInitializeEx(nullptr, COINIT_MULTITHREADED)))
: m_mastering_voice(nullptr), m_volume(1.0f),
m_cleanup_com(SUCCEEDED(CoInitializeEx(nullptr, COINIT_MULTITHREADED)))
{
}
XAudio2_7::~XAudio2_7()
{
Stop();
if (m_cleanup_com)
CoUninitialize();
Stop();
if (m_cleanup_com)
CoUninitialize();
}
bool XAudio2_7::Start()
{
HRESULT hr;
HRESULT hr;
// callback doesn't seem to run on a specific CPU anyways
IXAudio2* xaudptr;
if (FAILED(hr = XAudio2Create(&xaudptr, 0, XAUDIO2_DEFAULT_PROCESSOR)))
{
PanicAlert("XAudio2_7 init failed: %#X", hr);
Stop();
return false;
}
m_xaudio2 = std::unique_ptr<IXAudio2, Releaser>(xaudptr);
// callback doesn't seem to run on a specific CPU anyways
IXAudio2* xaudptr;
if (FAILED(hr = XAudio2Create(&xaudptr, 0, XAUDIO2_DEFAULT_PROCESSOR)))
{
PanicAlert("XAudio2_7 init failed: %#X", hr);
Stop();
return false;
}
m_xaudio2 = std::unique_ptr<IXAudio2, Releaser>(xaudptr);
// XAudio2 master voice
// XAUDIO2_DEFAULT_CHANNELS instead of 2 for expansion?
if (FAILED(hr = m_xaudio2->CreateMasteringVoice(&m_mastering_voice, 2, m_mixer->GetSampleRate())))
{
PanicAlert("XAudio2_7 master voice creation failed: %#X", hr);
Stop();
return false;
}
// XAudio2 master voice
// XAUDIO2_DEFAULT_CHANNELS instead of 2 for expansion?
if (FAILED(hr = m_xaudio2->CreateMasteringVoice(&m_mastering_voice, 2, m_mixer->GetSampleRate())))
{
PanicAlert("XAudio2_7 master voice creation failed: %#X", hr);
Stop();
return false;
}
// Volume
m_mastering_voice->SetVolume(m_volume);
// Volume
m_mastering_voice->SetVolume(m_volume);
m_voice_context = std::unique_ptr<StreamingVoiceContext2_7>
(new StreamingVoiceContext2_7(m_xaudio2.get(), m_mixer.get(), m_sound_sync_event));
m_voice_context = std::unique_ptr<StreamingVoiceContext2_7>(
new StreamingVoiceContext2_7(m_xaudio2.get(), m_mixer.get(), m_sound_sync_event));
return true;
return true;
}
void XAudio2_7::SetVolume(int volume)
{
//linear 1- .01
m_volume = (float)volume / 100.f;
// linear 1- .01
m_volume = (float)volume / 100.f;
if (m_mastering_voice)
m_mastering_voice->SetVolume(m_volume);
if (m_mastering_voice)
m_mastering_voice->SetVolume(m_volume);
}
void XAudio2_7::Clear(bool mute)
{
m_muted = mute;
m_muted = mute;
if (m_voice_context)
{
if (m_muted)
m_voice_context->Stop();
else
m_voice_context->Play();
}
if (m_voice_context)
{
if (m_muted)
m_voice_context->Stop();
else
m_voice_context->Play();
}
}
void XAudio2_7::Stop()
{
//m_sound_sync_event.Set();
// m_sound_sync_event.Set();
m_voice_context.reset();
m_voice_context.reset();
if (m_mastering_voice)
{
m_mastering_voice->DestroyVoice();
m_mastering_voice = nullptr;
}
if (m_mastering_voice)
{
m_mastering_voice->DestroyVoice();
m_mastering_voice = nullptr;
}
m_xaudio2.reset(); // release interface
m_xaudio2.reset(); // release interface
if (m_xaudio2_dll)
{
::FreeLibrary(m_xaudio2_dll);
m_xaudio2_dll = nullptr;
}
if (m_xaudio2_dll)
{
::FreeLibrary(m_xaudio2_dll);
m_xaudio2_dll = nullptr;
}
}

View File

@ -32,41 +32,41 @@ class XAudio2_7 final : public SoundStream
#ifdef _WIN32
private:
static void ReleaseIXAudio2(IXAudio2 *ptr);
static void ReleaseIXAudio2(IXAudio2* ptr);
class Releaser
{
public:
template <typename R>
void operator()(R *ptr)
{
ReleaseIXAudio2(ptr);
}
};
class Releaser
{
public:
template <typename R>
void operator()(R* ptr)
{
ReleaseIXAudio2(ptr);
}
};
std::unique_ptr<IXAudio2, Releaser> m_xaudio2;
std::unique_ptr<StreamingVoiceContext2_7> m_voice_context;
IXAudio2MasteringVoice *m_mastering_voice;
std::unique_ptr<IXAudio2, Releaser> m_xaudio2;
std::unique_ptr<StreamingVoiceContext2_7> m_voice_context;
IXAudio2MasteringVoice* m_mastering_voice;
Common::Event m_sound_sync_event;
float m_volume;
Common::Event m_sound_sync_event;
float m_volume;
const bool m_cleanup_com;
const bool m_cleanup_com;
static HMODULE m_xaudio2_dll;
static HMODULE m_xaudio2_dll;
static bool InitLibrary();
static bool InitLibrary();
public:
XAudio2_7();
virtual ~XAudio2_7();
XAudio2_7();
virtual ~XAudio2_7();
bool Start() override;
void Stop() override;
bool Start() override;
void Stop() override;
void Clear(bool mute) override;
void SetVolume(int volume) override;
void Clear(bool mute) override;
void SetVolume(int volume) override;
static bool isValid() { return InitLibrary(); }
static bool isValid() { return InitLibrary(); }
#endif
};

View File

@ -6,23 +6,35 @@
* Copyright (c) 2006, Creative Labs Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided
* Redistribution and use in source and binary forms, with or without modification, are permitted
* provided
* that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this list of conditions and
* * Redistributions of source code must retain the above copyright notice, this list of
* conditions and
* the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
* and the following disclaimer in the documentation and/or other materials provided with the distribution.
* * Neither the name of Creative Labs Inc. nor the names of its contributors may be used to endorse or
* * Redistributions in binary form must reproduce the above copyright notice, this list of
* conditions
* and the following disclaimer in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Creative Labs Inc. nor the names of its contributors may be used to
* endorse or
* promote products derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
* IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE
* POSSIBILITY OF SUCH DAMAGE.
*/
@ -42,101 +54,104 @@
#include <AL/alc.h>
#endif
/*
* Init call
*/
ALDeviceList::ALDeviceList()
{
ALDEVICEINFO ALDeviceInfo;
ALDEVICEINFO ALDeviceInfo;
// DeviceInfo vector stores, for each enumerated device, it's device name, selection status, spec version #, and extension support
vDeviceInfo.clear();
vDeviceInfo.reserve(10);
// DeviceInfo vector stores, for each enumerated device, it's device name, selection status, spec
// version #, and extension support
vDeviceInfo.clear();
vDeviceInfo.reserve(10);
defaultDeviceIndex = 0;
defaultDeviceIndex = 0;
// grab function pointers for 1.0-API functions, and if successful proceed to enumerate all devices
//if (LoadOAL10Library(nullptr, &ALFunction) == TRUE) {
if (alcIsExtensionPresent(nullptr, "ALC_ENUMERATION_EXT"))
{
const char *devices = alcGetString(nullptr, ALC_DEVICE_SPECIFIER);
const char *defaultDeviceName = alcGetString(nullptr, ALC_DEFAULT_DEVICE_SPECIFIER);
// go through device list (each device terminated with a single nullptr, list terminated with double nullptr)
for (s32 index = 0; devices != nullptr && strlen(devices) > 0; index++, devices += strlen(devices) + 1)
{
if (strcmp(defaultDeviceName, devices) == 0)
{
defaultDeviceIndex = index;
}
ALCdevice *device = alcOpenDevice(devices);
if (device)
{
ALCcontext *context = alcCreateContext(device, nullptr);
if (context)
{
alcMakeContextCurrent(context);
// if new actual device name isn't already in the list, then add it...
const char *actualDeviceName = alcGetString(device, ALC_DEVICE_SPECIFIER);
bool bNewName = true;
for (s32 i = 0; i < GetNumDevices(); i++)
{
if (strcmp(GetDeviceName(i), actualDeviceName) == 0)
{
bNewName = false;
}
}
if ((bNewName) && (actualDeviceName != nullptr) && (strlen(actualDeviceName) > 0))
{
ALDeviceInfo.bSelected = true;
ALDeviceInfo.strDeviceName = actualDeviceName;
alcGetIntegerv(device, ALC_MAJOR_VERSION, sizeof(s32), &ALDeviceInfo.iMajorVersion);
alcGetIntegerv(device, ALC_MINOR_VERSION, sizeof(s32), &ALDeviceInfo.iMinorVersion);
// grab function pointers for 1.0-API functions, and if successful proceed to enumerate all
// devices
// if (LoadOAL10Library(nullptr, &ALFunction) == TRUE) {
if (alcIsExtensionPresent(nullptr, "ALC_ENUMERATION_EXT"))
{
const char* devices = alcGetString(nullptr, ALC_DEVICE_SPECIFIER);
const char* defaultDeviceName = alcGetString(nullptr, ALC_DEFAULT_DEVICE_SPECIFIER);
// go through device list (each device terminated with a single nullptr, list terminated with
// double nullptr)
for (s32 index = 0; devices != nullptr && strlen(devices) > 0;
index++, devices += strlen(devices) + 1)
{
if (strcmp(defaultDeviceName, devices) == 0)
{
defaultDeviceIndex = index;
}
ALCdevice* device = alcOpenDevice(devices);
if (device)
{
ALCcontext* context = alcCreateContext(device, nullptr);
if (context)
{
alcMakeContextCurrent(context);
// if new actual device name isn't already in the list, then add it...
const char* actualDeviceName = alcGetString(device, ALC_DEVICE_SPECIFIER);
bool bNewName = true;
for (s32 i = 0; i < GetNumDevices(); i++)
{
if (strcmp(GetDeviceName(i), actualDeviceName) == 0)
{
bNewName = false;
}
}
if ((bNewName) && (actualDeviceName != nullptr) && (strlen(actualDeviceName) > 0))
{
ALDeviceInfo.bSelected = true;
ALDeviceInfo.strDeviceName = actualDeviceName;
alcGetIntegerv(device, ALC_MAJOR_VERSION, sizeof(s32), &ALDeviceInfo.iMajorVersion);
alcGetIntegerv(device, ALC_MINOR_VERSION, sizeof(s32), &ALDeviceInfo.iMinorVersion);
ALDeviceInfo.pvstrExtensions = new std::vector<std::string>;
ALDeviceInfo.pvstrExtensions = new std::vector<std::string>;
// Check for ALC Extensions
if (alcIsExtensionPresent(device, "ALC_EXT_CAPTURE") == AL_TRUE)
ALDeviceInfo.pvstrExtensions->push_back("ALC_EXT_CAPTURE");
if (alcIsExtensionPresent(device, "ALC_EXT_EFX") == AL_TRUE)
ALDeviceInfo.pvstrExtensions->push_back("ALC_EXT_EFX");
// Check for ALC Extensions
if (alcIsExtensionPresent(device, "ALC_EXT_CAPTURE") == AL_TRUE)
ALDeviceInfo.pvstrExtensions->push_back("ALC_EXT_CAPTURE");
if (alcIsExtensionPresent(device, "ALC_EXT_EFX") == AL_TRUE)
ALDeviceInfo.pvstrExtensions->push_back("ALC_EXT_EFX");
// Check for AL Extensions
if (alIsExtensionPresent("AL_EXT_OFFSET") == AL_TRUE)
ALDeviceInfo.pvstrExtensions->push_back("AL_EXT_OFFSET");
// Check for AL Extensions
if (alIsExtensionPresent("AL_EXT_OFFSET") == AL_TRUE)
ALDeviceInfo.pvstrExtensions->push_back("AL_EXT_OFFSET");
if (alIsExtensionPresent("AL_EXT_LINEAR_DISTANCE") == AL_TRUE)
ALDeviceInfo.pvstrExtensions->push_back("AL_EXT_LINEAR_DISTANCE");
if (alIsExtensionPresent("AL_EXT_EXPONENT_DISTANCE") == AL_TRUE)
ALDeviceInfo.pvstrExtensions->push_back("AL_EXT_EXPONENT_DISTANCE");
if (alIsExtensionPresent("AL_EXT_LINEAR_DISTANCE") == AL_TRUE)
ALDeviceInfo.pvstrExtensions->push_back("AL_EXT_LINEAR_DISTANCE");
if (alIsExtensionPresent("AL_EXT_EXPONENT_DISTANCE") == AL_TRUE)
ALDeviceInfo.pvstrExtensions->push_back("AL_EXT_EXPONENT_DISTANCE");
if (alIsExtensionPresent("EAX2.0") == AL_TRUE)
ALDeviceInfo.pvstrExtensions->push_back("EAX2.0");
if (alIsExtensionPresent("EAX3.0") == AL_TRUE)
ALDeviceInfo.pvstrExtensions->push_back("EAX3.0");
if (alIsExtensionPresent("EAX4.0") == AL_TRUE)
ALDeviceInfo.pvstrExtensions->push_back("EAX4.0");
if (alIsExtensionPresent("EAX5.0") == AL_TRUE)
ALDeviceInfo.pvstrExtensions->push_back("EAX5.0");
if (alIsExtensionPresent("EAX2.0") == AL_TRUE)
ALDeviceInfo.pvstrExtensions->push_back("EAX2.0");
if (alIsExtensionPresent("EAX3.0") == AL_TRUE)
ALDeviceInfo.pvstrExtensions->push_back("EAX3.0");
if (alIsExtensionPresent("EAX4.0") == AL_TRUE)
ALDeviceInfo.pvstrExtensions->push_back("EAX4.0");
if (alIsExtensionPresent("EAX5.0") == AL_TRUE)
ALDeviceInfo.pvstrExtensions->push_back("EAX5.0");
if (alIsExtensionPresent("EAX-RAM") == AL_TRUE)
ALDeviceInfo.pvstrExtensions->push_back("EAX-RAM");
if (alIsExtensionPresent("EAX-RAM") == AL_TRUE)
ALDeviceInfo.pvstrExtensions->push_back("EAX-RAM");
// Get Source Count
ALDeviceInfo.uiSourceCount = GetMaxNumSources();
// Get Source Count
ALDeviceInfo.uiSourceCount = GetMaxNumSources();
vDeviceInfo.push_back(ALDeviceInfo);
}
alcMakeContextCurrent(nullptr);
alcDestroyContext(context);
}
alcCloseDevice(device);
}
}
}
//}
vDeviceInfo.push_back(ALDeviceInfo);
}
alcMakeContextCurrent(nullptr);
alcDestroyContext(context);
}
alcCloseDevice(device);
}
}
}
//}
ResetFilters();
ResetFilters();
}
/*
@ -144,16 +159,16 @@ ALDeviceList::ALDeviceList()
*/
ALDeviceList::~ALDeviceList()
{
for (auto& di : vDeviceInfo)
{
if (di.pvstrExtensions)
{
di.pvstrExtensions->clear();
delete di.pvstrExtensions;
}
}
for (auto& di : vDeviceInfo)
{
if (di.pvstrExtensions)
{
di.pvstrExtensions->clear();
delete di.pvstrExtensions;
}
}
vDeviceInfo.clear();
vDeviceInfo.clear();
}
/*
@ -161,32 +176,33 @@ ALDeviceList::~ALDeviceList()
*/
s32 ALDeviceList::GetNumDevices()
{
return (s32)vDeviceInfo.size();
return (s32)vDeviceInfo.size();
}
/*
* Returns the device name at an index in the complete device list
*/
char * ALDeviceList::GetDeviceName(s32 index)
char* ALDeviceList::GetDeviceName(s32 index)
{
if (index < GetNumDevices())
return (char *)vDeviceInfo[index].strDeviceName.c_str();
else
return nullptr;
if (index < GetNumDevices())
return (char*)vDeviceInfo[index].strDeviceName.c_str();
else
return nullptr;
}
/*
* Returns the major and minor version numbers for a device at a specified index in the complete list
* Returns the major and minor version numbers for a device at a specified index in the complete
* list
*/
void ALDeviceList::GetDeviceVersion(s32 index, s32 *major, s32 *minor)
void ALDeviceList::GetDeviceVersion(s32 index, s32* major, s32* minor)
{
if (index < GetNumDevices())
{
if (major)
*major = vDeviceInfo[index].iMajorVersion;
if (minor)
*minor = vDeviceInfo[index].iMinorVersion;
}
if (index < GetNumDevices())
{
if (major)
*major = vDeviceInfo[index].iMajorVersion;
if (minor)
*minor = vDeviceInfo[index].iMinorVersion;
}
}
/*
@ -194,32 +210,32 @@ void ALDeviceList::GetDeviceVersion(s32 index, s32 *major, s32 *minor)
*/
u32 ALDeviceList::GetMaxNumSources(s32 index)
{
if (index < GetNumDevices())
return vDeviceInfo[index].uiSourceCount;
else
return 0;
if (index < GetNumDevices())
return vDeviceInfo[index].uiSourceCount;
else
return 0;
}
/*
* Checks if the extension is supported on the given device
*/
bool ALDeviceList::IsExtensionSupported(s32 index, char *szExtName)
bool ALDeviceList::IsExtensionSupported(s32 index, char* szExtName)
{
bool bReturn = false;
bool bReturn = false;
if (index < GetNumDevices())
{
for (auto& ext : *vDeviceInfo[index].pvstrExtensions)
{
if (!strcasecmp(ext.c_str(), szExtName))
{
bReturn = true;
break;
}
}
}
if (index < GetNumDevices())
{
for (auto& ext : *vDeviceInfo[index].pvstrExtensions)
{
if (!strcasecmp(ext.c_str(), szExtName))
{
bReturn = true;
break;
}
}
}
return bReturn;
return bReturn;
}
/*
@ -227,7 +243,7 @@ bool ALDeviceList::IsExtensionSupported(s32 index, char *szExtName)
*/
s32 ALDeviceList::GetDefaultDevice()
{
return defaultDeviceIndex;
return defaultDeviceIndex;
}
/*
@ -235,15 +251,15 @@ s32 ALDeviceList::GetDefaultDevice()
*/
void ALDeviceList::FilterDevicesMinVer(s32 major, s32 minor)
{
s32 dMajor = 0, dMinor = 0;
for (u32 i = 0; i < vDeviceInfo.size(); i++)
{
GetDeviceVersion(i, &dMajor, &dMinor);
if ((dMajor < major) || ((dMajor == major) && (dMinor < minor)))
{
vDeviceInfo[i].bSelected = false;
}
}
s32 dMajor = 0, dMinor = 0;
for (u32 i = 0; i < vDeviceInfo.size(); i++)
{
GetDeviceVersion(i, &dMajor, &dMinor);
if ((dMajor < major) || ((dMajor == major) && (dMinor < minor)))
{
vDeviceInfo[i].bSelected = false;
}
}
}
/*
@ -251,39 +267,39 @@ void ALDeviceList::FilterDevicesMinVer(s32 major, s32 minor)
*/
void ALDeviceList::FilterDevicesMaxVer(s32 major, s32 minor)
{
s32 dMajor = 0, dMinor = 0;
for (u32 i = 0; i < vDeviceInfo.size(); i++)
{
GetDeviceVersion(i, &dMajor, &dMinor);
if ((dMajor > major) || ((dMajor == major) && (dMinor > minor)))
{
vDeviceInfo[i].bSelected = false;
}
}
s32 dMajor = 0, dMinor = 0;
for (u32 i = 0; i < vDeviceInfo.size(); i++)
{
GetDeviceVersion(i, &dMajor, &dMinor);
if ((dMajor > major) || ((dMajor == major) && (dMinor > minor)))
{
vDeviceInfo[i].bSelected = false;
}
}
}
/*
* Deselects device which don't support the given extension name
*/
void ALDeviceList::FilterDevicesExtension(char *szExtName)
void ALDeviceList::FilterDevicesExtension(char* szExtName)
{
bool bFound;
bool bFound;
for (auto& di : vDeviceInfo)
{
bFound = false;
for (auto& ext : *di.pvstrExtensions)
{
if (!strcasecmp(ext.c_str(), szExtName))
{
bFound = true;
break;
}
}
for (auto& di : vDeviceInfo)
{
bFound = false;
for (auto& ext : *di.pvstrExtensions)
{
if (!strcasecmp(ext.c_str(), szExtName))
{
bFound = true;
break;
}
}
if (!bFound)
di.bSelected = false;
}
if (!bFound)
di.bSelected = false;
}
}
/*
@ -291,12 +307,12 @@ void ALDeviceList::FilterDevicesExtension(char *szExtName)
*/
void ALDeviceList::ResetFilters()
{
for (s32 i = 0; i < GetNumDevices(); i++)
{
vDeviceInfo[i].bSelected = true;
}
for (s32 i = 0; i < GetNumDevices(); i++)
{
vDeviceInfo[i].bSelected = true;
}
filterIndex = 0;
filterIndex = 0;
}
/*
@ -304,18 +320,18 @@ void ALDeviceList::ResetFilters()
*/
s32 ALDeviceList::GetFirstFilteredDevice()
{
s32 i;
s32 i;
for (i = 0; i < GetNumDevices(); i++)
{
if (vDeviceInfo[i].bSelected == true)
{
break;
}
}
for (i = 0; i < GetNumDevices(); i++)
{
if (vDeviceInfo[i].bSelected == true)
{
break;
}
}
filterIndex = i + 1;
return i;
filterIndex = i + 1;
return i;
}
/*
@ -323,18 +339,18 @@ s32 ALDeviceList::GetFirstFilteredDevice()
*/
s32 ALDeviceList::GetNextFilteredDevice()
{
s32 i;
s32 i;
for (i = filterIndex; i < GetNumDevices(); i++)
{
if (vDeviceInfo[i].bSelected == true)
{
break;
}
}
for (i = filterIndex; i < GetNumDevices(); i++)
{
if (vDeviceInfo[i].bSelected == true)
{
break;
}
}
filterIndex = i + 1;
return i;
filterIndex = i + 1;
return i;
}
/*
@ -342,29 +358,29 @@ s32 ALDeviceList::GetNextFilteredDevice()
*/
u32 ALDeviceList::GetMaxNumSources()
{
ALuint uiSources[256];
u32 iSourceCount = 0;
ALuint uiSources[256];
u32 iSourceCount = 0;
// Clear AL Error Code
alGetError();
// Clear AL Error Code
alGetError();
// Generate up to 256 Sources, checking for any errors
for (iSourceCount = 0; iSourceCount < 256; iSourceCount++)
{
alGenSources(1, &uiSources[iSourceCount]);
if (alGetError() != AL_NO_ERROR)
break;
}
// Generate up to 256 Sources, checking for any errors
for (iSourceCount = 0; iSourceCount < 256; iSourceCount++)
{
alGenSources(1, &uiSources[iSourceCount]);
if (alGetError() != AL_NO_ERROR)
break;
}
// Release the Sources
alDeleteSources(iSourceCount, uiSources);
if (alGetError() != AL_NO_ERROR)
{
for (auto& uiSource : uiSources)
{
alDeleteSources(1, &uiSource);
}
}
// Release the Sources
alDeleteSources(iSourceCount, uiSources);
if (alGetError() != AL_NO_ERROR)
{
for (auto& uiSource : uiSources)
{
alDeleteSources(1, &uiSource);
}
}
return iSourceCount;
return iSourceCount;
}

View File

@ -10,43 +10,43 @@
#include "Common/CommonTypes.h"
#ifdef _WIN32
#pragma warning(disable: 4786) //disable warning "identifier was truncated to
//'255' characters in the browser information"
#pragma warning(disable : 4786) // disable warning "identifier was truncated to
//'255' characters in the browser information"
#endif
typedef struct
{
std::string strDeviceName;
s32 iMajorVersion;
s32 iMinorVersion;
u32 uiSourceCount;
std::vector<std::string>* pvstrExtensions;
bool bSelected;
std::string strDeviceName;
s32 iMajorVersion;
s32 iMinorVersion;
u32 uiSourceCount;
std::vector<std::string>* pvstrExtensions;
bool bSelected;
} ALDEVICEINFO, *LPALDEVICEINFO;
class ALDeviceList
{
private:
std::vector<ALDEVICEINFO> vDeviceInfo;
s32 defaultDeviceIndex;
s32 filterIndex;
std::vector<ALDEVICEINFO> vDeviceInfo;
s32 defaultDeviceIndex;
s32 filterIndex;
public:
ALDeviceList();
~ALDeviceList();
s32 GetNumDevices();
char* GetDeviceName(s32 index);
void GetDeviceVersion(s32 index, s32* major, s32* minor);
u32 GetMaxNumSources(s32 index);
bool IsExtensionSupported(s32 index, char* szExtName);
s32 GetDefaultDevice();
void FilterDevicesMinVer(s32 major, s32 minor);
void FilterDevicesMaxVer(s32 major, s32 minor);
void FilterDevicesExtension(char* szExtName);
void ResetFilters();
s32 GetFirstFilteredDevice();
s32 GetNextFilteredDevice();
ALDeviceList();
~ALDeviceList();
s32 GetNumDevices();
char* GetDeviceName(s32 index);
void GetDeviceVersion(s32 index, s32* major, s32* minor);
u32 GetMaxNumSources(s32 index);
bool IsExtensionSupported(s32 index, char* szExtName);
s32 GetDefaultDevice();
void FilterDevicesMinVer(s32 major, s32 minor);
void FilterDevicesMaxVer(s32 major, s32 minor);
void FilterDevicesExtension(char* szExtName);
void ResetFilters();
s32 GetFirstFilteredDevice();
s32 GetNextFilteredDevice();
private:
u32 GetMaxNumSources();
u32 GetMaxNumSources();
};