//////////////////////////////////////////////////////////////////////////////////////// // // Nestopia - NES/Famicom emulator written in C++ // // Copyright (C) 2003-2008 Martin Freij // // This file is part of Nestopia. // // Nestopia is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // Nestopia is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Nestopia; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // //////////////////////////////////////////////////////////////////////////////////////// #include "NstIoFile.hpp" #include "NstIoLog.hpp" #include "NstIoArchive.hpp" #include "NstResourceString.hpp" #include "NstManagerPaths.hpp" #include "NstWindowUser.hpp" #include "NstDialogSound.hpp" #include "NstManagerSound.hpp" #include "NstManagerSoundRecorder.hpp" #include "NstManagerPreferences.hpp" namespace Nestopia { namespace Managers { struct Sound::Callbacks { static bool NST_CALLBACK Lock(Nes::Sound::UserData,Nes::Sound::Output&); static void NST_CALLBACK Unlock(Nes::Sound::UserData,Nes::Sound::Output&); }; Sound::Sound ( Window::Custom& window, Window::Menu& m, Emulator& e, const Paths& p, const Preferences& r, const Configuration& cfg ) : Manager ( e, m, this, &Sound::OnEmuEvent, IDM_OPTIONS_SOUND, &Sound::OnMenuOptionsSound ), paths ( p ), preferences ( r ), directSound ( window ), dialog ( new Window::Sound(e,directSound.GetAdapters(),p,cfg) ), recorder ( new Recorder(m,dialog->GetRecorder(),e) ) { Nes::Sound::Output::lockCallback.Set( &Callbacks::Lock, this ); Nes::Sound::Output::unlockCallback.Set( &Callbacks::Unlock, this ); UpdateSettings(); } Sound::~Sound() { Nes::Sound::Output::lockCallback.Unset(); Nes::Sound::Output::unlockCallback.Unset(); } bool Sound::CanRunInBackground() const { if (emulator.IsNsf()) return menu[IDM_MACHINE_NSF_OPTIONS_PLAYINBACKGROUND].Checked(); else return preferences[Managers::Preferences::RUN_IN_BACKGROUND]; } void Sound::OnEmuEvent(const Emulator::Event event,const Emulator::Data data) { switch (event) { case Emulator::EVENT_POWER_ON: case Emulator::EVENT_SPEED: if (emuOutput && emulator.NetPlayers() == 0) { Nes::Sound( emulator ).SetSpeed( emulator.GetSpeed() ); Nes::Sound( emulator ).EmptyBuffer(); } break; case Emulator::EVENT_REWINDING_START: case Emulator::EVENT_REWINDING_STOP: Nes::Sound(emulator).SetVolume ( Nes::Sound::CHANNEL_DPCM, event == Emulator::EVENT_REWINDING_START ? 0 : dialog->GetVolume(Nes::Sound::CHANNEL_DPCM) ); case Emulator::EVENT_REWINDING_PREPARE: directSound.StopStream(); break; case Emulator::EVENT_NETPLAY_MODE: menu[IDM_OPTIONS_SOUND].Enable( !data ); break; } } void Sound::OnMenuOptionsSound(uint) { dialog->Open(); UpdateSettings(); } void Sound::Save(Configuration& cfg) const { dialog->Save( cfg ); } void Sound::UpdateSettings() { cstring errMsg = NULL; if (dialog->SoundEnabled()) { Nes::Sound nesSound( emulator ); errMsg = directSound.Update ( dialog->GetAdapter(), nesSound.GetSampleRate(), 16, nesSound.GetSpeaker() == Nes::Sound::SPEAKER_STEREO ? DirectX::DirectSound::STEREO : DirectX::DirectSound::MONO, emulator.IsNsf() ? 500 : (dialog->GetLatency() + 2) * 21, dialog->GetPool(), CanRunInBackground() ); if (errMsg == NULL) { emuOutput = &output; nesSound.SetSpeed( emulator.GetSpeed() ); recorder->Enable( &directSound.GetWaveFormat() ); return; } } else { directSound.Destroy(); } Disable( errMsg ); } void Sound::Disable(cstring const errMsg) { emuOutput = NULL; Nes::Sound( emulator ).SetVolume( Nes::Sound::ALL_CHANNELS, 0 ); recorder->Enable( NULL ); if (errMsg) Window::User::Fail( Resource::String( IDS_ERR_SOUND_FAILED ).Invoke( errMsg ) ); } void Sound::StartEmulation() const { Nes::Sound( emulator ).EmptyBuffer(); } void Sound::StopEmulation() { directSound.StopStream(); } #ifdef NST_MSVC_OPTIMIZE #pragma optimize("t", on) #endif bool NST_CALLBACK Sound::Callbacks::Lock(Nes::Sound::UserData data,Nes::Sound::Output&) { Sound& sound = *static_cast(data); if (sound.directSound.Streaming()) { return sound.directSound.LockStream( sound.output.samples, sound.output.length ); } else { if (sound.CanRunInBackground() != sound.directSound.GlobalFocus()) sound.UpdateSettings(); sound.directSound.StartStream(); return false; } } void NST_CALLBACK Sound::Callbacks::Unlock(Nes::Sound::UserData data,Nes::Sound::Output&) { Sound& sound = *static_cast(data); if (sound.recorder->IsRecording()) sound.recorder->Flush( sound.output ); sound.directSound.UnlockStream( sound.output.samples, sound.output.length ); } #ifdef NST_MSVC_OPTIMIZE #pragma optimize("", on) #endif } }