diff --git a/Core/Gameboy/Carts/GbsCart.h b/Core/Gameboy/Carts/GbsCart.h index 59239d3e..b47edb6d 100644 --- a/Core/Gameboy/Carts/GbsCart.h +++ b/Core/Gameboy/Carts/GbsCart.h @@ -12,8 +12,9 @@ class GbsCart : public GbCart { private: uint8_t _prgBank = 1; - uint16_t _currentTrack = 0; - GbsHeader _header; + uint8_t _currentTrack = 0; + uint64_t _startClock = 0; + GbsHeader _header = {}; public: GbsCart(GbsHeader header) @@ -27,7 +28,7 @@ public: _memoryManager->MapRegisters(0x2000, 0x3FFF, RegisterAccess::Write); } - void InitPlayback(uint16_t selectedTrack) + void InitPlayback(uint8_t selectedTrack) { _currentTrack = selectedTrack; @@ -62,7 +63,7 @@ public: state = {}; state.SP = _header.StackPointer[0] | (_header.StackPointer[1] << 8); state.PC = 0; - state.A = selectedTrack; + state.A = (uint8_t)selectedTrack; state.IME = true; //enable CPU interrupts //Disable boot room @@ -81,6 +82,7 @@ public: //Clear all IRQ requests (needed when switching tracks) _memoryManager->ClearIrqRequest(0xFF); + _startClock = _gameboy->GetMasterClock(); _prgBank = 1; RefreshMappings(); } @@ -93,7 +95,9 @@ public: info.Comment = string(_header.Copyright, 32); info.TrackNumber = _currentTrack + 1; info.TrackCount = _header.TrackCount; - info.Position = (double)_gameboy->GetMasterClock() / _gameboy->GetMasterClockRate(); + info.Position = (double)(_gameboy->GetMasterClock() - _startClock) / _gameboy->GetMasterClockRate(); + info.Length = 0; + info.FadeLength = 0; return info; } @@ -102,7 +106,11 @@ public: int selectedTrack = _currentTrack; switch(p.Action) { case AudioPlayerAction::NextTrack: selectedTrack++; break; - case AudioPlayerAction::PrevTrack: selectedTrack--; break; + case AudioPlayerAction::PrevTrack: + if(GetAudioTrackInfo().Position < 2) { + selectedTrack--; + } + break; case AudioPlayerAction::SelectTrack: selectedTrack = (int)p.TrackNumber; break; } @@ -112,8 +120,14 @@ public: selectedTrack = 0; } - auto lock = _gameboy->GetEmulator()->AcquireLock(); - InitPlayback(selectedTrack); + //Asynchronously move to the next file + //Can't do this in the current thread in some contexts (e.g when track reaches end) + //because this is called from the emulation thread, which may cause infinite recursion + thread switchTrackTask([this, selectedTrack]() { + auto lock = _gameboy->GetEmulator()->AcquireLock(); + InitPlayback(selectedTrack); + }); + switchTrackTask.detach(); } void RefreshMappings() override @@ -132,6 +146,6 @@ public: void Serialize(Serializer& s) override { - s.Stream(_prgBank, _currentTrack); + s.Stream(_prgBank, _currentTrack, _startClock); } }; \ No newline at end of file diff --git a/Core/NES/Mappers/NsfMapper.cpp b/Core/NES/Mappers/NsfMapper.cpp index d453a29d..74cff377 100644 --- a/Core/NES/Mappers/NsfMapper.cpp +++ b/Core/NES/Mappers/NsfMapper.cpp @@ -148,19 +148,12 @@ void NsfMapper::Reset(bool softReset) _console->GetCpu()->SetIrqMask((uint8_t)IRQSource::External); _irqCounter = 0; - _allowSilenceDetection = false; - _trackEndCounter = -1; - _trackEnded = false; - _trackFadeCounter = -1; - _mmc5Audio.reset(new Mmc5Audio(_console)); _vrc6Audio.reset(new Vrc6Audio(_console)); _vrc7Audio.reset(new Vrc7Audio(_console)); _fdsAudio.reset(new FdsAudio(_console)); _namcoAudio.reset(new Namco163Audio(_console)); _sunsoftAudio.reset(new Sunsoft5bAudio(_console)); - - InternalSelectTrack(_songNumber); } void NsfMapper::GetMemoryRanges(MemoryRanges& ranges) @@ -203,41 +196,6 @@ void NsfMapper::ClearIrq() _console->GetCpu()->ClearIrqSource(IRQSource::External); } -void NsfMapper::ClockLengthAndFadeCounters() -{ - NesSoundMixer* mixer = _console->GetSoundMixer(); - if(_trackEndCounter > 0) { - _trackEndCounter--; - if(_trackEndCounter == 0) { - _trackEnded = true; - } - } - - if((_trackEndCounter < 0 || _allowSilenceDetection) && _silenceDetectDelay > 0) { - //No track length specified - if(mixer->GetMuteFrameCount() * NesSoundMixer::CycleLength > _silenceDetectDelay) { - //Auto detect end of track after AutoDetectSilenceDelay (in ms) has gone by without sound - _trackEnded = true; - _trackFadeCounter = 0; - mixer->ResetMuteFrameCount(); - } - } - - if(_trackEnded) { - if(_trackFadeCounter > 0) { - if(_fadeLength != 0) { - double fadeRatio = (double)_trackFadeCounter / (double)_fadeLength * 1.2; - mixer->SetFadeRatio(std::max(0.0, fadeRatio - 0.2)); - } - _trackFadeCounter--; - } - - if(_trackFadeCounter <= 0) { - SelectNextTrack(); - } - } -} - void NsfMapper::SelectNextTrack() { if(!_settings->GetAudioPlayerConfig().Repeat) { @@ -251,7 +209,6 @@ void NsfMapper::SelectNextTrack() } } SelectTrack(_songNumber); - _trackEnded = false; } void NsfMapper::ProcessCpuClock() @@ -284,8 +241,6 @@ void NsfMapper::ProcessCpuClock() } } - ClockLengthAndFadeCounters(); - if(_nsfHeader.SoundChips & NsfSoundChips::MMC5) { _mmc5Audio->Clock(); } @@ -381,48 +336,6 @@ void NsfMapper::WriteRegister(uint16_t addr, uint8_t value) } } -void NsfMapper::InternalSelectTrack(uint8_t trackNumber) -{ - _songNumber = trackNumber; - - //Selecting tracking after a reset - _console->GetSoundMixer()->SetFadeRatio(1.0); - NesConfig& cfg = _console->GetNesConfig(); - - uint32_t clockRate = _emu->GetMasterClockRate(); - - //Set track length/fade counters (NSFe) - if(_nsfHeader.TrackLength[trackNumber] >= 0) { - _trackEndCounter = (int32_t)((double)_nsfHeader.TrackLength[trackNumber] / 1000.0 * clockRate); - _allowSilenceDetection = false; - } else if(_nsfHeader.TotalSongs > 1) { - //Only apply a maximum duration to multi-track NSFs - //Single track NSFs will loop or restart after a portion of silence - //Substract 1 sec from default track time to account for 1 sec default fade time - if(cfg.NsfMoveToNextTrackAfterTime) { - _trackEndCounter = (cfg.NsfMoveToNextTrackTime - 1) * clockRate; - _allowSilenceDetection = true; - } else { - _trackEndCounter = 0; - _allowSilenceDetection = false; - } - } - if(_nsfHeader.TrackFade[trackNumber] >= 0) { - _trackFadeCounter = (int32_t)((double)_nsfHeader.TrackFade[trackNumber] / 1000.0 * clockRate); - } else { - //Default to 1 sec fade if none is specified (negative number) - _trackFadeCounter = clockRate; - } - - if(cfg.NsfAutoDetectSilence) { - _silenceDetectDelay = (uint32_t)((double)cfg.NsfAutoDetectSilenceDelay / 1000.0 * clockRate); - } else { - _silenceDetectDelay = 0; - } - - _fadeLength = _trackFadeCounter; -} - void NsfMapper::SelectTrack(uint8_t trackNumber) { if(trackNumber < _nsfHeader.TotalSongs) { @@ -452,7 +365,7 @@ ConsoleFeatures NsfMapper::GetAvailableFeatures() AudioTrackInfo NsfMapper::GetAudioTrackInfo() { - NesConfig& cfg = _console->GetNesConfig(); + NesConfig& cfg = _console->GetNesConfig(); AudioTrackInfo track = {}; track.Artist = _nsfHeader.ArtistName; @@ -462,9 +375,8 @@ AudioTrackInfo NsfMapper::GetAudioTrackInfo() track.SongTitle = _nsfHeader.TrackNames.size() > _songNumber ? _nsfHeader.TrackNames[_songNumber] : ""; track.Position = _console->GetPpuFrame().FrameCount / _console->GetFps(); if(_nsfHeader.TrackLength[_songNumber] > 0) { - track.Length = _nsfHeader.TrackLength[_songNumber] / 1000 + _nsfHeader.TrackFade[_songNumber] / 1000; - } else if(cfg.NsfMoveToNextTrackAfterTime) { - track.Length = cfg.NsfMoveToNextTrackTime; + track.Length = _nsfHeader.TrackLength[_songNumber] / 1000.0 + _nsfHeader.TrackFade[_songNumber] / 1000.0; + track.FadeLength = _nsfHeader.TrackFade[_songNumber] / 1000.0; } track.TrackNumber = _songNumber + 1; track.TrackCount = _nsfHeader.TotalSongs; @@ -476,7 +388,12 @@ void NsfMapper::ProcessAudioPlayerAction(AudioPlayerActionParams p) int selectedTrack = _songNumber; switch(p.Action) { case AudioPlayerAction::NextTrack: selectedTrack++; break; - case AudioPlayerAction::PrevTrack: selectedTrack--; break; + case AudioPlayerAction::PrevTrack: + if(GetAudioTrackInfo().Position < 2) { + selectedTrack--; + } + break; + case AudioPlayerAction::SelectTrack: selectedTrack = (int)p.TrackNumber; break; } @@ -501,7 +418,6 @@ void NsfMapper::Serialize(Serializer& s) s.Stream(_sunsoftAudio.get()); s.Stream( - _irqCounter, _mmc5MultiplierValues[0], _mmc5MultiplierValues[1], _trackEndCounter, _trackFadeCounter, - _fadeLength, _silenceDetectDelay, _trackEnded, _allowSilenceDetection, _hasBankSwitching, _songNumber + _irqCounter, _mmc5MultiplierValues[0], _mmc5MultiplierValues[1], _hasBankSwitching, _songNumber ); } \ No newline at end of file diff --git a/Core/NES/Mappers/NsfMapper.h b/Core/NES/Mappers/NsfMapper.h index 7f01072f..f793e13f 100644 --- a/Core/NES/Mappers/NsfMapper.h +++ b/Core/NES/Mappers/NsfMapper.h @@ -22,9 +22,9 @@ private: Sunsoft = 0x20 }; - EmuSettings* _settings; + EmuSettings* _settings = nullptr; - NsfHeader _nsfHeader; + NsfHeader _nsfHeader = {}; unique_ptr _mmc5Audio; unique_ptr _vrc6Audio; unique_ptr _vrc7Audio; @@ -32,17 +32,11 @@ private: unique_ptr _namcoAudio; unique_ptr _sunsoftAudio; - uint8_t _mmc5MultiplierValues[2]; + uint8_t _mmc5MultiplierValues[2] = {}; uint32_t _irqCounter = 0; - int32_t _trackEndCounter; - int32_t _trackFadeCounter; - int32_t _fadeLength; - uint32_t _silenceDetectDelay; - bool _trackEnded; - bool _allowSilenceDetection; - bool _hasBankSwitching; + bool _hasBankSwitching = false; uint8_t _songNumber = 0; @@ -74,8 +68,6 @@ private: bool HasBankSwitching(); - void InternalSelectTrack(uint8_t trackNumber); - void ClockLengthAndFadeCounters(); void SelectNextTrack(); protected: diff --git a/Core/NES/NesSoundMixer.cpp b/Core/NES/NesSoundMixer.cpp index bcf4988f..05cd5f0d 100644 --- a/Core/NES/NesSoundMixer.cpp +++ b/Core/NES/NesSoundMixer.cpp @@ -49,8 +49,6 @@ void NesSoundMixer::Reset() /*if(_oggMixer) { _oggMixer->Reset(_settings->GetSampleRate()); }*/ - _fadeRatio = 1.0; - _muteFrameCount = 0; _sampleCount = 0; _previousOutputLeft = 0; @@ -223,21 +221,16 @@ void NesSoundMixer::EndFrame(uint32_t time) for(size_t i = 0, len = _timestamps.size(); i < len; i++) { uint32_t stamp = _timestamps[i]; for(uint32_t j = 0; j < MaxChannelCount; j++) { - if(_channelOutput[j][stamp] != 0) { - //Assume any change in output means sound is playing, disregarding volume options - //NSF tracks that mute the triangle channel by setting it to a high-frequency value will not be considered silent - muteFrame = false; - } _currentOutput[j] += _channelOutput[j][stamp]; } int16_t currentOutput = GetOutputVolume(false) * 4; - blip_add_delta(_blipBufLeft, stamp, (int)((currentOutput - _previousOutputLeft) * _fadeRatio)); + blip_add_delta(_blipBufLeft, stamp, (int)(currentOutput - _previousOutputLeft)); _previousOutputLeft = currentOutput; if(_hasPanning) { currentOutput = GetOutputVolume(true) * 4; - blip_add_delta(_blipBufRight, stamp, (int)((currentOutput - _previousOutputRight) * _fadeRatio)); + blip_add_delta(_blipBufRight, stamp, (int)(currentOutput - _previousOutputRight)); _previousOutputRight = currentOutput; } } @@ -247,31 +240,11 @@ void NesSoundMixer::EndFrame(uint32_t time) blip_end_frame(_blipBufRight, time); } - if(muteFrame) { - _muteFrameCount++; - } else { - _muteFrameCount = 0; - } - //Reset everything _timestamps.clear(); memset(_channelOutput, 0, sizeof(_channelOutput)); } -void NesSoundMixer::SetFadeRatio(double fadeRatio) -{ - _fadeRatio = fadeRatio; -} - -uint32_t NesSoundMixer::GetMuteFrameCount() -{ - return _muteFrameCount; -} - -void NesSoundMixer::ResetMuteFrameCount() -{ - _muteFrameCount = 0; -} //TODO /* OggMixer* NesSoundMixer::GetOggMixer() diff --git a/Core/NES/NesSoundMixer.h b/Core/NES/NesSoundMixer.h index bffc0a42..bc768797 100644 --- a/Core/NES/NesSoundMixer.h +++ b/Core/NES/NesSoundMixer.h @@ -23,11 +23,9 @@ private: static constexpr uint32_t MaxSamplesPerFrame = MaxSampleRate / 60 * 4 * 2; //x4 to allow CPU overclocking up to 10x, x2 for panning stereo static constexpr uint32_t MaxChannelCount = 11; - NesConsole* _console; - EmuSettings* _settings; - SoundMixer* _mixer; - double _fadeRatio; - uint32_t _muteFrameCount; + NesConsole* _console = nullptr; + EmuSettings* _settings = nullptr; + SoundMixer* _mixer = nullptr; //TODO //unique_ptr _oggMixer; @@ -40,20 +38,20 @@ private: int16_t _previousOutputRight = 0; vector _timestamps; - int16_t _channelOutput[MaxChannelCount][CycleLength]; - int16_t _currentOutput[MaxChannelCount]; + int16_t _channelOutput[MaxChannelCount][CycleLength] = {}; + int16_t _currentOutput[MaxChannelCount] = {}; - blip_t* _blipBufLeft; - blip_t* _blipBufRight; - int16_t* _outputBuffer; + blip_t* _blipBufLeft = nullptr; + blip_t* _blipBufRight = nullptr; + int16_t* _outputBuffer = nullptr; size_t _sampleCount = 0; - double _volumes[MaxChannelCount]; - double _panning[MaxChannelCount]; + double _volumes[MaxChannelCount] = {}; + double _panning[MaxChannelCount] = {}; - uint32_t _sampleRate; - uint32_t _clockRate; + uint32_t _sampleRate = 0; + uint32_t _clockRate = 0; - bool _hasPanning; + bool _hasPanning = false; __forceinline double GetChannelOutput(AudioChannel channel, bool forRightChannel); __forceinline int16_t GetOutputVolume(bool forRightChannel); @@ -73,11 +71,6 @@ public: void PlayAudioBuffer(uint32_t cycle); void AddDelta(AudioChannel channel, uint32_t time, int16_t delta); - //For NSF/NSFe - uint32_t GetMuteFrameCount(); - void ResetMuteFrameCount(); - void SetFadeRatio(double fadeRatio); - //OggMixer* GetOggMixer(); void Serialize(Serializer& s) override; diff --git a/Core/SNES/Console.cpp b/Core/SNES/Console.cpp index 739fa817..15e347af 100644 --- a/Core/SNES/Console.cpp +++ b/Core/SNES/Console.cpp @@ -257,7 +257,8 @@ AudioTrackInfo Console::GetAudioTrackInfo() track.GameTitle = spc->GameTitle; track.SongTitle = spc->SongTitle; track.Position = _ppu->GetFrameCount() / GetFps(); - track.Length = -1; + track.Length = spc->TrackLength + (spc->FadeLength / 1000.0); + track.FadeLength = spc->FadeLength / 1000.0; track.TrackNumber = _spcTrackNumber + 1; track.TrackCount = (uint32_t)_spcPlaylist.size(); @@ -270,7 +271,11 @@ void Console::ProcessAudioPlayerAction(AudioPlayerActionParams p) if(_spcTrackNumber >= 0) { int i = (int)_spcTrackNumber; switch(p.Action) { - case AudioPlayerAction::PrevTrack: i--; break; + case AudioPlayerAction::PrevTrack: + if(GetAudioTrackInfo().Position < 2) { + i--; + } + break; case AudioPlayerAction::NextTrack: i++; break; } @@ -280,7 +285,13 @@ void Console::ProcessAudioPlayerAction(AudioPlayerActionParams p) i = 0; } - _emu->LoadRom(VirtualFile(_spcPlaylist[i]), VirtualFile()); + //Asynchronously move to the next file + //Can't do this in the current thread in some contexts (e.g when track reaches end) + //because this is called from the emulation thread. + thread switchTrackTask([this, i]() { + _emu->LoadRom(VirtualFile(_spcPlaylist[i]), VirtualFile()); + }); + switchTrackTask.detach(); } } diff --git a/Core/SNES/SpcFileData.h b/Core/SNES/SpcFileData.h index a4bdb473..cc1e3221 100644 --- a/Core/SNES/SpcFileData.h +++ b/Core/SNES/SpcFileData.h @@ -27,6 +27,9 @@ public: uint8_t DspRegs[128]; uint8_t SpcRam[0x10000]; + uint32_t TrackLength; + uint32_t FadeLength; + SpcFileData(uint8_t* spcData) { SongTitle = string(spcData + 0x2E, spcData + 0x2E + 0x20); @@ -35,6 +38,20 @@ public: Artist = string(spcData + 0xB1, spcData + 0xB1 + 0x20); Comment = string(spcData + 0x7E, spcData + 0x7E + 0x20); + string strTrackLength = string(spcData + 0xA9, spcData + 0xA9 + 0x03); + string strFadeLength = string(spcData + 0xAC, spcData + 0xAC + 0x05); + bool isStringValue = true; + for(char c : strTrackLength) { + if(c != 0 && (c < '0' || c > '9')) { + isStringValue = false; + } + } + + if(isStringValue) { + TrackLength = std::stoi(strTrackLength); + FadeLength = std::stoi(strFadeLength); + } + memcpy(SpcRam, spcData + 0x100, 0xFFC0); memcpy(SpcRam + 0xFFC0, spcData + 0x101C0, 0x40); diff --git a/Core/Shared/Audio/AudioPlayerHud.cpp b/Core/Shared/Audio/AudioPlayerHud.cpp index 5f0372df..194e73a7 100644 --- a/Core/Shared/Audio/AudioPlayerHud.cpp +++ b/Core/Shared/Audio/AudioPlayerHud.cpp @@ -3,6 +3,7 @@ #include "Shared/Audio/SoundMixer.h" #include "Shared/Video/DebugHud.h" #include "Shared/Emulator.h" +#include "Shared/EmuSettings.h" #include "Shared/Video/DrawStringCommand.h" static constexpr double PI = 3.14159265358979323846; @@ -30,6 +31,9 @@ string AudioPlayerHud::FormatSeconds(uint32_t s) void AudioPlayerHud::Draw() { AudioTrackInfo trackInfo = _emu->GetAudioTrackInfo(); + if(trackInfo.Position <= 1) { + _changeTrackPending = false; + } _hud->DrawRectangle(0, 0, 256, 240, 0, true, 1); @@ -64,7 +68,7 @@ void AudioPlayerHud::Draw() if(trackInfo.Length <= 0) { _hud->DrawString(215, 208, " " + position + " ", 0xFFFFFF, 0, 1); } else { - position += " / " + FormatSeconds(trackInfo.Length); + position += " / " + FormatSeconds((uint32_t)trackInfo.Length); _hud->DrawString(177, 208, " " + position + " ", 0xFFFFFF, 0, 1); constexpr int barWidth = 222; @@ -116,6 +120,7 @@ void AudioPlayerHud::Draw() _hud->DrawString(228, top + 10, "20kHz", fgColor, bgColor, 1); if(_amplitudes.size() >= N / 2) { + bool silent = true; for(int i = 0; i < 8; i++) { for(int j = 0; j < 32; j++) { double freqRange = ranges[i][1] - ranges[i][0]; @@ -133,14 +138,57 @@ void AudioPlayerHud::Draw() avgAmp *= ranges[i][2]; avgAmp = std::min(maxVal, avgAmp); + if(avgAmp >= 1) { + silent = false; + } + int red = std::min(255, (int)(256 * (avgAmp / maxVal) * 2)); int green = std::max(0, std::min(255, (int)(256 * ((maxVal - avgAmp) / maxVal) * 2))); _hud->DrawRectangle(i*32+j, 190, 1, (int)-avgAmp, red << 16 | green << 8, true, 1); } } + + if(!silent) { + _silenceTimer.Reset(); + } else { + AudioConfig audioCfg = _emu->GetSettings()->GetAudioConfig(); + if(audioCfg.AudioPlayerAutoDetectSilence && _silenceTimer.GetElapsedMS() >= audioCfg.AudioPlayerSilenceDelay * 1000) { + //Silence detected, move to next track + _silenceTimer.Reset(); + MoveToNextTrack(); + } + } } } +void AudioPlayerHud::MoveToNextTrack() +{ + if(!_changeTrackPending) { + _changeTrackPending = true; + AudioPlayerActionParams params = {}; + params.Action = AudioPlayerAction::NextTrack; + _emu->ProcessAudioPlayerAction(params); + } +} + +uint32_t AudioPlayerHud::GetVolume() +{ + AudioTrackInfo info = _emu->GetAudioTrackInfo(); + + if(info.Length > 0) { + if(info.Position >= info.Length) { + //Switch to next track + MoveToNextTrack(); + return 0; + } else if(info.Position >= info.Length - info.FadeLength) { + double fadeStart = info.Length - info.FadeLength; + double ratio = 1.0 - ((info.Position - fadeStart) / info.FadeLength); + return (uint32_t)(ratio * _emu->GetSettings()->GetAudioPlayerConfig().Volume); + } + } + return _emu->GetSettings()->GetAudioPlayerConfig().Volume; +} + void AudioPlayerHud::ProcessSamples(int16_t* samples, size_t sampleCount, uint32_t sampleRate) { _sampleRate = sampleRate; diff --git a/Core/Shared/Audio/AudioPlayerHud.h b/Core/Shared/Audio/AudioPlayerHud.h index 1011b259..03fc6878 100644 --- a/Core/Shared/Audio/AudioPlayerHud.h +++ b/Core/Shared/Audio/AudioPlayerHud.h @@ -2,6 +2,7 @@ #include "stdafx.h" #include #include "Utilities/kissfft.h" +#include "Utilities/Timer.h" class Emulator; class SoundMixer; @@ -20,16 +21,21 @@ private: std::vector _amplitudes; std::deque _samples; + Timer _silenceTimer; + bool _changeTrackPending = false; + uint32_t _sampleRate; double _hannWindow[N] = {}; double _input[N] = {}; std::complex _out[N] = {}; string FormatSeconds(uint32_t s); + void MoveToNextTrack(); public: AudioPlayerHud(Emulator* emu); void Draw(); + uint32_t GetVolume(); void ProcessSamples(int16_t* samples, size_t sampleCount, uint32_t sampleRate); }; \ No newline at end of file diff --git a/Core/Shared/Audio/AudioPlayerTypes.h b/Core/Shared/Audio/AudioPlayerTypes.h index ce1fc317..3380194a 100644 --- a/Core/Shared/Audio/AudioPlayerTypes.h +++ b/Core/Shared/Audio/AudioPlayerTypes.h @@ -9,7 +9,8 @@ struct AudioTrackInfo string Comment; double Position; - int32_t Length; + double Length; + double FadeLength; uint32_t TrackNumber; uint32_t TrackCount; diff --git a/Core/Shared/Audio/SoundMixer.cpp b/Core/Shared/Audio/SoundMixer.cpp index 2e960f32..5fb94abc 100644 --- a/Core/Shared/Audio/SoundMixer.cpp +++ b/Core/Shared/Audio/SoundMixer.cpp @@ -66,11 +66,11 @@ void SoundMixer::StopAudio(bool clearBuffer) void SoundMixer::PlayAudioBuffer(int16_t* samples, uint32_t sampleCount, uint32_t sourceRate) { EmuSettings* settings = _emu->GetSettings(); - bool isAudioPlayer = _emu->GetAudioPlayerHud() ? true : false; + AudioPlayerHud* audioPlayer = _emu->GetAudioPlayerHud(); AudioConfig cfg = settings->GetAudioConfig(); - uint32_t masterVolume = isAudioPlayer ? settings->GetAudioPlayerConfig().Volume : cfg.MasterVolume; - if(!isAudioPlayer && settings->CheckFlag(EmulationFlags::InBackground)) { + uint32_t masterVolume = audioPlayer ? audioPlayer->GetVolume() : cfg.MasterVolume; + if(!audioPlayer && settings->CheckFlag(EmulationFlags::InBackground)) { if(cfg.MuteSoundInBackground) { masterVolume = 0; } else if(cfg.ReduceSoundInBackground) { @@ -95,8 +95,8 @@ void SoundMixer::PlayAudioBuffer(int16_t* samples, uint32_t sampleCount, uint32_ ProcessEqualizer(out, count, targetRate); } - if(_emu->GetAudioPlayerHud()) { - _emu->GetAudioPlayerHud()->ProcessSamples(out, count, targetRate); + if(audioPlayer) { + audioPlayer->ProcessSamples(out, count, targetRate); } if(cfg.ReverbEnabled) { diff --git a/Core/Shared/Emulator.cpp b/Core/Shared/Emulator.cpp index 053c9673..deebbde2 100644 --- a/Core/Shared/Emulator.cpp +++ b/Core/Shared/Emulator.cpp @@ -96,8 +96,8 @@ void Emulator::Run() return; } - auto emulationLock = _emulationLock.AcquireSafe(); auto lock = _runLock.AcquireSafe(); + auto emulationLock = _emulationLock.AcquireSafe(); _stopFlag = false; _isRunAheadFrame = false; @@ -337,6 +337,8 @@ void Emulator::PowerCycle() bool Emulator::LoadRom(VirtualFile romFile, VirtualFile patchFile, bool stopRom, bool forPowerCycle) { + auto lock = _loadLock.AcquireSafe(); + if(!romFile.IsValid()) { return false; } @@ -418,7 +420,7 @@ bool Emulator::LoadRom(VirtualFile romFile, VirtualFile patchFile, bool stopRom, _cheatManager->ClearCheats(false); - auto lock = _debuggerLock.AcquireSafe(); + auto debuggerLock = _debuggerLock.AcquireSafe(); if(_debugger) { //Reset debugger if it was running before _debugger->Release(); @@ -682,16 +684,18 @@ void Emulator::WaitForLock() _threadPaused = true; //Spin wait until we are allowed to start again - while(_lockCounter > 0) {} + while(_lockCounter > 0 && !_stopFlag) {} shared_ptr debugger = _debugger; if(debugger) { - while(debugger->HasBreakRequest()) {} + while(debugger->HasBreakRequest() && !_stopFlag) {} } - _threadPaused = false; + if(!_stopFlag) { + _threadPaused = false; - _runLock.Acquire(); + _runLock.Acquire(); + } } } @@ -833,7 +837,13 @@ ConsoleMemoryInfo Emulator::GetMemory(SnesMemoryType type) AudioTrackInfo Emulator::GetAudioTrackInfo() { - return _console->GetAudioTrackInfo(); + AudioTrackInfo track = _console->GetAudioTrackInfo(); + AudioConfig audioCfg = _settings->GetAudioConfig(); + if(track.Length <= 0 && audioCfg.AudioPlayerEnableTrackLength) { + track.Length = audioCfg.AudioPlayerTrackLength; + track.FadeLength = 1; + } + return track; } void Emulator::ProcessAudioPlayerAction(AudioPlayerActionParams p) diff --git a/Core/Shared/Emulator.h b/Core/Shared/Emulator.h index 58c32a64..46cad73f 100644 --- a/Core/Shared/Emulator.h +++ b/Core/Shared/Emulator.h @@ -73,6 +73,7 @@ private: atomic _lockCounter; SimpleLock _runLock; SimpleLock _emulationLock; + SimpleLock _loadLock; SimpleLock _debuggerLock; atomic _stopFlag; diff --git a/Core/Shared/SettingTypes.h b/Core/Shared/SettingTypes.h index 212c32ee..71d40fa9 100644 --- a/Core/Shared/SettingTypes.h +++ b/Core/Shared/SettingTypes.h @@ -139,6 +139,11 @@ struct AudioConfig double Band18Gain = 0; double Band19Gain = 0; double Band20Gain = 0; + + bool AudioPlayerEnableTrackLength = true; + uint32_t AudioPlayerTrackLength = 120; + bool AudioPlayerAutoDetectSilence = true; + uint32_t AudioPlayerSilenceDelay = 3; }; //Update ControllerTypeNames when changing this @@ -407,10 +412,6 @@ struct NesConfig bool FdsAutoInsertDisk = false; VsDualOutputOption VsDualVideoOutput = VsDualOutputOption::Both; VsDualOutputOption VsDualAudioOutput = VsDualOutputOption::Both; - bool NsfMoveToNextTrackAfterTime = true; - uint32_t NsfMoveToNextTrackTime = 120; - bool NsfAutoDetectSilence = true; - uint32_t NsfAutoDetectSilenceDelay = 3000; bool SpritesEnabled = true; bool BackgroundEnabled = true; diff --git a/Core/Shared/Video/DrawStringCommand.h b/Core/Shared/Video/DrawStringCommand.h index 419c946b..f9cae5dc 100644 --- a/Core/Shared/Video/DrawStringCommand.h +++ b/Core/Shared/Video/DrawStringCommand.h @@ -226,7 +226,7 @@ public: _backColor = (~backColor & 0xFF000000) | (backColor & 0xFFFFFF); } - static TextSize MeasureString(string text, int maxWidth = 0) + static TextSize MeasureString(string text, uint32_t maxWidth = 0) { uint32_t maxX = 0; uint32_t x = 0; diff --git a/NewUI/Config/AudioConfig.cs b/NewUI/Config/AudioConfig.cs index cccac0d0..3722c987 100644 --- a/NewUI/Config/AudioConfig.cs +++ b/NewUI/Config/AudioConfig.cs @@ -53,49 +53,59 @@ namespace Mesen.GUI.Config [Reactive] [MinMax(-20.0, 20.0)] public double Band19Gain { get; set; } = 0; [Reactive] [MinMax(-20.0, 20.0)] public double Band20Gain { get; set; } = 0; + [Reactive] public bool AudioPlayerEnableTrackLength { get; set; } = true; + [Reactive] public UInt32 AudioPlayerTrackLength { get; set; } = 120; + [Reactive] public bool AudioPlayerAutoDetectSilence { get; set; } = true; + [Reactive] public UInt32 AudioPlayerSilenceDelay { get; set; } = 3; + public void ApplyConfig() { ConfigApi.SetAudioConfig(new InteropAudioConfig() { - AudioDevice = this.AudioDevice, - EnableAudio = this.EnableAudio, - DisableDynamicSampleRate = this.DisableDynamicSampleRate, + AudioDevice = AudioDevice, + EnableAudio = EnableAudio, + DisableDynamicSampleRate = DisableDynamicSampleRate, - MasterVolume = this.MasterVolume, - SampleRate = (UInt32)this.SampleRate, - AudioLatency = this.AudioLatency, + MasterVolume = MasterVolume, + SampleRate = (UInt32)SampleRate, + AudioLatency = AudioLatency, - MuteSoundInBackground = this.MuteSoundInBackground, - ReduceSoundInBackground = this.ReduceSoundInBackground, - ReduceSoundInFastForward = this.ReduceSoundInFastForward, - VolumeReduction = this.VolumeReduction, + MuteSoundInBackground = MuteSoundInBackground, + ReduceSoundInBackground = ReduceSoundInBackground, + ReduceSoundInFastForward = ReduceSoundInFastForward, + VolumeReduction = VolumeReduction, - ReverbEnabled = this.ReverbEnabled, - ReverbStrength = this.ReverbStrength, - ReverbDelay = this.ReverbDelay, - CrossFeedEnabled = this.CrossFeedEnabled, - CrossFeedRatio = this.CrossFeedRatio, + ReverbEnabled = ReverbEnabled, + ReverbStrength = ReverbStrength, + ReverbDelay = ReverbDelay, + CrossFeedEnabled = CrossFeedEnabled, + CrossFeedRatio = CrossFeedRatio, - EnableEqualizer = this.EnableEqualizer, - Band1Gain = this.Band1Gain, - Band2Gain = this.Band2Gain, - Band3Gain = this.Band3Gain, - Band4Gain = this.Band4Gain, - Band5Gain = this.Band5Gain, - Band6Gain = this.Band6Gain, - Band7Gain = this.Band7Gain, - Band8Gain = this.Band8Gain, - Band9Gain = this.Band9Gain, - Band10Gain = this.Band10Gain, - Band11Gain = this.Band11Gain, - Band12Gain = this.Band12Gain, - Band13Gain = this.Band13Gain, - Band14Gain = this.Band14Gain, - Band15Gain = this.Band15Gain, - Band16Gain = this.Band16Gain, - Band17Gain = this.Band17Gain, - Band18Gain = this.Band18Gain, - Band19Gain = this.Band19Gain, - Band20Gain = this.Band20Gain + EnableEqualizer = EnableEqualizer, + Band1Gain = Band1Gain, + Band2Gain = Band2Gain, + Band3Gain = Band3Gain, + Band4Gain = Band4Gain, + Band5Gain = Band5Gain, + Band6Gain = Band6Gain, + Band7Gain = Band7Gain, + Band8Gain = Band8Gain, + Band9Gain = Band9Gain, + Band10Gain = Band10Gain, + Band11Gain = Band11Gain, + Band12Gain = Band12Gain, + Band13Gain = Band13Gain, + Band14Gain = Band14Gain, + Band15Gain = Band15Gain, + Band16Gain = Band16Gain, + Band17Gain = Band17Gain, + Band18Gain = Band18Gain, + Band19Gain = Band19Gain, + Band20Gain = Band20Gain, + + AudioPlayerEnableTrackLength = AudioPlayerEnableTrackLength, + AudioPlayerTrackLength = AudioPlayerTrackLength, + AudioPlayerAutoDetectSilence = AudioPlayerAutoDetectSilence, + AudioPlayerSilenceDelay = AudioPlayerSilenceDelay }); } } @@ -144,6 +154,11 @@ namespace Mesen.GUI.Config public double Band18Gain; public double Band19Gain; public double Band20Gain; + + [MarshalAs(UnmanagedType.I1)] public bool AudioPlayerEnableTrackLength; + public UInt32 AudioPlayerTrackLength; + [MarshalAs(UnmanagedType.I1)] public bool AudioPlayerAutoDetectSilence; + public UInt32 AudioPlayerSilenceDelay; } public enum AudioSampleRate diff --git a/NewUI/Config/NesConfig.cs b/NewUI/Config/NesConfig.cs index 4138c130..65d4181a 100644 --- a/NewUI/Config/NesConfig.cs +++ b/NewUI/Config/NesConfig.cs @@ -23,10 +23,6 @@ namespace Mesen.GUI.Config [Reactive] public bool FdsAutoInsertDisk { get; set; } = false; [Reactive] public VsDualOutputOption VsDualVideoOutput { get; set; } = VsDualOutputOption.Both; [Reactive] public VsDualOutputOption VsDualAudioOutput { get; set; } = VsDualOutputOption.Both; - [Reactive] public bool NsfMoveToNextTrackAfterTime { get; set; } = true; - [Reactive] public UInt32 NsfMoveToNextTrackTime { get; set; } = 120; - [Reactive] public bool NsfAutoDetectSilence { get; set; } = true; - [Reactive] public UInt32 NsfAutoDetectSilenceDelay { get; set; } = 3000; //Video [Reactive] public bool DisableSprites { get; set; } = false; @@ -130,10 +126,6 @@ namespace Mesen.GUI.Config FdsAutoInsertDisk = FdsAutoInsertDisk, VsDualVideoOutput = VsDualVideoOutput, VsDualAudioOutput = VsDualAudioOutput, - NsfMoveToNextTrackAfterTime = NsfMoveToNextTrackAfterTime, - NsfMoveToNextTrackTime = NsfMoveToNextTrackTime, - NsfAutoDetectSilence = NsfAutoDetectSilence, - NsfAutoDetectSilenceDelay = NsfAutoDetectSilenceDelay, SpritesEnabled = !DisableSprites, BackgroundEnabled = !DisableBackground, @@ -260,10 +252,6 @@ namespace Mesen.GUI.Config [MarshalAs(UnmanagedType.I1)] public bool FdsAutoInsertDisk; public VsDualOutputOption VsDualVideoOutput; public VsDualOutputOption VsDualAudioOutput; - [MarshalAs(UnmanagedType.I1)] public bool NsfMoveToNextTrackAfterTime; - public UInt32 NsfMoveToNextTrackTime; - [MarshalAs(UnmanagedType.I1)] public bool NsfAutoDetectSilence; - public UInt32 NsfAutoDetectSilenceDelay; [MarshalAs(UnmanagedType.I1)] public bool SpritesEnabled; [MarshalAs(UnmanagedType.I1)] public bool BackgroundEnabled; diff --git a/NewUI/Localization/resources.en.xml b/NewUI/Localization/resources.en.xml index 7ee5077c..22ad7eb9 100644 --- a/NewUI/Localization/resources.en.xml +++ b/NewUI/Localization/resources.en.xml @@ -101,16 +101,6 @@ Extracting file, please wait... - - - Title: - Artist: - Copyright: - Sound Chips: - REC - Slow Motion - Rewinding - Fast Forward
Close @@ -133,6 +123,13 @@ Volume Reset to Defaults + Audio Player Settings + When a track has an unknown duration: + Move to the next track after + Limit track duration to + seconds of silence + seconds + Advanced Disable dynamic sample rate Enable reverb @@ -328,12 +325,6 @@ Automatically fast forward FDS games when disk or BIOS is loading Automatically switch disks for FDS games - NSF Settings - Move to next track after - milliseconds of silence - Limit track run time to - seconds - VS. DualSystem Settings Play audio for: Show video for: diff --git a/NewUI/Views/AudioConfigView.axaml b/NewUI/Views/AudioConfigView.axaml index c7e7a959..9a3ee96d 100644 --- a/NewUI/Views/AudioConfigView.axaml +++ b/NewUI/Views/AudioConfigView.axaml @@ -74,8 +74,7 @@ Value="{CompiledBinding Config.MasterVolume}" HorizontalAlignment="Left" /> - - + @@ -93,6 +92,22 @@ /> + + + + + + + + + + + + + + + + diff --git a/NewUI/Views/NesConfigView.axaml b/NewUI/Views/NesConfigView.axaml index 453c36d7..dc1408b8 100644 --- a/NewUI/Views/NesConfigView.axaml +++ b/NewUI/Views/NesConfigView.axaml @@ -74,19 +74,6 @@ - - - - - - - - - - - - -