Files
SourMesen e391fd8f9a Code: Add auto-formatting rules for C++ (clang-format) and enforce in CI via "clang-format" and "dotnet format" (#78)
* Code: Add auto-formatting rules for C++ projects (clang-format) and enforce code style via "clang-format" and "dotnet format"

* fix ui format warnings
2026-04-30 06:03:55 -07:00

134 lines
2.9 KiB
C++

#include "pch.h"
#include <algorithm>
#include "NES/HdPacks/OggReader.h"
#include "NES/HdPacks/OggMixer.h"
#include "Shared/Emulator.h"
enum class OggPlaybackOptions
{
None = 0x00,
Loop = 0x01
};
OggMixer::OggMixer(Emulator* emu)
{
_emu = emu;
}
void OggMixer::Reset(uint32_t sampleRate)
{
_bgm.reset();
_sfx.clear();
_sfxVolume = 128;
_bgmVolume = 128;
_options = 0;
_sampleRate = sampleRate;
_paused = false;
}
void OggMixer::SetPlaybackOptions(uint8_t options)
{
_options = options;
bool loop = (options & (int)OggPlaybackOptions::Loop) != 0;
if(_bgm) {
_bgm->SetLoopFlag(loop);
}
}
void OggMixer::SetPausedFlag(bool paused)
{
_paused = paused;
}
void OggMixer::StopBgm()
{
_bgm.reset();
}
void OggMixer::StopSfx()
{
_sfx.clear();
}
void OggMixer::SetBgmVolume(uint8_t volume)
{
_bgmVolume = volume;
}
void OggMixer::SetSfxVolume(uint8_t volume)
{
_sfxVolume = volume;
}
bool OggMixer::IsBgmPlaying()
{
return !_paused && _bgm;
}
bool OggMixer::IsSfxPlaying()
{
return _sfx.size() > 0;
}
void OggMixer::SetSampleRate(int sampleRate)
{
_sampleRate = sampleRate;
if(_bgm) {
_bgm->SetSampleRate(sampleRate);
}
for(shared_ptr<OggReader>& sfx : _sfx) {
sfx->SetSampleRate(sampleRate);
}
}
bool OggMixer::Play(string filename, bool isSfx, uint32_t startOffset, uint32_t loopPosition)
{
shared_ptr<OggReader> reader(new OggReader(_emu));
bool loop = !isSfx && (_options & (int)OggPlaybackOptions::Loop) != 0;
if(reader->Init(filename, loop, _sampleRate, startOffset, loopPosition)) {
if(_emu->IsRunAheadFrame()) {
//If this was done on runahead frames, SFX/BGM will attempt to start playing multiple times (once per runahead frame)
//This causes all sound effects to be duplicated.
//Skipping the processing on runahead here is an imperfect workaround - it allows 1 runahead frame to usually work properly
//but attempting to use over 1 frame of runahead will instead cause SFX to not play properly at times.
//Ideally, the exact state of the BGM/SFX (what is playing exactly, and at which part of their playback they are) should
//be part of the save state data and restored like any other state (but this isn't trivial to implement at the moment)
return true;
}
if(isSfx) {
_sfx.push_back(reader);
} else {
_bgm = reader;
}
return true;
}
return false;
}
int OggMixer::GetBgmOffset()
{
if(_bgm) {
return _bgm->GetOffset();
} else {
return -1;
}
}
void OggMixer::MixAudio(int16_t* out, uint32_t sampleCount, uint32_t sampleRate)
{
if(_bgm && !_paused) {
_bgm->SetSampleRate(sampleRate);
_bgm->ApplySamples(out, sampleCount, _bgmVolume);
if(_bgm->IsPlaybackOver()) {
_bgm.reset();
}
}
for(shared_ptr<OggReader>& sfx : _sfx) {
sfx->SetSampleRate(sampleRate);
sfx->ApplySamples(out, sampleCount, _sfxVolume);
}
_sfx.erase(std::remove_if(_sfx.begin(), _sfx.end(), [](const shared_ptr<OggReader>& o) { return o->IsPlaybackOver(); }), _sfx.end());
}