Mesen/Linux/SdlSoundManager.cpp

203 lines
5.4 KiB
C++
Executable file

#include "SdlSoundManager.h"
#include "../Core/EmulationSettings.h"
#include "../Core/MessageManager.h"
#include "../Core/SoundMixer.h"
#include "../Core/Console.h"
SdlSoundManager::SdlSoundManager(shared_ptr<Console> console)
{
_console = console;
if(InitializeAudio(48000, true)) {
_console->GetSoundMixer()->RegisterAudioDevice(this);
}
}
SdlSoundManager::~SdlSoundManager()
{
Release();
}
void SdlSoundManager::FillAudioBuffer(void *userData, uint8_t *stream, int len)
{
SdlSoundManager* soundManager = (SdlSoundManager*)userData;
soundManager->ReadFromBuffer(stream, len);
}
void SdlSoundManager::Release()
{
if(_audioDeviceID != 0) {
Stop();
SDL_CloseAudioDevice(_audioDeviceID);
}
if(_buffer) {
delete[] _buffer;
_buffer = nullptr;
_bufferSize = 0;
}
}
bool SdlSoundManager::InitializeAudio(uint32_t sampleRate, bool isStereo)
{
if(SDL_InitSubSystem(SDL_INIT_AUDIO) != 0) {
MessageManager::Log("[Audio] Failed to initialize audio subsystem");
return false;
}
int isCapture = 0;
_sampleRate = sampleRate;
_isStereo = isStereo;
_previousLatency = _console->GetSettings()->GetAudioLatency();
int bytesPerSample = 2 * (isStereo ? 2 : 1);
int32_t requestedByteLatency = (int32_t)((float)(sampleRate * _console->GetSettings()->GetAudioLatency()) / 1000.0f * bytesPerSample);
_bufferSize = (int32_t)std::ceil((double)requestedByteLatency * 2 / 0x10000) * 0x10000;
_buffer = new uint8_t[_bufferSize];
memset(_buffer, 0, _bufferSize);
SDL_AudioSpec audioSpec;
SDL_memset(&audioSpec, 0, sizeof(audioSpec));
audioSpec.freq = sampleRate;
audioSpec.format = AUDIO_S16SYS; //16-bit samples
audioSpec.channels = isStereo ? 2 : 1;
audioSpec.samples = 1024;
audioSpec.callback = &SdlSoundManager::FillAudioBuffer;
audioSpec.userdata = this;
SDL_AudioSpec obtainedSpec;
_audioDeviceID = SDL_OpenAudioDevice(_deviceName.empty() ? nullptr : _deviceName.c_str(), isCapture, &audioSpec, &obtainedSpec, 0);
if(_audioDeviceID == 0 && !_deviceName.empty()) {
MessageManager::Log("[Audio] Failed opening audio device '" + _deviceName + "', will retry with default device.");
_audioDeviceID = SDL_OpenAudioDevice(nullptr, isCapture, &audioSpec, &obtainedSpec, 0);
}
_writePosition = 0;
_readPosition = 0;
_needReset = false;
return _audioDeviceID != 0;
}
string SdlSoundManager::GetAvailableDevices()
{
string deviceString;
for(string device : GetAvailableDeviceInfo()) {
deviceString += device + std::string("||");
}
return deviceString;
}
vector<string> SdlSoundManager::GetAvailableDeviceInfo()
{
vector<string> deviceList;
int isCapture = 0;
int deviceCount = SDL_GetNumAudioDevices(isCapture);
if(deviceCount == -1) {
//No devices found
} else {
for(int i = 0; i < deviceCount; i++) {
deviceList.push_back(SDL_GetAudioDeviceName(i, isCapture));
}
}
return deviceList;
}
void SdlSoundManager::SetAudioDevice(string deviceName)
{
if(deviceName.compare(_deviceName) != 0) {
_deviceName = deviceName;
_needReset = true;
}
}
void SdlSoundManager::ReadFromBuffer(uint8_t* output, uint32_t len)
{
if(_readPosition + len < _bufferSize) {
memcpy(output, _buffer+_readPosition, len);
_readPosition += len;
} else {
int remainingBytes = (_bufferSize - _readPosition);
memcpy(output, _buffer+_readPosition, remainingBytes);
memcpy(output+remainingBytes, _buffer, len - remainingBytes);
_readPosition = len - remainingBytes;
}
if(_readPosition >= _writePosition && _readPosition - _writePosition < _bufferSize / 2) {
_bufferUnderrunEventCount++;
}
}
void SdlSoundManager::WriteToBuffer(uint8_t* input, uint32_t len)
{
if(_writePosition + len < _bufferSize) {
memcpy(_buffer+_writePosition, input, len);
_writePosition += len;
} else {
int remainingBytes = _bufferSize - _writePosition;
memcpy(_buffer+_writePosition, input, remainingBytes);
memcpy(_buffer, ((uint8_t*)input)+remainingBytes, len - remainingBytes);
_writePosition = len - remainingBytes;
}
}
void SdlSoundManager::UpdateSoundSettings()
{
uint32_t sampleRate = _console->GetSettings()->GetSampleRate();
uint32_t latency = _console->GetSettings()->GetAudioLatency();
if(_sampleRate != sampleRate || _needReset || _previousLatency != latency) {
Release();
InitializeAudio(sampleRate, true);
}
}
void SdlSoundManager::PlayBuffer(int16_t *soundBuffer, uint32_t sampleCount, uint32_t sampleRate, bool isStereo)
{
uint32_t bytesPerSample = (SoundMixer::BitsPerSample / 8) * (isStereo ? 2 : 1);
UpdateSoundSettings();
WriteToBuffer((uint8_t*)soundBuffer, sampleCount * bytesPerSample);
int32_t byteLatency = (int32_t)((float)(sampleRate * _previousLatency) / 1000.0f * bytesPerSample);
int32_t playWriteByteLatency = _writePosition - _readPosition;
if(playWriteByteLatency < 0) {
playWriteByteLatency = _bufferSize - _readPosition + _writePosition;
}
if(playWriteByteLatency > byteLatency) {
//Start playing
SDL_PauseAudioDevice(_audioDeviceID, 0);
}
}
void SdlSoundManager::Pause()
{
SDL_PauseAudioDevice(_audioDeviceID, 1);
}
void SdlSoundManager::Stop()
{
Pause();
_readPosition = 0;
_writePosition = 0;
ResetStats();
}
void SdlSoundManager::ProcessEndOfFrame()
{
ProcessLatency(_readPosition, _writePosition);
uint32_t emulationSpeed = _console->GetSettings()->GetEmulationSpeed();
if(_averageLatency > 0 && emulationSpeed <= 100 && emulationSpeed > 0 && std::abs(_averageLatency - _console->GetSettings()->GetAudioLatency()) > 50) {
//Latency is way off (over 50ms gap), stop audio & start again
Stop();
}
}