#include "EngineImpl.h" #include "Synth.h" #include "Components/External/Events/EventIn.h" #include "Components/External/Events/EventInWithId.h" #include "Components/External/Events/EventOut.h" #include "Components/External/Controls/ControlIn.h" #include "Components/External/Controls/ControlOut.h" #include "Components/External/Switches/SwitchIn.h" #include "Components/External/Switches/SwitchOut.h" #include "Components/JustDance/PlayMenuMusic.h" #include "Components/JustDance/StopMenuMusic.h" #include "Components/JustDance/JDSound.h" #include "Components/JustDance/JDSoundSyncOnTransition.h" #include "Components/JustDance/JDRemoteSound.h" #include "Components/JustDance/PlayEventBusMusic.h" #include "Components/JustDance/StopEventBusMusic.h" #include "Components/JustDance/StopAllEventBusMixes.h" #include "Components/JustDance/PlayMusicMultiTrack.h" #include "Commands.h" #include "Inc/Defs.h" #include "Util/Logger.h" #include "Util/Directory.h" #include "Util/StringHash.h" Soundwich::IClientSystem* Soundwich::Engine::Impl::_clientSystem = nullptr; Soundwich::Engine::Impl::Impl( ) : _audioDevice( nullptr ) #if defined( USE_PROCESSING_THREAD ) , _processing( false ) #endif #if defined( USE_DEFERRED_COMMANDS ) , _sendCommands( 256 ) , _returnCommands( 64 ) #endif , _logWriter( _logStream ) { _synthFactory = new SynthFactory( this ); _logStream.Open( Util::Directory::GetCurrent( ) + "log.txt" ); LOG_INFO( GetLog( ), "Initialize Soundwich engine" ); } Soundwich::Engine::Impl::~Impl( ) { #if defined( USE_PROCESSING_THREAD ) // signal process thread to finish _processing = false; // wake it up if it is asleep waiting for a tick _processThreadTickEvent.Signal( ); // wait for it to finish _processThread.Join( ); #endif #if defined( USE_DEFERRED_COMMANDS ) // discard pending return commands _returnCommands.Clear( ); #endif // release the audio device (ensures rendering has stopped) DefaultAudioDevice::Release( _audioDevice ); // destroy synths for( SynthDictionary::Iterator it = _synths.Begin( ); it != _synths.End( ); ) { Synth* synth = it->second; _synths.Erase( it++ ); delete synth; } delete _synthFactory; } SoundResult Soundwich::Engine::Impl::Run( ) { #if defined( USE_NATIVE_AUDIO ) // create the audio device SoundResult result = DefaultAudioDevice::Create( &_audioDevice, this ) ? Ok : Error; #else SoundResult result = Ok; #endif #if defined( USE_PROCESSING_THREAD ) if( result == Ok ) { // start the processing thread _processThread.Start( Engine::Impl::StartInternalProcessing, this ); } #endif return result; } void Soundwich::Engine::Impl::SetClientSystem( Soundwich::IClientSystem* clientSystem ) { _clientSystem = clientSystem; } Soundwich::IClientSystem* Soundwich::Engine::Impl::GetClientSystem( ) { AssertMessage( _clientSystem, "Client system is null" ); return _clientSystem; } SoundResult Soundwich::Engine::Impl::SetDataPath( const Util::String& dataPath ) { _dataPath = dataPath; return Ok; } SoundResult Soundwich::Engine::Impl::CreateSynthFromXmlFile( const Util::String& name, Soundwich::Synth** synth, const Util::String& fileName ) { SoundResult result = Ok; *synth = new Synth( this, name ); result = (*synth)->LoadFromXmlFile( ( _dataPath + "\\" + fileName + ".xws" ).Chars( ) ); if( result == Ok ) { #if defined( USE_PROCESSING_THREAD ) Util::AutoLock lock ( _synthsLock ); #endif _synths.Add( name, *synth ); } else { delete *synth; *synth = nullptr; } return result; } SoundResult Soundwich::Engine::Impl::CreateSynthFromMemory( const Util::String& name, Soundwich::Synth** synth, void* data, uint32 size ) { SoundResult result = Ok; *synth = new Synth( this, name ); result = (*synth)->LoadFromMemory( data, size ); if( result == Ok ) { #if defined( USE_PROCESSING_THREAD ) Util::AutoLock lock ( _synthsLock ); #endif _synths.Add( name, *synth ); } else { delete *synth; *synth = nullptr; } return result; } SoundResult Soundwich::Engine::Impl::CreateSynthFromXmlMemory( const Util::String& name, Soundwich::Synth** synth, const Util::String& xml ) { SoundResult result = Ok; *synth = new Synth( this, name ); result = (*synth)->LoadFromXmlMemory( xml ); if( result == Ok ) { #if defined( USE_PROCESSING_THREAD ) Util::AutoLock lock ( _synthsLock ); #endif _synths.Add( name, *synth ); } else { delete *synth; *synth = nullptr; } return result; } SoundResult Soundwich::Engine::Impl::SaveSynthToFile( Soundwich::Synth* synth, const Util::String& fileName ) { AssertMessage( synth, "Synth is null" ); return synth->SaveToFile( ( _dataPath + "\\" + fileName + ".bws" ).Chars( ) ); } SoundResult Soundwich::Engine::Impl::SaveSynthToXmlFile( Soundwich::Synth* synth, const Util::String& fileName ) { AssertMessage( synth, "Synth is null" ); return synth->SaveToXmlFile( ( _dataPath + "\\" + fileName + ".xws" ).Chars( ) ); } SoundResult Soundwich::Engine::Impl::DestroySynth( Synth* synth ) { SoundResult result = Ok; if( !_synths.ContainsValue( synth ) ) { result = UnknownSynth; } else { #if defined( USE_DEFERRED_COMMANDS ) _sendCommands.Send( new InternalDestroySynthCommand( synth ) ); #else InternalDestroySynth( synth ); #endif } return result; } SoundResult Soundwich::Engine::Impl::ActivateSynth( Synth* synth ) { #if defined( USE_DEFERRED_COMMANDS ) _sendCommands.Send( new InternalActivateSynthCommand( synth ) ); #else InternalActivateSynth( synth ); #endif return Ok; } SoundResult Soundwich::Engine::Impl::DeactivateSynth( Synth* synth ) { #if defined( USE_DEFERRED_COMMANDS ) _sendCommands.Send( new InternalDeactivateSynthCommand( synth ) ); #else InternalDeactivateSynth( synth ); #endif return Ok; } SoundResult Soundwich::Engine::Impl::GetSynth( const Util::String& name, Synth** synth ) { SoundResult result = Ok; if( !_synths.TryGetValue( name, *synth ) ) { result = UnknownSynth; } return result; } SoundResult Soundwich::Engine::Impl::GetEventInComponent( Synth* synth, const Util::String& componentName, EventIn** eventIn ) { SoundResult result = Ok; Component* component; if( !synth->GetComponents( ).TryGetValue( componentName, component ) ) { result = UnknownSynth; } else { if( component->GetDerivedType( ) != EventIn::GetType( ) ) { *eventIn = nullptr; result = ComponentNotEventIn; } else { *eventIn = (EventIn*)component; } } return result; } SoundResult Soundwich::Engine::Impl::GetEventInWithIdComponent( Synth* synth, const Util::String& componentName, EventInWithId** eventIn ) { SoundResult result = Ok; Component* component; if( !synth->GetComponents( ).TryGetValue( componentName, component ) ) { result = UnknownSynth; } else { if( component->GetDerivedType( ) != EventInWithId::GetType( ) ) { *eventIn = nullptr; result = ComponentNotEventIn; } else { *eventIn = (EventInWithId*)component; } } return result; } SoundResult Soundwich::Engine::Impl::GetControlInComponent( Synth* synth, const Util::String& componentName, ControlIn** controlIn ) { SoundResult result = Ok; Component* component; if( !synth->GetComponents( ).TryGetValue( componentName, component ) ) { result = UnknownSynth; } else { if( component->GetDerivedType( ) != ControlIn::GetType( ) ) { *controlIn = nullptr; result = ComponentNotControlIn; } else { *controlIn = (ControlIn*)component; } } return result; } SoundResult Soundwich::Engine::Impl::GetSwitchInComponent( Synth* synth, const Util::String& componentName, SwitchIn** switchIn ) { SoundResult result = Ok; Component* component; if( !synth->GetComponents( ).TryGetValue( componentName, component ) ) { result = UnknownSynth; } else { if( component->GetDerivedType( ) != SwitchIn::GetType( ) ) { *switchIn = nullptr; result = Error; } else { *switchIn = (SwitchIn*)component; } } return result; } SoundResult Soundwich::Engine::Impl::GetEventOutComponent( Synth* synth, const Util::String& componentName, EventOut** eventOut ) { SoundResult result = Ok; Component* component; if( !synth->GetComponents( ).TryGetValue( componentName, component ) ) { result = UnknownSynth; } else { if( component->GetDerivedType( ) != EventOut::GetType( ) ) { *eventOut = nullptr; result = Error; } else { *eventOut = (EventOut*)component; } } return result; } SoundResult Soundwich::Engine::Impl::GetControlOutComponent( Synth* synth, const Util::String& componentName, ControlOut** controlOut ) { SoundResult result = Ok; Component* component; if( !synth->GetComponents( ).TryGetValue( componentName, component ) ) { result = UnknownSynth; } else { if( component->GetDerivedType( ) != ControlOut::GetType( ) ) { *controlOut = nullptr; result = Error; } else { *controlOut = (ControlOut*)component; } } return result; } SoundResult Soundwich::Engine::Impl::GetSwitchOutComponent( Synth* synth, const Util::String& componentName, SwitchOut** switchOut ) { SoundResult result = Ok; Component* component; if( !synth->GetComponents( ).TryGetValue( componentName, component ) ) { result = UnknownSynth; } else { if( component->GetDerivedType( ) != SwitchOut::GetType( ) ) { *switchOut = nullptr; result = Error; } else { *switchOut = (SwitchOut*)component; } } return result; } SoundResult Soundwich::Engine::Impl::Tick( ) { #if defined( USE_DEFERRED_COMMANDS ) // process all external commands returned from the engine while( _returnCommands.Receive( ) ); _returnCommands.Clean( ); #endif #if defined( USE_PROCESSING_THREAD ) // signal the engine to process all internal commands if threaded _processThreadTickEvent.Signal( ); #else #if defined( USE_DEFERRED_COMMANDS ) // process all commands and tick the engine while( _sendCommands.Receive( ) ); _sendCommands.Clean( ); #endif InternalTick( ); #endif return Ok; } SoundResult Soundwich::Engine::Impl::SetEventIn( EventIn* eventIn ) { #if defined( USE_DEFERRED_COMMANDS ) _sendCommands.Send( new InternalSetEventInCommand( eventIn ) ); #else InternalSetEventIn( eventIn ); #endif return Ok; } SoundResult Soundwich::Engine::Impl::SetEventInWithId( EventInWithId* eventIn, int32 val ) { #if defined( USE_DEFERRED_COMMANDS ) _sendCommands.Send( new InternalSetEventInWithIdCommand( eventIn, val ) ); #else InternalSetEventInWithId( eventIn, val ); #endif return Ok; } SoundResult Soundwich::Engine::Impl::SetControlIn( ControlIn* controlIn, flt32 val ) { #if defined( USE_DEFERRED_COMMANDS ) _sendCommands.Send( new InternalSetControlInCommand( controlIn, val ) ); #else InternalSetControlIn( controlIn, val ); #endif return Ok; } SoundResult Soundwich::Engine::Impl::SetSwitchIn( SwitchIn* switchIn, int32 val ) { #if defined( USE_DEFERRED_COMMANDS ) _sendCommands.Send( new InternalSetSwitchInCommand( switchIn, val ) ); #else InternalSetSwitchIn( switchIn, val ); #endif return Ok; } SoundResult Soundwich::Engine::Impl::GetComponent( Synth* synth, const Util::String& componentName, Component** component ) { return synth->GetComponents( ).TryGetValue( componentName, *component ) ? Ok : Error; } SoundResult Soundwich::Engine::Impl::SwitchTracking( Soundwich::Synth* synth, bool enable, const char* exportFilePath) { synth->SwitchTracking(enable, exportFilePath); return Ok; } SoundResult Soundwich::Engine::Impl::SetSynthDebugLogging( Synth* synth, bool enable ) { synth->SetDebugLogging( enable ); return Ok; } SoundResult Soundwich::Engine::Impl::SetComponentDebugLogging( Component* component, bool enable ) { component->SetDebugLogging( enable ); return Ok; } SoundResult Soundwich::Engine::Impl::SetConnectionDebugLogging( Connection* connection, bool enable ) { connection->SetDebugLogging( enable ); return Ok; } SoundResult Soundwich::Engine::Impl::RegisterEventOutCallback( IEventOutCallback* cb, EventOut* eventOut ) { eventOut->RegisterCallback( cb ); return Ok; } SoundResult Soundwich::Engine::Impl::RegisterControlOutCallback( IControlOutCallback* cb, ControlOut* controlOut ) { controlOut->RegisterCallback( cb ); return Ok; } SoundResult Soundwich::Engine::Impl::RegisterSwitchOutCallback( ISwitchOutCallback* cb, SwitchOut* switchOut ) { switchOut->RegisterCallback( cb ); return Ok; } SoundResult Soundwich::Engine::Impl::RegisterJustDancePlayMenuMusicCallback( IJustDancePlayMenuMusicCallback* cb ) { PlayMenuMusic::RegisterCallback( cb ); return Ok; } SoundResult Soundwich::Engine::Impl::RegisterJustDanceStopMenuMusicCallback( IJustDanceStopMenuMusicCallback* cb ) { StopMenuMusic::RegisterCallback( cb ); return Ok; } SoundResult Soundwich::Engine::Impl::RegisterJustDanceSoundEffectCallback( IJustDanceSoundEffectCallback* cb ) { JDSound::RegisterCallback( cb ); return Ok; } SoundResult Soundwich::Engine::Impl::RegisterJustDanceSoundEffectOnTrackTransitionCallback( IJustDanceSoundEffectOnTrackTransitionCallback* cb ) { JDSoundSyncOnTransition::RegisterCallback( cb ); return Ok; } SoundResult Soundwich::Engine::Impl::RegisterJustDanceRemoteSoundEffectCallback( IJustDanceRemoteSoundEffectCallback* cb ) { JDRemoteSound::RegisterCallback( cb ); return Ok; } SoundResult Soundwich::Engine::Impl::RegisterJustDancePlayEventBusMusicCallback( IJustDancePlayEventBusMusicCallback* cb ) { PlayEventBusMusic::RegisterCallback( cb ); return Ok; } SoundResult Soundwich::Engine::Impl::RegisterJustDanceStopEventBusMusicCallback( IJustDanceStopEventBusMusicCallback* cb ) { StopEventBusMusic::RegisterCallback( cb ); return Ok; } SoundResult Soundwich::Engine::Impl::RegisterJustDanceStopAllEventBusMixesCallback(IJustDanceStopAllEventBusMixesCallback* cb) { StopAllEventBusMixes::RegisterCallback(cb); return Ok; } SoundResult Soundwich::Engine::Impl::RegisterJustDancePlayMusicMultiTrackCallback( IJustDancePlayMusicMultiTrackCallback* cb ) { PlayMusicMultiTrack::RegisterCallback( cb ); return Ok; } SoundResult Soundwich::Engine::Impl::RegisterSoundwichTrackingCallback(ISoundwichTracking* cb) { for (SynthDictionary::Iterator item = _synths.begin(); item != _synths.end(); ++item) { Synth* synth = item->second; if (synth != nullptr) { synth->RegisterTracker(cb); } } return Ok; } SoundResult Soundwich::Engine::Impl::UnregisterEventOutCallback( IEventOutCallback* cb, EventOut* eventOut ) { eventOut->UnregisterCallback( cb ); return Ok; } SoundResult Soundwich::Engine::Impl::UnregisterControlOutCallback( IControlOutCallback* cb, ControlOut* controlOut ) { controlOut->UnregisterCallback( cb ); return Ok; } SoundResult Soundwich::Engine::Impl::UnregisterSwitchOutCallback( ISwitchOutCallback* cb, SwitchOut* switchOut ) { switchOut->UnregisterCallback( cb ); return Ok; } SoundResult Soundwich::Engine::Impl::UnregisterJustDancePlayMenuMusicCallback( IJustDancePlayMenuMusicCallback* cb ) { PlayMenuMusic::UnregisterCallback( cb ); return Ok; } SoundResult Soundwich::Engine::Impl::UnregisterJustDanceStopMenuMusicCallback( IJustDanceStopMenuMusicCallback* cb ) { StopMenuMusic::UnregisterCallback( cb ); return Ok; } SoundResult Soundwich::Engine::Impl::UnregisterJustDanceSoundEffectCallback( IJustDanceSoundEffectCallback* cb ) { JDSound::UnregisterCallback( cb ); return Ok; } SoundResult Soundwich::Engine::Impl::UnregisterJustDanceSoundEffectOnTrackTransitionCallback( IJustDanceSoundEffectOnTrackTransitionCallback* cb ) { JDSoundSyncOnTransition::UnregisterCallback( cb ); return Ok; } SoundResult Soundwich::Engine::Impl::UnregisterJustDanceRemoteSoundEffectCallback( IJustDanceRemoteSoundEffectCallback* cb ) { JDRemoteSound::UnregisterCallback( cb ); return Ok; } SoundResult Soundwich::Engine::Impl::UnregisterJustDancePlayEventBusMusicCallback( IJustDancePlayEventBusMusicCallback* cb ) { PlayEventBusMusic::UnregisterCallback( cb ); return Ok; } SoundResult Soundwich::Engine::Impl::UnregisterJustDanceStopEventBusMusicCallback( IJustDanceStopEventBusMusicCallback* cb ) { StopEventBusMusic::UnregisterCallback( cb ); return Ok; } SoundResult Soundwich::Engine::Impl::UnregisterJustDanceStopAllEventBusMixesCallback(IJustDanceStopAllEventBusMixesCallback* cb) { StopAllEventBusMixes::UnregisterCallback(cb); return Ok; } SoundResult Soundwich::Engine::Impl::UnregisterJustDancePlayMusicMultiTrackCallback( IJustDancePlayMusicMultiTrackCallback* cb ) { PlayMusicMultiTrack::UnregisterCallback( cb ); return Ok; } SoundResult Soundwich::Engine::Impl::UnregisterSoundwichTrackingCallback(ISoundwichTracking* cb) { for (SynthDictionary::Iterator item = _synths.begin(); item != _synths.end(); ++item) { Synth* synth = item->second; if (synth != nullptr) { synth->UnregisterTracker(cb); } } return Ok; } void Soundwich::Engine::Impl::SetEventOut( EventOut* eventOut ) { #if defined( USE_DEFERRED_COMMANDS ) _returnCommands.Send( new ExternalSetEventOutCommand( eventOut ) ); #else ExternalSetEventOut( eventOut ); #endif } void Soundwich::Engine::Impl::SetControlOut( ControlOut* controlOut, flt32 val ) { #if defined( USE_DEFERRED_COMMANDS ) _returnCommands.Send( new ExternalSetControlOutCommand( controlOut, val ) ); #else ExternalSetControlOut( controlOut, val ); #endif } void Soundwich::Engine::Impl::SetSwitchOut( SwitchOut* switchOut, int32 val ) { #if defined( USE_DEFERRED_COMMANDS ) _returnCommands.Send( new ExternalSetSwitchOutCommand( switchOut, val ) ); #else ExternalSetSwitchOut( switchOut, val ); #endif } void Soundwich::Engine::Impl::StartInternalProcessing( void* data ) { Engine::Impl* engine = (Engine::Impl*)data; // run the process loop engine->InternalProcessingLoop( ); } void Soundwich::Engine::Impl::InternalProcessingLoop( ) { #if defined( USE_PROCESSING_THREAD ) _processing = true; while( _processing ) { // wait on main thread Tick( ) _processThreadTickEvent.Wait( ); // process commands from main thread while( _sendCommands.Receive( ) ); _sendCommands.Clean( ); InternalTick( ); } #endif } void Soundwich::Engine::Impl::InternalShutdown( ) { for( SynthArray::Iterator it = _ticking.Begin( ); it != _ticking.End( ); ++it ) { Synth* synth = *it; _ticking.Remove( synth ); #if defined( USE_NATIVE_AUDIO ) if( _rendering.Contains( synth ) ) { _renderLock.Lock( ); _rendering.Remove( synth ); _renderLock.Unlock( ); } #endif } } void Soundwich::Engine::Impl::InternalDestroySynth( Synth* synth ) { // ensure we are not currently active, deactivate if so InternalDeactivateSynth( synth ); #if defined( USE_DEFERRED_COMMANDS ) // signal the main thread to destroy the synth next Tick( ) _returnCommands.Send( new ExternalDestroySynthCommand( synth ) ); #else ExternalDestroySynth( synth ); #endif } void Soundwich::Engine::Impl::InternalActivateSynth( Synth* synth ) { if( !_ticking.Contains( synth ) ) { _ticking.PushBack( synth ); // give it a first tick to initialize values synth->Tick( 0.0f ); } #if defined( USE_NATIVE_AUDIO ) if( !_rendering.Contains( synth ) ) { _renderLock.Lock( ); _rendering.PushBack( synth ); _renderLock.Unlock( ); } #endif } void Soundwich::Engine::Impl::InternalDeactivateSynth( Synth* synth ) { if( _ticking.Contains( synth ) ) { _ticking.Remove( synth ); } #if defined( USE_NATIVE_AUDIO ) if( _rendering.Contains( synth ) ) { _renderLock.Lock( ); _rendering.Remove( synth ); _renderLock.Unlock( ); } #endif } void Soundwich::Engine::Impl::InternalTick( ) { // round to nearest millisec, in seconds flt32 interval = 0.001f * Util::Maths::Round( (flt32)_systemTimer.GetTime( true ) * 0.001f ); for ( SynthArray::Iterator item = _ticking.begin() ; item != _ticking.end() ; ++item ) { (*item)->Tick( interval ); } #if defined( ENABLE_STATISTICS ) Stats stats; for ( SynthArray::Iterator item = _ticking.begin() ; item != _ticking.end() ; ++item ) { (*item)->GetStats( stats ); } #endif } void Soundwich::Engine::Impl::InternalSetEventIn( EventIn* eventIn ) { eventIn->Set( ); } void Soundwich::Engine::Impl::InternalSetEventInWithId( EventInWithId* eventIn, int32 val ) { eventIn->Set( val ); } void Soundwich::Engine::Impl::InternalSetControlIn( ControlIn* controlIn, flt32 val ) { controlIn->SetValue( val ); } void Soundwich::Engine::Impl::InternalSetSwitchIn( SwitchIn* switchIn, int32 val ) { switchIn->SetValue( val ); } void Soundwich::Engine::Impl::ExternalDestroySynth( Synth* synth ) { _synths.Remove( synth->GetName( ) ); delete synth; } void Soundwich::Engine::Impl::ExternalSetEventOut( EventOut* eventOut ) { eventOut->ExecuteCallbacks( ); } void Soundwich::Engine::Impl::ExternalSetControlOut( ControlOut* controlOut, flt32 val ) { controlOut->ExecuteCallbacks( val ); } void Soundwich::Engine::Impl::ExternalSetSwitchOut( SwitchOut* switchOut, int32 val ) { switchOut->ExecuteCallbacks( val ); } void Soundwich::Engine::Impl::RenderAudio( ) { #if defined( USE_NATIVE_AUDIO ) _renderLock.Lock( ); SynthArray rendering = _rendering; _renderLock.Unlock( ); for( auto item : rendering ) { item->Render( ); } #endif } void Soundwich::Engine::Impl::GetStats( Engine::Stats& stats ) { for ( SynthDictionary::Iterator item = _synths.begin() ; item != _synths.end() ; ++item ) { Synth* synth = item->second; synth->GetStats( stats ); } }