From 3afff7c3ab40037ae09072961ca7f1eb2bfc4128 Mon Sep 17 00:00:00 2001 From: tmj-fstate Date: Wed, 29 Nov 2017 01:50:57 +0100 Subject: [PATCH] audio subsystem: completed base functionality of the renderer, external and engine sounds moved from cab to vehicle, minor fixes to various sound-related methods --- Button.cpp | 54 +- Button.h | 57 +- Driver.cpp | 91 +- DynObj.cpp | 1966 +++++++++++++++++++++++++----------------- DynObj.h | 131 ++- Event.cpp | 40 +- Gauge.cpp | 51 +- Gauge.h | 50 +- Globals.cpp | 2 + Globals.h | 1 + McZapkie/MOVER.h | 20 +- McZapkie/Mover.cpp | 109 ++- McZapkie/hamulce.cpp | 183 ++-- Model3d.cpp | 50 +- Model3d.h | 11 +- Train.cpp | 930 +++++--------------- Train.h | 58 +- World.cpp | 10 +- audio.cpp | 2 + audio.h | 2 +- audiorenderer.cpp | 129 ++- audiorenderer.h | 54 +- dumb3d.h | 2 +- renderer.cpp | 22 +- simulation.cpp | 4 +- sound.cpp | 288 +++++-- sound.h | 100 ++- 27 files changed, 2488 insertions(+), 1929 deletions(-) diff --git a/Button.cpp b/Button.cpp index c7b501a4..0636888b 100644 --- a/Button.cpp +++ b/Button.cpp @@ -13,6 +13,7 @@ http://mozilla.org/MPL/2.0/. #include "Model3d.h" #include "Console.h" #include "logs.h" +#include "renderer.h" void TButton::Clear(int i) { @@ -24,8 +25,8 @@ void TButton::Clear(int i) Update(); // kasowanie bitu Feedback, o ile jakiś ustawiony }; -void TButton::Init(std::string const &asName, TModel3d *pModel, bool bNewOn) -{ +void TButton::Init( std::string const &asName, TModel3d *pModel, bool bNewOn ) { + if( pModel == nullptr ) { return; } pModelOn = pModel->GetFromName( asName + "_on" ); @@ -34,7 +35,7 @@ void TButton::Init(std::string const &asName, TModel3d *pModel, bool bNewOn) Update(); }; -void TButton::Load(cParser &Parser, TModel3d *pModel1, TModel3d *pModel2) { +void TButton::Load( cParser &Parser, TDynamicObject const *Owner, TModel3d *pModel1, TModel3d *pModel2 ) { std::string submodelname; @@ -52,25 +53,32 @@ void TButton::Load(cParser &Parser, TModel3d *pModel1, TModel3d *pModel2) { } } + // bind defined sounds with the button owner + m_soundfxincrease.owner( Owner ); + m_soundfxdecrease.owner( Owner ); + if( pModel1 ) { // poszukiwanie submodeli w modelu Init( submodelname, pModel1, false ); - if( ( pModelOn != nullptr ) || ( pModelOff != nullptr ) ) { - // we got our models, bail out - return; - } } - if( pModel2 ) { + if( ( pModelOn == nullptr ) + && ( pModelOff == nullptr ) + && ( pModel2 != nullptr ) ) { // poszukiwanie submodeli w modelu Init( submodelname, pModel2, false ); - if( ( pModelOn != nullptr ) || ( pModelOff != nullptr ) ) { - // we got our models, bail out - return; - } } - // if we failed to locate even one state submodel, cry - ErrorLog( "Failed to locate sub-model \"" + submodelname + "\" in 3d model \"" + pModel1->NameGet() + "\"" ); -}; + + if( ( pModelOn == nullptr ) + && ( pModelOff == nullptr ) ) { + // if we failed to locate even one state submodel, cry + ErrorLog( "Bad model: failed to locate sub-model \"" + submodelname + "\" in 3d model \"" + pModel1->NameGet() + "\"" ); + } + + // pass submodel location to defined sounds + auto const offset { model_offset() }; + m_soundfxincrease.offset( offset ); + m_soundfxdecrease.offset( offset ); +} bool TButton::Load_mapping( cParser &Input ) { @@ -85,6 +93,22 @@ TButton::Load_mapping( cParser &Input ) { return true; // return value marks a key: value pair was extracted, nothing about whether it's recognized } +// returns offset of submodel associated with the button from the model centre +glm::vec3 +TButton::model_offset() const { + + auto const + submodel { ( + pModelOn ? pModelOn : + pModelOff ? pModelOff : + nullptr ) }; + + return ( + submodel != nullptr ? + submodel->offset( 1.f ) : + glm::vec3() ); +} + void TButton::Turn( bool const State ) { diff --git a/Button.h b/Button.h index 534bcd6d..5ac94579 100644 --- a/Button.h +++ b/Button.h @@ -12,18 +12,28 @@ http://mozilla.org/MPL/2.0/. #include "Classes.h" #include "sound.h" -class TButton -{ // animacja dwustanowa, włącza jeden z dwóch submodeli (jednego - // z nich może nie być) - private: - TSubModel - *pModelOn { nullptr }, - *pModelOff { nullptr }; // submodel dla stanu załączonego i wyłączonego - bool m_state { false }; - bool const *bData { nullptr }; - int iFeedbackBit { 0 }; // Ra: bit informacji zwrotnej, do wyprowadzenia na pulpit - sound_source m_soundfxincrease; // sound associated with increasing control's value - sound_source m_soundfxdecrease; // sound associated with decreasing control's value +// animacja dwustanowa, włącza jeden z dwóch submodeli (jednego z nich może nie być) +class TButton { + +public: +// methods + TButton() = default; + void Clear(int const i = -1); + inline void FeedbackBitSet( int const i ) { + iFeedbackBit = 1 << i; }; + void Turn( bool const State ); + inline + bool Active() { + return ( ( pModelOn != nullptr ) + || ( pModelOff != nullptr ) ); } + void Update(); + void Init( std::string const &asName, TModel3d *pModel, bool bNewOn = false ); + void Load( cParser &Parser, TDynamicObject const *Owner, TModel3d *pModel1, TModel3d *pModel2 = nullptr ); + void AssignBool(bool const *bValue); + // returns offset of submodel associated with the button from the model centre + glm::vec3 model_offset() const; + +private: // methods // imports member data pair from the config file bool @@ -32,20 +42,15 @@ class TButton void play(); - public: - TButton() = default; - void Clear(int const i = -1); - inline void FeedbackBitSet(int const i) { - iFeedbackBit = 1 << i; }; - void Turn( bool const State ); - inline - bool Active() { - return ( ( pModelOn != nullptr ) - || ( pModelOff != nullptr ) ); } - void Update(); - void Init(std::string const &asName, TModel3d *pModel, bool bNewOn = false); - void Load(cParser &Parser, TModel3d *pModel1, TModel3d *pModel2 = NULL); - void AssignBool(bool const *bValue); +// members + TSubModel + *pModelOn { nullptr }, + *pModelOff { nullptr }; // submodel dla stanu załączonego i wyłączonego + bool m_state { false }; + bool const *bData { nullptr }; + int iFeedbackBit { 0 }; // Ra: bit informacji zwrotnej, do wyprowadzenia na pulpit + sound_source m_soundfxincrease { sound_placement::internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE }; // sound associated with increasing control's value + sound_source m_soundfxdecrease { sound_placement::internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE }; // sound associated with decreasing control's value }; //--------------------------------------------------------------------------- diff --git a/Driver.cpp b/Driver.cpp index 75c1b2ba..24c9e81e 100644 --- a/Driver.cpp +++ b/Driver.cpp @@ -998,8 +998,11 @@ TCommandType TController::TableUpdate(double &fVelDes, double &fDist, double &fN if (go == cm_Unknown) // jeśli nie było komendy wcześniej go = cm_Ready; // gotów do odjazdu z W4 (semafor może // zatrzymać) - if (false == tsGuardSignal->empty()) // jeśli mamy głos kierownika, to odegrać + if( ( tsGuardSignal != nullptr ) + && ( false == tsGuardSignal->empty() ) ); { + // jeśli mamy głos kierownika, to odegrać iDrivigFlags |= moveGuardSignal; + } continue; // nie analizować prędkości } // koniec startu z zatrzymania } // koniec obsługi początkowych stacji @@ -2577,6 +2580,10 @@ bool TController::IncSpeed() if (tsGuardSignal->GetStatus() & DSBSTATUS_PLAYING) // jeśli gada, to nie jedziemy return false; #else + if( ( tsGuardSignal != nullptr ) + && ( true == tsGuardSignal->is_playing() ) ) { + return false; + } #endif bool OK = true; if( ( iDrivigFlags & moveDoorOpened ) @@ -2590,7 +2597,7 @@ bool TController::IncSpeed() } if( true == mvOccupied->DepartureSignal ) { // shut off departure warning - mvOccupied->DepartureSignal = false; + mvOccupied->signal_departure( false ); } if (mvControlling->SlippingWheels) return false; // jak poślizg, to nie przyspieszamy @@ -2984,7 +2991,7 @@ void TController::Doors(bool what) if( ( true == mvOccupied->DoorClosureWarning ) && ( false == mvOccupied->DepartureSignal ) && ( true == TestFlag( iDrivigFlags, moveDoorOpened ) ) ) { - mvOccupied->DepartureSignal = true; // załącenie bzyczka + mvOccupied->signal_departure( true ); // załącenie bzyczka fActionTime = -3.0 - 0.1 * Random( 10 ); // 3-4 second wait } if( fActionTime > -0.5 ) { @@ -3094,6 +3101,11 @@ bool TController::PutCommand( std::string NewCommand, double NewValue1, double N pVehicle->GetPosition().x, pVehicle->GetPosition().y, pVehicle->GetPosition().z, false); #else + tsGuardSignal = new sound_source( sound_placement::external ); + tsGuardSignal->deserialize( NewCommand, sound_type::single ); + tsGuardSignal->owner( pVehicle ); + // place virtual conductor some distance away + tsGuardSignal->offset( { pVehicle->MoverParameters->Dim.W * -0.75f, 1.7f, fLength * -0.25f } ); #endif iGuardRadio = 0; // nie przez radio } @@ -3109,6 +3121,12 @@ bool TController::PutCommand( std::string NewCommand, double NewValue1, double N pVehicle->GetPosition().x, pVehicle->GetPosition().y, pVehicle->GetPosition().z, false); #else + tsGuardSignal = new sound_source( sound_placement::external ); + tsGuardSignal->deserialize( NewCommand, sound_type::single ); + tsGuardSignal->owner( pVehicle ); + // command will be transmitted by radio + // TODO: put the exact location in the proper cab + tsGuardSignal->offset( { 0.f, 1.f, pVehicle->MoverParameters->Dim.L * 0.75f } ); #endif iGuardRadio = iRadioChannel; } @@ -4475,43 +4493,48 @@ TController::UpdateSituation(double dt) { // jeśli można jechać, to odpalić dźwięk kierownika oraz zamknąć drzwi w // składzie, jeśli nie mamy czekać na sygnał też trzeba odpalić if (iDrivigFlags & moveGuardSignal) - { // komunikat od kierownika tu, bo musi być wolna droga i odczekany czas - // stania + { // komunikat od kierownika tu, bo musi być wolna droga i odczekany czas stania iDrivigFlags &= ~moveGuardSignal; // tylko raz nadać - if( iDrivigFlags & moveDoorOpened ) // jeśli drzwi otwarte - if( !mvOccupied - ->DoorOpenCtrl ) // jeśli drzwi niesterowane przez maszynistę - Doors( false ); // a EZT zamknie dopiero po odegraniu komunikatu kierownika - -#ifdef EU07_USE_OLD_SOUNDCODE - tsGuardSignal->Stop(); -#else -#endif - // w zasadzie to powinien mieć flagę, czy jest dźwiękiem radiowym, czy - // bezpośrednim - // albo trzeba zrobić dwa dźwięki, jeden bezpośredni, słyszalny w - // pobliżu, a drugi radiowy, słyszalny w innych lokomotywach - // na razie zakładam, że to nie jest dźwięk radiowy, bo trzeba by zrobić - // obsługę kanałów radiowych itd. - if( !iGuardRadio ) { - // jeśli nie przez radio -#ifdef EU07_USE_OLD_SOUNDCODE - tsGuardSignal->Play( 1.0, 0, !FreeFlyModeFlag, pVehicle->GetPosition() ); // dla true jest głośniej -#else -#endif + if( ( iDrivigFlags & moveDoorOpened ) + && ( false == mvOccupied->DoorOpenCtrl ) ) { + // jeśli drzwi otwarte, niesterowane przez maszynistę + Doors( false ); // a EZT zamknie dopiero po odegraniu komunikatu kierownika } - else { - // if (iGuardRadio==iRadioChannel) //zgodność kanału - // if (!FreeFlyModeFlag) //obserwator musi być w środku pojazdu - // (albo może mieć radio przenośne) - kierownik mógłby powtarzać - // przy braku reakcji - if( SquareMagnitude( pVehicle->GetPosition() - Global::pCameraPosition ) < 2000 * 2000 ) { - // w odległości mniejszej niż 2km + if( tsGuardSignal != nullptr ) { #ifdef EU07_USE_OLD_SOUNDCODE - tsGuardSignal->Play( 1.0, 0, true, pVehicle->GetPosition() ); // dźwięk niby przez radio + tsGuardSignal->Stop(); #else + tsGuardSignal->stop(); #endif + // w zasadzie to powinien mieć flagę, czy jest dźwiękiem radiowym, czy + // bezpośrednim + // albo trzeba zrobić dwa dźwięki, jeden bezpośredni, słyszalny w + // pobliżu, a drugi radiowy, słyszalny w innych lokomotywach + // na razie zakładam, że to nie jest dźwięk radiowy, bo trzeba by zrobić + // obsługę kanałów radiowych itd. + if( !iGuardRadio ) { + // jeśli nie przez radio +#ifdef EU07_USE_OLD_SOUNDCODE + tsGuardSignal->Play( 1.0, 0, !FreeFlyModeFlag, pVehicle->GetPosition() ); // dla true jest głośniej +#else + tsGuardSignal->play( sound_flags::exclusive ); +#endif + } + else { + // if (iGuardRadio==iRadioChannel) //zgodność kanału + // if (!FreeFlyModeFlag) //obserwator musi być w środku pojazdu + // (albo może mieć radio przenośne) - kierownik mógłby powtarzać + // przy braku reakcji + if( SquareMagnitude( pVehicle->GetPosition() - Global::pCameraPosition ) < 2000 * 2000 ) { + // w odległości mniejszej niż 2km +#ifdef EU07_USE_OLD_SOUNDCODE + tsGuardSignal->Play( 1.0, 0, true, pVehicle->GetPosition() ); // dźwięk niby przez radio +#else + // TODO: proper system for sending/receiving radio messages + tsGuardSignal->play( sound_flags::exclusive ); +#endif + } } } } diff --git a/DynObj.cpp b/DynObj.cpp index c1846335..59941932 100644 --- a/DynObj.cpp +++ b/DynObj.cpp @@ -1218,8 +1218,9 @@ int TDynamicObject::DettachStatus(int dir) // rzeczywistych od strony (dir): // 0=przód,1=tył // Ra: dziwne, że ta funkcja nie jest używana - if (!MoverParameters->Couplers[dir].CouplingFlag) + if( MoverParameters->Couplers[ dir ].CouplingFlag == coupling::faux ) { return 0; // jeśli nic nie podłączone, to jest OK + } return (MoverParameters->DettachStatus(dir)); // czy jest w odpowiedniej odległości? } @@ -1243,10 +1244,154 @@ int TDynamicObject::Dettach(int dir) d = d->Next(); // i w drugą stronę } } - if (MoverParameters->Couplers[dir].CouplingFlag) // odczepianie, o ile coś podłączone - MoverParameters->Dettach(dir); - return MoverParameters->Couplers[dir] - .CouplingFlag; // sprzęg po rozłączaniu (czego się nie da odpiąć + if( MoverParameters->Couplers[ dir ].CouplingFlag ) { + // odczepianie, o ile coś podłączone + MoverParameters->Dettach( dir ); + } + // sprzęg po rozłączaniu (czego się nie da odpiąć + return MoverParameters->Couplers[dir].CouplingFlag; +} + +void +TDynamicObject::couple( int const Side ) { + + if( MoverParameters->Couplers[ Side ].Connected == nullptr ) { return; } + + if( MoverParameters->Couplers[ Side ].CouplingFlag == coupling::faux ) { + // najpierw hak + if( ( MoverParameters->Couplers[ Side ].Connected->Couplers[ Side ].AllowedFlag + & MoverParameters->Couplers[ Side ].AllowedFlag + & coupling::coupler ) == coupling::coupler ) { + if( MoverParameters->Attach( + Side, 2, + MoverParameters->Couplers[ Side ].Connected, + coupling::coupler ) ) { + // tmp->MoverParameters->Couplers[CouplNr].Render=true; //podłączony sprzęg będzie widoczny + m_couplersounds[ Side ].dsbCouplerAttach.play(); + // one coupling type per key press + return; + } + else { + WriteLog( "Mechanical coupling failed." ); + } + } + } + if( false == TestFlag( MoverParameters->Couplers[ Side ].CouplingFlag, coupling::brakehose ) ) { + // pneumatyka + if( ( MoverParameters->Couplers[ Side ].Connected->Couplers[ Side ].AllowedFlag + & MoverParameters->Couplers[ Side ].AllowedFlag + & coupling::brakehose ) == coupling::brakehose ) { + if( MoverParameters->Attach( + Side, 2, + MoverParameters->Couplers[ Side ].Connected, + ( MoverParameters->Couplers[ Side ].CouplingFlag | coupling::brakehose ) ) ) { + // TODO: dedicated sound for connecting cable-type connections + m_couplersounds[ Side ].dsbCouplerDetach.play(); + + SetPneumatic( Side != 0, true ); + if( Side == side::front ) { + PrevConnected->SetPneumatic( Side != 0, true ); + } + else { + NextConnected->SetPneumatic( Side != 0, true ); + } + // one coupling type per key press + return; + } + } + } + if( false == TestFlag( MoverParameters->Couplers[ Side ].CouplingFlag, coupling::mainhose ) ) { + // zasilajacy + if( ( MoverParameters->Couplers[ Side ].Connected->Couplers[ Side ].AllowedFlag + & MoverParameters->Couplers[ Side ].AllowedFlag + & coupling::mainhose ) == coupling::mainhose ) { + if( MoverParameters->Attach( + Side, 2, + MoverParameters->Couplers[ Side ].Connected, + ( MoverParameters->Couplers[ Side ].CouplingFlag | coupling::mainhose ) ) ) { + // TODO: dedicated sound for connecting cable-type connections + m_couplersounds[ Side ].dsbCouplerDetach.play(); + + SetPneumatic( Side != 0, false ); + if( Side == side::front ) { + PrevConnected->SetPneumatic( Side != 0, false ); + } + else { + NextConnected->SetPneumatic( Side != 0, false ); + } + // one coupling type per key press + return; + } + } + } + if( false == TestFlag( MoverParameters->Couplers[ Side ].CouplingFlag, coupling::control ) ) { + // ukrotnionko + if( ( MoverParameters->Couplers[ Side ].Connected->Couplers[ Side ].AllowedFlag + & MoverParameters->Couplers[ Side ].AllowedFlag + & coupling::control ) == coupling::control ) { + if( MoverParameters->Attach( + Side, 2, + MoverParameters->Couplers[ Side ].Connected, + ( MoverParameters->Couplers[ Side ].CouplingFlag | coupling::control ) ) ) { + // TODO: dedicated sound for connecting cable-type connections + m_couplersounds[ Side ].dsbCouplerAttach.play(); + // one coupling type per key press + return; + } + } + } + if( false == TestFlag( MoverParameters->Couplers[ Side ].CouplingFlag, coupling::gangway ) ) { + // mostek + if( ( MoverParameters->Couplers[ Side ].Connected->Couplers[ Side ].AllowedFlag + & MoverParameters->Couplers[ Side ].AllowedFlag + & coupling::gangway ) == coupling::gangway ) { + if( MoverParameters->Attach( + Side, 2, + MoverParameters->Couplers[ Side ].Connected, + ( MoverParameters->Couplers[ Side ].CouplingFlag | coupling::gangway ) ) ) { + // TODO: dedicated gangway sound + m_couplersounds[ Side ].dsbCouplerAttach.play(); + // one coupling type per key press + return; + } + } + } + if( false == TestFlag( MoverParameters->Couplers[ Side ].CouplingFlag, coupling::heating ) ) { + // heating + if( ( MoverParameters->Couplers[ Side ].Connected->Couplers[ Side ].AllowedFlag + & MoverParameters->Couplers[ Side ].AllowedFlag + & coupling::heating ) == coupling::heating ) { + if( MoverParameters->Attach( + Side, 2, + MoverParameters->Couplers[ Side ].Connected, + ( MoverParameters->Couplers[ Side ].CouplingFlag | coupling::heating ) ) ) { + + // TODO: dedicated 'click' sound for connecting cable-type connections + m_couplersounds[ Side ].dsbCouplerDetach.play(); + // one coupling type per key press + return; + } + } + } + +} + +int +TDynamicObject::uncouple( int const Side ) { + + if( ( DettachStatus( Side ) >= 0 ) + || ( true == TestFlag( MoverParameters->Couplers[ Side ].CouplingFlag, coupling::permanent ) ) ) { + // can't uncouple, return existing coupling state + return MoverParameters->Couplers[ Side ].CouplingFlag; + } + // jeżeli sprzęg niezablokowany, jest co odczepić i się da + auto const couplingflag { Dettach( Side ) }; + if( couplingflag == coupling::faux ) { + // dźwięk odczepiania + m_couplersounds[ Side ].dsbCouplerAttach.play(); + m_couplersounds[ Side ].dsbCouplerDetach.play(); + } + return couplingflag; } void TDynamicObject::CouplersDettach(double MinDist, int MyScanDir) { @@ -1466,7 +1611,8 @@ void TDynamicObject::ABuScanObjects( int Direction, double Distance ) } //----------ABu: koniec skanowania pojazdow -TDynamicObject::TDynamicObject() +TDynamicObject::TDynamicObject() : + rsStukot( MaxAxles, sound_source( sound_placement::external ) ) { modelShake = vector3(0, 0, 0); fTrackBlock = 10000.0; // brak przeszkody na drodze @@ -1839,58 +1985,63 @@ TDynamicObject::Init(std::string Name, // nazwa pojazdu, np. "EU07-424" create_controller( DriverType, !TrainName.empty() ); // McZapkie-250202 - iAxles = (MaxAxles < MoverParameters->NAxles) ? MaxAxles : MoverParameters->NAxles; // ilość osi + iAxles = std::min( MoverParameters->NAxles, MaxAxles ); // ilość osi + rsStukot.resize( iAxles, sound_source( sound_placement::external ) ); // wczytywanie z pliku nazwatypu.mmd, w tym model LoadMMediaFile(asBaseDir, Type_Name, asReplacableSkin); // McZapkie-100402: wyszukiwanie submodeli sprzegów - btCoupler1.Init("coupler1", mdModel, false); // false - ma być wyłączony - btCoupler2.Init("coupler2", mdModel, false); + btCoupler1.Init( "coupler1", mdModel, false ); // false - ma być wyłączony + btCoupler2.Init( "coupler2", mdModel, false); + // brake hoses btCPneumatic1.Init("cpneumatic1", mdModel); btCPneumatic2.Init("cpneumatic2", mdModel); btCPneumatic1r.Init("cpneumatic1r", mdModel); btCPneumatic2r.Init("cpneumatic2r", mdModel); + // main hoses btPneumatic1.Init("pneumatic1", mdModel); btPneumatic2.Init("pneumatic2", mdModel); btPneumatic1r.Init("pneumatic1r", mdModel); btPneumatic2r.Init("pneumatic2r", mdModel); - btCCtrl1.Init("cctrl1", mdModel, false); - btCCtrl2.Init("cctrl2", mdModel, false); - btCPass1.Init("cpass1", mdModel, false); - btCPass2.Init("cpass2", mdModel, false); + // control cables + btCCtrl1.Init( "cctrl1", mdModel, false); + btCCtrl2.Init( "cctrl2", mdModel, false); + // gangways + btCPass1.Init( "cpass1", mdModel, false); + btCPass2.Init( "cpass2", mdModel, false); // sygnaly // ABu 060205: Zmiany dla koncowek swiecacych: - btEndSignals11.Init("endsignal13", mdModel, false); - btEndSignals21.Init("endsignal23", mdModel, false); - btEndSignals13.Init("endsignal12", mdModel, false); - btEndSignals23.Init("endsignal22", mdModel, false); + btEndSignals11.Init( "endsignal13", mdModel, false); + btEndSignals21.Init( "endsignal23", mdModel, false); + btEndSignals13.Init( "endsignal12", mdModel, false); + btEndSignals23.Init( "endsignal22", mdModel, false); iInventory[ side::front ] |= btEndSignals11.Active() ? light::redmarker_left : 0; // informacja, czy ma poszczególne światła iInventory[ side::front ] |= btEndSignals13.Active() ? light::redmarker_right : 0; iInventory[ side::rear ] |= btEndSignals21.Active() ? light::redmarker_left : 0; iInventory[ side::rear ] |= btEndSignals23.Active() ? light::redmarker_right : 0; // ABu: to niestety zostawione dla kompatybilnosci modeli: - btEndSignals1.Init("endsignals1", mdModel, false); - btEndSignals2.Init("endsignals2", mdModel, false); - btEndSignalsTab1.Init("endtab1", mdModel, false); - btEndSignalsTab2.Init("endtab2", mdModel, false); + btEndSignals1.Init( "endsignals1", mdModel, false); + btEndSignals2.Init( "endsignals2", mdModel, false); + btEndSignalsTab1.Init( "endtab1", mdModel, false); + btEndSignalsTab2.Init( "endtab2", mdModel, false); iInventory[ side::front ] |= btEndSignals1.Active() ? ( light::redmarker_left | light::redmarker_right ) : 0; iInventory[ side::front ] |= btEndSignalsTab1.Active() ? light::rearendsignals : 0; // tabliczki blaszane iInventory[ side::rear ] |= btEndSignals2.Active() ? ( light::redmarker_left | light::redmarker_right ) : 0; iInventory[ side::rear ] |= btEndSignalsTab2.Active() ? light::rearendsignals : 0; // ABu Uwaga! tu zmienic w modelu! - btHeadSignals11.Init("headlamp13", mdModel, false); // lewe - btHeadSignals12.Init("headlamp11", mdModel, false); // górne - btHeadSignals13.Init("headlamp12", mdModel, false); // prawe - btHeadSignals21.Init("headlamp23", mdModel, false); - btHeadSignals22.Init("headlamp21", mdModel, false); - btHeadSignals23.Init("headlamp22", mdModel, false); + btHeadSignals11.Init( "headlamp13", mdModel, false); // lewe + btHeadSignals12.Init( "headlamp11", mdModel, false); // górne + btHeadSignals13.Init( "headlamp12", mdModel, false); // prawe + btHeadSignals21.Init( "headlamp23", mdModel, false); + btHeadSignals22.Init( "headlamp21", mdModel, false); + btHeadSignals23.Init( "headlamp22", mdModel, false); iInventory[ side::front ] |= btHeadSignals11.Active() ? light::headlight_left : 0; iInventory[ side::front ] |= btHeadSignals12.Active() ? light::headlight_upper : 0; iInventory[ side::front ] |= btHeadSignals13.Active() ? light::headlight_right : 0; iInventory[ side::rear ] |= btHeadSignals21.Active() ? light::headlight_left : 0; iInventory[ side::rear ] |= btHeadSignals22.Active() ? light::headlight_upper : 0; iInventory[ side::rear ] |= btHeadSignals23.Active() ? light::headlight_right : 0; - btMechanik1.Init("mechanik1", mdLowPolyInt, false); - btMechanik2.Init("mechanik2", mdLowPolyInt, false); + btMechanik1.Init( "mechanik1", mdLowPolyInt, false); + btMechanik2.Init( "mechanik2", mdLowPolyInt, false); TurnOff(); // resetowanie zmiennych submodeli if( mdLowPolyInt != nullptr ) { @@ -1929,23 +2080,26 @@ TDynamicObject::Init(std::string Name, // nazwa pojazdu, np. "EU07-424" } } // wyszukiwanie zderzakow - if (mdModel) // jeśli ma w czym szukać - for (int i = 0; i < 2; i++) - { - asAnimName = std::string("buffer_left0") + to_string(i + 1); - smBuforLewy[i] = mdModel->GetFromName(asAnimName); - if (smBuforLewy[i]) - smBuforLewy[i]->WillBeAnimated(); // ustawienie flagi animacji - asAnimName = std::string("buffer_right0") + to_string(i + 1); - smBuforPrawy[i] = mdModel->GetFromName(asAnimName); - if (smBuforPrawy[i]) - smBuforPrawy[i]->WillBeAnimated(); + if( mdModel ) { + // jeśli ma w czym szukać + for( int i = 0; i < 2; i++ ) { + asAnimName = std::string( "buffer_left0" ) + to_string( i + 1 ); + smBuforLewy[ i ] = mdModel->GetFromName( asAnimName ); + if( smBuforLewy[ i ] ) + smBuforLewy[ i ]->WillBeAnimated(); // ustawienie flagi animacji + asAnimName = std::string( "buffer_right0" ) + to_string( i + 1 ); + smBuforPrawy[ i ] = mdModel->GetFromName( asAnimName ); + if( smBuforPrawy[ i ] ) + smBuforPrawy[ i ]->WillBeAnimated(); } - for (int i = 0; i < iAxles; i++) // wyszukiwanie osi (0 jest na końcu, dlatego dodajemy - // długość?) - dRailPosition[i] = - (Reversed ? -dWheelsPosition[i] : (dWheelsPosition[i] + MoverParameters->Dim.L)) + - fDist; + } + for( int i = 0; i < iAxles; i++ ) { + // wyszukiwanie osi (0 jest na końcu, dlatego dodajemy długość?) + dRailPosition[ i ] = ( + Reversed ? + -dWheelsPosition[ i ] : + ( dWheelsPosition[ i ] + MoverParameters->Dim.L ) ) + fDist; + } // McZapkie-250202 end. Track->AddDynamicObject(this); // wstawiamy do toru na pozycję 0, a potem przesuniemy // McZapkie: zmieniono na ilosc osi brane z chk @@ -1971,9 +2125,8 @@ TDynamicObject::Init(std::string Name, // nazwa pojazdu, np. "EU07-424" fDist -= 0.5 * MoverParameters->Dim.L; // dodajemy pół długości pojazdu, bo // ustawiamy jego środek (zliczanie na // minus) - switch (iNumAxles) - { // Ra: pojazdy wstawiane są na tor początkowy, a potem - // przesuwane + switch (iNumAxles) { + // Ra: pojazdy wstawiane są na tor początkowy, a potem przesuwane case 2: // ustawianie osi na torze Axle0.Init(Track, this, iDirection ? 1 : -1); Axle0.Move((iDirection ? fDist : -fDist) + fAxleDistHalf, false); @@ -1998,12 +2151,10 @@ TDynamicObject::Init(std::string Name, // nazwa pojazdu, np. "EU07-424" // Axle3.Move((iDirection?fDist:-fDist)+(fAxleDistHalf-MoverParameters->ADist*0.5),false); break; } - Move(0.0001); // potrzebne do wyliczenia aktualnej pozycji; nie może być zero, - // bo nie przeliczy - // pozycji - // teraz jeszcze trzeba przypisać pojazdy do nowego toru, bo przesuwanie - // początkowe osi nie + // potrzebne do wyliczenia aktualnej pozycji; nie może być zero, bo nie przeliczy pozycji + // teraz jeszcze trzeba przypisać pojazdy do nowego toru, bo przesuwanie początkowe osi nie // zrobiło tego + Move( 0.0001 ); ABuCheckMyTrack(); // zmiana toru na ten, co oś Axle0 (oś z przodu) TLocation loc; // Ra: ustawienie pozycji do obliczania sprzęgów loc.X = -vPosition.x; @@ -2029,6 +2180,7 @@ TDynamicObject::Init(std::string Name, // nazwa pojazdu, np. "EU07-424" MoverParameters->ComputeConstans(); // wektor podłogi dla wagonów, przesuwa ładunek vFloor = vector3(0, 0, MoverParameters->Floor); + // długość większa od zera oznacza OK; 2mm docisku? return MoverParameters->Dim.L; } @@ -2260,13 +2412,10 @@ void TDynamicObject::AttachPrev(TDynamicObject *Object, int iType) loc.Z=Object->vPosition.y; Object->MoverParameters->Loc=loc; //ustawienie dodawanego pojazdu */ - MoverParameters->Attach(iDirection, Object->iDirection ^ 1, Object->MoverParameters, iType, - true); + MoverParameters->Attach(iDirection, Object->iDirection ^ 1, Object->MoverParameters, iType, true); MoverParameters->Couplers[iDirection].Render = false; - Object->MoverParameters->Attach(Object->iDirection ^ 1, iDirection, MoverParameters, iType, - true); - Object->MoverParameters->Couplers[Object->iDirection ^ 1].Render = - true; // rysowanie sprzęgu w dołączanym + Object->MoverParameters->Attach(Object->iDirection ^ 1, iDirection, MoverParameters, iType, true); + Object->MoverParameters->Couplers[Object->iDirection ^ 1].Render = true; // rysowanie sprzęgu w dołączanym if (iDirection) { //łączenie standardowe NextConnected = Object; // normalnie doczepiamy go sobie do sprzęgu 1 @@ -2310,9 +2459,10 @@ bool TDynamicObject::UpdateForce(double dt, double dt1, bool FullVer) { if (!bEnabled) return false; - if (dt > 0) - MoverParameters->ComputeTotalForce(dt, dt1, - FullVer); // wywalenie WS zależy od ustawienia kierunku + if( dt > 0 ) { + // wywalenie WS zależy od ustawienia kierunku + MoverParameters->ComputeTotalForce( dt, dt1, FullVer ); + } return true; } @@ -2895,9 +3045,7 @@ bool TDynamicObject::Update(double dt, double dt1) } // McZapkie-260202 - dMoveLen przyda sie przy stukocie kol - dDOMoveLen = - GetdMoveLen() + MoverParameters->ComputeMovement(dt, dt1, ts, tp, tmpTraction, l, r); - // if (dDOMoveLen!=0.0) //Ra: nie może być, bo blokuje Event0 + dDOMoveLen = GetdMoveLen() + MoverParameters->ComputeMovement(dt, dt1, ts, tp, tmpTraction, l, r); if( Mechanik ) Mechanik->MoveDistanceAdd( dDOMoveLen ); // dodanie aktualnego przemieszczenia Move(dDOMoveLen); @@ -2909,65 +3057,52 @@ bool TDynamicObject::Update(double dt, double dt1) Global::ABuDebug = dDOMoveLen / dt1; ResetdMoveLen(); -#ifdef EU07_USE_OLD_SOUNDCODE // McZapkie-260202 // tupot mew, tfu, stukot kol: - // taka prowizorka zeby sciszyc stukot dalekiej lokomotywy - double ObjectDist; - double vol = 0; - ObjectDist = SquareMagnitude(Global::pCameraPosition - vPosition); - // McZapkie-270202 - if (MyTrack->fSoundDistance != -1) - { - if (ObjectDist < rsStukot[0].dSoundAtt * rsStukot[0].dSoundAtt * 15.0) - { - vol = (20.0 + MyTrack->iDamageFlag) / 21; - if (MyTrack->eEnvironment == e_tunnel) - { - vol *= 1.1; - // freq=1.02; + if( MyTrack->fSoundDistance != -1 ) { + + if( MyTrack->fSoundDistance != dRailLength ) { + dRailLength = MyTrack->fSoundDistance; + for( int i = 0; i < iAxles; ++i ) { + dRailPosition[ i ] = dWheelsPosition[ i ] + MoverParameters->Dim.L; } - else if (MyTrack->eEnvironment == e_bridge) - { - vol *= 1.2; - // freq=0.99; //MC: stukot w zaleznosci od - // tego gdzie - // jest tor - } - if (MyTrack->fSoundDistance != dRailLength) - { - dRailLength = MyTrack->fSoundDistance; - for (int i = 0; i < iAxles; i++) - { - dRailPosition[i] = dWheelsPosition[i] + MoverParameters->Dim.L; + } + if( dRailLength != -1 ) { + if( std::abs( MoverParameters->V ) > 0 ) { + + double volume = ( 20.0 + MyTrack->iDamageFlag ) / 21; + switch( MyTrack->eEnvironment ) { + case e_tunnel: { + volume *= 1.1; + break; + } + case e_bridge: { + volume *= 1.2; + break; + } + default: { + break; + } } - } - if (dRailLength != -1) - { - if (abs(MoverParameters->V) > 0) - { - for (int i = 0; i < iAxles; i++) - { - dRailPosition[i] -= dDOMoveLen * Sign(dDOMoveLen); - if (dRailPosition[i] < 0) - { - // McZapkie-040302 - if (i == iAxles - 1) - { - rsStukot[0].Stop(); - MoverParameters->AccV += - 0.5 * GetVelocity() / (1 + MoverParameters->Vmax); - } - else - { - rsStukot[i + 1].Stop(); - } - rsStukot[i].Play(vol, 0, MechInside, vPosition); // poprawic pozycje o uklad osi - if (i == 1) - MoverParameters->AccV -= - 0.5 * GetVelocity() / (1 + MoverParameters->Vmax); - dRailPosition[i] += dRailLength; + + for( int i = 0; i < iAxles; ++i ) { + dRailPosition[ i ] -= dDOMoveLen * Sign( dDOMoveLen ); + if( dRailPosition[ i ] < 0 ) { +/* + // McZapkie-040302 + if( i == iAxles - 1 ) { + rsStukot[ 0 ].stop(); + MoverParameters->AccV += 0.5 * GetVelocity() / ( 1 + MoverParameters->Vmax ); } + else { + rsStukot[ i + 1 ].stop(); + } + if( i == 1 ) { + MoverParameters->AccV -= 0.5 * GetVelocity() / ( 1 + MoverParameters->Vmax ); + } +*/ + rsStukot[ i ].gain( volume ).play(); + dRailPosition[ i ] += dRailLength; } } } @@ -2975,32 +3110,6 @@ bool TDynamicObject::Update(double dt, double dt1) } // McZapkie-260202 end - // yB: przyspieszacz (moze zadziala, ale dzwiek juz jest) - int flag = MoverParameters->Hamulec->GetSoundFlag(); - if ((bBrakeAcc) && (TestFlag(flag, sf_Acc)) && (ObjectDist < 2500)) - { - sBrakeAcc->SetVolume(-ObjectDist * 3 - (FreeFlyModeFlag ? 0 : 2000)); - sBrakeAcc->Play(0, 0, 0); - sBrakeAcc->SetPan(10000 * sin(ModCamRot)); - } - if ((rsUnbrake.AM != 0) && (ObjectDist < 5000)) - { - if ((TestFlag(flag, sf_CylU)) && - ((MoverParameters->BrakePress * MoverParameters->MaxBrakePress[3]) > 0.05)) - { - vol = Min0R( - 0.2 + - 1.6 * sqrt((MoverParameters->BrakePress > 0 ? MoverParameters->BrakePress : 0) / - MoverParameters->MaxBrakePress[3]), - 1); - vol = vol + (FreeFlyModeFlag ? 0 : -0.5) - ObjectDist / 5000; - rsUnbrake.SetPan(10000 * sin(ModCamRot)); - rsUnbrake.Play(vol, DSBPLAY_LOOPING, MechInside, GetPosition()); - } - else - rsUnbrake.Stop(); - } -#endif // fragment z EXE Kursa /* if (MoverParameters->TrainType==dt_ET42) { @@ -3112,11 +3221,12 @@ bool TDynamicObject::Update(double dt, double dt1) { if( ( MoverParameters->PantFrontVolt == 0.0 ) && ( MoverParameters->PantRearVolt == 0.0 ) ) { -#ifdef EU07_USE_OLD_SOUNDCODE - sPantUp.Play( vol, 0, MechInside, vPosition ); -#else - sPantUp.play(); -#endif + for( auto &pantograph : m_pantographsounds ) { + if( pantograph.sPantUp.offset().z > 0 ) { + // limit to pantographs located in the front half of the vehicle + pantograph.sPantUp.play( sound_flags::exclusive ); + } + } } if (p->hvPowerWire) { // TODO: wyliczyć trzeba prąd przypadający na pantograf i wstawić do GetVoltage() @@ -3144,11 +3254,12 @@ bool TDynamicObject::Update(double dt, double dt1) { if( ( MoverParameters->PantRearVolt == 0.0 ) && ( MoverParameters->PantFrontVolt == 0.0 ) ) { -#ifdef EU07_USE_OLD_SOUNDCODE - sPantUp.Play( vol, 0, MechInside, vPosition ); -#else - sPantUp.play(); -#endif + for( auto &pantograph : m_pantographsounds ) { + if( pantograph.sPantUp.offset().z < 0 ) { + // limit to pantographs located in the rear half of the vehicle + pantograph.sPantUp.play( sound_flags::exclusive ); + } + } } if (p->hvPowerWire) { // TODO: wyliczyć trzeba prąd przypadający na pantograf i wstawić do GetVoltage() @@ -3235,20 +3346,22 @@ bool TDynamicObject::Update(double dt, double dt1) } // koniec pętli po pantografach if ((MoverParameters->PantFrontSP == false) && (MoverParameters->PantFrontUp == false)) { -#ifdef EU07_USE_OLD_SOUNDCODE - sPantDown.Play(vol, 0, MechInside, vPosition); -#else - sPantDown.play(); -#endif + for( auto &pantograph : m_pantographsounds ) { + if( pantograph.sPantDown.offset().z > 0 ) { + // limit to pantographs located in the front half of the vehicle + pantograph.sPantDown.play( sound_flags::exclusive ); + } + } MoverParameters->PantFrontSP = true; } if ((MoverParameters->PantRearSP == false) && (MoverParameters->PantRearUp == false)) { -#ifdef EU07_USE_OLD_SOUNDCODE - sPantDown.Play(vol, 0, MechInside, vPosition); -#else - sPantDown.play(); -#endif + for( auto &pantograph : m_pantographsounds ) { + if( pantograph.sPantDown.offset().z < 0 ) { + // limit to pantographs located in the rear half of the vehicle + pantograph.sPantDown.play( sound_flags::exclusive ); + } + } MoverParameters->PantRearSP = true; } /* @@ -3349,59 +3462,29 @@ bool TDynamicObject::Update(double dt, double dt1) } // NBMX Obsluga drzwi, MC: zuniwersalnione - if ((dDoorMoveL < MoverParameters->DoorMaxShiftL) && (MoverParameters->DoorLeftOpened)) - { -#ifdef EU07_USE_OLD_SOUNDCODE - rsDoorOpen.Play(1, 0, MechInside, vPosition); -#else - rsDoorClose.stop(); - rsDoorOpen.play(); -#endif - dDoorMoveL += dt1 * 0.5 * MoverParameters->DoorOpenSpeed; - } - if ((dDoorMoveL > 0) && (!MoverParameters->DoorLeftOpened)) - { -#ifdef EU07_USE_OLD_SOUNDCODE - rsDoorClose.Play(1, 0, MechInside, vPosition); -#else - rsDoorOpen.stop(); - rsDoorClose.play(); -#endif - dDoorMoveL -= dt1 * MoverParameters->DoorCloseSpeed; - if (dDoorMoveL < 0) - dDoorMoveL = 0; + if( ( dDoorMoveL < MoverParameters->DoorMaxShiftL ) + && ( true == MoverParameters->DoorLeftOpened ) ) { + dDoorMoveL += dt1 * MoverParameters->DoorOpenSpeed; + dDoorMoveL = std::min( dDoorMoveL, MoverParameters->DoorMaxShiftL ); } - if ((dDoorMoveR < MoverParameters->DoorMaxShiftR) && (MoverParameters->DoorRightOpened)) - { -#ifdef EU07_USE_OLD_SOUNDCODE - rsDoorOpen.Play(1, 0, MechInside, vPosition); -#else - rsDoorClose.stop(); - rsDoorOpen.play(); -#endif - dDoorMoveR += dt1 * 0.5 * MoverParameters->DoorOpenSpeed; + if( ( dDoorMoveL > 0 ) + && ( false == MoverParameters->DoorLeftOpened ) ) { + dDoorMoveL -= dt1 * MoverParameters->DoorCloseSpeed; + dDoorMoveL = std::max( dDoorMoveL, 0.0 ); + } + if( ( dDoorMoveR < MoverParameters->DoorMaxShiftR ) + && ( true == MoverParameters->DoorRightOpened ) ) { + dDoorMoveR += dt1 * MoverParameters->DoorOpenSpeed; + dDoorMoveR = std::min( dDoorMoveR, MoverParameters->DoorMaxShiftR ); } - if ((dDoorMoveR > 0) && (!MoverParameters->DoorRightOpened)) - { -#ifdef EU07_USE_OLD_SOUNDCODE - rsDoorClose.Play(1, 0, MechInside, vPosition); -#else - rsDoorOpen.stop(); - rsDoorClose.play(); -#endif + if( ( dDoorMoveR > 0 ) + && ( false == MoverParameters->DoorRightOpened ) ) { dDoorMoveR -= dt1 * MoverParameters->DoorCloseSpeed; - if (dDoorMoveR < 0) - dDoorMoveR = 0; + dDoorMoveR = std::max( dDoorMoveR, 0.0 ); } // compartment lights -/* - if( ( ctOwner != nullptr ? - ctOwner->Controlling()->Battery != SectionLightsActive : - MoverParameters->Battery != SectionLightsActive ) ) { - // if the vehicle has a controller, we base the light state on state of the controller otherwise we check the vehicle itself -*/ - // the version above won't work as the vehicles don't turn batteries off :| + // if the vehicle has a controller, we base the light state on state of the controller otherwise we check the vehicle itself if( ( ctOwner != nullptr ? ctOwner->Controlling()->Battery != SectionLightsActive : SectionLightsActive == true ) ) { // without controller lights are off. NOTE: this likely mess up the EMU @@ -3516,241 +3599,147 @@ void TDynamicObject::TurnOff() // przeliczanie dźwięków, bo będzie słychać bez wyświetlania sektora z pojazdem void TDynamicObject::RenderSounds() { -#ifdef EU07_USE_OLD_SOUNDCODE - // McZapkie-010302: ulepszony dzwiek silnika - double freq; - double vol = 0; - double dt = Timer::GetDeltaRenderTime(); - if (MoverParameters->Power > 0) - { - if ((rsSilnik.AM != 0) - && ((MoverParameters->Mains) - // McZapkie-280503: zeby dla dumb dzialal silnik na jalowych obrotach - || (MoverParameters->EngineType == DieselEngine))) - { - if ((fabs(MoverParameters->enrot) > 0.01) || - (MoverParameters->EngineType == Dumb)) //&& (MoverParameters->EnginePower>0.1)) - { - freq = rsSilnik.FM * fabs(MoverParameters->enrot) + rsSilnik.FA; - if (MoverParameters->EngineType == Dumb) - freq = freq - 0.2 * MoverParameters->EnginePower / (1 + MoverParameters->Power * 1000); - rsSilnik.AdjFreq(freq, dt); - if (MoverParameters->EngineType == DieselEngine) - { - if (MoverParameters->enrot > 0) - { - if (MoverParameters->EnginePower > 0) - vol = rsSilnik.AM * MoverParameters->dizel_fill + rsSilnik.AA; - else - vol = - rsSilnik.AM * fabs(MoverParameters->enrot / MoverParameters->dizel_nmax) + - rsSilnik.AA * 0.9; + // McZapkie-010302: ulepszony dzwiek silnika + double frequency { 1.0 }; + double volume { 1.0 }; + double const dt { Timer::GetDeltaRenderTime() }; + + // engine sounds + if( MoverParameters->Power > 0 ) { + + if( ( true == MoverParameters->Mains ) + || ( MoverParameters->EngineType == DieselEngine ) ) { + + if( ( std::fabs( MoverParameters->enrot ) > 0.01 ) + // McZapkie-280503: zeby dla dumb dzialal silnik na jalowych obrotach + || ( MoverParameters->EngineType == Dumb ) ) { + + // frequency calculation + frequency = rsSilnik.m_frequencyfactor * std::fabs( MoverParameters->enrot ) + rsSilnik.m_frequencyoffset; + if( MoverParameters->EngineType == Dumb ) { + frequency -= 0.2 * MoverParameters->EnginePower / ( 1 + MoverParameters->Power * 1000 ); + } + + // base volume calculation + switch( MoverParameters->EngineType ) { + case DieselEngine: { + if( MoverParameters->enrot > 0.0 ) { + if( MoverParameters->EnginePower > 0 ) { + volume = rsSilnik.m_amplitudefactor * MoverParameters->dizel_fill + rsSilnik.m_amplitudeoffset; + } + else { + volume = rsSilnik.m_amplitudefactor * std::fabs( MoverParameters->enrot / MoverParameters->dizel_nmax ) + rsSilnik.m_amplitudeoffset * 0.9f; + } + } + else { + volume = 0.f; + } + break; + } + case DieselElectric: { + volume = + rsSilnik.m_amplitudefactor * ( MoverParameters->EnginePower / 1000 / MoverParameters->Power ) + + 0.2 * ( MoverParameters->enrot * 60 ) / ( MoverParameters->DElist[ MoverParameters->MainCtrlPosNo ].RPM ) + + rsSilnik.m_amplitudeoffset; + break; + } + case ElectricInductionMotor: { + volume = + rsSilnik.m_amplitudefactor * ( MoverParameters->EnginePower + std::fabs( MoverParameters->enrot * 2 ) ) + + rsSilnik.m_amplitudeoffset; + break; + } + default: { + volume = + rsSilnik.m_amplitudefactor * ( MoverParameters->EnginePower / 1000 + std::fabs( MoverParameters->enrot ) * 60.0 ) + + rsSilnik.m_amplitudeoffset; + break; } - else - vol = 0; } - else if (MoverParameters->EngineType == DieselElectric) - vol = rsSilnik.AM * - (MoverParameters->EnginePower / 1000 / MoverParameters->Power) + - 0.2 * (MoverParameters->enrot * 60) / - (MoverParameters->DElist[MoverParameters->MainCtrlPosNo].RPM) + - rsSilnik.AA; - else if (MoverParameters->EngineType == ElectricInductionMotor) - vol = rsSilnik.AM * - (MoverParameters->EnginePower + fabs(MoverParameters->enrot * 2)) + - rsSilnik.AA; - else - vol = rsSilnik.AM * (MoverParameters->EnginePower / 1000 + - fabs(MoverParameters->enrot) * 60.0) + - rsSilnik.AA; - // McZapkie-250302 - natezenie zalezne od obrotow i mocy - if ((vol < 1) && (MoverParameters->EngineType == ElectricSeriesMotor) && - (MoverParameters->EnginePower < 100)) - { - float volrnd = - Random(100) * MoverParameters->enrot / (1 + MoverParameters->nmax); - if (volrnd < 2) - vol = vol + volrnd / 200.0; + + if( MoverParameters->EngineType == ElectricSeriesMotor ) { + + // volume variation + if( ( volume < 1.0 ) + && ( MoverParameters->EnginePower < 100 ) ) { + + auto const volumevariation{ Random( 100 ) * MoverParameters->enrot / ( 1 + MoverParameters->nmax ) }; + if( volumevariation < 2 ) { + volume += volumevariation / 200; + } + } + + if( ( MoverParameters->DynamicBrakeFlag ) + && ( MoverParameters->EnginePower > 0.1 ) ) { + // Szociu - 29012012 - jeżeli uruchomiony jest hamulec elektrodynamiczny, odtwarzany jest dźwięk silnika + volume += 0.8; + } } + + // volume environmental factor switch( MyTrack->eEnvironment ) { case e_tunnel: { - vol += 0.1; + volume += 0.1; break; } case e_canyon: { - vol += 0.05; + volume += 0.05; break; } default: { break; } } - if( ( MoverParameters->DynamicBrakeFlag ) - && ( MoverParameters->EnginePower > 0.1 ) - && ( MoverParameters->EngineType == ElectricSeriesMotor ) ) - // Szociu - 29012012 - jeżeli uruchomiony jest hamulec elektrodynamiczny, odtwarzany jest dźwięk silnika - vol += 0.8; - if (enginevolume > 0.0001) - if (MoverParameters->EngineType != DieselElectric) - { - rsSilnik.Play(enginevolume, DSBPLAY_LOOPING, MechInside, GetPosition()); + if( enginevolume >= 0.05 ) { + + if( MoverParameters->EngineType != DieselElectric ) { + + rsSilnik + .pitch( frequency ) + .gain( enginevolume ) + .play( sound_flags::exclusive | sound_flags::looping ); } - else - { - sConverter.UpdateAF(vol, freq, MechInside, GetPosition()); + else { - float fincvol; - fincvol = 0; - if ((MoverParameters->ConverterFlag) && - (MoverParameters->enrot * 60 > MoverParameters->DElist[0].RPM)) - { - fincvol = (MoverParameters->DElist[MoverParameters->MainCtrlPos].RPM - - (MoverParameters->enrot * 60)); - fincvol /= (0.05 * MoverParameters->DElist[0].RPM); + sConverter + .pitch( frequency ) + .gain( volume ) + .play( sound_flags::exclusive | sound_flags::looping ); + + float fincvol { 0 }; + if( ( MoverParameters->ConverterFlag ) + && ( MoverParameters->enrot * 60 > MoverParameters->DElist[ 0 ].RPM ) ) { + + fincvol = ( MoverParameters->DElist[ MoverParameters->MainCtrlPos ].RPM - ( MoverParameters->enrot * 60 ) ); + fincvol /= ( 0.05 * MoverParameters->DElist[ 0 ].RPM ); }; - if (fincvol > 0.02) - rsDiesielInc.Play(fincvol, DSBPLAY_LOOPING, MechInside, GetPosition()); - else - rsDiesielInc.Stop(); + if( fincvol > 0.02 ) { + rsDieselInc + .gain( fincvol ) + .play( sound_flags::exclusive | sound_flags::looping ); + } + else { + rsDieselInc.stop(); + } } - } - else - rsSilnik.Stop(); - } - enginevolume = (enginevolume + vol) * 0.5; - if( enginevolume < 0.01 ) { - rsSilnik.Stop(); - } - if ( ( MoverParameters->EngineType == ElectricSeriesMotor ) - || ( MoverParameters->EngineType == ElectricInductionMotor ) - && ( rsWentylator.AM != 0 ) ) - { - if (MoverParameters->RventRot > 0.1) { - // play ventilator sound if the ventilators are rotating fast enough... - freq = rsWentylator.FM * MoverParameters->RventRot + rsWentylator.FA; - rsWentylator.AdjFreq(freq, dt); - if( MoverParameters->EngineType == ElectricInductionMotor ) { - - vol = rsWentylator.AM * std::sqrt( std::fabs( MoverParameters->dizel_fill ) ) + rsWentylator.AA; } - else { - - vol = rsWentylator.AM * MoverParameters->RventRot + rsWentylator.AA; - } - rsWentylator.Play(vol, DSBPLAY_LOOPING, MechInside, GetPosition()); } else { - // ...otherwise shut down the sound - rsWentylator.Stop(); + rsSilnik.stop(); } } - if (MoverParameters->TrainType == dt_ET40) - { - if (MoverParameters->Vel > 0.1) - { - freq = rsPrzekladnia.FM * (MoverParameters->Vel) + rsPrzekladnia.FA; - rsPrzekladnia.AdjFreq(freq, dt); - vol = rsPrzekladnia.AM * (MoverParameters->Vel) + rsPrzekladnia.AA; - rsPrzekladnia.Play(vol, DSBPLAY_LOOPING, MechInside, GetPosition()); - } - else - rsPrzekladnia.Stop(); + enginevolume = ( enginevolume + volume ) * 0.5; + if( enginevolume < 0.05 ) { + rsSilnik.stop(); } - } - - // youBy: dzwiek ostrych lukow i ciasnych zwrotek - - if ((ts.R * ts.R > 1) && (MoverParameters->Vel > 0)) - vol = MoverParameters->AccN * MoverParameters->AccN; - else - vol = 0; - // vol+=(50000/ts.R*ts.R); - - if (vol > 0.001) - { - rscurve.Play(2 * vol, DSBPLAY_LOOPING, MechInside, GetPosition()); - } - else - rscurve.Stop(); - - // McZapkie-280302 - pisk mocno zacisnietych hamulcow - trzeba jeszcze - // zabezpieczyc przed - // brakiem deklaracji w mmedia.dta - if (rsPisk.AM != 0) - { - if ((MoverParameters->Vel > (rsPisk.GetStatus() != 0 ? 0.01 : 0.5)) && - (!MoverParameters->SlippingWheels) && (MoverParameters->UnitBrakeForce > rsPisk.AM)) - { - vol = MoverParameters->UnitBrakeForce / (rsPisk.AM + 1) + rsPisk.AA; - rsPisk.Play(vol, DSBPLAY_LOOPING, MechInside, GetPosition()); - } - else - rsPisk.Stop(); - } - - if (MoverParameters->SandDose) // Dzwiek piasecznicy - sSand.TurnOn(MechInside, GetPosition()); - else - sSand.TurnOff(MechInside, GetPosition()); - sSand.Update(MechInside, GetPosition()); - if (MoverParameters->Hamulec->GetStatus() & b_rls) // Dzwiek odluzniacza - sReleaser.TurnOn(MechInside, GetPosition()); - else - sReleaser.TurnOff(MechInside, GetPosition()); - //sReleaser.Update(MechInside, GetPosition()); - double releaser_vol = 1; - if (MoverParameters->BrakePress < 0.1) - releaser_vol = MoverParameters->BrakePress * 10; - sReleaser.UpdateAF(releaser_vol, 1, MechInside, GetPosition()); - - // McZapkie! - dzwiek compressor.wav tylko gdy dziala sprezarka - if (MoverParameters->VeselVolume != 0) - { - if (MoverParameters->CompressorFlag) - sCompressor.TurnOn(MechInside, GetPosition()); - else - sCompressor.TurnOff(MechInside, GetPosition()); - sCompressor.Update(MechInside, GetPosition()); - } - if (MoverParameters->PantCompFlag) // Winger 160404 - dzwiek malej sprezarki - sSmallCompressor.TurnOn(MechInside, GetPosition()); - else - sSmallCompressor.TurnOff(MechInside, GetPosition()); - sSmallCompressor.Update(MechInside, GetPosition()); - - // youBy - przenioslem, bo diesel tez moze miec turbo - if( (MoverParameters->TurboTest > 0) - && (MoverParameters->MainCtrlPos >= MoverParameters->TurboTest)) - { - // udawanie turbo: (6.66*(eng_vol-0.85)) - if (eng_turbo > 6.66 * (enginevolume - 0.8) + 0.2 * dt) - eng_turbo = eng_turbo - 0.2 * dt; // 0.125 - else if (eng_turbo < 6.66 * (enginevolume - 0.8) - 0.4 * dt) - eng_turbo = eng_turbo + 0.4 * dt; // 0.333 - else - eng_turbo = 6.66 * (enginevolume - 0.8); - - sTurbo.TurnOn(MechInside, GetPosition()); - // sTurbo.UpdateAF(eng_turbo,0.7+(eng_turbo*0.6),MechInside,GetPosition()); - sTurbo.UpdateAF(3 * eng_turbo - 1, 0.4 + eng_turbo * 0.4, MechInside, GetPosition()); - // eng_vol_act=enginevolume; - // eng_frq_act=eng_frq; - } - else - sTurbo.TurnOff(MechInside, GetPosition()); - +/* +// NOTE: experimentally disabled to see if it's still used anywhere if (MoverParameters->TrainType == dt_PseudoDiesel) { // ABu: udawanie woodwarda dla lok. spalinowych // jesli silnik jest podpiety pod dzwiek przetwornicy - if (MoverParameters->ConverterFlag) // NBMX dzwiek przetwornicy - { - sConverter.TurnOn(MechInside, GetPosition()); - } - else - sConverter.TurnOff(MechInside, GetPosition()); // glosnosc zalezy od stosunku mocy silnika el. do mocy max double eng_vol; @@ -3812,105 +3801,505 @@ void TDynamicObject::RenderSounds() { eng_vol_act = eng_vol; // eng_frq_act=eng_frq; } - else - { - if (MoverParameters->ConverterFlag) // NBMX dzwiek przetwornicy - sConverter.TurnOn(MechInside, GetPosition()); - else - sConverter.TurnOff(MechInside, GetPosition()); - sConverter.Update(MechInside, GetPosition()); - } +*/ - if( TestFlag( MoverParameters->WarningSignal, 1 ) ) { - sHorn1.TurnOn( MechInside, GetPosition() ); - } - else { - sHorn1.TurnOff( MechInside, GetPosition() ); - } - if( TestFlag( MoverParameters->WarningSignal, 2 ) ) { - sHorn2.TurnOn( MechInside, GetPosition() ); - } - else { - sHorn2.TurnOff( MechInside, GetPosition() ); - } + if( ( MoverParameters->EngineType == ElectricSeriesMotor ) + || ( MoverParameters->EngineType == ElectricInductionMotor ) ) { - if (MoverParameters->DoorClosureWarning) - { - if (MoverParameters->DepartureSignal) // NBMX sygnal odjazdu, MC: pod warunkiem ze jest - // zdefiniowane w chk - sDepartureSignal.TurnOn(MechInside, GetPosition()); - else - sDepartureSignal.TurnOff(MechInside, GetPosition()); - sDepartureSignal.Update(MechInside, GetPosition()); - } - sHorn1.Update(MechInside, GetPosition()); - sHorn2.Update(MechInside, GetPosition()); - // McZapkie: w razie wykolejenia - if (MoverParameters->EventFlag) - { - if (TestFlag(MoverParameters->DamageFlag, dtrain_out) && GetVelocity() > 0) - rsDerailment.Play(1, 0, true, GetPosition()); - if (GetVelocity() == 0) - rsDerailment.Stop(); - } -#else -// McZapkie! - dzwiek compressor.wav tylko gdy dziala sprezarka -if( MoverParameters->VeselVolume != 0 ) { + if( MoverParameters->RventRot > 0.1 ) { + // play ventilator sound if the ventilators are rotating fast enough... + volume = ( + MoverParameters->EngineType == ElectricInductionMotor ? + rsWentylator.m_amplitudefactor * std::sqrt( std::fabs( MoverParameters->dizel_fill ) ) + rsWentylator.m_amplitudeoffset : + rsWentylator.m_amplitudefactor * MoverParameters->RventRot + rsWentylator.m_amplitudeoffset ); - if( MoverParameters->TrainType != dt_PseudoDiesel ) { - // NBMX dzwiek przetwornicy - if( MoverParameters->ConverterFlag ) { - sConverter.play(); + rsWentylator + .pitch( rsWentylator.m_frequencyfactor * MoverParameters->RventRot + rsWentylator.m_frequencyoffset ) + .gain( volume ) + .play( sound_flags::exclusive | sound_flags::looping ); + } + else { + // ...otherwise shut down the sound + rsWentylator.stop(); + } + } + + + if( MoverParameters->TrainType == dt_ET40 ) { + if( MoverParameters->Vel > 0.1 ) { + rsPrzekladnia + .pitch( rsPrzekladnia.m_frequencyfactor * ( MoverParameters->Vel ) + rsPrzekladnia.m_frequencyoffset ) + .gain( rsPrzekladnia.m_amplitudefactor * ( MoverParameters->Vel ) + rsPrzekladnia.m_amplitudeoffset ) + .play( sound_flags::exclusive | sound_flags::looping ); + } + else { + rsPrzekladnia.stop(); + } + } + + + if( MoverParameters->dizel_engage > 0.1 ) { + if( std::abs( MoverParameters->dizel_engagedeltaomega ) > 0.2 ) { + + frequency = rsEngageSlippery.m_frequencyfactor * std::fabs( MoverParameters->dizel_engagedeltaomega ) + rsEngageSlippery.m_frequencyoffset; + volume = rsEngageSlippery.m_amplitudefactor * ( MoverParameters->dizel_engage ) + rsEngageSlippery.m_amplitudeoffset; + } + else { + frequency = 1.f; // rsEngageSlippery.FA+0.7*rsEngageSlippery.FM*(fabs(mvControlled->enrot)+mvControlled->nmax); + volume = ( + MoverParameters->dizel_engage > 0.2 ? + 0.2 * rsEngageSlippery.m_amplitudefactor * ( MoverParameters->enrot / MoverParameters->nmax ) + rsEngageSlippery.m_amplitudeoffset : + 0.f ); + } + rsEngageSlippery + .pitch( frequency ) + .gain( volume ) + .play( sound_flags::exclusive | sound_flags::looping ); } else { - sConverter.stop(); + rsEngageSlippery.stop(); } + + + // youBy - przenioslem, bo diesel tez moze miec turbo + if( MoverParameters->TurboTest > 0 ) { + // udawanie turbo: + auto const goalpitch { 6.66 * ( enginevolume - 0.8 ) }; + auto const goalvolume{ 3 * eng_turbo - 1 }; + auto const currentvolume { sTurbo.gain() }; + auto const changerate{ 0.4 * dt }; + + if( ( MoverParameters->MainCtrlPos >= MoverParameters->TurboTest ) + && ( MoverParameters->enrot > 0.1 ) ) { + + eng_turbo = ( + eng_turbo > goalpitch ? + std::max( goalpitch, eng_turbo - changerate * 0.5 ) : + std::min( goalpitch, eng_turbo + changerate ) ); + + volume = ( + currentvolume > goalvolume ? + std::max( goalvolume, currentvolume - changerate ) : + std::min( goalvolume, currentvolume + changerate ) ); + + sTurbo + .pitch( eng_turbo * 0.4 + 0.4 ) + .gain( volume ) + .play( sound_flags::exclusive | sound_flags::looping ); + } + else { + eng_turbo = std::max( goalpitch, eng_turbo - changerate * 0.5 ); + volume = std::max( 0.0, sTurbo.gain() - 10.0 * dt ); + if( volume > 0.05 ) { + sTurbo + .pitch( 0.4 + eng_turbo * 0.4 ) + .gain( volume ); + } + else { + sTurbo.stop(); + } + } + } + + + // diesel startup + if( MoverParameters->EngineType == DieselEngine ) { + if( true == MoverParameters->dizel_enginestart ) { + dsbDieselIgnition.play( sound_flags::exclusive | sound_flags::looping ); + } + else { + dsbDieselIgnition.stop(); + } + } + + } // if enginepower + + // NBMX dzwiek przetwornicy + if( MoverParameters->ConverterFlag ) { + sConverter.play( sound_flags::exclusive | sound_flags::looping ); } else { + sConverter.stop(); } - if( MoverParameters->CompressorFlag ) { - sCompressor.play(); - } - else { - sCompressor.stop(); + if( MoverParameters->VeselVolume != 0 ) { + // McZapkie! - dzwiek compressor.wav tylko gdy dziala sprezarka + if( MoverParameters->CompressorFlag ) { + sCompressor.play( sound_flags::exclusive | sound_flags::looping ); + } + else { + sCompressor.stop(); + } } + + // Winger 160404 - dzwiek malej sprezarki if( MoverParameters->PantCompFlag ) { - // Winger 160404 - dzwiek malej sprezarki - sSmallCompressor.play(); + sSmallCompressor.play( sound_flags::exclusive | sound_flags::looping ); } else { sSmallCompressor.stop(); } - - if( TestFlag( MoverParameters->WarningSignal, 1 ) ) { - sHorn1.play(); + + // brake system and braking sounds: + if( m_lastbrakepressure != -1.f ) { + // calculate rate of pressure change in brake cylinder, once it's been initialized + auto const brakepressuredifference { m_lastbrakepressure - MoverParameters->BrakePress }; + m_brakepressurechange = interpolate( m_brakepressurechange, 10 * ( brakepressuredifference / dt ), 0.1f ); + } + m_lastbrakepressure = MoverParameters->BrakePress; + if( m_brakepressurechange > 0.05f ) { + // NOTE: can't use the leak rate directly due to irregular results produced by some brake type implementations + rsUnbrake + .gain( static_cast( std::max( MoverParameters->BrakePress, 0.0 ) / MoverParameters->MaxBrakePress[ 3 ] ) ) + .play( sound_flags::exclusive | sound_flags::looping ); } else { - sHorn1.stop(); - } - if( TestFlag( MoverParameters->WarningSignal, 2 ) ) { - sHorn2.play(); - } - else { - sHorn2.stop(); + // don't stop the sound too abruptly + volume = std::max( 0.0, rsUnbrake.gain() - 0.2 * dt ); + rsUnbrake.gain( volume ); + if( volume < 0.05 ) { + rsUnbrake.stop(); + } } + // Dzwiek odluzniacza + if( MoverParameters->Hamulec->GetStatus() & b_rls ) { + sReleaser + .gain( + clamp( + MoverParameters->BrakePress * 1.25f, // arbitrary multiplier + 0.f, 1.f ) ) + .play( sound_flags::exclusive | sound_flags::looping ); + } + else { + sReleaser.stop(); + } + + if( MoverParameters->SlippingWheels ) { + + if( ( MoverParameters->UnitBrakeForce > 100.0 ) + && ( GetVelocity() > 1.0 ) ) { + + auto const velocitydifference { GetVelocity() / MoverParameters->Vmax }; + rsSlippery + .gain( rsSlippery.m_amplitudefactor * velocitydifference + rsSlippery.m_amplitudeoffset ) + .play( sound_flags::exclusive | sound_flags::looping ); + } + } + else { + rsSlippery.stop(); + } + + // Dzwiek piasecznicy + if( MoverParameters->SandDose ) { + sSand.play( sound_flags::exclusive | sound_flags::looping ); + } + else { + sSand.stop(); + } + + if( /*( false == mvOccupied->SlippingWheels ) + &&*/ ( MoverParameters->UnitBrakeForce > 10.0 ) + && ( GetVelocity() > 0.05 ) ) { + + rsBrake + .pitch( clamp( rsBrake.m_frequencyfactor * GetVelocity() + rsBrake.m_frequencyoffset, 0.5, 1.15 ) ) // arbitrary limits + .gain( rsBrake.m_amplitudefactor * std::sqrt( ( GetVelocity() * MoverParameters->UnitBrakeForce ) ) + rsBrake.m_amplitudeoffset ) + .play( sound_flags::exclusive | sound_flags::looping ); + } + else { + rsBrake.stop(); + } + + // yB: przyspieszacz (moze zadziala, ale dzwiek juz jest) + if( true == bBrakeAcc ) { + if( true == TestFlag( MoverParameters->Hamulec->GetSoundFlag(), sf_Acc ) ) { + sBrakeAcc.play( sound_flags::exclusive ); + } + } + + // McZapkie-280302 - pisk mocno zacisnietych hamulcow + if( /*( false == MoverParameters->SlippingWheels ) + &&*/ ( MoverParameters->UnitBrakeForce > rsPisk.m_amplitudefactor ) + && ( MoverParameters->Vel > ( + true == rsPisk.is_playing() ? + 0.05 : + 0.5 ) ) ) { + + rsPisk + .gain( MoverParameters->UnitBrakeForce / ( rsPisk.m_amplitudefactor + 1 ) + rsPisk.m_amplitudeoffset ) + .play( sound_flags::exclusive | sound_flags::looping ); + } + else { + // don't stop the sound too abruptly + volume = std::max( 0.0, rsPisk.gain() - ( rsPisk.gain() * 1.5 * dt ) ); + rsPisk.gain( volume ); + if( volume < 0.05 ) { + rsPisk.stop(); + } + } + + // other sounds + // NBMX sygnal odjazdu if( MoverParameters->DoorClosureWarning ) { if( MoverParameters->DepartureSignal ) { - // NBMX sygnal odjazdu // MC: pod warunkiem ze jest zdefiniowane w chk - sDepartureSignal.play(); + sDepartureSignal.play( sound_flags::exclusive | sound_flags::looping ); } else { sDepartureSignal.stop(); } } + // NBMX Obsluga drzwi, MC: zuniwersalnione + if( ( true == MoverParameters->DoorLeftOpened ) + && ( dDoorMoveL < MoverParameters->DoorMaxShiftL ) ) { -} + for( auto &door : m_doorsounds ) { + if( door.rsDoorClose.offset().x > 0.f ) { + // determine left side doors from their offset + door.rsDoorOpen.play( sound_flags::exclusive ); + door.rsDoorClose.stop(); + } + } + } + if( ( false == MoverParameters->DoorLeftOpened ) + && ( dDoorMoveL > 0.01 ) ) { + + for( auto &door : m_doorsounds ) { + if( door.rsDoorClose.offset().x > 0.f ) { + // determine left side doors from their offset + door.rsDoorClose.play( sound_flags::exclusive ); + door.rsDoorOpen.stop(); + } + } + } + if( ( true == MoverParameters->DoorRightOpened ) + && ( dDoorMoveR < MoverParameters->DoorMaxShiftR ) ) { + + for( auto &door : m_doorsounds ) { + if( door.rsDoorClose.offset().x < 0.f ) { + // determine left side doors from their offset + door.rsDoorOpen.play( sound_flags::exclusive ); + door.rsDoorClose.stop(); + } + } + } + if( ( false == MoverParameters->DoorRightOpened ) + && ( dDoorMoveR > 0.01 ) ) { + + for( auto &door : m_doorsounds ) { + if( door.rsDoorClose.offset().x < 0.f ) { + // determine left side doors from their offset + door.rsDoorClose.play( sound_flags::exclusive ); + door.rsDoorOpen.stop(); + } + } + } + // horns + if( TestFlag( MoverParameters->WarningSignal, 1 ) ) { + sHorn1.play( sound_flags::exclusive | sound_flags::looping ); + } + else { + sHorn1.stop(); + } + if( TestFlag( MoverParameters->WarningSignal, 2 ) ) { + sHorn2.play( sound_flags::exclusive | sound_flags::looping ); + } + else { + sHorn2.stop(); + } + + // szum w czasie jazdy + if( GetVelocity() > 0.5 ) { + + volume = rsRunningNoise.m_amplitudefactor * MoverParameters->Vel + rsRunningNoise.m_amplitudeoffset; + frequency = rsRunningNoise.m_frequencyfactor * MoverParameters->Vel + rsRunningNoise.m_frequencyoffset; + + if( false == TestFlag( MoverParameters->DamageFlag, dtrain_wheelwear ) ) { + // McZpakie-221103: halas zalezny od kola + switch( MyTrack->eEnvironment ) { + case e_tunnel: { + volume *= 3; + frequency *= 0.95; + break; + } + case e_canyon: { + volume *= 1.1; + break; + } + case e_bridge: { + volume *= 2; + frequency *= 0.98; + break; + } + default: { + break; + } + } + } + else { + // uszkodzone kolo (podkucie) + switch( MyTrack->eEnvironment ) { + case e_tunnel: { + volume *= 2; + break; + } + case e_canyon: { + volume *= 1.1; + break; + } + case e_bridge: { + volume *= 1.5; + break; + } + default: { + break; + } + } + } + if( std::abs( MoverParameters->nrot ) > 0.01 ) { + // hamulce wzmagaja halas + volume *= 1 + MoverParameters->UnitBrakeForce / ( 1 + MoverParameters->MaxBrakeForce ); + } + // scale volume by track quality + volume *= ( 20.0 + MyTrack->iDamageFlag ) / 21; + // scale volume with curve radius and vehicle speed + // TBD, TODO: disable the scaling for sounds combined from speed-based samples? + volume *= + interpolate( + 0.0, 1.0, + clamp( + MoverParameters->Vel / 80.0, + 0.0, 1.0 ) ); + rsRunningNoise + .pitch( clamp( frequency, 0.5, 1.15 ) ) // arbitrary limits to prevent the pitch going out of whack + .gain( volume ) + .play( sound_flags::exclusive | sound_flags::looping ); + } + else { + rsRunningNoise.stop(); + } + + // youBy: dzwiek ostrych lukow i ciasnych zwrotek + if( ( ts.R * ts.R > 1 ) + && ( MoverParameters->Vel > 0 ) ) { + // scale volume with curve radius and vehicle speed + volume = + MoverParameters->AccN * MoverParameters->AccN + * interpolate( + 0.0, 1.0, + clamp( + MoverParameters->Vel / 40.0, + 0.0, 1.0 ) ); + } + else { + volume = 0; + } + if( volume > 0.05 ) { + rscurve + .gain( 2.0 * volume ) + .play( sound_flags::exclusive | sound_flags::looping ); + } + else { + rscurve.stop(); + } + + // McZapkie! - to wazne - SoundFlag wystawiane jest przez moje moduly + // gdy zachodza pewne wydarzenia komentowane dzwiekiem. + if( TestFlag( MoverParameters->SoundFlag, sound::relay ) ) { + // przekaznik - gdy bezpiecznik, automatyczny rozruch itp + if( true == TestFlag( MoverParameters->SoundFlag, sound::parallel ) ) { + if( TestFlag( MoverParameters->SoundFlag, sound::loud ) ) + dsbWejscie_na_bezoporow.play(); + else + dsbWejscie_na_drugi_uklad.play(); + } + else { + dsbRelay + .gain( + true == TestFlag( MoverParameters->SoundFlag, sound::loud ) ? + 1.0f : + 0.8f ) + .play(); + } + } + + if( TestFlag( MoverParameters->SoundFlag, sound::pneumatic ) ) { + // pneumatic relay + dsbPneumaticRelay + .gain( + true == TestFlag( MoverParameters->SoundFlag, sound::loud ) ? + 1.0f : + 0.8f ) + .play(); + } + + // couplers + int couplerindex { 0 }; + for( auto &couplersounds : m_couplersounds ) { + + auto &coupler { MoverParameters->Couplers[ couplerindex ] }; + + if( true == TestFlag( coupler.sounds, sound::bufferclash ) ) { + // zderzaki uderzaja o siebie + couplersounds.dsbBufferClamp + .gain( + true == TestFlag( coupler.sounds, sound::loud ) ? + 1.f : + 0.65f ) + .play( sound_flags::exclusive ); + } + if( true == TestFlag( coupler.sounds, sound::couplerstretch ) ) { + // sprzegi sie rozciagaja + couplersounds.dsbCouplerStretch + .gain( + true == TestFlag( coupler.sounds, sound::loud ) ? + 1.f : + 0.65f ) + .play( sound_flags::exclusive ); + } + + coupler.sounds = 0; + ++couplerindex; + } + + MoverParameters->SoundFlag = 0; + // McZapkie! - koniec obslugi dzwiekow z mover.pas + +// special events + if( MoverParameters->EventFlag ) { + // TODO: dedicated sound, played alongside regular noise + if( true == TestFlag( MoverParameters->DamageFlag, dtrain_wheelwear ) ) { +#ifdef EU07_USE_OLD_SOUNDCODE + if( rsRunningNoise.AM != 0 ) { + rsRunningNoise.Stop(); + float am = rsRunningNoise.AM; + float fa = rsRunningNoise.FA; + float fm = rsRunningNoise.FM; + rsRunningNoise.Init( "lomotpodkucia.wav", -1, 0, 0, 0, true ); // MC: zmiana szumu na lomot + if( rsRunningNoise.AM == 1 ) + rsRunningNoise.AM = am; + rsRunningNoise.AA = 0.7; + rsRunningNoise.FA = fa; + rsRunningNoise.FM = fm; + } +#else #endif -}; + } + // McZapkie: w razie wykolejenia + if( true == TestFlag( MoverParameters->DamageFlag, dtrain_out ) ) { + if( GetVelocity() > 0 ) { + rsDerailment.play(); + } + else { + rsDerailment.stop(); + } + } + + MoverParameters->EventFlag = false; + } +} // McZapkie-250202 // wczytywanie pliku z danymi multimedialnymi (dzwieki) @@ -4421,37 +4810,42 @@ void TDynamicObject::LoadMMediaFile( std::string BaseDir, std::string TypeName, pant1x = -pant1x; pant2x = -pant2x; } - if( pants ) { - for( int i = 0; i < iAnimType[ ANIM_PANTS ]; ++i ) { // przepisanie współczynników do pantografów (na razie - // nie będzie lepiej) - pants[ i ].fParamPants->fAngleL = - pants[i].fParamPants->fAngleL0; // początkowy kąt dolnego ramienia - pants[ i ].fParamPants->fAngleU = - pants[i].fParamPants->fAngleU0; // początkowy kąt + if( pants ) { + for( int i = 0; i < iAnimType[ ANIM_PANTS ]; ++i ) { + // przepisanie współczynników do pantografów (na razie nie będzie lepiej) + pants[ i ].fParamPants->fAngleL = pants[ i ].fParamPants->fAngleL0; // początkowy kąt dolnego ramienia + pants[ i ].fParamPants->fAngleU = pants[ i ].fParamPants->fAngleU0; // początkowy kąt // pants[i].fParamPants->PantWys=1.22*sin(pants[i].fParamPants->fAngleL)+1.755*sin(pants[i].fParamPants->fAngleU); // //wysokość początkowa // pants[i].fParamPants->PantWys=1.176289*sin(pants[i].fParamPants->fAngleL)+1.724482197*sin(pants[i].fParamPants->fAngleU); // //wysokość początkowa - if( pants[ i ].fParamPants->fHeight == 0.0 ) // gdy jest nieprawdopodobna wartość (np. nie znaleziony ślizg) + if( pants[ i ].fParamPants->fHeight == 0.0 ) // gdy jest nieprawdopodobna wartość (np. nie znaleziony ślizg) { // gdy pomiary modelu nie udały się, odczyt podanych parametrów z MMD - pants[ i ].fParamPants->vPos.x = ( i & 1 ) ? pant2x : pant1x; - pants[ i ].fParamPants->fHeight = - ( i & 1 ) ? pant2h : - pant1h; // wysokość ślizgu jest zapisana w MMD + pants[ i ].fParamPants->vPos.x = + ( i & 1 ) ? + pant2x : + pant1x; + pants[ i ].fParamPants->fHeight = + ( i & 1 ) ? + pant2h : + pant1h; // wysokość ślizgu jest zapisana w MMD } - pants[ i ].fParamPants->PantWys = - pants[ i ].fParamPants->fLenL1 * sin( pants[ i ].fParamPants->fAngleL ) + - pants[ i ].fParamPants->fLenU1 * sin( pants[ i ].fParamPants->fAngleU ) + - pants[i].fParamPants->fHeight; // wysokość początkowa + pants[ i ].fParamPants->PantWys = + pants[ i ].fParamPants->fLenL1 * sin( pants[ i ].fParamPants->fAngleL ) + + pants[ i ].fParamPants->fLenU1 * sin( pants[ i ].fParamPants->fAngleU ) + + pants[ i ].fParamPants->fHeight; // wysokość początkowa // pants[i].fParamPants->vPos.y=panty-panth-pants[i].fParamPants->PantWys; // //np. 4.429-0.097=4.332=~4.335 // pants[i].fParamPants->vPos.z=0; //niezerowe dla pantografów // asymetrycznych - pants[ i ].fParamPants->PantTraction = pants[ i ].fParamPants->PantWys; + pants[ i ].fParamPants->PantTraction = pants[ i ].fParamPants->PantWys; // połowa szerokości ślizgu; jest w "Power: CSW=" - pants[ i ].fParamPants->fWidth = 0.5 * MoverParameters->EnginePowerSource.CollectorParameters.CSW; + pants[ i ].fParamPants->fWidth = 0.5 * MoverParameters->EnginePowerSource.CollectorParameters.CSW; + + // create sound emitters for the pantograph + m_pantographsounds.emplace_back(); } - } + } } else if (token == "animpistonprefix:") { @@ -4553,16 +4947,16 @@ void TDynamicObject::LoadMMediaFile( std::string BaseDir, std::string TypeName, */ else if( token == "animdoorprefix:" ) { - // nazwa animowanych drzwi + // nazwa animowanych drzwi int i, j; parser.getTokens(1, false); parser >> token; for (i = 0, j = 0; i < ANIM_DOORS; ++i) j += iAnimType[i]; // zliczanie wcześniejszych animacji for (i = 0; i < iAnimType[ANIM_DOORS]; ++i) // liczba drzwi { // NBMX wrzesien 2003: wyszukiwanie drzwi o nazwie str* + // ustalenie submodelu asAnimName = token + std::to_string(i + 1); - pAnimations[i + j].smAnimated = - mdModel->GetFromName(asAnimName); // ustalenie submodelu + pAnimations[i + j].smAnimated = mdModel->GetFromName(asAnimName); if (pAnimations[i + j].smAnimated) { //++iAnimatedDoors; pAnimations[i + j].smAnimated->WillBeAnimated(); // wyłączenie optymalizacji transformu @@ -4589,13 +4983,15 @@ void TDynamicObject::LoadMMediaFile( std::string BaseDir, std::string TypeName, pAnimations[i + j].fSpeed = (pAnimations[i + j].fSpeed + 100) / 100; // Ra: te współczynniki są bez sensu, bo modyfikują wektor przesunięcia } + // create sound emitters for the door + m_doorsounds.emplace_back(); } } } while( ( token != "" ) && ( token != "endmodels" ) ); - } + } // models else if( token == "sounds:" ) { // dzwieki @@ -4606,25 +5002,19 @@ void TDynamicObject::LoadMMediaFile( std::string BaseDir, std::string TypeName, // polozenia osi w/m srodka pojazdu parser.getTokens( 1, false ); parser >> dSDist; - for( int i = 0; i < iAxles; i++ ) { + for( int i = 0; i < iAxles; ++i ) { parser.getTokens( 1, false ); parser >> dWheelsPosition[ i ]; parser.getTokens(); parser >> token; if( token != "end" ) { -#ifdef EU07_USE_OLD_SOUNDCODE - rsStukot[ i ].Init( - token, dSDist, - GetPosition().x, GetPosition().y + dWheelsPosition[ i ], GetPosition().z, - true ); -#else rsStukot[ i ].deserialize( token + " " + std::to_string( dSDist ), sound_type::single, sound_parameters::range ); - // TODO: set per-wheel offset -#endif + rsStukot[ i ].owner( this ); + rsStukot[ i ].offset( { 0, 0, -dWheelsPosition[ i ] } ); } } if( token != "end" ) { - // TODO: double-check if this if() and/or retrieval makes sense here + // TODO: check if this if() and/or retrieval makes sense here parser.getTokens( 1, false ); parser >> token; } } @@ -4632,223 +5022,91 @@ void TDynamicObject::LoadMMediaFile( std::string BaseDir, std::string TypeName, else if( ( token == "engine:" ) && ( MoverParameters->Power > 0 ) ) { // plik z dzwiekiem silnika, mnozniki i ofsety amp. i czest. -#ifdef EU07_USE_OLD_SOUNDCODE - double attenuation; - parser.getTokens( 2, false ); - parser - >> token - >> attenuation; - rsSilnik.Init( - token, attenuation, - GetPosition().x, GetPosition().y, GetPosition().z, - true, true ); - if( rsSilnik.GetWaveTime() == 0 ) { - ErrorLog( "Missed sound: \"" + token + "\" for " + asFileName ); - } - parser.getTokens( 1, false ); - parser >> rsSilnik.AM; - if( MoverParameters->EngineType == DieselEngine ) { - - rsSilnik.AM /= ( MoverParameters->Power + MoverParameters->nmax * 60 ); - } - else if( MoverParameters->EngineType == DieselElectric ) { - - rsSilnik.AM /= ( MoverParameters->Power * 3 ); - } - else { - - rsSilnik.AM /= ( MoverParameters->Power + MoverParameters->nmax * 60 + MoverParameters->Power + MoverParameters->Power ); - } - parser.getTokens( 3, false ); - parser - >> rsSilnik.AA - >> rsSilnik.FM // MoverParameters->nmax; - >> rsSilnik.FA; -#else rsSilnik.deserialize( parser, sound_type::single, sound_parameters::range | sound_parameters::amplitude | sound_parameters::frequency ); -#endif + rsSilnik.owner( this ); + + auto const amplitudedivisor = static_cast( ( + MoverParameters->EngineType == DieselEngine ? MoverParameters->Power + MoverParameters->nmax * 60 : + MoverParameters->EngineType == DieselElectric ? MoverParameters->Power * 3 : + MoverParameters->Power + MoverParameters->nmax * 60 + MoverParameters->Power + MoverParameters->Power ) ); + rsSilnik.m_amplitudefactor /= amplitudedivisor; } else if( ( token == "ventilator:" ) && ( ( MoverParameters->EngineType == ElectricSeriesMotor ) || ( MoverParameters->EngineType == ElectricInductionMotor ) ) ) { // plik z dzwiekiem wentylatora, mnozniki i ofsety amp. i czest. -#ifdef EU07_USE_OLD_SOUNDCODE - double attenuation; - parser.getTokens( 2, false ); - parser - >> token - >> attenuation; - rsWentylator.Init( - token, attenuation, - GetPosition().x, GetPosition().y, GetPosition().z, - true, true ); - parser.getTokens( 4, false ); - parser - >> rsWentylator.AM - >> rsWentylator.AA - >> rsWentylator.FM - >> rsWentylator.FA; - rsWentylator.AM /= MoverParameters->RVentnmax; - rsWentylator.FM /= MoverParameters->RVentnmax; -#else rsWentylator.deserialize( parser, sound_type::single, sound_parameters::range | sound_parameters::amplitude | sound_parameters::frequency ); -#endif + rsWentylator.owner( this ); + + rsWentylator.m_amplitudefactor /= MoverParameters->RVentnmax; + rsWentylator.m_frequencyfactor /= MoverParameters->RVentnmax; } else if( ( token == "transmission:" ) && ( MoverParameters->EngineType == ElectricSeriesMotor ) ) { // plik z dzwiekiem, mnozniki i ofsety amp. i czest. -#ifdef EU07_USE_OLD_SOUNDCODE - double attenuation; - parser.getTokens( 2, false ); - parser - >> token - >> attenuation; - rsPrzekladnia.Init( - token, attenuation, - GetPosition().x, GetPosition().y, GetPosition().z, - true ); - rsPrzekladnia.AM = 0.029; - rsPrzekladnia.AA = 0.1; - rsPrzekladnia.FM = 0.005; - rsPrzekladnia.FA = 1.0; -#else - rsPrzekladnia.deserialize( parser, sound_type::single, sound_parameters::range | sound_parameters::amplitude | sound_parameters::frequency ); -#endif + rsPrzekladnia.deserialize( parser, sound_type::single, sound_parameters::range ); + + // NOTE, TODO: fixed parameters, put them into simulation-side calculations + rsPrzekladnia.m_amplitudefactor = 0.029; + rsPrzekladnia.m_amplitudeoffset = 0.1; + rsPrzekladnia.m_frequencyfactor = 0.005; + rsPrzekladnia.m_frequencyoffset = 1.0; + + rsPrzekladnia.owner( this ); } - else if( token == "brake:" ){ + else if( token == "brake:" ) { // plik z piskiem hamulca, mnozniki i ofsety amplitudy. -#ifdef EU07_USE_OLD_SOUNDCODE - double attenuation; - parser.getTokens( 2, false ); - parser - >> token - >> attenuation; - rsPisk.Init( - token, attenuation, - GetPosition().x, GetPosition().y, GetPosition().z, - true ); - rsPisk.AM = parser.getToken(); - rsPisk.AA = parser.getToken() * ( 105 - Random( 10 ) ) / 100; - rsPisk.FM = 1.0; - rsPisk.FA = 0.0; -#else rsPisk.deserialize( parser, sound_type::single, sound_parameters::range | sound_parameters::amplitude ); -#endif + rsPisk.owner( this ); + + rsPisk.m_amplitudeoffset *= ( 105.f - Random( 10.f ) ) / 100.f; } else if( token == "brakeacc:" ) { // plik z przyspieszaczem (upust po zlapaniu hamowania) -#ifdef EU07_USE_OLD_SOUNDCODE - parser.getTokens( 1, false ); parser >> token; - sBrakeAcc = TSoundsManager::GetFromName( token, true ); -#else sBrakeAcc.deserialize( parser, sound_type::single ); -#endif + sBrakeAcc.owner( this ); + bBrakeAcc = true; } else if( token == "unbrake:" ) { // plik z piskiem hamulca, mnozniki i ofsety amplitudy. -#ifdef EU07_USE_OLD_SOUNDCODE - double attenuation; - parser.getTokens( 2, false ); - parser - >> token - >> attenuation; - rsUnbrake.Init( - token, attenuation, - GetPosition().x, GetPosition().y, GetPosition().z, - true ); - rsUnbrake.AM = 1.0; - rsUnbrake.AA = 0.0; - rsUnbrake.FM = 1.0; - rsUnbrake.FA = 0.0; -#else rsUnbrake.deserialize( parser, sound_type::single, sound_parameters::range ); -#endif + rsUnbrake.owner( this ); } else if( token == "derail:" ) { // dzwiek przy wykolejeniu -#ifdef EU07_USE_OLD_SOUNDCODE - double attenuation; - parser.getTokens( 2, false ); - parser - >> token - >> attenuation; - rsDerailment.Init( - token, attenuation, - GetPosition().x, GetPosition().y, GetPosition().z, - true ); - rsDerailment.AM = 1.0; - rsDerailment.AA = 0.0; - rsDerailment.FM = 1.0; - rsDerailment.FA = 0.0; -#else rsDerailment.deserialize( parser, sound_type::single, sound_parameters::range ); -#endif + rsDerailment.owner( this ); } else if( token == "dieselinc:" ) { // dzwiek przy wlazeniu na obroty woodwarda -#ifdef EU07_USE_OLD_SOUNDCODE - double attenuation; - parser.getTokens( 2, false ); - parser - >> token - >> attenuation; - rsDiesielInc.Init( - token, attenuation, - GetPosition().x, GetPosition().y, GetPosition().z, - true ); - rsDiesielInc.AM = 1.0; - rsDiesielInc.AA = 0.0; - rsDiesielInc.FM = 1.0; - rsDiesielInc.FA = 0.0; -#else - rsDiesielInc.deserialize( parser, sound_type::single, sound_parameters::range ); -#endif + rsDieselInc.deserialize( parser, sound_type::single, sound_parameters::range ); + rsDieselInc.owner( this ); } else if( token == "curve:" ) { -#ifdef EU07_USE_OLD_SOUNDCODE - double attenuation; - parser.getTokens( 2, false ); - parser - >> token - >> attenuation; - rscurve.Init( - token, attenuation, - GetPosition().x, GetPosition().y, GetPosition().z, - true ); - rscurve.AM = 1.0; - rscurve.AA = 0.0; - rscurve.FM = 1.0; - rscurve.FA = 0.0; -#else rscurve.deserialize( parser, sound_type::single, sound_parameters::range ); -#endif + rscurve.owner( this ); } else if( token == "horn1:" ) { // pliki z trabieniem -#ifdef EU07_USE_OLD_SOUNDCODE - sHorn1.Load( parser, GetPosition() ); -#else sHorn1.deserialize( parser, sound_type::multipart, sound_parameters::range ); -#endif + sHorn1.owner( this ); } else if( token == "horn2:" ) { // pliki z trabieniem wysokoton. -#ifdef EU07_USE_OLD_SOUNDCODE - sHorn2.Load( parser, GetPosition() ); -#else sHorn2.deserialize( parser, sound_type::multipart, sound_parameters::range ); -#endif + sHorn2.owner( this ); + if( iHornWarning ) { iHornWarning = 2; // numer syreny do użycia po otrzymaniu sygnału do jazdy } @@ -4856,157 +5114,256 @@ void TDynamicObject::LoadMMediaFile( std::string BaseDir, std::string TypeName, else if( token == "departuresignal:" ) { // pliki z sygnalem odjazdu -#ifdef EU07_USE_OLD_SOUNDCODE - sDepartureSignal.Load( parser, GetPosition() ); -#else sDepartureSignal.deserialize( parser, sound_type::multipart, sound_parameters::range ); -#endif + sDepartureSignal.owner( this ); } else if( token == "pantographup:" ) { // pliki dzwiekow pantografow -#ifdef EU07_USE_OLD_SOUNDCODE - parser.getTokens( 1, false ); parser >> token; - sPantUp.Init( - token, 50, - GetPosition().x, GetPosition().y, GetPosition().z, - true ); -#else - sPantUp.deserialize( parser, sound_type::single ); -#endif + sound_source pantographup { sound_placement::external }; + pantographup.deserialize( parser, sound_type::single ); + pantographup.owner( this ); + for( auto &pantograph : m_pantographsounds ) { + pantograph.sPantUp = pantographup; + } } else if( token == "pantographdown:" ) { // pliki dzwiekow pantografow -#ifdef EU07_USE_OLD_SOUNDCODE - parser.getTokens( 1, false ); parser >> token; - sPantDown.Init( - token, 50, - GetPosition().x, GetPosition().y, GetPosition().z, - true ); -#else - sPantDown.deserialize( parser, sound_type::single ); -#endif + sound_source pantographdown { sound_placement::external }; + pantographdown.deserialize( parser, sound_type::single ); + pantographdown.owner( this ); + for( auto &pantograph : m_pantographsounds ) { + pantograph.sPantDown = pantographdown; + } } else if( token == "compressor:" ) { // pliki ze sprezarka -#ifdef EU07_USE_OLD_SOUNDCODE - sCompressor.Load( parser, GetPosition() ); -#else sCompressor.deserialize( parser, sound_type::multipart, sound_parameters::range ); -#endif + sCompressor.owner( this ); } else if( token == "converter:" ) { // pliki z przetwornica -#ifdef EU07_USE_OLD_SOUNDCODE - sConverter.Load( parser, GetPosition() ); -#else sConverter.deserialize( parser, sound_type::multipart, sound_parameters::range ); -#endif + sConverter.owner( this ); } else if( token == "turbo:" ) { // pliki z turbogeneratorem -#ifdef EU07_USE_OLD_SOUNDCODE - sTurbo.Load( parser, GetPosition() ); -#else sTurbo.deserialize( parser, sound_type::multipart, sound_parameters::range ); -#endif + sTurbo.owner( this ); } else if( token == "small-compressor:" ) { // pliki z przetwornica -#ifdef EU07_USE_OLD_SOUNDCODE - sSmallCompressor.Load( parser, GetPosition() ); -#else sSmallCompressor.deserialize( parser, sound_type::multipart, sound_parameters::range ); -#endif + sSmallCompressor.owner( this ); } else if( token == "dooropen:" ) { -#ifdef EU07_USE_OLD_SOUNDCODE - parser.getTokens( 1, false ); parser >> token; - rsDoorOpen.Init( - token, 50, - GetPosition().x, GetPosition().y, GetPosition().z, - true ); -#else - rsDoorOpen.deserialize( parser, sound_type::single ); -#endif + sound_source dooropen { sound_placement::general }; + dooropen.deserialize( parser, sound_type::single ); + dooropen.owner( this ); + for( auto &door : m_doorsounds ) { + door.rsDoorOpen = dooropen; + } } else if( token == "doorclose:" ) { -#ifdef EU07_USE_OLD_SOUNDCODE - parser.getTokens( 1, false ); parser >> token; - rsDoorClose.Init( - token, 50, - GetPosition().x, GetPosition().y, GetPosition().z, - true ); -#else - rsDoorClose.deserialize( parser, sound_type::single ); -#endif + sound_source doorclose { sound_placement::general }; + doorclose.deserialize( parser, sound_type::single ); + doorclose.owner( this ); + for( auto &door : m_doorsounds ) { + door.rsDoorClose = doorclose; + } } else if( token == "sand:" ) { -#ifdef EU07_USE_OLD_SOUNDCODE - // pliki z piasecznica - sSand.Load( parser, GetPosition() ); -#else sSand.deserialize( parser, sound_type::multipart, sound_parameters::range ); -#endif + sSand.owner( this ); } else if( token == "releaser:" ) { // pliki z odluzniaczem -#ifdef EU07_USE_OLD_SOUNDCODE - sReleaser.Load( parser, GetPosition() ); -#else sReleaser.deserialize( parser, sound_type::multipart, sound_parameters::range ); -#endif + sReleaser.owner( this ); } } while( ( token != "" ) && ( token != "endsounds" ) ); - } + } // sounds: - else if (token == "internaldata:") { - // dalej nie czytaj - do { - // zbieranie informacji o kabinach - token = ""; - parser.getTokens(); parser >> token; - if(token == "cab0model:") - { - parser.getTokens(); parser >> token; - if( token != "none" ) { iCabs = 2; } + else if( token == "internaldata:" ) { + // dalej nie czytaj + do { + // zbieranie informacji o kabinach + token = ""; + parser.getTokens(); parser >> token; + if( token == "cab0model:" ) { + parser.getTokens(); parser >> token; + if( token != "none" ) { iCabs |= 0x2; } } - else if (token == "cab1model:") - { - parser.getTokens(); parser >> token; - if( token != "none" ) { iCabs = 1; } + else if( token == "cab1model:" ) { + parser.getTokens(); parser >> token; + if( token != "none" ) { iCabs |= 0x1; } } - else if (token == "cab2model:") - { - parser.getTokens(); parser >> token; - if( token != "none" ) { iCabs = 4; } - } + else if( token == "cab2model:" ) { + parser.getTokens(); parser >> token; + if( token != "none" ) { iCabs |= 0x4; } + } + // engine sounds + else if( token == "ignition:" ) { + // odpalanie silnika + dsbDieselIgnition.deserialize( parser, sound_type::single ); + dsbDieselIgnition.owner( this ); + } + else if( token == "engageslippery:" ) { + // tarcie tarcz sprzegla: + rsEngageSlippery.deserialize( parser, sound_type::single, sound_parameters::amplitude | sound_parameters::frequency ); + rsEngageSlippery.owner( this ); - } while( token != "" ); + rsEngageSlippery.m_frequencyfactor /= ( 1 + MoverParameters->nmax ); + } + else if( token == "relay:" ) { + // styczniki itp: + dsbRelay.deserialize( parser, sound_type::single ); + dsbRelay.owner( this ); + } + else if( token == "pneumaticrelay:" ) { + // wylaczniki pneumatyczne: + dsbPneumaticRelay.deserialize( parser, sound_type::single ); + dsbPneumaticRelay.owner( this ); + } + else if( token == "wejscie_na_bezoporow:" ) { + // hunter-111211: wydzielenie wejscia na bezoporowa i na drugi uklad do pliku + dsbWejscie_na_bezoporow.deserialize( parser, sound_type::single ); + dsbWejscie_na_bezoporow.owner( this ); + } + else if( token == "wejscie_na_drugi_uklad:" ) { + dsbWejscie_na_drugi_uklad.deserialize( parser, sound_type::single ); + dsbWejscie_na_drugi_uklad.owner( this ); + } + // braking sounds + else if( token == "brakesound:" ) { + // hamowanie zwykle: + rsBrake.deserialize( parser, sound_type::single, sound_parameters::amplitude | sound_parameters::frequency ); + rsBrake.owner( this ); - Stop_InternalData = true; - } + rsBrake.m_amplitudefactor /= ( 1 + MoverParameters->MaxBrakeForce * 1000 ); + rsBrake.m_frequencyfactor /= ( 1 + MoverParameters->Vmax ); + } + else if( token == "slipperysound:" ) { + // sanie: + rsSlippery.deserialize( parser, sound_type::single, sound_parameters::amplitude ); + rsSlippery.owner( this ); - } while( ( token != "" ) - && ( false == Stop_InternalData ) ); + rsSlippery.m_amplitudefactor /= ( 1 + MoverParameters->Vmax ); + } + // coupler sounds + else if( token == "couplerattach:" ) { + // laczenie: + sound_source couplerattach { sound_placement::external }; + couplerattach.deserialize( parser, sound_type::single ); + couplerattach.owner( this ); + for( auto &couplersounds : m_couplersounds ) { + couplersounds.dsbCouplerAttach = couplerattach; + } + } + else if( token == "couplerdetach:" ) { + // rozlaczanie: + sound_source couplerdetach { sound_placement::external }; + couplerdetach.deserialize( parser, sound_type::single ); + couplerdetach.owner( this ); + for( auto &couplersounds : m_couplersounds ) { + couplersounds.dsbCouplerDetach = couplerdetach; + } + } + else if( token == "couplerstretch:" ) { + // coupler stretching + sound_source couplerstretch { sound_placement::external }; + couplerstretch.deserialize( parser, sound_type::single ); + couplerstretch.owner( this ); + for( auto &couplersounds : m_couplersounds ) { + couplersounds.dsbCouplerStretch = couplerstretch; + } + } + else if( token == "bufferclamp:" ) { + // buffers hitting one another + sound_source bufferclash { sound_placement::external }; + bufferclash.deserialize( parser, sound_type::single ); + bufferclash.owner( this ); + for( auto &couplersounds : m_couplersounds ) { + couplersounds.dsbBufferClamp = bufferclash; + } + } + else if( token == "runningnoise:" ) { + // szum podczas jazdy: + rsRunningNoise.deserialize( parser, sound_type::single, sound_parameters::amplitude | sound_parameters::frequency ); + rsRunningNoise.owner( this ); + + rsRunningNoise.m_amplitudefactor /= ( 1 + MoverParameters->Vmax ); + rsRunningNoise.m_frequencyfactor /= ( 1 + MoverParameters->Vmax ); + } + + } while( token != "" ); + + } // internaldata: + + } while( token != "" ); if( !iAnimations ) { // if the animations weren't defined the model is likely to be non-functional. warrants a warning. ErrorLog( "Animations tag is missing from the .mmd file \"" + asFileName + "\"" ); } + // assign default samples to sound emitters which weren't included in the config file + // engine + if( MoverParameters->Power > 0 ) { + if( true == dsbWejscie_na_bezoporow.empty() ) { + // hunter-111211: domyslne, gdy brak + dsbWejscie_na_bezoporow.deserialize( "wejscie_na_bezoporow.wav", sound_type::single ); + dsbWejscie_na_bezoporow.owner( this ); + } + if( true == dsbWejscie_na_drugi_uklad.empty() ) { + dsbWejscie_na_drugi_uklad.deserialize( "wescie_na_drugi_uklad.wav", sound_type::single ); + dsbWejscie_na_drugi_uklad.owner( this ); + } + } + // braking sounds + if( true == rsUnbrake.empty() ) { + rsUnbrake.deserialize( "[1007]estluz.wav", sound_type::single ); + rsUnbrake.owner( this ); + } + // couplers + for( auto &couplersounds : m_couplersounds ) { + if( true == couplersounds.dsbCouplerAttach.empty() ) { + couplersounds.dsbCouplerAttach.deserialize( "couplerattach.wav", sound_type::single ); + couplersounds.dsbCouplerAttach.owner( this ); + } + if( true == couplersounds.dsbCouplerDetach.empty() ) { + couplersounds.dsbCouplerDetach.deserialize( "couplerdetach.wav", sound_type::single ); + couplersounds.dsbCouplerDetach.owner( this ); + } + if( true == couplersounds.dsbCouplerStretch.empty() ) { + couplersounds.dsbCouplerStretch.deserialize( "en57_couplerstretch.wav", sound_type::single ); + couplersounds.dsbCouplerStretch.owner( this ); + } + if( true == couplersounds.dsbBufferClamp.empty() ) { + couplersounds.dsbBufferClamp.deserialize( "en57_bufferclamp.wav", sound_type::single ); + couplersounds.dsbBufferClamp.owner( this ); + } + } + // other sounds + if( true == rscurve.empty() ) { + // hunter-111211: domyslne, gdy brak + rscurve.deserialize( "curve.wav", sound_type::single ); + rscurve.owner( this ); + } + + if (mdModel) mdModel->Init(); // obrócenie modelu oraz optymalizacja, również zapisanie binarnego if (mdLoad) @@ -5016,6 +5373,67 @@ void TDynamicObject::LoadMMediaFile( std::string BaseDir, std::string TypeName, Global::asCurrentTexturePath = szTexturePath; // kiedyś uproszczone wnętrze mieszało tekstury nieba Global::asCurrentDynamicPath = ""; + + // position sound emitters which weren't defined in the config file + auto const nullvector { glm::vec3() }; + // engine sounds , centre of the vehicle + auto const enginelocation { glm::vec3 {0.f, MoverParameters->Dim.H * 0.5f, 0.f } }; + std::vector enginesounds = { + &dsbDieselIgnition, + &rsSilnik, + &dsbRelay, &dsbWejscie_na_bezoporow, &dsbWejscie_na_drugi_uklad, + &rsPrzekladnia, &rsEngageSlippery, &rsDieselInc, &sTurbo, + &rsWentylator, &sConverter, &sCompressor, &sSmallCompressor + }; + for( auto sound : enginesounds ) { + if( sound->offset() == nullvector ) { + sound->offset( enginelocation ); + } + } + // TODO: other sound types + // pantographs + if( pants != nullptr ) { + std::size_t pantographindex { 0 }; + for( auto &pantographsounds : m_pantographsounds ) { + auto const pantographoffset { + glm::vec3( + 0.f, + pants[ pantographindex ].fParamPants->vPos.y, + -pants[ pantographindex ].fParamPants->vPos.x ) }; + pantographsounds.sPantUp.offset( pantographoffset ); + pantographsounds.sPantDown.offset( pantographoffset ); + ++pantographindex; + } + } + // doors + std::size_t dooranimationfirstindex { 0 }; + for( std::size_t i = 0; i < ANIM_DOORS; ++i ) { + // zliczanie wcześniejszych animacji + dooranimationfirstindex += iAnimType[ i ]; + } + std::size_t doorindex { 0 }; + for( auto &doorsounds : m_doorsounds ) { + + auto submodel { pAnimations[ dooranimationfirstindex + doorindex ].smAnimated }; + if( submodel == nullptr ) { continue; } + + auto const dooroffset { pAnimations[ dooranimationfirstindex + doorindex ].smAnimated->offset( std::numeric_limits::max() ) }; + + doorsounds.rsDoorClose.offset( dooroffset ); + doorsounds.rsDoorOpen.offset( dooroffset ); + ++doorindex; + } + // couplers + auto const frontcoupleroffset { glm::vec3{ 0.f, 1.f, MoverParameters->Dim.L * 0.5f } }; + m_couplersounds[ side::front ].dsbCouplerAttach.offset( frontcoupleroffset ); + m_couplersounds[ side::front ].dsbCouplerDetach.offset( frontcoupleroffset ); + m_couplersounds[ side::front ].dsbCouplerStretch.offset( frontcoupleroffset ); + m_couplersounds[ side::front ].dsbBufferClamp.offset( frontcoupleroffset ); + auto const rearcoupleroffset{ glm::vec3{ 0.f, 1.f, MoverParameters->Dim.L * -0.5f } }; + m_couplersounds[ side::rear ].dsbCouplerAttach.offset( rearcoupleroffset ); + m_couplersounds[ side::rear ].dsbCouplerDetach.offset( rearcoupleroffset ); + m_couplersounds[ side::rear ].dsbCouplerStretch.offset( rearcoupleroffset ); + m_couplersounds[ side::rear ].dsbBufferClamp.offset( rearcoupleroffset ); } //--------------------------------------------------------------------------- diff --git a/DynObj.h b/DynObj.h index 96cba4bf..1288ab8a 100644 --- a/DynObj.h +++ b/DynObj.h @@ -258,12 +258,33 @@ private: TSubModel *smWiper; // wycieraczka (poniekąd też wajcha) // Ra: koneic animacji do ogarnięcia - private: - void ABuLittleUpdate(double ObjSqrDist); - bool btnOn; // ABu: czy byly uzywane buttony, jesli tak, to po renderingu wylacz - // bo ten sam model moze byc jeszcze wykorzystany przez inny obiekt! - double ComputeRadius( Math3D::vector3 p1, Math3D::vector3 p2, Math3D::vector3 p3, Math3D::vector3 p4); +private: +// types + struct coupler_sounds { + sound_source dsbCouplerAttach { sound_placement::external }; // moved from cab + sound_source dsbCouplerDetach { sound_placement::external }; // moved from cab + sound_source dsbCouplerStretch { sound_placement::external }; // moved from cab + sound_source dsbBufferClamp { sound_placement::external }; // moved from cab + }; + struct pantograph_sounds { + sound_source sPantUp { sound_placement::external }; + sound_source sPantDown { sound_placement::external }; + }; + + struct door_sounds { + sound_source rsDoorOpen{ sound_placement::general }; // Ra: przeniesione z kabiny + sound_source rsDoorClose{ sound_placement::general }; + }; + +// methods + void ABuLittleUpdate(double ObjSqrDist); + double ComputeRadius( Math3D::vector3 p1, Math3D::vector3 p2, Math3D::vector3 p3, Math3D::vector3 p4); + void ABuBogies(); + void ABuModelRoll(); + void TurnOff(); + +// members TButton btCoupler1; // sprzegi TButton btCoupler2; TAirCoupler btCPneumatic1; // sprzegi powietrzne //yB - zmienione z Button na AirCoupler - krzyzyki @@ -303,44 +324,55 @@ private: double dRailLength; double dRailPosition[MaxAxles]; // licznik pozycji osi w/m szyny double dWheelsPosition[MaxAxles]; // pozycja osi w/m srodka pojazdu - sound_source rsStukot[MaxAxles]; // dzwieki poszczegolnych osi //McZapkie-270202 - sound_source rsSilnik; // McZapkie-010302 - silnik - sound_source rsWentylator; // McZapkie-030302 - sound_source rsPisk; // McZapkie-260302 - sound_source rsDerailment; // McZapkie-051202 - sound_source rsPrzekladnia; - sound_source sHorn1; - sound_source sHorn2; - sound_source sCompressor; // NBMX wrzesien 2003 - sound_source sConverter; - sound_source sSmallCompressor; - sound_source sDepartureSignal; - sound_source sTurbo; - sound_source sSand; - sound_source sReleaser; - - // Winger 010304 - sound_source sPantUp; - sound_source sPantDown; - sound_source rsDoorOpen; // Ra: przeniesione z kabiny - sound_source rsDoorClose; + std::vector rsStukot; // dzwieki poszczegolnych osi //McZapkie-270202 + // engine sounds + sound_source dsbDieselIgnition { sound_placement::engine }; // moved from cab + sound_source rsSilnik { sound_placement::engine }; + sound_source dsbRelay { sound_placement::engine }; + sound_source dsbWejscie_na_bezoporow { sound_placement::engine }; // moved from cab + sound_source dsbWejscie_na_drugi_uklad { sound_placement::engine }; // moved from cab + sound_source rsPrzekladnia { sound_placement::engine }; + sound_source rsEngageSlippery { sound_placement::engine }; // moved from cab + sound_source rsDieselInc { sound_placement::engine }; // youBy + sound_source sTurbo { sound_placement::engine }; + sound_source rsWentylator { sound_placement::engine }; // McZapkie-030302 + sound_source sConverter { sound_placement::engine }; + sound_source sCompressor { sound_placement::engine }; // NBMX wrzesien 2003 + sound_source sSmallCompressor { sound_placement::engine }; + // braking sounds + sound_source dsbPneumaticRelay { sound_placement::external }; + sound_source rsUnbrake { sound_placement::external }; // yB - odglos luzowania + sound_source sReleaser { sound_placement::external }; + sound_source rsSlippery { sound_placement::external, EU07_SOUND_BRAKINGCUTOFFRANGE }; // moved from cab + sound_source sSand { sound_placement::external }; + sound_source rsBrake { sound_placement::external, EU07_SOUND_BRAKINGCUTOFFRANGE }; // moved from cab + sound_source sBrakeAcc { sound_placement::external }; + bool bBrakeAcc; + sound_source rsPisk { sound_placement::external, EU07_SOUND_BRAKINGCUTOFFRANGE }; // McZapkie-260302 + sound_source rsDerailment { sound_placement::external, 250.f }; // McZapkie-051202 + // moving part and other external sounds + std::array m_couplersounds; // always front and rear + std::vector m_pantographsounds; // typically 2 but can be less (or more?) + std::vector m_doorsounds; // can expect symmetrical arrangement, but don't count on it + sound_source sDepartureSignal { sound_placement::general }; + sound_source sHorn1 { sound_placement::external }; + sound_source sHorn2 { sound_placement::external }; + sound_source rsRunningNoise { sound_placement::external, EU07_SOUND_RUNNINGNOISECUTOFFRANGE }; + sound_source rscurve { sound_placement::external, EU07_SOUND_RUNNINGNOISECUTOFFRANGE }; // youBy double eng_vol_act; double eng_frq_act; double eng_dfrq; double eng_turbo; - void ABuBogies(); - void ABuModelRoll(); Math3D::vector3 modelShake; bool renderme; // yB - czy renderowac - // TRealSound sBrakeAcc; //dźwięk przyspieszacza - sound_source sBrakeAcc; - bool bBrakeAcc; - sound_source rsUnbrake; // yB - odglos luzowania float ModCamRot; int iInventory[ 2 ] { 0, 0 }; // flagi bitowe posiadanych submodeli (np. świateł) - void TurnOff(); + bool btnOn; // ABu: czy byly uzywane buttony, jesli tak, to po renderingu wylacz + // bo ten sam model moze byc jeszcze wykorzystany przez inny obiekt! + float m_lastbrakepressure { -1.f }; // helper, cached level of pressure in brake cylinder + float m_brakepressurechange { 0.f }; // recent change of pressure in brake cylinder public: int iHornWarning; // numer syreny do użycia po otrzymaniu sygnału do jazdy @@ -376,10 +408,10 @@ private: void SetPneumatic(bool front, bool red); std::string asName; std::string name() const { - return this ? asName : std::string(); }; + return this ? + asName : + std::string(); }; - sound_source rsDiesielInc; // youBy - sound_source rscurve; // youBy // std::ofstream PneuLogFile; //zapis parametrow pneumatycznych // youBy - dym // TSmoke Smog; @@ -430,13 +462,17 @@ private: inline Math3D::vector3 RearPosition() { return vCoulpler[iDirection]; }; inline Math3D::vector3 AxlePositionGet() { - return iAxleFirst ? Axle1.pPosition : Axle0.pPosition; }; + return iAxleFirst ? + Axle1.pPosition : + Axle0.pPosition; }; inline Math3D::vector3 VectorFront() const { return vFront; }; - inline Math3D::vector3 VectorUp() { + inline Math3D::vector3 VectorUp() const { return vUp; }; inline Math3D::vector3 VectorLeft() const { return vLeft; }; + inline double const * Matrix() const { + return mMatrix.readArray(); }; inline double * Matrix() { return mMatrix.getArray(); }; inline double GetVelocity() { @@ -446,7 +482,9 @@ private: inline double GetWidth() const { return MoverParameters->Dim.W; }; inline TTrack * GetTrack() { - return (iAxleFirst ? Axle1.GetTrack() : Axle0.GetTrack()); }; + return (iAxleFirst ? + Axle1.GetTrack() : + Axle0.GetTrack()); }; // McZapkie-260202 void LoadMMediaFile(std::string BaseDir, std::string TypeName, std::string ReplacableSkin); @@ -455,13 +493,22 @@ private: return (Axle1.GetTrack() == MyTrack ? Axle1.GetDirection() : Axle0.GetDirection()); }; // zwraca kierunek pojazdu na torze z aktywną osą inline double RaDirectionGet() { - return iAxleFirst ? Axle1.GetDirection() : Axle0.GetDirection(); }; + return iAxleFirst ? + Axle1.GetDirection() : + Axle0.GetDirection(); }; // zwraca przesunięcie wózka względem Point1 toru z aktywną osią inline double RaTranslationGet() { - return iAxleFirst ? Axle1.GetTranslation() : Axle0.GetTranslation(); }; + return iAxleFirst ? + Axle1.GetTranslation() : + Axle0.GetTranslation(); }; // zwraca tor z aktywną osią inline TTrack * RaTrackGet() { - return iAxleFirst ? Axle1.GetTrack() : Axle0.GetTrack(); }; + return iAxleFirst ? + Axle1.GetTrack() : + Axle0.GetTrack(); }; + + void couple( int const Side ); + int uncouple( int const Side ); void CouplersDettach(double MinDist, int MyScanDir); void RadioStop(); void Damage(char flag); diff --git a/Event.cpp b/Event.cpp index 9db96750..534f618b 100644 --- a/Event.cpp +++ b/Event.cpp @@ -1022,34 +1022,27 @@ event_manager::CheckQuery() { return false; } case tp_Sound: { -#ifdef EU07_USE_OLD_SOUNDCODE + if( m_workevent->Params[ 9 ].tsTextSound == nullptr ) { + break; + } switch( m_workevent->Params[ 0 ].asInt ) { // trzy możliwe przypadki: case 0: { - m_workevent->Params[ 9 ].tsTextSound->Stop(); + m_workevent->Params[ 9 ].tsTextSound->stop(); break; } case 1: { - m_workevent->Params[ 9 ].tsTextSound->Play( - 1, - 0, - true, - m_workevent->Params[ 9 ].tsTextSound->vSoundPosition ); + m_workevent->Params[ 9 ].tsTextSound->play( sound_flags::exclusive ); break; } case -1: { - m_workevent->Params[ 9 ].tsTextSound->Play( - 1, - DSBPLAY_LOOPING, - true, - m_workevent->Params[ 9 ].tsTextSound->vSoundPosition ); + m_workevent->Params[ 9 ].tsTextSound->play( sound_flags::exclusive | sound_flags::looping ); break; } default: { break; } } -#endif break; } case tp_Disable: @@ -1574,14 +1567,19 @@ event_manager::InitLaunchers() { } } - launcher->Event1 = ( - launcher->asEvent1Name != "none" ? - simulation::Events.FindEvent( launcher->asEvent1Name ) : - nullptr ); - launcher->Event2 = ( - launcher->asEvent2Name != "none" ? - simulation::Events.FindEvent( launcher->asEvent2Name ) : - nullptr ); + if( launcher->asEvent1Name != "none" ) { + launcher->Event1 = simulation::Events.FindEvent( launcher->asEvent1Name ); + if( launcher->Event1 == nullptr ) { + ErrorLog( "Bad scenario: event launcher \"" + launcher->name() + "\" cannot find event \"" + launcher->asEvent1Name + "\"" ); + } + } + + if( launcher->asEvent2Name != "none" ) { + launcher->Event2 = simulation::Events.FindEvent( launcher->asEvent2Name ); + if( launcher->Event2 == nullptr ) { + ErrorLog( "Bad scenario: event launcher \"" + launcher->name() + "\" cannot find event \"" + launcher->asEvent2Name + "\"" ); + } + } } } diff --git a/Gauge.cpp b/Gauge.cpp index d785599e..c22d156d 100644 --- a/Gauge.cpp +++ b/Gauge.cpp @@ -19,24 +19,23 @@ http://mozilla.org/MPL/2.0/. #include "Model3d.h" #include "Timer.h" #include "logs.h" +#include "renderer.h" void TGauge::Init(TSubModel *NewSubModel, TGaugeType eNewType, double fNewScale, double fNewOffset, double fNewFriction, double fNewValue) { // ustawienie parametrów animacji submodelu - if (NewSubModel) - { // warunek na wszelki wypadek, gdyby się submodel nie - // podłączył + if (NewSubModel) { + // warunek na wszelki wypadek, gdyby się submodel nie podłączył fFriction = fNewFriction; fValue = fNewValue; fOffset = fNewOffset; fScale = fNewScale; SubModel = NewSubModel; eType = eNewType; - if (eType == gt_Digital) - { + if (eType == gt_Digital) { + TSubModel *sm = SubModel->ChildGet(); - do - { // pętla po submodelach potomnych i obracanie ich o kąt zależy od - // cyfry w (fValue) + do { + // pętla po submodelach potomnych i obracanie ich o kąt zależy od cyfry w (fValue) if (sm->pName.size()) { // musi mieć niepustą nazwę if (sm->pName[0] >= '0') @@ -48,10 +47,17 @@ void TGauge::Init(TSubModel *NewSubModel, TGaugeType eNewType, double fNewScale, } else // a banan może być z optymalizacją? NewSubModel->WillBeAnimated(); // wyłączenie ignowania jedynkowego transformu + // pass submodel location to defined sounds + auto const offset { model_offset() }; + m_soundfxincrease.offset( offset ); + m_soundfxdecrease.offset( offset ); + for( auto &soundfxrecord : m_soundfxvalues ) { + soundfxrecord.second.offset( offset ); + } } }; -bool TGauge::Load(cParser &Parser, TModel3d *md1, TModel3d *md2, double mul) { +bool TGauge::Load( cParser &Parser, TDynamicObject const *Owner, TModel3d *md1, TModel3d *md2, double mul ) { std::string submodelname, gaugetypename; double scale, offset, friction; @@ -83,10 +89,17 @@ bool TGauge::Load(cParser &Parser, TModel3d *md1, TModel3d *md2, double mul) { } } + // bind defined sounds with the button owner + m_soundfxincrease.owner( Owner ); + m_soundfxdecrease.owner( Owner ); + for( auto &soundfxrecord : m_soundfxvalues ) { + soundfxrecord.second.owner( Owner ); + } + scale *= mul; - TSubModel *submodel = md1->GetFromName( submodelname ); + TSubModel *submodel = md1->GetFromName( submodelname ); if( scale == 0.0 ) { - ErrorLog( "Scale of 0.0 defined for sub-model \"" + submodelname + "\" in 3d model \"" + md1->NameGet() + "\". Forcing scale of 1.0 to prevent division by 0" ); + ErrorLog( "Bad model: scale of 0.0 defined for sub-model \"" + submodelname + "\" in 3d model \"" + md1->NameGet() + "\". Forcing scale of 1.0 to prevent division by 0" ); scale = 1.0; } if (submodel) // jeśli nie znaleziony @@ -94,7 +107,7 @@ bool TGauge::Load(cParser &Parser, TModel3d *md1, TModel3d *md2, double mul) { else if (md2) // a jest podany drugi model (np. zewnętrzny) submodel = md2->GetFromName(submodelname); // to może tam będzie, co za różnica gdzie if( submodel == nullptr ) { - ErrorLog( "Failed to locate sub-model \"" + submodelname + "\" in 3d model \"" + md1->NameGet() + "\"" ); + ErrorLog( "Bad model: failed to locate sub-model \"" + submodelname + "\" in 3d model \"" + md1->NameGet() + "\"" ); } std::map gaugetypes { @@ -132,7 +145,7 @@ TGauge::Load_mapping( cParser &Input ) { if( indexstart != std::string::npos ) { m_soundfxvalues.emplace( std::stoi( key.substr( indexstart, indexend - indexstart ) ), - sound_source().deserialize( Input, sound_type::single ) ); + sound_source( sound_placement::internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE ).deserialize( Input, sound_type::single ) ); } } return true; // return value marks a key: value pair was extracted, nothing about whether it's recognized @@ -284,8 +297,6 @@ void TGauge::Update() { } }; -void TGauge::Render(){}; - void TGauge::AssignFloat(float *fValue) { cDataType = 'f'; @@ -317,4 +328,14 @@ void TGauge::UpdateValue() } }; +// returns offset of submodel associated with the button from the model centre +glm::vec3 +TGauge::model_offset() const { + + return ( + SubModel != nullptr ? + SubModel->offset( 1.f ) : + glm::vec3() ); +} + //--------------------------------------------------------------------------- diff --git a/Gauge.h b/Gauge.h index 081ffade..9f76b335 100644 --- a/Gauge.h +++ b/Gauge.h @@ -24,6 +24,30 @@ enum TGaugeType { // animowany wskaźnik, mogący przyjmować wiele stanów pośrednich class TGauge { +public: +// methods + TGauge() = default; + inline + void Clear() { *this = TGauge(); } + void Init(TSubModel *NewSubModel, TGaugeType eNewTyp, double fNewScale = 1, double fNewOffset = 0, double fNewFriction = 0, double fNewValue = 0); + bool Load(cParser &Parser, TDynamicObject const *Owner, TModel3d *md1, TModel3d *md2 = nullptr, double mul = 1.0); + void PermIncValue(double fNewDesired); + void IncValue(double fNewDesired); + void DecValue(double fNewDesired); + void UpdateValue( double fNewDesired ); + void UpdateValue( double fNewDesired, sound_source &Fallbacksound ); + void PutValue(double fNewDesired); + double GetValue() const; + void Update(); + void AssignFloat(float *fValue); + void AssignDouble(double *dValue); + void AssignInt(int *iValue); + void UpdateValue(); + // returns offset of submodel associated with the button from the model centre + glm::vec3 model_offset() const; +// members + TSubModel *SubModel; // McZapkie-310302: zeby mozna bylo sprawdzac czy zainicjowany poprawnie + private: // methods // imports member data pair from the config file @@ -44,32 +68,10 @@ private: double *dData { nullptr }; int *iData; }; - sound_source m_soundfxincrease; // sound associated with increasing control's value - sound_source m_soundfxdecrease; // sound associated with decreasing control's value + sound_source m_soundfxincrease { sound_placement::internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE }; // sound associated with increasing control's value + sound_source m_soundfxdecrease { sound_placement::internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE }; // sound associated with decreasing control's value std::map m_soundfxvalues; // sounds associated with specific values -public: -// methods - TGauge() = default; - inline - void Clear() { *this = TGauge(); } - void Init(TSubModel *NewSubModel, TGaugeType eNewTyp, double fNewScale = 1, double fNewOffset = 0, double fNewFriction = 0, double fNewValue = 0); - bool Load(cParser &Parser, TModel3d *md1, TModel3d *md2 = nullptr, double mul = 1.0); - void PermIncValue(double fNewDesired); - void IncValue(double fNewDesired); - void DecValue(double fNewDesired); - void UpdateValue( double fNewDesired ); - void UpdateValue( double fNewDesired, sound_source &Fallbacksound ); - void PutValue(double fNewDesired); - double GetValue() const; - void Update(); - void Render(); - void AssignFloat(float *fValue); - void AssignDouble(double *dValue); - void AssignInt(int *iValue); - void UpdateValue(); -// members - TSubModel *SubModel; // McZapkie-310302: zeby mozna bylo sprawdzac czy zainicjowany poprawnie }; //--------------------------------------------------------------------------- diff --git a/Globals.cpp b/Globals.cpp index 8bf348b4..adaa1160 100644 --- a/Globals.cpp +++ b/Globals.cpp @@ -132,6 +132,8 @@ bool Global::FullPhysics { true }; // full calculations performed for each simul // parametry testowe (do testowania scenerii i obiektów) bool Global::bWireFrame = false; bool Global::bSoundEnabled = true; +float Global::AudioVolume = 1.0f; + int Global::iWriteLogEnabled = 3; // maska bitowa: 1-zapis do pliku, 2-okienko, 4-nazwy torów bool Global::MultipleLogs{ false }; diff --git a/Globals.h b/Globals.h index 9c7fe26a..3d88a3f1 100644 --- a/Globals.h +++ b/Globals.h @@ -166,6 +166,7 @@ public: static bool bFreeFly; static bool bWireFrame; static bool bSoundEnabled; + static float AudioVolume; // McZapkie-131202 static bool bAdjustScreenFreq; static bool bEnableTraction; diff --git a/McZapkie/MOVER.h b/McZapkie/MOVER.h index e63e297d..241295c0 100644 --- a/McZapkie/MOVER.h +++ b/McZapkie/MOVER.h @@ -197,14 +197,15 @@ static int const s_SHPebrake = 64; //hamuje static int const s_CAtest = 128; /*dzwieki*/ -static int const sound_none = 0; -static int const sound_loud = 1; -static int const sound_couplerstretch = 2; -static int const sound_bufferclamp = 4; -static int const sound_bufferbump = 8; -static int const sound_relay = 16; -static int const sound_manyrelay = 32; -static int const sound_brakeacc = 64; +enum sound { + none, + loud = 0x1, + couplerstretch = 0x2, + bufferclash = 0x4, + relay = 0x10, + parallel = 0x20, + pneumatic = 0x40 +}; //szczególne typy pojazdów (inna obsługa) dla zmiennej TrainType //zamienione na flagi bitowe, aby szybko wybierać grupę (np. EZT+SZT) @@ -602,6 +603,8 @@ struct TCoupling { power_coupling power_high; power_coupling power_low; // TODO: implement this + + int sounds { 0 }; // sounds emitted by the coupling devices }; class TMoverParameters @@ -1167,6 +1170,7 @@ public: bool DoorLeft(bool State); //obsluga drzwi lewych bool DoorRight(bool State); //obsluga drzwi prawych bool DoorBlockedFlag(void); //sprawdzenie blokady drzwi + bool signal_departure( bool const State, int const Notify = range::consist ); // toggles departure warning /* funkcje dla samochodow*/ bool ChangeOffsetH(double DeltaOffset); diff --git a/McZapkie/Mover.cpp b/McZapkie/Mover.cpp index 5a1a95bf..e339284f 100644 --- a/McZapkie/Mover.cpp +++ b/McZapkie/Mover.cpp @@ -1369,11 +1369,11 @@ double TMoverParameters::ComputeMovement(double dt, double dt1, const TTrackShap if (EngineType == ElectricSeriesMotor) if (AutoRelayCheck()) - SetFlag(SoundFlag, sound_relay); + SetFlag(SoundFlag, sound::relay); if (EngineType == DieselEngine) if (dizel_Update(dt)) - SetFlag(SoundFlag, sound_relay); + SetFlag(SoundFlag, sound::relay); // uklady hamulcowe: if (VeselVolume > 0) Compressor = CompressedVolume / VeselVolume; @@ -1530,7 +1530,7 @@ double TMoverParameters::FastComputeMovement(double dt, const TTrackShape &Shape if (EngineType == DieselEngine) if (dizel_Update(dt)) - SetFlag(SoundFlag, sound_relay); + SetFlag(SoundFlag, sound::relay); // uklady hamulcowe: if (VeselVolume > 0) Compressor = CompressedVolume / VeselVolume; @@ -1703,7 +1703,7 @@ bool TMoverParameters::IncMainCtrl(int CtrlSpeed) if( RList[ MainCtrlPos ].Bn > 1 ) { if( true == MaxCurrentSwitch( false )) { // wylaczanie wysokiego rozruchu - SetFlag( SoundFlag, sound_relay ); + SetFlag( SoundFlag, sound::relay ); } // Q TODO: // if (EngineType=ElectricSeriesMotor) and (MainCtrlPos=1) // then @@ -2988,6 +2988,10 @@ bool TMoverParameters::BrakeDelaySwitch(int BDS) } else rBDS = false; + if( true == rBDS ) { + // if setting was changed emit the sound of pneumatic relay + SetFlag( SoundFlag, sound::pneumatic ); + } return rBDS; } @@ -3086,8 +3090,8 @@ void TMoverParameters::CompressorCheck(double dt) else { CompressedVolume = CompressedVolume * 0.8; - SetFlag(SoundFlag, sound_relay | sound_loud); - // SetFlag(SoundFlag, sound_loud); + SetFlag(SoundFlag, sound::relay | sound::loud); + // SetFlag(SoundFlag, sound::loud); } } } @@ -4078,6 +4082,31 @@ double TMoverParameters::CouplerForce(int CouplerN, double dt) // tempdist:=tempdist+CoupleDist; //ABu: proby szybkiego naprawienia bledu } + dV = V - (double)DirPatch( CouplerN, CNext ) * Couplers[ CouplerN ].Connected->V; + absdV = abs( dV ); + // potentially generate sounds on clash or stretch + if( ( newdist < 0.0 ) + && ( Couplers[ CouplerN ].Dist > newdist ) + && ( dV < -0.5 ) ) { + // 090503: dzwieki pracy zderzakow + SetFlag( + Couplers[ CouplerN ].sounds, + ( absdV > 5.0 ? + ( sound::bufferclash | sound::loud ) : + sound::bufferclash ) ); + } + else if( ( Couplers[ CouplerN ].CouplingFlag != coupling::faux ) + && ( newdist > 0.001 ) + && ( Couplers[ CouplerN ].Dist <= 0.001 ) + && ( absdV > 0.005 ) ) { + // 090503: dzwieki pracy sprzegu + SetFlag( + Couplers[ CouplerN ].sounds, + ( absdV > 0.1 ? + ( sound::couplerstretch | sound::loud ) : + sound::couplerstretch ) ); + } + // blablabla // ABu: proby znalezienia problemu ze zle odbijajacymi sie skladami //if (Couplers[CouplerN].CouplingFlag=ctrain_virtual) and (newdist>0) then @@ -4099,23 +4128,6 @@ double TMoverParameters::CouplerForce(int CouplerN, double dt) Couplers[CouplerN].Connected->Couplers[CNext].FmaxB) * CouplerTune / 2.0; } - dV = V - (double)DirPatch(CouplerN, CNext) * Couplers[CouplerN].Connected->V; - absdV = abs(dV); - if ((newdist < -0.001) && (Couplers[CouplerN].Dist >= -0.001) && - (absdV > 0.010)) // 090503: dzwieki pracy zderzakow - { - if (SetFlag(SoundFlag, sound_bufferclamp)) - if (absdV > 0.5) - SetFlag(SoundFlag, sound_loud); - } - else if ((newdist > 0.002) && (Couplers[CouplerN].Dist <= 0.002) && - (absdV > 0.005)) // 090503: dzwieki pracy sprzegu - { - if (Couplers[CouplerN].CouplingFlag > 0) - if (SetFlag(SoundFlag, sound_couplerstretch)) - if (absdV > 0.1) - SetFlag(SoundFlag, sound_loud); - } distDelta = abs(newdist) - abs(Couplers[CouplerN].Dist); // McZapkie-191103: poprawka na histereze Couplers[CouplerN].Dist = newdist; @@ -4919,7 +4931,7 @@ bool TMoverParameters::FuseOn(void) { FuseFlag = false; // wlaczenie ponowne obwodu FO = true; - SetFlag(SoundFlag, sound_relay | sound_loud); + SetFlag(SoundFlag, sound::relay | sound::loud); } } return FO; @@ -4935,7 +4947,7 @@ void TMoverParameters::FuseOff(void) { FuseFlag = true; EventFlag = true; - SetFlag(SoundFlag, sound_relay | sound_loud); + SetFlag(SoundFlag, sound::relay | sound::loud); } } @@ -5206,7 +5218,7 @@ bool TMoverParameters::AutoRelayCheck(void) // MainCtrlActualPos:=MainCtrlPos; //hunter-111012: // szybkie wchodzenie na bezoporowa (303E) OK = true; - SetFlag(SoundFlag, sound_manyrelay | sound_loud); + SetFlag(SoundFlag, sound::parallel | sound::loud); } else if ((LastRelayTime > CtrlDelay) && (ARFASI)) { @@ -5240,13 +5252,13 @@ bool TMoverParameters::AutoRelayCheck(void) if ((RList[MainCtrlActualPos].R == 0) && (!(MainCtrlActualPos == MainCtrlPosNo))) // wejscie na bezoporowa { - SetFlag(SoundFlag, sound_manyrelay | sound_loud); + SetFlag(SoundFlag, sound::parallel | sound::loud); } else if ((RList[MainCtrlActualPos].R > 0) && (RList[MainCtrlActualPos - 1].R == 0)) // wejscie na drugi uklad { - SetFlag(SoundFlag, sound_manyrelay); + SetFlag(SoundFlag, sound::parallel); } } } @@ -5259,7 +5271,7 @@ bool TMoverParameters::AutoRelayCheck(void) // MainCtrlActualPos:=MainCtrlPos; //hunter-111012: // szybkie wchodzenie na bezoporowa (303E) OK = true; - SetFlag(SoundFlag, sound_manyrelay); + SetFlag(SoundFlag, sound::parallel); } else if (LastRelayTime > CtrlDownDelay) { @@ -5272,7 +5284,7 @@ bool TMoverParameters::AutoRelayCheck(void) if (RList[MainCtrlActualPos].R == 0) // dzwieki schodzenia z bezoporowej} { - SetFlag(SoundFlag, sound_manyrelay); + SetFlag(SoundFlag, sound::parallel); } } } @@ -5304,7 +5316,7 @@ bool TMoverParameters::AutoRelayCheck(void) StLinFlag = true; // ybARC - zalaczenie stycznikow liniowych MainCtrlActualPos = 1; DelayCtrlFlag = false; - SetFlag(SoundFlag, sound_relay | sound_loud); + SetFlag(SoundFlag, sound::relay | sound::loud); OK = true; } } @@ -5858,6 +5870,32 @@ bool TMoverParameters::DoorRight(bool State) return DR; } +// toggles departure warning +bool +TMoverParameters::signal_departure( bool const State, int const Notify ) { + + if( DepartureSignal == State ) { + // TBD: should the command be passed to other vehicles regardless of whether it affected the primary target? + return false; + } + + DepartureSignal = State; + if( Notify != range::local ) { + // wysłanie wyłączenia do pozostałych? + SendCtrlToNext( + "DepartureSignal", + ( State == true ? + 1 : + 0 ), + CabNo, + ( Notify == range::unit ? + ctrain_controll | ctrain_depot : + ctrain_controll ) ); + } + + return true; +} + // ************************************************************************************************* // Q: 20160713 // Przesuwa pojazd o podaną wartość w bok względem toru (dla samochodów) @@ -8227,6 +8265,13 @@ bool TMoverParameters::RunCommand( std::string Command, double CValue1, double C } OK = SendCtrlToNext( Command, CValue1, CValue2, Couplertype ); } + else if( Command == "DepartureSignal" ) { + DepartureSignal = ( + CValue1 == 1 ? + true : + false ); + OK = SendCtrlToNext( Command, CValue1, CValue2, Couplertype ); + } else if (Command == "PantFront") /*Winger 160204*/ { // Ra: uwzględnić trzeba jeszcze zgodność sprzęgów // Czemu EZT ma być traktowane inaczej? Ukrotnienie ma, a człon może być odwrócony @@ -8346,6 +8391,8 @@ bool TMoverParameters::RunCommand( std::string Command, double CValue1, double C if( true == Hamulec->SetBDF( brakesetting ) ) { BrakeDelayFlag = brakesetting; OK = true; + // if setting was changed emit the sound of pneumatic relay + SetFlag( SoundFlag, sound::pneumatic ); } else { OK = false; diff --git a/McZapkie/hamulce.cpp b/McZapkie/hamulce.cpp index 7951791b..fdfa2bbc 100644 --- a/McZapkie/hamulce.cpp +++ b/McZapkie/hamulce.cpp @@ -578,47 +578,58 @@ void TESt::CheckReleaser( double const dt ) } } -void TESt::CheckState( double const BCP, double &dV1 ) -{ - double VVP; - double BVP; - double CVP; +void TESt::CheckState( double const BCP, double &dV1 ) { - BVP = BrakeRes->P(); - VVP = ValveRes->P(); - // if (BVPP() - 0.0; + double const VVP { ValveRes->P() }; + double const BVP { BrakeRes->P() }; + double const CVP { CntrlRes->P() }; // sprawdzanie stanu - if (((BrakeStatus & b_hld) == b_hld) && (BCP > 0.25)) - if ((VVP + 0.003 + BCP / BVM < CVP)) - BrakeStatus |= b_on; // hamowanie stopniowe - else if ((VVP - 0.003 + (BCP - 0.1) / BVM > CVP)) - BrakeStatus &= ~( b_on | b_hld ); // luzowanie - else if ((VVP + BCP / BVM > CVP)) - BrakeStatus &= ~b_on; // zatrzymanie napelaniania - else - ; - else if ((VVP + 0.10 < CVP) && (BCP < 0.25)) // poczatek hamowania - { - if ((BrakeStatus & b_hld) == b_off) - { - ValveRes->CreatePress(0.02 * VVP); - SoundFlag |= sf_Acc; - ValveRes->Act(); + if( BCP > 0.25 ) { + + if( ( BrakeStatus & b_hld ) == b_hld ) { + + if( ( VVP + 0.003 + BCP / BVM ) < CVP ) { + // hamowanie stopniowe + BrakeStatus |= b_on; + } + else { + if( ( VVP + BCP / BVM ) > CVP ) { + // zatrzymanie napelaniania + BrakeStatus &= ~b_on; + } + if( ( VVP - 0.003 + ( BCP - 0.1 ) / BVM ) > CVP ) { + // luzowanie + BrakeStatus &= ~( b_on | b_hld ); + } + } } - BrakeStatus |= (b_on | b_hld); + else { - // ValveRes.CreatePress(0); - // dV1:=1; + if( ( VVP + BCP / BVM < CVP ) + && ( ( CVP - VVP ) * BVM > 0.25 ) ) { + // zatrzymanie luzowanie + BrakeStatus |= b_hld; + } + } } - else if ((VVP + (BCP - 0.1) / BVM < CVP) && ((CVP - VVP) * BVM > 0.25) && - (BCP > 0.25)) // zatrzymanie luzowanie - BrakeStatus |= b_hld; + else { - if ((BrakeStatus & b_hld) == b_off) + if( VVP + 0.1 < CVP ) { + // poczatek hamowania + if( ( BrakeStatus & b_hld ) == 0 ) { + // przyspieszacz + ValveRes->CreatePress( 0.02 * VVP ); + SoundFlag |= sf_Acc; + ValveRes->Act(); + } + BrakeStatus |= ( b_on | b_hld ); + } + } + + if( ( BrakeStatus & b_hld ) == 0 ) { SoundFlag |= sf_CylU; + } } double TESt::CVs( double const BP ) @@ -828,6 +839,10 @@ double TEStEP2::GetPF( double const PP, double const dt, double const Vel ) else if ((VVP + BCP / BVM < CVP - 0.12) && (BCP > 0.25)) // zatrzymanie luzowanie BrakeStatus |= b_hld; + if( ( BrakeStatus & b_hld ) == 0 ) { + SoundFlag |= sf_CylU; + } + // przeplyw ZS <-> PG if ((BVP < CVP - 0.2) || (BrakeStatus != b_off) || (BCP > 0.25)) temp = 0; @@ -1211,6 +1226,40 @@ double TLSt::GetPF( double const PP, double const dt, double const Vel ) double dV1{ 0.0 }; // sprawdzanie stanu + // NOTE: partial copypaste from checkstate() of base class + // TODO: clean inheritance for checkstate() and checkreleaser() and reuse these instead of manual copypaste + if( ( ( BrakeStatus & b_hld ) == b_hld ) && ( BCP > 0.25 ) ) { + if( ( VVP + 0.003 + BCP / BVM < CVP ) ) { + // hamowanie stopniowe + BrakeStatus |= b_on; + } + else if( ( VVP - 0.003 + ( BCP - 0.1 ) / BVM > CVP ) ) { + // luzowanie + BrakeStatus &= ~( b_on | b_hld ); + } + else if( ( VVP + BCP / BVM > CVP ) ) { + // zatrzymanie napelaniania + BrakeStatus &= ~b_on; + } + } + else if ((VVP + 0.10 < CVP) && (BCP < 0.25)) { + // poczatek hamowania + if ((BrakeStatus & b_hld) == b_off) + { + SoundFlag |= sf_Acc; + } + BrakeStatus |= (b_on | b_hld); + } + else if( ( VVP + ( BCP - 0.1 ) / BVM < CVP ) + && ( ( CVP - VVP ) * BVM > 0.25 ) + && ( BCP > 0.25 ) ) { + // zatrzymanie luzowanie + BrakeStatus |= b_hld; + } + if( ( BrakeStatus & b_hld ) == 0 ) { + SoundFlag |= sf_CylU; + } + // equivalent of checkreleaser() in the base class? if( ( BrakeStatus & b_rls ) == b_rls ) { if( CVP < 0.0 ) { BrakeStatus &= ~b_rls; @@ -1219,18 +1268,11 @@ double TLSt::GetPF( double const PP, double const dt, double const Vel ) { // 008 dV = PF1( CVP, BCP, 0.024 ) * dt; CntrlRes->Flow( dV ); -/* - // NOTE: attempted fix, disabled because it breaks when releaser is used while releasing breakes - dV = PF1(CVP, VVP, 0.024) * dt; - CntrlRes->Flow( dV ); - dV1 = dV; //minus potem jest - ImplsRes->Flow( -dV1 ); -*/ } } - double temp; // przeplyw ZS <-> PG + double temp; if (((CVP - BCP) * BVM > 0.5)) temp = 0.0; else if ((VVP > CVP + 0.4)) @@ -1888,22 +1930,51 @@ void TKE::CheckState( double const BCP, double &dV1 ) CVP = CntrlRes->P(); // sprawdzanie stanu - if ((BrakeStatus & b_hld) == b_hld) - if ((VVP + 0.003 + BCP / BVM < CVP)) - BrakeStatus |= b_on; // hamowanie stopniowe; - else if ((VVP - 0.003 + BCP / BVM > CVP)) - BrakeStatus &= ~( b_on | b_hld ); // luzowanie; - else if ((VVP + BCP / BVM > CVP)) - BrakeStatus &= ~b_on; // zatrzymanie napelaniania; - else - ; - else if ((VVP + 0.10 < CVP) && (BCP < 0.1)) // poczatek hamowania - { - BrakeStatus |= (b_on | b_hld); - ValveRes->CreatePress(0.8 * VVP); // przyspieszacz + if( BCP > 0.1 ) { + + if( ( BrakeStatus & b_hld ) == b_hld ) { + + if( ( VVP + 0.003 + BCP / BVM ) < CVP ) { + // hamowanie stopniowe; + BrakeStatus |= b_on; + } + else { + if( ( VVP + BCP / BVM ) > CVP ) { + // zatrzymanie napelaniania; + BrakeStatus &= ~b_on; + } + if( ( VVP - 0.003 + BCP / BVM ) > CVP ) { + // luzowanie; + BrakeStatus &= ~( b_on | b_hld ); + } + } + } + else { + + if( ( VVP + BCP / BVM < CVP ) + && ( ( CVP - VVP ) * BVM > 0.25 ) ) { + // zatrzymanie luzowanie + BrakeStatus |= b_hld; + } + } + } + else { + + if( VVP + 0.1 < CVP ) { + // poczatek hamowania + if( ( BrakeStatus & b_hld ) == 0 ) { + // przyspieszacz + ValveRes->CreatePress( 0.8 * VVP ); + SoundFlag |= sf_Acc; + ValveRes->Act(); + } + BrakeStatus |= ( b_on | b_hld ); + } + } + + if( ( BrakeStatus & b_hld ) == 0 ) { + SoundFlag |= sf_CylU; } - else if ((VVP + BCP / BVM < CVP) && ((CVP - VVP) * BVM > 0.25)) // zatrzymanie luzowanie - BrakeStatus |= b_hld; } double TKE::CVs( double const BP ) diff --git a/Model3d.cpp b/Model3d.cpp index 50c9c0d3..d2e5e6c8 100644 --- a/Model3d.cpp +++ b/Model3d.cpp @@ -1050,14 +1050,17 @@ void TSubModel::ColorsSet( glm::vec3 const &Ambient, glm::vec3 const &Diffuse, g */ }; -void TSubModel::ParentMatrix(float4x4 *m) -{ // pobranie transformacji względem wstawienia modelu - // jeśli nie zostało wykonane Init() (tzn. zaraz po wczytaniu T3D), to - // dodatkowy obrót - // obrót T3D jest wymagany np. do policzenia wysokości pantografów - *m = float4x4(*fMatrix); // skopiowanie, bo będziemy mnożyć - // m(3)[1]=m[3][1]+0.054; //w górę o wysokość ślizgu (na razie tak) - TSubModel *sm = this; +void TSubModel::ParentMatrix( float4x4 *m ) const { // pobranie transformacji względem wstawienia modelu + // jeśli nie zostało wykonane Init() (tzn. zaraz po wczytaniu T3D), + // to dodatkowy obrót obrót T3D jest wymagany np. do policzenia wysokości pantografów + if( fMatrix != nullptr ) { + // skopiowanie, bo będziemy mnożyć + *m = float4x4( *fMatrix ); + } + else { + m->Identity(); + } + auto *sm = this; while (sm->Parent) { // przenieść tę funkcję do modelu if (sm->Parent->GetMatrix()) @@ -1160,6 +1163,37 @@ TSubModel *TModel3d::GetFromName(std::string const &Name) } }; +// returns offset vector from root +glm::vec3 +TSubModel::offset( float const Geometrytestoffsetthreshold ) const { + + float4x4 parentmatrix; + ParentMatrix( &parentmatrix ); + + auto offset { glm::vec3 { glm::make_mat4( parentmatrix.readArray() ) * glm::vec4 { 0, 0, 0, 1 } } }; + + if( glm::length2( offset ) < Geometrytestoffsetthreshold ) { + // offset of zero generally means the submodel has optimized identity matrix + // for such cases we resort to an estimate from submodel geometry + // TODO: do proper bounding area calculation for submodel when loading mesh and grab the centre point from it here + if( m_geometry != null_handle ) { + auto const &vertices{ GfxRenderer.Vertices( m_geometry ) }; + if( false == vertices.empty() ) { + // transformation matrix for the submodel can still contain rotation and/or scaling, + // so we pass the vertex positions through it rather than just grab them directly + offset = glm::vec3(); + auto const vertexfactor { 1.f / vertices.size() }; + auto const transformationmatrix { glm::make_mat4( parentmatrix.readArray() ) }; + for( auto const &vertex : vertices ) { + offset += glm::vec3 { transformationmatrix * glm::vec4 { vertex.position, 1 } } * vertexfactor; + } + } + } + } + + return offset; +} + bool TModel3d::LoadFromFile(std::string const &FileName, bool dynamic) { // wczytanie modelu z pliku diff --git a/Model3d.h b/Model3d.h index 57a01408..8244835d 100644 --- a/Model3d.h +++ b/Model3d.h @@ -124,7 +124,9 @@ private: TSubModel *Next { nullptr }; TSubModel *Child { nullptr }; +public: // temporary access, clean this up during refactoring gfx::geometry_handle m_geometry { 0, 0 }; // geometry of the submodel +private: material_handle m_material { null_handle }; // numer tekstury, -1 wymienna, 0 brak bool bWire { false }; // nie używane, ale wczytywane float Opacity { 1.0f }; @@ -138,9 +140,7 @@ public: // chwilowo float m_boundingradius { 0 }; size_t iAnimOwner{ 0 }; // roboczy numer egzemplarza, który ustawił animację TAnimType b_aAnim{ at_None }; // kody animacji oddzielnie, bo zerowane -public: float4x4 *mAnimMatrix{ nullptr }; // macierz do animacji kwaternionowych (należy do AnimContainer) -public: TSubModel **smLetter{ nullptr }; // wskaźnik na tablicę submdeli do generoania tekstu (docelowo zapisać do E3D) TSubModel *Parent{ nullptr }; // nadrzędny, np. do wymnażania macierzy int iVisible{ 1 }; // roboczy stan widoczności @@ -172,7 +172,10 @@ public: void SetRotateIK1(float3 vNewAngles); TSubModel * GetFromName( std::string const &search, bool i = true ); inline float4x4 * GetMatrix() { return fMatrix; }; - inline void Hide() { iVisible = 0; }; + inline float4x4 const * GetMatrix() const { return fMatrix; }; + // returns offset vector from root + glm::vec3 offset( float const Geometrytestoffsetthreshold = 0.f ) const; + inline void Hide() { iVisible = 0; }; void create_geometry( std::size_t &Dataoffset, gfx::geometrybank_handle const &Bank ); int FlagsCheck(); @@ -200,7 +203,7 @@ public: return *(fMatrix->TranslationGet()) + Child->Translation1Get(); } material_handle GetMaterial() const { return m_material; } - void ParentMatrix(float4x4 *m); + void ParentMatrix(float4x4 *m) const; float MaxY( float4x4 const &m ); void deserialize(std::istream&); diff --git a/Train.cpp b/Train.cpp index 81ceec1d..db9f5a56 100644 --- a/Train.cpp +++ b/Train.cpp @@ -1540,8 +1540,6 @@ void TTrain::OnCommand_linebreakertoggle( TTrain *Train, command_data const &Com // TODO: consider arranging a better way to start the diesel engines if( Train->mvControlled->EngineType == DieselEngine ) { if( Train->mvControlled->MainSwitch( true ) ) { - // sound feedback, engine start for diesel vehicle - Train->dsbDieselIgnition.play(); // side-effects Train->mvControlled->ConverterSwitch( ( Train->ggConverterButton.GetValue() > 0.5 ) || ( Train->mvControlled->ConverterStart == start::automatic ) ); Train->mvControlled->CompressorSwitch( Train->ggCompressorButton.GetValue() > 0.5 ); @@ -1584,7 +1582,9 @@ void TTrain::OnCommand_linebreakertoggle( TTrain *Train, command_data const &Com } // play sound immediately when the switch is hit, not after release if( Train->fMainRelayTimer > 0.0f ) { +#ifdef EU07_USE_OLD_SOUNDCODE Train->dsbRelay.play(); +#endif Train->fMainRelayTimer = 0.0f; } } @@ -1595,7 +1595,9 @@ void TTrain::OnCommand_linebreakertoggle( TTrain *Train, command_data const &Com // ...after opening circuit, or holding for too short time to close it // hunter-091012: przeniesione z mover.pas, zeby dzwiek sie nie zapetlal, if( Train->fMainRelayTimer > 0.0f ) { +#ifdef EU07_USE_OLD_SOUNDCODE Train->dsbRelay.play(); +#endif Train->fMainRelayTimer = 0.0f; } // we don't exactly know which of the two buttons was used, so reset both @@ -1920,7 +1922,9 @@ void TTrain::OnCommand_motorconnectorsopen( TTrain *Train, command_data const &C Train->ggStLinOffButton.UpdateValue( 1.0, Train->dsbSwitch ); // effect if( true == Train->mvControlled->StLinFlag ) { +#ifdef EU07_USE_OLD_SOUNDCODE Train->dsbRelay.play(); +#endif } // yBARC - zmienione na przeciwne, bo true to zalaczone Train->mvControlled->StLinFlag = false; @@ -2729,14 +2733,14 @@ void TTrain::OnCommand_departureannounce( TTrain *Train, command_data const &Com // only reacting to press, so the sound can loop uninterrupted if( false == Train->mvControlled->DepartureSignal ) { // turn on - Train->mvControlled->DepartureSignal = true; + Train->mvControlled->signal_departure( true ); // visual feedback Train->ggDepartureSignalButton.UpdateValue( 1.0, Train->dsbSwitch ); } } else if( Command.action == GLFW_RELEASE ) { // turn off - Train->mvControlled->DepartureSignal = false; + Train->mvControlled->signal_departure( false ); // visual feedback Train->ggDepartureSignalButton.UpdateValue( 0.0, Train->dsbSwitch ); } @@ -2899,18 +2903,23 @@ void TTrain::OnKeyDown(int cKey) CouplNr = -2; temp = (DynamicObject->ABuScanNearestObject(DynamicObject->GetTrack(), 1, 1500, CouplNr)); } - if (temp) + if( (temp) + && (temp != DynamicObject ) ) + // NOTE: the command to occupied vehicle will be passed through new command system, so we skip it here + // TODO: clean this up when whole control system is unified { - if (Global::ctrlState) - if (temp->MoverParameters->BrakeDelaySwitch(bdelay_R + bdelay_M)) - { - dsbPneumaticRelay.play(); - } - else - ; - else if (temp->MoverParameters->BrakeDelaySwitch(bdelay_P)) - { + if( ( temp->MoverParameters->BrakeDelayFlag & bdelay_M ) != 0 ) { + // can't speed it up any more than this + return; + } + auto const fasterbrakesetting = ( + temp->MoverParameters->BrakeDelayFlag < bdelay_R ? + temp->MoverParameters->BrakeDelayFlag << 1 : + temp->MoverParameters->BrakeDelayFlag | bdelay_M ); + if( true == temp->MoverParameters->BrakeDelaySwitch( fasterbrakesetting ) ) { +#ifdef EU07_USE_OLD_SOUNDCODE dsbPneumaticRelay.play(); +#endif } } } @@ -3002,8 +3011,7 @@ void TTrain::OnKeyDown(int cKey) // bylo mozna ustawic dowolny // wagon int CouplNr = -2; - if( !FreeFlyModeFlag ) - { + if( false == FreeFlyModeFlag ) { #ifdef EU07_USE_OLD_COMMAND_SYSTEM if( Global::ctrlState ) if (mvOccupied->BrakeDelaySwitch(bdelay_R)) @@ -3018,8 +3026,7 @@ void TTrain::OnKeyDown(int cKey) } #endif } - else - { + else { TDynamicObject *temp; temp = (DynamicObject->ABuScanNearestObject(DynamicObject->GetTrack(), -1, 1500, CouplNr)); if (temp == NULL) @@ -3027,18 +3034,24 @@ void TTrain::OnKeyDown(int cKey) CouplNr = -2; temp = (DynamicObject->ABuScanNearestObject(DynamicObject->GetTrack(), 1, 1500, CouplNr)); } - if (temp) + if( (temp) + && (temp != DynamicObject ) ) + // NOTE: the command to occupied vehicle will be passed through new command system, so we skip it here + // TODO: clean this up when whole control system is unified { - if (Global::ctrlState) - if (temp->MoverParameters->BrakeDelaySwitch(bdelay_R)) - { - dsbPneumaticRelay.play(); - } - else - ; - else if (temp->MoverParameters->BrakeDelaySwitch(bdelay_G)) - { + if( temp->MoverParameters->BrakeDelayFlag == bdelay_G ) { + // can't slow it down any more than this + return; + } + auto const slowerbrakesetting = ( + temp->MoverParameters->BrakeDelayFlag < bdelay_M ? + temp->MoverParameters->BrakeDelayFlag >> 1 : + temp->MoverParameters->BrakeDelayFlag ^ bdelay_M ); + + if( true == temp->MoverParameters->BrakeDelaySwitch( slowerbrakesetting ) ) { +#ifdef EU07_USE_OLD_SOUNDCODE dsbPneumaticRelay.play(); +#endif } } } @@ -3082,221 +3095,52 @@ void TTrain::OnKeyDown(int cKey) false; // wyjście z maszynowego wyłącza sprężarkę pomocniczą */ } - else if (cKey == Global::Keys[k_Couple]) - { // ABu051104: male zmiany, zeby mozna bylo laczyc odlegle wagony - // da sie zoptymalizowac, ale nie ma na to czasu :( - if (iCabn > 0) - { - if (!FreeFlyModeFlag) // tryb 'kabinowy' - { /* - if (mvControlled->Couplers[iCabn-1].CouplingFlag==0) - { -if - (mvControlled->Attach(iCabn-1,mvControlled->Couplers[iCabn-1].Connected,ctrain_coupler)) - { - dsbCouplerAttach->SetVolume(DSBVOLUME_MAX); - dsbCouplerAttach->Play(0,0,0); - //ABu: aha, a guzik, nie dziala i nie bedzie, a przydalo by sie - cos takiego: - //DynamicObject->NextConnected=mvControlled->Couplers[iCabn-1].Connected; - //DynamicObject->PrevConnected=mvControlled->Couplers[iCabn-1].Connected; - } - } - else - if - (!TestFlag(mvControlled->Couplers[iCabn-1].CouplingFlag,ctrain_pneumatic)) - { - //ABu021104: zeby caly czas bylo widac sprzegi: -if - (mvControlled->Attach(iCabn-1,mvControlled->Couplers[iCabn-1].Connected,mvControlled->Couplers[iCabn-1].CouplingFlag+ctrain_pneumatic)) -//if - (mvControlled->Attach(iCabn-1,mvControlled->Couplers[iCabn-1].Connected,ctrain_pneumatic)) - { - rsHiss.Play(1,DSBPLAY_LOOPING,true,DynamicObject->GetPosition()); - } - }*/ - } - else - { // tryb freefly - int CouplNr = -1; // normalnie żaden ze sprzęgów - TDynamicObject *tmp; - tmp = DynamicObject->ABuScanNearestObject(DynamicObject->GetTrack(), 1, 1500, CouplNr); - if (tmp == nullptr) - tmp = DynamicObject->ABuScanNearestObject(DynamicObject->GetTrack(), -1, 1500, CouplNr); - if( ( CouplNr != -1 ) - && ( tmp != nullptr ) - && ( tmp->MoverParameters->Couplers[ CouplNr ].Connected != nullptr ) ) - { - if (tmp->MoverParameters->Couplers[CouplNr].CouplingFlag == 0) // najpierw hak - { - if ((tmp->MoverParameters->Couplers[CouplNr].Connected->Couplers[CouplNr].AllowedFlag - & tmp->MoverParameters->Couplers[CouplNr].AllowedFlag - & ctrain_coupler) == ctrain_coupler) - if (tmp->MoverParameters->Attach( - CouplNr, 2, - tmp->MoverParameters->Couplers[CouplNr].Connected, - ctrain_coupler)) - { - // tmp->MoverParameters->Couplers[CouplNr].Render=true; - // //podłączony sprzęg będzie widoczny - if (DynamicObject->Mechanik) // na wszelki wypadek - DynamicObject->Mechanik->CheckVehicles(Connect); // aktualizacja flag kierunku w składzie - dsbCouplerAttach.play(); - // one coupling type per key press - return; - } - else - WriteLog("Mechanical coupling failed."); - } - if (!TestFlag(tmp->MoverParameters->Couplers[CouplNr].CouplingFlag, ctrain_pneumatic)) // pneumatyka - { - if ((tmp->MoverParameters->Couplers[CouplNr].Connected->Couplers[CouplNr].AllowedFlag - & tmp->MoverParameters->Couplers[CouplNr].AllowedFlag - & ctrain_pneumatic) == ctrain_pneumatic) - if (tmp->MoverParameters->Attach( - CouplNr, 2, - tmp->MoverParameters->Couplers[CouplNr].Connected, - (tmp->MoverParameters->Couplers[CouplNr].CouplingFlag | ctrain_pneumatic))) - { - // TODO: dedicated 'click' sound for connecting cable-type connections - dsbCouplerDetach.play(); -// rsHiss.Play( 1, DSBPLAY_LOOPING, true, tmp->GetPosition() ); - DynamicObject->SetPneumatic(CouplNr != 0, true); // Ra: to mi się nie podoba !!!! - tmp->SetPneumatic(CouplNr != 0, true); - // one coupling type per key press - return; - } - } - if (!TestFlag(tmp->MoverParameters->Couplers[CouplNr].CouplingFlag, ctrain_scndpneumatic)) // zasilajacy - { - if ((tmp->MoverParameters->Couplers[CouplNr].Connected->Couplers[CouplNr].AllowedFlag - & tmp->MoverParameters->Couplers[CouplNr].AllowedFlag - & ctrain_scndpneumatic) == ctrain_scndpneumatic) - if (tmp->MoverParameters->Attach( - CouplNr, 2, - tmp->MoverParameters->Couplers[CouplNr].Connected, - (tmp->MoverParameters->Couplers[CouplNr].CouplingFlag | ctrain_scndpneumatic))) - { - // TODO: dedicated 'click' sound for connecting cable-type connections - dsbCouplerDetach.play(); -// rsHiss.Play( 1, DSBPLAY_LOOPING, true, tmp->GetPosition() ); - DynamicObject->SetPneumatic( CouplNr != 0, false ); // Ra: to mi się nie podoba !!!! - tmp->SetPneumatic(CouplNr != 0, false); - // one coupling type per key press - return; - } - } - if (!TestFlag(tmp->MoverParameters->Couplers[CouplNr].CouplingFlag, ctrain_controll)) // ukrotnionko - { - if ((tmp->MoverParameters->Couplers[CouplNr].Connected->Couplers[CouplNr].AllowedFlag & - tmp->MoverParameters->Couplers[CouplNr].AllowedFlag & - ctrain_controll) == ctrain_controll) - if (tmp->MoverParameters->Attach( - CouplNr, 2, - tmp->MoverParameters->Couplers[CouplNr].Connected, - (tmp->MoverParameters->Couplers[CouplNr].CouplingFlag | ctrain_controll))) - { - // TODO: dedicated 'click' sound for connecting cable-type connections - dsbCouplerAttach.play(); - // one coupling type per key press - return; - } - } - if (!TestFlag(tmp->MoverParameters->Couplers[CouplNr].CouplingFlag, ctrain_passenger)) // mostek - { - if ((tmp->MoverParameters->Couplers[CouplNr].Connected->Couplers[CouplNr].AllowedFlag & - tmp->MoverParameters->Couplers[CouplNr].AllowedFlag & - ctrain_passenger) == ctrain_passenger) - if (tmp->MoverParameters->Attach( - CouplNr, 2, - tmp->MoverParameters->Couplers[CouplNr].Connected, - (tmp->MoverParameters->Couplers[CouplNr].CouplingFlag | ctrain_passenger))) - { - dsbCouplerAttach.play(); -/* - DynamicObject->SetPneumatic(CouplNr != 0, false); - tmp->SetPneumatic(CouplNr != 0, false); -*/ - // one coupling type per key press - return; - } - } - if( false == TestFlag( tmp->MoverParameters->Couplers[ CouplNr ].CouplingFlag, ctrain_heating ) ) { - // heating - if( ( tmp->MoverParameters->Couplers[ CouplNr ].Connected->Couplers[ CouplNr ].AllowedFlag - & tmp->MoverParameters->Couplers[ CouplNr ].AllowedFlag - & ctrain_heating ) == ctrain_heating ) - if( tmp->MoverParameters->Attach( - CouplNr, 2, - tmp->MoverParameters->Couplers[ CouplNr ].Connected, - ( tmp->MoverParameters->Couplers[ CouplNr ].CouplingFlag | ctrain_heating ) ) ) { + else if (cKey == Global::Keys[k_Couple]) { - // TODO: dedicated 'click' sound for connecting cable-type connections - dsbCouplerAttach.play(); - // one coupling type per key press - return; - } - } - } + if( true == FreeFlyModeFlag ) { + // tryb freefly + int CouplNr = -1; // normalnie żaden ze sprzęgów + TDynamicObject *tmp; + tmp = DynamicObject->ABuScanNearestObject(DynamicObject->GetTrack(), 1, 1500, CouplNr); + if (tmp == nullptr) + tmp = DynamicObject->ABuScanNearestObject(DynamicObject->GetTrack(), -1, 1500, CouplNr); + + if( ( CouplNr != -1 ) + && ( tmp != nullptr ) ) { + + tmp->couple( CouplNr ); } + if( DynamicObject->Mechanik ) { + // aktualizacja flag kierunku w składzie + DynamicObject->Mechanik->CheckVehicles( Connect ); + } + } + } - else if (cKey == Global::Keys[k_DeCouple]) - { // ABu051104: male zmiany, - // zeby mozna bylo rozlaczac - // odlegle wagony - if (iCabn > 0) - { - if (!FreeFlyModeFlag) // tryb 'kabinowy' (pozwala również rozłączyć sprzęgi zablokowane) - { - if (DynamicObject->DettachStatus(iCabn - 1) < 0) // jeśli jest co odczepić - if (DynamicObject->Dettach(iCabn - 1)) // iCab==1:przód,iCab==2:tył - { - dsbCouplerDetach.play(); // w kabinie ten dźwięk? - } + else if (cKey == Global::Keys[k_DeCouple]) { + + if( true == FreeFlyModeFlag ) { + // tryb freefly + int CouplNr = -1; + auto *tmp { DynamicObject->ABuScanNearestObject( DynamicObject->GetTrack(), 1, 1500, CouplNr ) }; + if( tmp == nullptr ) { + tmp = DynamicObject->ABuScanNearestObject( DynamicObject->GetTrack(), -1, 1500, CouplNr ); } - else - { // tryb freefly - int CouplNr = -1; - TDynamicObject *tmp; - tmp = DynamicObject->ABuScanNearestObject(DynamicObject->GetTrack(), 1, 1500, CouplNr); - if (tmp == NULL) - tmp = DynamicObject->ABuScanNearestObject(DynamicObject->GetTrack(), -1, 1500, CouplNr); - if (tmp && (CouplNr != -1)) - { - if ((tmp->MoverParameters->Couplers[CouplNr].CouplingFlag & ctrain_depot) == 0) // jeżeli sprzęg niezablokowany - if (tmp->DettachStatus(CouplNr) < 0) // jeśli jest co odczepić i się da - if (!tmp->Dettach(CouplNr)) - { // dźwięk odczepiania - dsbCouplerDetach.play(); - } - } + + if( ( CouplNr != -1 ) + && ( tmp != nullptr ) ) { + + tmp->uncouple( CouplNr ); } if( DynamicObject->Mechanik ) { // aktualizacja skrajnych pojazdów w składzie DynamicObject->Mechanik->CheckVehicles( Disconnect ); } } + } -#ifdef EU07_USE_OLD_COMMAND_SYSTEM - else if (cKey == Global::Keys[k_UpperSign]) // ABu 060205: światło górne - - // wyłączenie - { - if (mvOccupied->LightsPosNo > 0) //kręciolek od swiatel - { - if ((mvOccupied->LightsPos > 1) || (mvOccupied->LightsWrap)) - { - mvOccupied->LightsPos--; - if (mvOccupied->LightsPos < 1) - { - mvOccupied->LightsPos = mvOccupied->LightsPosNo; - } - play_sound( dsbSwitch ); - SetLights(); - } - } - } -#endif + // else if (DebugModeFlag) { // przesuwanie składu o 100m @@ -3510,9 +3354,6 @@ bool TTrain::Update( double const Deltatime ) vMechMovement = Vec; } - DWORD stat; - double dt = Deltatime; // Timer::GetDeltaTime(); - if (DynamicObject->mdKabina) { // Ra: TODO: odczyty klawiatury/pulpitu nie // powinny być uzależnione od istnienia modelu @@ -3536,10 +3377,10 @@ bool TTrain::Update( double const Deltatime ) if (fTachoVelocity > 1) // McZapkie-270503: podkrecanie tachometru { if (fTachoCount < maxtacho) - fTachoCount += dt * 3; // szybciej zacznij stukac + fTachoCount += Deltatime * 3; // szybciej zacznij stukac } else if (fTachoCount > 0) - fTachoCount -= dt * 0.66; // schodz powoli - niektore haslery to ze 4 + fTachoCount -= Deltatime * 0.66; // schodz powoli - niektore haslery to ze 4 // sekundy potrafia stukac // Ra 2014-09: napięcia i prądy muszą być ustalone najpierw, bo wysyłane są ewentualnie na PoKeys @@ -3747,7 +3588,7 @@ bool TTrain::Update( double const Deltatime ) // Ra 15-01: to musi stąd wylecieć - zależności nie mogą być w kabinie if (mvControlled->ConverterFlag == true) { - fConverterTimer += dt; + fConverterTimer += Deltatime; if ((mvControlled->CompressorFlag == true) && (mvControlled->CompressorPower == 1) && ((mvControlled->EngineType == ElectricSeriesMotor) || (mvControlled->TrainType == dt_EZT)) && @@ -4481,7 +4322,7 @@ bool TTrain::Update( double const Deltatime ) if (fBlinkTimer > fCzuwakBlink) fBlinkTimer = -fCzuwakBlink; else - fBlinkTimer += dt; + fBlinkTimer += Deltatime; // hunter-091012: dodanie testu czuwaka if( ( TestFlag( mvOccupied->SecuritySystem.Status, s_aware ) ) @@ -4969,7 +4810,7 @@ bool TTrain::Update( double const Deltatime ) { if (Global::ctrlState) { - mvOccupied->BrakeCtrlPos2 -= dt / 20.0; + mvOccupied->BrakeCtrlPos2 -= Deltatime / 20.0; if (mvOccupied->BrakeCtrlPos2 < -1.5) mvOccupied->BrakeCtrlPos2 = -1.5; } @@ -4988,7 +4829,7 @@ bool TTrain::Update( double const Deltatime ) { if (Global::ctrlState) { - mvOccupied->BrakeCtrlPos2 += (mvOccupied->BrakeCtrlPos2 > 2 ? 0 : dt / 20.0); + mvOccupied->BrakeCtrlPos2 += (mvOccupied->BrakeCtrlPos2 > 2 ? 0 : Deltatime / 20.0); if (mvOccupied->BrakeCtrlPos2 < -3) mvOccupied->BrakeCtrlPos2 = -3; } @@ -5284,7 +5125,6 @@ bool TTrain::Update( double const Deltatime ) // sounds update_sounds( Deltatime ); - m_updated = true; return true; //(DynamicObject->Update(dt)); } // koniec update @@ -5403,212 +5243,25 @@ TTrain::update_sounds( double const Deltatime ) { } } */ - // szum w czasie jazdy - vol = 0.0; - dfreq = 1.0; - if( rsRunningNoise.AM != 0 ) { - if( DynamicObject->GetVelocity() != 0 ) { - if( !TestFlag( mvOccupied->DamageFlag, - dtrain_wheelwear ) ) // McZpakie-221103: halas zalezny od kola - { - dfreq = rsRunningNoise.FM * mvOccupied->Vel + rsRunningNoise.FA; - vol = rsRunningNoise.AM * mvOccupied->Vel + rsRunningNoise.AA; - switch( tor->eEnvironment ) { - case e_tunnel: - { - vol *= 3; - dfreq *= 0.95; - } - break; - case e_canyon: - { - vol *= 1.1; - } - break; - case e_bridge: - { - vol *= 2; - dfreq *= 0.98; - } - break; - } - } - else // uszkodzone kolo (podkucie) - if( fabs( mvOccupied->nrot ) > 0.01 ) { - dfreq = rsRunningNoise.FM * mvOccupied->Vel + rsRunningNoise.FA; - vol = rsRunningNoise.AM * mvOccupied->Vel + rsRunningNoise.AA; - switch( tor->eEnvironment ) { - case e_tunnel: - { - vol *= 2; - } - break; - case e_canyon: - { - vol *= 1.1; - } - break; - case e_bridge: - { - vol *= 1.5; - } - break; - } - } - if( fabs( mvOccupied->nrot ) > 0.01 ) - vol *= 1 + - mvOccupied->UnitBrakeForce / - ( 1 + mvOccupied->MaxBrakeForce ); // hamulce wzmagaja halas - vol = vol * ( 20.0 + tor->iDamageFlag ) / 21; - rsRunningNoise.AdjFreq( dfreq, 0 ); - rsRunningNoise.Play( vol, DSBPLAY_LOOPING, true, DynamicObject->GetPosition() ); - } - else - rsRunningNoise.Stop(); - } - - if( rsBrake.AM != 0 ) { - if( ( !mvOccupied->SlippingWheels ) && ( mvOccupied->UnitBrakeForce > 10.0 ) && - ( DynamicObject->GetVelocity() > 0.01 ) ) { - // vol=rsBrake.AA+rsBrake.AM*(DynamicObject->GetVelocity()*100+mvOccupied->UnitBrakeForce); - vol = - rsBrake.AM * sqrt( ( DynamicObject->GetVelocity() * mvOccupied->UnitBrakeForce ) ); - dfreq = rsBrake.FA + rsBrake.FM * DynamicObject->GetVelocity(); - rsBrake.AdjFreq( dfreq, 0 ); - rsBrake.Play( vol, DSBPLAY_LOOPING, true, DynamicObject->GetPosition() ); - } - else { - rsBrake.Stop(); - } - } - - if( rsEngageSlippery.AM != 0 ) { - if /*((fabs(mvControlled->dizel_engagedeltaomega)>0.2) && */( - mvControlled->dizel_engage > 0.1 ) { - if( fabs( mvControlled->dizel_engagedeltaomega ) > 0.2 ) { - dfreq = rsEngageSlippery.FA + - rsEngageSlippery.FM * fabs( mvControlled->dizel_engagedeltaomega ); - vol = rsEngageSlippery.AA + rsEngageSlippery.AM * ( mvControlled->dizel_engage ); - } - else { - dfreq = - 1; // rsEngageSlippery.FA+0.7*rsEngageSlippery.FM*(fabs(mvControlled->enrot)+mvControlled->nmax); - if( mvControlled->dizel_engage > 0.2 ) - vol = - rsEngageSlippery.AA + - 0.2 * rsEngageSlippery.AM * ( mvControlled->enrot / mvControlled->nmax ); - else - vol = 0; - } - rsEngageSlippery.AdjFreq( dfreq, 0 ); - rsEngageSlippery.Play( vol, DSBPLAY_LOOPING, true, DynamicObject->GetPosition() ); - } - else { - rsEngageSlippery.Stop(); - } - } - - if( FreeFlyModeFlag ) - rsFadeSound.Stop(); // wyłącz to cholerne cykanie! - else - rsFadeSound.Play( 1, DSBPLAY_LOOPING, true, DynamicObject->GetPosition() ); - - // McZapkie! - to wazne - SoundFlag wystawiane jest przez moje moduly - // gdy zachodza pewne wydarzenia komentowane dzwiekiem. - // Mysle ze wystarczy sprawdzac a potem zerowac SoundFlag tutaj - // a nie w DynObject - gdyby cos poszlo zle to po co szarpac dzwiekiem co - // 10ms. - - if( TestFlag( mvOccupied->SoundFlag, sound_relay ) ) { - // przekaznik - gdy bezpiecznik, automatyczny rozruch itp - if( mvOccupied->EventFlag || TestFlag( mvOccupied->SoundFlag, sound_loud ) ) { - mvOccupied->EventFlag = false; // Ra: w kabinie? - if( dsbRelay != nullptr ) { dsbRelay->SetVolume( DSBVOLUME_MAX ); } - } - else { - if( dsbRelay != nullptr ) { dsbRelay->SetVolume( -40 ); } - } - if( !TestFlag( mvOccupied->SoundFlag, sound_manyrelay ) ) - dsbRelay.play(); - else { - if( TestFlag( mvOccupied->SoundFlag, sound_loud ) ) - dsbWejscie_na_bezoporow.play(); - else - dsbWejscie_na_drugi_uklad.play(); - } - } - - if( dsbBufferClamp != nullptr ) { - if( TestFlag( mvOccupied->SoundFlag, sound_bufferclamp ) ) // zderzaki uderzaja o siebie - { - if( TestFlag( mvOccupied->SoundFlag, sound_loud ) ) - dsbBufferClamp->SetVolume( DSBVOLUME_MAX ); - else - dsbBufferClamp->SetVolume( -20 ); - dsbBufferClamp.play(); - } - } - if( dsbCouplerStretch ) - if( TestFlag( mvOccupied->SoundFlag, sound_couplerstretch ) ) // sprzegi sie rozciagaja - { - if( TestFlag( mvOccupied->SoundFlag, sound_loud ) ) - dsbCouplerStretch->SetVolume( DSBVOLUME_MAX ); - else - dsbCouplerStretch->SetVolume( -20 ); - dsbCouplerStretch.play(); - } - - if( mvOccupied->SoundFlag == 0 ) - if( mvOccupied->EventFlag ) - if( TestFlag( mvOccupied->DamageFlag, dtrain_wheelwear ) ) { // Ra: przenieść do DynObj! - if( rsRunningNoise.AM != 0 ) { - rsRunningNoise.Stop(); - float am = rsRunningNoise.AM; - float fa = rsRunningNoise.FA; - float fm = rsRunningNoise.FM; - rsRunningNoise.Init( "lomotpodkucia.wav", -1, 0, 0, 0, true ); // MC: zmiana szumu na lomot - if( rsRunningNoise.AM == 1 ) - rsRunningNoise.AM = am; - rsRunningNoise.AA = 0.7; - rsRunningNoise.FA = fa; - rsRunningNoise.FM = fm; - } - mvOccupied->EventFlag = false; - } - - mvOccupied->SoundFlag = 0; - // McZapkie! - koniec obslugi dzwiekow z mover.pas - - - if( mvControlled->SlippingWheels ) { - // Ra 2014-12: lokomotywy 181/182 dostają SlippingWheels po zahamowaniu powyżej 2.85 bara i buczały - double veldiff = ( DynamicObject->GetVelocity() - fTachoVelocity ) / mvControlled->Vmax; - if( veldiff < -0.01 ) { - // 1% Vmax rezerwy, żeby 181/182 nie buczały po zahamowaniu, ale to proteza - rsSlippery.Play( -rsSlippery.AM * veldiff + rsSlippery.AA, DSBPLAY_LOOPING, true, DynamicObject->GetPosition() ); - if( mvControlled->TrainType == dt_181 ) // alarm przy poslizgu dla 181/182 - BOMBARDIER - if( dsbSlipAlarm ) - play_sound( dsbSlipAlarm, DSBPLAY_LOOPING ); - } - else { - if( ( mvOccupied->UnitBrakeForce > 100.0 ) && ( DynamicObject->GetVelocity() > 1.0 ) ) { - rsSlippery.Play( rsSlippery.AM * veldiff + rsSlippery.AA, DSBPLAY_LOOPING, true, DynamicObject->GetPosition() ); - if( mvControlled->TrainType == dt_181 ) - if( dsbSlipAlarm ) - dsbSlipAlarm->Stop(); - } - } - } - else { - rsSlippery.Stop(); - if( mvControlled->TrainType == dt_181 ) - if( dsbSlipAlarm ) - dsbSlipAlarm->Stop(); - } #endif + + // ambient sound + // since it's typically ticking of the clock we can center it on tachometer or on middle of compartment bounding area + rsFadeSound.play( sound_flags::exclusive | sound_flags::looping ); + + if( mvControlled->TrainType == dt_181 ) { + // alarm przy poslizgu dla 181/182 - BOMBARDIER + if( ( mvControlled->SlippingWheels ) + && ( DynamicObject->GetVelocity() > 1.0 ) ) { + dsbSlipAlarm.play( sound_flags::exclusive | sound_flags::looping ); + } + else { + dsbSlipAlarm.stop(); + } + } + // McZapkie-141102: SHP i czuwak, TODO: sygnalizacja kabinowa - if (mvOccupied->SecuritySystem.Status > 0) - { + if (mvOccupied->SecuritySystem.Status > 0) { // hunter-091012: rozdzielenie alarmow if( TestFlag( mvOccupied->SecuritySystem.Status, s_CAalarm ) || TestFlag( mvOccupied->SecuritySystem.Status, s_SHPalarm ) ) { @@ -5632,65 +5285,21 @@ TTrain::update_sounds( double const Deltatime ) { Console::BitsClear( 1 << 14 ); // ustawienie bitu 16 na PoKeys } } -#ifdef EU07_USE_OLD_SOUNDCODE - /* if ((mvControlled->Mains) && - (mvControlled->EngineType==ElectricSeriesMotor)) - { - //tu dac w przyszlosci zaleznosc od wlaczenia przetwornicy - if (mvControlled->ConverterFlag) //NBMX -obsluga przetwornicy - { - //glosnosc zalezna od nap. sieci - //-2000 do 0 - long tmpVol; - int trackVol; - trackVol=3550-2000; - if (mvControlled->RunningTraction.TractionVoltage<2000) - { - tmpVol=0; - } - else - { - tmpVol=mvControlled->RunningTraction.TractionVoltage-2000; - } - sConverter.Volume(-2000*(trackVol-tmpVol)/trackVol); - if (!sConverter.Playing()) - sConverter.TurnOn(); - } - else //wyl przetwornicy - sConverter.TurnOff(); - } - else - { - if (sConverter.Playing()) - sConverter.TurnOff(); - } - sConverter.Update(); - */ - - // if (fabs(DynamicObject->GetVelocity())>0.5) - if( dsbHasler != nullptr ) { - if( ( false == FreeFlyModeFlag ) && ( fTachoCount > maxtacho ) ) { - dsbHasler->GetStatus( &stat ); - if( !( stat & DSBSTATUS_PLAYING ) ) - play_sound( dsbHasler, DSBVOLUME_MAX, DSBPLAY_LOOPING ); - } - else { - if( ( true == FreeFlyModeFlag ) || ( fTachoCount < 1 ) ) { - dsbHasler->GetStatus( &stat ); - if( stat & DSBSTATUS_PLAYING ) - dsbHasler->Stop(); - } - } + if( fTachoCount > 3.f ) { + dsbHasler.play( sound_flags::exclusive | sound_flags::looping ); + } + else if( fTachoCount < 1.f ) { + dsbHasler.stop(); } -#endif } bool TTrain::CabChange(int iDirection) { // McZapkie-090902: zmiana kabiny 1->0->2 i z powrotem - if (DynamicObject->Mechanik ? DynamicObject->Mechanik->AIControllFlag : - true) // jeśli prowadzi AI albo jest w innym członie - { // jak AI prowadzi, to nie można mu mieszać + if( ( DynamicObject->Mechanik == nullptr ) + || ( true == DynamicObject->Mechanik->AIControllFlag ) ) { + // jeśli prowadzi AI albo jest w innym członie + // jak AI prowadzi, to nie można mu mieszać if (abs(DynamicObject->MoverParameters->ActiveCab + iDirection) > 1) return false; // ewentualna zmiana pojazdu DynamicObject->MoverParameters->ActiveCab = @@ -5721,14 +5330,6 @@ bool TTrain::LoadMMediaFile(std::string const &asFileName) cParser parser(asFileName, cParser::buffer_FILE); // NOTE: yaml-style comments are disabled until conflict in use of # is resolved // parser.addCommentStyle( "#", "\n" ); - //Wartości domyślne by nie wysypywało przy wybrakowanych mmd @240816 Stele - // NOTE: should be no longer needed as safety checks were added, - // but leaving the defaults for the sake of incomplete mmd files - dsbPneumaticSwitch.deserialize( "silence1.wav", sound_type::single ); - dsbBufferClamp.deserialize( "en57_bufferclamp.wav", sound_type::single ); - dsbCouplerDetach.deserialize( "couplerdetach.wav", sound_type::single ); - dsbCouplerStretch.deserialize( "en57_couplerstretch.wav", sound_type::single ); - dsbCouplerAttach.deserialize( "couplerattach.wav", sound_type::single ); std::string token; do @@ -5750,250 +5351,91 @@ bool TTrain::LoadMMediaFile(std::string const &asFileName) { // nastawnik: dsbNastawnikJazdy.deserialize( parser, sound_type::single ); + dsbNastawnikJazdy.owner( DynamicObject ); } else if (token == "ctrlscnd:") { // hunter-081211: nastawnik bocznikowania dsbNastawnikBocz.deserialize( parser, sound_type::single ); + dsbNastawnikBocz.owner( DynamicObject ); } else if (token == "reverserkey:") { // hunter-131211: dzwiek kierunkowego dsbReverserKey.deserialize( parser, sound_type::single ); + dsbReverserKey.owner( DynamicObject ); } else if (token == "buzzer:") { // bzyczek shp: dsbBuzzer.deserialize( parser, sound_type::single ); + dsbBuzzer.owner( DynamicObject ); } else if (token == "slipalarm:") { // Bombardier 011010: alarm przy poslizgu: dsbSlipAlarm.deserialize( parser, sound_type::single ); + dsbSlipAlarm.owner( DynamicObject ); } else if (token == "tachoclock:") { // cykanie rejestratora: dsbHasler.deserialize( parser, sound_type::single ); + dsbHasler.owner( DynamicObject ); } else if (token == "switch:") { // przelaczniki: dsbSwitch.deserialize( parser, sound_type::single ); + dsbSwitch.owner( DynamicObject ); } else if (token == "pneumaticswitch:") { // stycznik EP: dsbPneumaticSwitch.deserialize( parser, sound_type::single ); - } - else if (token == "wejscie_na_bezoporow:") - { - // hunter-111211: wydzielenie wejscia na bezoporowa i na drugi uklad do pliku - dsbWejscie_na_bezoporow.deserialize( parser, sound_type::single ); - } - else if (token == "wejscie_na_drugi_uklad:") - { - dsbWejscie_na_drugi_uklad.deserialize( parser, sound_type::single ); - } - else if (token == "relay:") - { - // styczniki itp: - dsbRelay.deserialize( parser, sound_type::single ); - if( true == dsbWejscie_na_bezoporow.empty() ) { - // hunter-111211: domyslne, gdy brak - dsbWejscie_na_bezoporow.deserialize( "wejscie_na_bezoporow.wav", sound_type::single ); - } - if (true == dsbWejscie_na_drugi_uklad.empty()) { - dsbWejscie_na_drugi_uklad.deserialize( "wescie_na_drugi_uklad.wav", sound_type::single ); - } - } - else if (token == "pneumaticrelay:") - { - // wylaczniki pneumatyczne: - dsbPneumaticRelay.deserialize( parser, sound_type::single ); - } - else if (token == "couplerattach:") - { - // laczenie: - dsbCouplerAttach.deserialize( parser, sound_type::single ); - } - else if (token == "couplerstretch:") - { - // laczenie: - dsbCouplerStretch.deserialize( parser, sound_type::single ); - } - else if (token == "couplerdetach:") - { - // rozlaczanie: - dsbCouplerDetach.deserialize( parser, sound_type::single ); - } - else if (token == "bufferclamp:") - { - // laczenie: - dsbBufferClamp.deserialize( parser, sound_type::single ); - } - else if (token == "ignition:") - { - // odpalanie silnika - dsbDieselIgnition.deserialize( parser, sound_type::single ); - } - else if (token == "brakesound:") - { - // hamowanie zwykle: -/* - rsBrake.Init(parser.getToken(), -1, 0, 0, 0, true, true); - parser.getTokens(4, false); - parser - >> rsBrake.AM - >> rsBrake.AA - >> rsBrake.FM - >> rsBrake.FA; - rsBrake.AM /= (1 + mvOccupied->MaxBrakeForce * 1000); - rsBrake.FM /= (1 + mvOccupied->Vmax); -*/ - rsBrake.deserialize( parser, sound_type::single, sound_parameters::amplitude | sound_parameters::frequency ); - } - else if (token == "slipperysound:") - { - // sanie: -/* - rsSlippery.Init(parser.getToken(), -1, 0, 0, 0, true); - parser.getTokens(2, false); - parser - >> rsSlippery.AM - >> rsSlippery.AA; - rsSlippery.FM = 0.0; - rsSlippery.FA = 1.0; - rsSlippery.AM /= (1 + mvOccupied->Vmax); -*/ - rsSlippery.deserialize( parser, sound_type::single, sound_parameters::amplitude ); + dsbPneumaticSwitch.owner( DynamicObject ); } else if (token == "airsound:") { // syk: -/* - rsHiss.Init(parser.getToken(), -1, 0, 0, 0, true); - parser.getTokens(2, false); - parser - >> rsHiss.AM - >> rsHiss.AA; - rsHiss.FM = 0.0; - rsHiss.FA = 1.0; -*/ rsHiss.deserialize( parser, sound_type::single, sound_parameters::amplitude ); + rsHiss.owner( DynamicObject ); } else if (token == "airsound2:") { // syk: -/* - rsHissU.Init(parser.getToken(), -1, 0, 0, 0, true); - parser.getTokens(2, false); - parser - >> rsHissU.AM - >> rsHissU.AA; - rsHissU.FM = 0.0; - rsHissU.FA = 1.0; -*/ rsHissU.deserialize( parser, sound_type::single, sound_parameters::amplitude ); + rsHissU.owner( DynamicObject ); } else if (token == "airsound3:") { // syk: -/* - rsHissE.Init(parser.getToken(), -1, 0, 0, 0, true); - parser.getTokens(2, false); - parser - >> rsHissE.AM - >> rsHissE.AA; - rsHissE.FM = 0.0; - rsHissE.FA = 1.0; -*/ rsHissE.deserialize( parser, sound_type::single, sound_parameters::amplitude ); + rsHissE.owner( DynamicObject ); } else if (token == "airsound4:") { // syk: -/* - rsHissX.Init(parser.getToken(), -1, 0, 0, 0, true); - parser.getTokens(2, false); - parser - >> rsHissX.AM - >> rsHissX.AA; - rsHissX.FM = 0.0; - rsHissX.FA = 1.0; -*/ rsHissX.deserialize( parser, sound_type::single, sound_parameters::amplitude ); + rsHissX.owner( DynamicObject ); } else if (token == "airsound5:") { // syk: -/* - rsHissT.Init(parser.getToken(), -1, 0, 0, 0, true); - parser.getTokens(2, false); - parser - >> rsHissT.AM - >> rsHissT.AA; - rsHissT.FM = 0.0; - rsHissT.FA = 1.0; -*/ rsHissT.deserialize( parser, sound_type::single, sound_parameters::amplitude ); - } - else if (token == "fadesound:") - { - // syk: -/* - rsFadeSound.Init(parser.getToken(), -1, 0, 0, 0, true); - rsFadeSound.AM = 1.0; - rsFadeSound.AA = 1.0; - rsFadeSound.FM = 1.0; - rsFadeSound.FA = 1.0; -*/ - rsFadeSound.deserialize( parser, sound_type::single ); + rsHissT.owner( DynamicObject ); } else if (token == "localbrakesound:") { // syk: -/* - rsSBHiss.Init(parser.getToken(), -1, 0, 0, 0, true); - parser.getTokens(2, false); - parser - >> rsSBHiss.AM - >> rsSBHiss.AA; - rsSBHiss.FM = 0.0; - rsSBHiss.FA = 1.0; -*/ rsSBHiss.deserialize( parser, sound_type::single, sound_parameters::amplitude ); + rsSBHiss.owner( DynamicObject ); } - else if (token == "runningnoise:") + else if (token == "fadesound:") { - // szum podczas jazdy: -/* - rsRunningNoise.Init(parser.getToken(), -1, 0, 0, 0, true, true); - parser.getTokens(4, false); - parser - >> rsRunningNoise.AM - >> rsRunningNoise.AA - >> rsRunningNoise.FM - >> rsRunningNoise.FA; - rsRunningNoise.AM /= (1 + mvOccupied->Vmax); - rsRunningNoise.FM /= (1 + mvOccupied->Vmax); -*/ - rsRunningNoise.deserialize( parser, sound_type::single, sound_parameters::amplitude | sound_parameters::frequency ); - } - else if (token == "engageslippery:") - { - // tarcie tarcz sprzegla: -/* - rsEngageSlippery.Init(parser.getToken(), -1, 0, 0, 0, true, true); - parser.getTokens(4, false); - parser - >> rsEngageSlippery.AM - >> rsEngageSlippery.AA - >> rsEngageSlippery.FM - >> rsEngageSlippery.FA; - rsEngageSlippery.FM /= (1 + mvOccupied->nmax); -*/ - rsEngageSlippery.deserialize( parser, sound_type::single, sound_parameters::amplitude | sound_parameters::frequency ); + // ambient sound: + rsFadeSound.deserialize( parser, sound_type::single ); + rsFadeSound.owner( DynamicObject ); } else if (token == "mechspring:") { @@ -6202,6 +5644,45 @@ bool TTrain::InitializeCab(int NewCabNo, std::string const &asFileName) { DynamicObject->mdKabina->Init(); // obrócenie modelu oraz optymalizacja, również zapisanie binarnego set_cab_controls(); + + // configure placement of sound emitters which aren't bound with any device model, and weren't placed manually + auto const nullvector { glm::vec3() }; + // try first to bind sounds to location of possible devices + if( dsbReverserKey.offset() == nullvector ) { + dsbReverserKey.offset( ggDirKey.model_offset() ); + } + if( dsbNastawnikJazdy.offset() == nullvector ) { + dsbNastawnikJazdy.offset( ggMainCtrl.model_offset() ); + } + if( dsbNastawnikBocz.offset() == nullvector ) { + dsbNastawnikBocz.offset( ggScndCtrl.model_offset() ); + } + if( dsbBuzzer.offset() == nullvector ) { + dsbBuzzer.offset( btLampkaCzuwaka.model_offset() ); + } + auto const brakeoffset { ggBrakeCtrl.model_offset() }; + std::vector brakesounds = { + &rsHiss, &rsHissU, &rsHissE, &rsHissX, &rsHissT, &rsSBHiss, + }; + for( auto sound : brakesounds ) { + if( sound->offset() == nullvector ) { + sound->offset( brakeoffset ); + } + } + // for whatever is left fallback on generic location, centre of the cab + auto const caboffset { glm::dvec3 { ( Cabine[ NewCabNo ].CabPos1 + Cabine[ NewCabNo ].CabPos2 ) * 0.5 } + glm::dvec3 { 0, 1, 0 } }; + std::vector sounds = { + &dsbReverserKey, &dsbNastawnikJazdy, &dsbNastawnikBocz, + &dsbSwitch, &dsbPneumaticSwitch, + &rsHiss, &rsHissU, &rsHissE, &rsHissX, &rsHissT, &rsSBHiss, + &rsFadeSound, + &dsbHasler, &dsbBuzzer, &dsbSlipAlarm + }; + for( auto sound : sounds ) { + if( sound->offset() == nullvector ) { + sound->offset( caboffset ); + } + } /* // HACK: for some reason simulation at the start is slow until a sound is played // until we do a proper fix, try to play a 'silent' sound when cab is entered @@ -6330,7 +5811,6 @@ void TTrain::Silence() // sConverter.Stop(); // sSmallCompressor->Stop(); dsbCouplerStretch.Stop(); - dsbEN57_CouplerStretch.Stop(); dsbBufferClamp.Stop(); #endif }; @@ -6776,26 +6256,26 @@ bool TTrain::initialize_button(cParser &Parser, std::string const &Label, int co }; auto lookup = lights.find( Label ); if( lookup != lights.end() ) { - lookup->second.Load( Parser, DynamicObject->mdKabina ); + lookup->second.Load( Parser, DynamicObject, DynamicObject->mdKabina ); } else if( Label == "i-instrumentlight:" ) { - btInstrumentLight.Load( Parser, DynamicObject->mdKabina, DynamicObject->mdModel ); + btInstrumentLight.Load( Parser, DynamicObject, DynamicObject->mdKabina, DynamicObject->mdModel ); InstrumentLightType = 0; } else if( Label == "i-instrumentlight_M:" ) { - btInstrumentLight.Load( Parser, DynamicObject->mdKabina, DynamicObject->mdModel ); + btInstrumentLight.Load( Parser, DynamicObject, DynamicObject->mdKabina, DynamicObject->mdModel ); InstrumentLightType = 1; } else if( Label == "i-instrumentlight_C:" ) { - btInstrumentLight.Load( Parser, DynamicObject->mdKabina, DynamicObject->mdModel ); + btInstrumentLight.Load( Parser, DynamicObject, DynamicObject->mdKabina, DynamicObject->mdModel ); InstrumentLightType = 2; } else if (Label == "i-doors:") { int i = Parser.getToken() - 1; auto &button = Cabine[Cabindex].Button(-1); // pierwsza wolna lampka - button.Load(Parser, DynamicObject->mdKabina); + button.Load(Parser, DynamicObject, DynamicObject->mdKabina); button.AssignBool(bDoors[0] + 3 * i); } else @@ -6887,61 +6367,73 @@ bool TTrain::initialize_gauge(cParser &Parser, std::string const &Label, int con }; auto lookup = gauges.find( Label ); if( lookup != gauges.end() ) { - lookup->second.Load( Parser, DynamicObject->mdKabina ); + lookup->second.Load( Parser, DynamicObject, DynamicObject->mdKabina ); m_controlmapper.insert( lookup->second, lookup->first ); } // ABu 090305: uniwersalne przyciski lub inne rzeczy else if( Label == "mainctrlact:" ) { - ggMainCtrlAct.Load( Parser, DynamicObject->mdKabina, DynamicObject->mdModel ); + ggMainCtrlAct.Load( Parser, DynamicObject, DynamicObject->mdKabina, DynamicObject->mdModel ); } // SEKCJA WSKAZNIKOW else if ((Label == "tachometer:") || (Label == "tachometerb:")) { // predkosciomierz wskaźnikowy z szarpaniem auto &gauge = Cabine[Cabindex].Gauge(-1); // pierwsza wolna gałka - gauge.Load(Parser, DynamicObject->mdKabina); + gauge.Load(Parser, DynamicObject, DynamicObject->mdKabina); gauge.AssignFloat(&fTachoVelocityJump); + // bind tachometer sound location to the meter + if( dsbHasler.offset() == glm::vec3() ) { + dsbHasler.offset( gauge.model_offset() ); + } } else if (Label == "tachometern:") { // predkosciomierz wskaźnikowy bez szarpania auto &gauge = Cabine[Cabindex].Gauge(-1); // pierwsza wolna gałka - gauge.Load(Parser, DynamicObject->mdKabina); + gauge.Load(Parser, DynamicObject, DynamicObject->mdKabina); gauge.AssignFloat(&fTachoVelocity); + // bind tachometer sound location to the meter + if( dsbHasler.offset() == glm::vec3() ) { + dsbHasler.offset( gauge.model_offset() ); + } } else if (Label == "tachometerd:") { // predkosciomierz cyfrowy auto &gauge = Cabine[Cabindex].Gauge(-1); // pierwsza wolna gałka - gauge.Load(Parser, DynamicObject->mdKabina); + gauge.Load(Parser, DynamicObject, DynamicObject->mdKabina); gauge.AssignFloat(&fTachoVelocity); + // bind tachometer sound location to the meter + if( dsbHasler.offset() == glm::vec3() ) { + dsbHasler.offset( gauge.model_offset() ); + } } else if ((Label == "hvcurrent1:") || (Label == "hvcurrent1b:")) { // 1szy amperomierz auto &gauge = Cabine[Cabindex].Gauge(-1); // pierwsza wolna gałka - gauge.Load(Parser, DynamicObject->mdKabina); + gauge.Load(Parser, DynamicObject, DynamicObject->mdKabina); gauge.AssignFloat(fHCurrent + 1); } else if ((Label == "hvcurrent2:") || (Label == "hvcurrent2b:")) { // 2gi amperomierz auto &gauge = Cabine[Cabindex].Gauge(-1); // pierwsza wolna gałka - gauge.Load(Parser, DynamicObject->mdKabina); + gauge.Load(Parser, DynamicObject, DynamicObject->mdKabina); gauge.AssignFloat(fHCurrent + 2); } else if ((Label == "hvcurrent3:") || (Label == "hvcurrent3b:")) { // 3ci amperomierz auto &gauge = Cabine[Cabindex].Gauge(-1); // pierwsza wolna gałska - gauge.Load(Parser, DynamicObject->mdKabina); + gauge.Load(Parser, DynamicObject, DynamicObject->mdKabina); gauge.AssignFloat(fHCurrent + 3); } else if ((Label == "hvcurrent:") || (Label == "hvcurrentb:")) { // amperomierz calkowitego pradu auto &gauge = Cabine[Cabindex].Gauge(-1); // pierwsza wolna gałka - gauge.Load(Parser, DynamicObject->mdKabina); + gauge.Load(Parser, DynamicObject, DynamicObject->mdKabina); gauge.AssignFloat(fHCurrent); } else if (Label == "eimscreen:") @@ -6951,7 +6443,7 @@ bool TTrain::initialize_gauge(cParser &Parser, std::string const &Label, int con Parser.getTokens(2, false); Parser >> i >> j; auto &gauge = Cabine[Cabindex].Gauge(-1); // pierwsza wolna gałka - gauge.Load(Parser, DynamicObject->mdKabina); + gauge.Load(Parser, DynamicObject, DynamicObject->mdKabina); gauge.AssignFloat(&fEIMParams[i][j]); } else if (Label == "brakes:") @@ -6961,7 +6453,7 @@ bool TTrain::initialize_gauge(cParser &Parser, std::string const &Label, int con Parser.getTokens(2, false); Parser >> i >> j; auto &gauge = Cabine[Cabindex].Gauge(-1); // pierwsza wolna gałka - gauge.Load(Parser, DynamicObject->mdKabina); + gauge.Load(Parser, DynamicObject, DynamicObject->mdKabina); gauge.AssignFloat(&fPress[i - 1][j]); } else if ((Label == "brakepress:") || (Label == "brakepressb:")) @@ -6969,55 +6461,55 @@ bool TTrain::initialize_gauge(cParser &Parser, std::string const &Label, int con // manometr cylindrow hamulcowych // Ra 2014-08: przeniesione do TCab auto &gauge = Cabine[Cabindex].Gauge(-1); // pierwsza wolna gałka - gauge.Load(Parser, DynamicObject->mdKabina, nullptr, 0.1); + gauge.Load(Parser, DynamicObject, DynamicObject->mdKabina, nullptr, 0.1); gauge.AssignDouble(&mvOccupied->BrakePress); } else if ((Label == "pipepress:") || (Label == "pipepressb:")) { // manometr przewodu hamulcowego auto &gauge = Cabine[Cabindex].Gauge(-1); // pierwsza wolna gałka - gauge.Load(Parser, DynamicObject->mdKabina, nullptr, 0.1); + gauge.Load(Parser, DynamicObject, DynamicObject->mdKabina, nullptr, 0.1); gauge.AssignDouble(&mvOccupied->PipePress); } else if (Label == "limpipepress:") { // manometr zbiornika sterujacego zaworu maszynisty - ggZbS.Load(Parser, DynamicObject->mdKabina, nullptr, 0.1); + ggZbS.Load(Parser, DynamicObject, DynamicObject->mdKabina, nullptr, 0.1); } else if (Label == "cntrlpress:") { // manometr zbiornika kontrolnego/rorzďż˝du auto &gauge = Cabine[Cabindex].Gauge(-1); // pierwsza wolna gałka - gauge.Load(Parser, DynamicObject->mdKabina, nullptr, 0.1); + gauge.Load(Parser, DynamicObject, DynamicObject->mdKabina, nullptr, 0.1); gauge.AssignDouble(&mvControlled->PantPress); } else if ((Label == "compressor:") || (Label == "compressorb:")) { // manometr sprezarki/zbiornika glownego auto &gauge = Cabine[Cabindex].Gauge(-1); // pierwsza wolna gałka - gauge.Load(Parser, DynamicObject->mdKabina, nullptr, 0.1); + gauge.Load(Parser, DynamicObject, DynamicObject->mdKabina, nullptr, 0.1); gauge.AssignDouble(&mvOccupied->Compressor); } // yB - dla drugiej sekcji else if (Label == "hvbcurrent1:") { // 1szy amperomierz - ggI1B.Load(Parser, DynamicObject->mdKabina); + ggI1B.Load(Parser, DynamicObject, DynamicObject->mdKabina); } else if (Label == "hvbcurrent2:") { // 2gi amperomierz - ggI2B.Load(Parser, DynamicObject->mdKabina); + ggI2B.Load(Parser, DynamicObject, DynamicObject->mdKabina); } else if (Label == "hvbcurrent3:") { // 3ci amperomierz - ggI3B.Load(Parser, DynamicObject->mdKabina); + ggI3B.Load(Parser, DynamicObject, DynamicObject->mdKabina); } else if (Label == "hvbcurrent:") { // amperomierz calkowitego pradu - ggItotalB.Load(Parser, DynamicObject->mdKabina); + ggItotalB.Load(Parser, DynamicObject, DynamicObject->mdKabina); } //************************************************************* else if (Label == "clock:") @@ -7034,61 +6526,61 @@ bool TTrain::initialize_gauge(cParser &Parser, std::string const &Label, int con else if (Label == "evoltage:") { // woltomierz napiecia silnikow - ggEngineVoltage.Load(Parser, DynamicObject->mdKabina); + ggEngineVoltage.Load(Parser, DynamicObject, DynamicObject->mdKabina); } else if (Label == "hvoltage:") { // woltomierz wysokiego napiecia auto &gauge = Cabine[Cabindex].Gauge(-1); // pierwsza wolna gałka - gauge.Load(Parser, DynamicObject->mdKabina); + gauge.Load(Parser, DynamicObject, DynamicObject->mdKabina); gauge.AssignFloat(&fHVoltage); } else if (Label == "lvoltage:") { // woltomierz niskiego napiecia - ggLVoltage.Load(Parser, DynamicObject->mdKabina); + ggLVoltage.Load(Parser, DynamicObject, DynamicObject->mdKabina); } else if (Label == "enrot1m:") { // obrotomierz auto &gauge = Cabine[Cabindex].Gauge(-1); // pierwsza wolna gałka - gauge.Load(Parser, DynamicObject->mdKabina); + gauge.Load(Parser, DynamicObject, DynamicObject->mdKabina); gauge.AssignFloat(fEngine + 1); } // ggEnrot1m.Load(Parser,DynamicObject->mdKabina); else if (Label == "enrot2m:") { // obrotomierz auto &gauge = Cabine[Cabindex].Gauge(-1); // pierwsza wolna gałka - gauge.Load(Parser, DynamicObject->mdKabina); + gauge.Load(Parser, DynamicObject, DynamicObject->mdKabina); gauge.AssignFloat(fEngine + 2); } // ggEnrot2m.Load(Parser,DynamicObject->mdKabina); else if (Label == "enrot3m:") { // obrotomierz auto &gauge = Cabine[Cabindex].Gauge(-1); // pierwsza wolna gałka - gauge.Load(Parser, DynamicObject->mdKabina); + gauge.Load(Parser, DynamicObject, DynamicObject->mdKabina); gauge.AssignFloat(fEngine + 3); } // ggEnrot3m.Load(Parser,DynamicObject->mdKabina); else if (Label == "engageratio:") { // np. ciśnienie sterownika sprzęgła auto &gauge = Cabine[Cabindex].Gauge(-1); // pierwsza wolna gałka - gauge.Load(Parser, DynamicObject->mdKabina); + gauge.Load(Parser, DynamicObject, DynamicObject->mdKabina); gauge.AssignDouble(&mvControlled->dizel_engage); } // ggEngageRatio.Load(Parser,DynamicObject->mdKabina); else if (Label == "maingearstatus:") { // np. ciśnienie sterownika skrzyni biegów - ggMainGearStatus.Load(Parser, DynamicObject->mdKabina); + ggMainGearStatus.Load(Parser, DynamicObject, DynamicObject->mdKabina); } else if (Label == "ignitionkey:") { - ggIgnitionKey.Load(Parser, DynamicObject->mdKabina); + ggIgnitionKey.Load(Parser, DynamicObject, DynamicObject->mdKabina); } else if (Label == "distcounter:") { // Ra 2014-07: licznik kilometrów auto &gauge = Cabine[Cabindex].Gauge(-1); // pierwsza wolna gałka - gauge.Load(Parser, DynamicObject->mdKabina); + gauge.Load(Parser, DynamicObject, DynamicObject->mdKabina); gauge.AssignDouble(&mvControlled->DistCounter); } else diff --git a/Train.h b/Train.h index fd1bd4fb..e60f3667 100644 --- a/Train.h +++ b/Train.h @@ -91,7 +91,6 @@ class TTrain vector3 GetWorldMechPosition(); bool Update( double const Deltatime ); void update_sounds( double const Deltatime ); - bool m_updated = false; void MechStop(); void SetLights(); // McZapkie-310302: ladowanie parametrow z pliku @@ -102,6 +101,7 @@ class TTrain // types typedef void( *command_handler )( TTrain *Train, command_data const &Command ); typedef std::unordered_map commandhandler_map; +// methods // clears state of all cabin controls void clear_cab_controls(); // sets cabin controls based on current state of the vehicle @@ -226,7 +226,7 @@ public: // reszta może by?publiczna TGauge ggMainCtrl; TGauge ggMainCtrlAct; TGauge ggScndCtrl; - TGauge ggScndCtrlButton; + TGauge ggScndCtrlButton; // NOTE: not used? TGauge ggDirKey; TGauge ggBrakeCtrl; TGauge ggLocalBrake; @@ -398,42 +398,23 @@ public: // reszta może by?publiczna double fMechRoll; double fMechPitch; - sound_source dsbNastawnikJazdy; - sound_source dsbNastawnikBocz; // hunter-081211 - sound_source dsbRelay; - sound_source dsbPneumaticRelay; - sound_source dsbSwitch; - sound_source dsbPneumaticSwitch; - sound_source dsbReverserKey; // hunter-121211 + sound_source dsbReverserKey { sound_placement::internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE }; // hunter-121211 + sound_source dsbNastawnikJazdy { sound_placement::internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE }; + sound_source dsbNastawnikBocz { sound_placement::internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE }; // hunter-081211 + sound_source dsbSwitch { sound_placement::internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE }; + sound_source dsbPneumaticSwitch { sound_placement::internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE }; - sound_source dsbCouplerAttach; // Ra: w kabinie???? - sound_source dsbCouplerDetach; // Ra: w kabinie??? + sound_source rsHiss { sound_placement::internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE }; // upuszczanie + sound_source rsHissU { sound_placement::internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE }; // napelnianie + sound_source rsHissE { sound_placement::internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE }; // nagle + sound_source rsHissX { sound_placement::internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE }; // fala + sound_source rsHissT { sound_placement::internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE }; // czasowy + sound_source rsSBHiss { sound_placement::internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE }; // local - sound_source dsbDieselIgnition; // Ra: w kabinie??? - - // Winger 010304 - sound_source dsbWejscie_na_bezoporow; - sound_source dsbWejscie_na_drugi_uklad; // hunter-081211: poprawka literowki - - // PSound dsbHiss1; - // PSound dsbHiss2; - - // McZapkie-280302 - sound_source rsBrake; - sound_source rsSlippery; - sound_source rsHiss; // upuszczanie - sound_source rsHissU; // napelnianie - sound_source rsHissE; // nagle - sound_source rsHissX; // fala - sound_source rsHissT; // czasowy - sound_source rsSBHiss; - sound_source rsRunningNoise; - sound_source rsEngageSlippery; - sound_source rsFadeSound; - - sound_source dsbHasler; - sound_source dsbBuzzer; - sound_source dsbSlipAlarm; // Bombardier 011010: alarm przy poslizgu dla 181/182 + sound_source rsFadeSound { sound_placement::internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE }; + sound_source dsbHasler { sound_placement::internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE }; + sound_source dsbBuzzer { sound_placement::internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE }; + sound_source dsbSlipAlarm { sound_placement::internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE }; // Bombardier 011010: alarm przy poslizgu dla 181/182 int iCabLightFlag; // McZapkie:120503: oswietlenie kabiny (0: wyl, 1: przyciemnione, 2: pelne) bool bCabLight; // hunter-091012: czy swiatlo jest zapalone? @@ -443,9 +424,6 @@ public: // reszta może by?publiczna vector3 MirrorPosition(bool lewe); private: - sound_source dsbCouplerStretch; - sound_source dsbEN57_CouplerStretch; - sound_source dsbBufferClamp; double fBlinkTimer; float fHaslerTimer; float fConverterTimer; // hunter-261211: dla przekaznika @@ -494,7 +472,9 @@ private: float fEIMParams[9][10]; // parametry dla silnikow asynchronicznych int RadioChannel() { return iRadioChannel; }; inline TDynamicObject *Dynamic() { return DynamicObject; }; + inline TDynamicObject const *Dynamic() const { return DynamicObject; }; inline TMoverParameters *Controlled() { return mvControlled; }; + inline TMoverParameters const *Controlled() const { return mvControlled; }; void DynamicSet(TDynamicObject *d); void Silence(); diff --git a/World.cpp b/World.cpp index 4c003b34..8bd79a40 100644 --- a/World.cpp +++ b/World.cpp @@ -732,7 +732,9 @@ void TWorld::OnKeyDown(int cKey) if (vehicle->MoverParameters->IncLocalBrakeLevelFAST()) if (Train) { // dźwięk oczywiście jest w kabinie +#ifdef EU07_USE_OLD_SOUNDCODE Train->dsbPneumaticRelay.play(); +#endif } } } @@ -751,7 +753,9 @@ void TWorld::OnKeyDown(int cKey) if (vehicle->MoverParameters->DecLocalBrakeLevelFAST()) if (Train) { // dźwięk oczywiście jest w kabinie +#ifdef EU07_USE_OLD_SOUNDCODE Train->dsbPneumaticRelay.play(); +#endif } } } @@ -2031,15 +2035,15 @@ void TWorld::CreateE3D(std::string const &Path, bool Dynamic) if( dynamic->iCabs ) { // jeśli ma jakąkolwiek kabinę delete Train; Train = new TTrain(); - if( dynamic->iCabs & 1 ) { + if( dynamic->iCabs & 0x1 ) { dynamic->MoverParameters->ActiveCab = 1; Train->Init( dynamic, true ); } - if( dynamic->iCabs & 4 ) { + if( dynamic->iCabs & 0x4 ) { dynamic->MoverParameters->ActiveCab = -1; Train->Init( dynamic, true ); } - if( dynamic->iCabs & 2 ) { + if( dynamic->iCabs & 0x2 ) { dynamic->MoverParameters->ActiveCab = 0; Train->Init( dynamic, true ); } diff --git a/audio.cpp b/audio.cpp index 673b64f1..3e0c8f70 100644 --- a/audio.cpp +++ b/audio.cpp @@ -12,6 +12,7 @@ http://mozilla.org/MPL/2.0/. #include "audio.h" #include "globals.h" #include "mczapkie/mctools.h" +#include "logs.h" #define DR_WAV_IMPLEMENTATION #include "dr_wav.h" @@ -137,6 +138,7 @@ buffer_manager::create( std::string const &Filename ) { return emplace( filelookup ); } // if we still didn't find anything, give up + ErrorLog( "Bad file: failed do locate audio file \"" + Filename + "\"" ); return null_handle; } diff --git a/audio.h b/audio.h index a907a41d..8bec3f11 100644 --- a/audio.h +++ b/audio.h @@ -14,7 +14,7 @@ http://mozilla.org/MPL/2.0/. namespace audio { -ALuint const null_resource { ~ALuint{ 0 } }; +ALuint const null_resource{ ~( ALuint { 0 } ) }; // wrapper for audio sample struct openal_buffer { diff --git a/audiorenderer.cpp b/audiorenderer.cpp index 2a1df7a7..55b97197 100644 --- a/audiorenderer.cpp +++ b/audiorenderer.cpp @@ -11,12 +11,15 @@ http://mozilla.org/MPL/2.0/. #include "audiorenderer.h" #include "sound.h" +#include "globals.h" #include "logs.h" namespace audio { openal_renderer renderer; +float const EU07_SOUND_CUTOFFRANGE { 3000.f }; // 2750 m = max expected emitter spawn range, plus safety margin + // starts playback of queued buffers void openal_source::play() { @@ -29,17 +32,16 @@ openal_source::play() { void openal_source::stop() { - ::alSourcei( id, AL_LOOPING, AL_FALSE ); + loop( false ); ::alSourceStop( id ); - // NOTE: we don't update the is_playing flag - // this way the state will change only on next update loop, - // giving the controller a chance to properly change state from cease to none - // even with multiple active sounds under control + is_playing = false; } // updates state of the source void -openal_source::update( int const Deltatime ) { +openal_source::update( double const Deltatime ) { + + update_deltatime = Deltatime; // cached for time-based processing of data from the controller // TODO: test whether the emitter was within range during the last tick, potentially update the counter and flag it for timeout ::alGetSourcei( id, AL_BUFFERS_PROCESSED, &buffer_index ); @@ -58,10 +60,79 @@ openal_source::update( int const Deltatime ) { controller->update( *this ); } +// configures state of the source to match the provided set of properties +void +openal_source::sync_with( sound_properties const &State ) { + +/* + // velocity + // not used yet + glm::vec3 const velocity { ( State.location - properties.location ) / update_deltatime }; +*/ + // location + properties.location = State.location; + auto sourceoffset { glm::vec3 { properties.location - glm::dvec3 { Global::pCameraPosition } } }; + if( glm::length2( sourceoffset ) > std::max( ( sound_range * sound_range ), ( EU07_SOUND_CUTOFFRANGE * EU07_SOUND_CUTOFFRANGE ) ) ) { + // range cutoff check + stop(); + return; + } + if( sound_range >= 0 ) { + ::alSourcefv( id, AL_POSITION, glm::value_ptr( sourceoffset ) ); + } + else { + // sounds with 'unlimited' range are positioned on top of the listener + ::alSourcefv( id, AL_POSITION, glm::value_ptr( glm::vec3() ) ); + } + // gain + if( ( State.placement_stamp != properties.placement_stamp ) + || ( State.base_gain != properties.base_gain ) ) { + // gain value has changed + properties.base_gain = State.base_gain; + properties.placement_gain = State.placement_gain; + properties.placement_stamp = State.placement_stamp; + + ::alSourcef( id, AL_GAIN, properties.base_gain * properties.placement_gain * Global::AudioVolume ); + } + // pitch + if( State.base_pitch != properties.base_pitch ) { + // pitch value has changed + properties.base_pitch = State.base_pitch; + + ::alSourcef( id, AL_PITCH, properties.base_pitch * pitch_variation ); + } +} + +// sets max audible distance for sounds emitted by the source +void +openal_source::range( float const Range ) { + + auto const range( + Range >= 0 ? + Range : + 5 ); // range of -1 means sound of unlimited range, positioned at the listener + ::alSourcef( id, AL_REFERENCE_DISTANCE, range * ( 1.f / 16.f ) ); + ::alSourcef( id, AL_ROLLOFF_FACTOR, 1.5f ); + // NOTE: we cache actual specified range, as we'll be giving 'unlimited' range special treatment + sound_range = Range; +} + +// sets modifier applied to the pitch of sounds emitted by the source +void +openal_source::pitch( float const Pitch ) { + + pitch_variation = Pitch; + // invalidate current pitch value to enforce change of next syns + properties.base_pitch = -1.f; +} + // toggles looping of the sound emitted by the source void openal_source::loop( bool const State ) { + if( is_looping == State ) { return; } + + is_looping = State; ::alSourcei( id, AL_LOOPING, @@ -78,9 +149,7 @@ openal_source::clear() { controller = nullptr; // unqueue bound buffers: // ensure no buffer is in use... - ::alSourcei( id, AL_LOOPING, AL_FALSE ); - ::alSourceStop( id ); - is_playing = false; + stop(); // ...prepare space for returned ids of unqueued buffers (not that we need that info)... std::vector bufferids; bufferids.resize( buffers.size() ); @@ -88,6 +157,8 @@ openal_source::clear() { ::alSourceUnqueueBuffers( id, bufferids.size(), bufferids.data() ); buffers.clear(); buffer_index = 0; + // reset properties + properties = sound_properties(); } @@ -125,6 +196,9 @@ openal_renderer::init() { // basic initialization failed return false; } + // +// ::alDistanceModel( AL_LINEAR_DISTANCE ); + ::alDistanceModel( AL_INVERSE_DISTANCE_CLAMPED ); // all done m_ready = true; return true; @@ -141,10 +215,45 @@ openal_renderer::insert( sound_source *Controller, audio::buffer_handle const So std::begin( buffers ), std::end( buffers ) ); } +// removes from the queue all sounds controlled by the specified sound emitter +void +openal_renderer::erase( sound_source const *Controller ) { + + auto source { std::begin( m_sources ) }; + while( source != std::end( m_sources ) ) { + if( source->controller == Controller ) { + // if the controller is the one specified, kill it + source->clear(); + m_sourcespares.push( *source ); + source = m_sources.erase( source ); + } + else { + // otherwise proceed through the list normally + ++source; + } + } +} + // updates state of all active emitters void -openal_renderer::update( int const Deltatime ) { +openal_renderer::update( double const Deltatime ) { + // update listener + glm::dmat4 cameramatrix; + Global::pCamera->SetMatrix( cameramatrix ); + auto rotationmatrix { glm::mat3{ cameramatrix } }; + glm::vec3 const orientation[] = { + glm::vec3{ 0, 0,-1 } * rotationmatrix , + glm::vec3{ 0, 1, 0 } * rotationmatrix }; + ::alListenerfv( AL_ORIENTATION, reinterpret_cast( orientation ) ); +/* + glm::dvec3 const listenerposition { Global::pCameraPosition }; + // not used yet + glm::vec3 const velocity { ( listenerposition - m_listenerposition ) / Deltatime }; + m_listenerposition = listenerposition; +*/ + + // update active emitters auto source { std::begin( m_sources ) }; while( source != std::end( m_sources ) ) { // update each source diff --git a/audiorenderer.h b/audiorenderer.h index ddc8ed57..220be99f 100644 --- a/audiorenderer.h +++ b/audiorenderer.h @@ -14,6 +14,15 @@ http://mozilla.org/MPL/2.0/. class sound_source; +// sound emitter state sync item +struct sound_properties { + glm::dvec3 location; + float base_gain { 1.f }; + float placement_gain { 1.f }; + std::uintptr_t placement_stamp { ~( std::uintptr_t{ 0 } ) }; + float base_pitch { 1.f }; +}; + namespace audio { // implementation part of the sound emitter @@ -29,6 +38,8 @@ struct openal_source { buffer_sequence buffers; // sequence of samples the source will emit int buffer_index; // currently queued sample from the buffer sequence bool is_playing { false }; + bool is_looping { false }; + sound_properties properties; // methods template @@ -47,25 +58,42 @@ struct openal_source { // starts playback of queued buffers void play(); + // updates state of the source + void + update( double const Deltatime ); + // configures state of the source to match the provided set of properties + void + sync_with( sound_properties const &State ); // stops the playback void stop(); - // updates state of the source - void - update( int const Deltatime ); // toggles looping of the sound emitted by the source void loop( bool const State ); + // sets max audible distance for sounds emitted by the source + void + range( float const Range ); + // sets modifier applied to the pitch of sounds emitted by the source + void + pitch( float const Pitch ); // releases bound buffers and resets state of the class variables // NOTE: doesn't release allocated implementation-side source void clear(); + +private: +// members + double update_deltatime; // time delta of most current update + float pitch_variation { 1.f }; // emitter-specific variation of the base pitch + float sound_range { 50.f }; // cached audible range of the emitted samples }; class openal_renderer { + friend class opengl_renderer; + public: // destructor ~openal_renderer(); @@ -89,9 +117,12 @@ public: // schedules playback of specified sample, under control of the specified sound emitter void insert( sound_source *Controller, audio::buffer_handle const Sound ); + // removes from the queue all sounds controlled by the specified sound emitter + void + erase( sound_source const *Controller ); // updates state of all active emitters void - update( int const Deltatime ); + update( double const Deltatime ); private: // types @@ -107,6 +138,7 @@ private: ALCdevice * m_device { nullptr }; ALCcontext * m_context { nullptr }; bool m_ready { false }; // renderer is initialized and functional + glm::dvec3 m_listenerposition; buffer_manager m_buffers; // TBD: list of sources as vector, sorted by distance, for openal implementations with limited number of active sources? @@ -116,6 +148,20 @@ private: extern openal_renderer renderer; +inline +float +amplitude_to_db( float const Amplitude ) { + + return 20.f * std::log10( Amplitude ); +} + +inline +float +db_to_amplitude( float const Decibels ) { + + return std::pow( 10.f, Decibels / 20.f ); +} + } // audio //--------------------------------------------------------------------------- diff --git a/dumb3d.h b/dumb3d.h index 74c4c312..2c59083b 100644 --- a/dumb3d.h +++ b/dumb3d.h @@ -162,7 +162,7 @@ class matrix4x4 } // Low-level access to the array. - const scalar_t *readArray(void) + const scalar_t *readArray(void) const { return e; } diff --git a/renderer.cpp b/renderer.cpp index 80cf167a..92c9e79d 100644 --- a/renderer.cpp +++ b/renderer.cpp @@ -441,6 +441,25 @@ opengl_renderer::Render_pass( rendermode const Mode ) { #endif switch_units( true, true, true ); Render( simulation::Region ); +/* + // debug: audio nodes + for( auto const &audiosource : audio::renderer.m_sources ) { + + ::glPushMatrix(); + auto const position = audiosource.properties.location - m_renderpass.camera.position(); + ::glTranslated( position.x, position.y, position.z ); + + ::glPushAttrib( GL_ENABLE_BIT ); + ::glDisable( GL_TEXTURE_2D ); + ::glColor3f( 0.36f, 0.75f, 0.35f ); + + ::gluSphere( m_quadric, 0.125, 4, 2 ); + + ::glPopAttrib(); + + ::glPopMatrix(); + } +*/ // ...translucent parts setup_drawing( true ); Render_Alpha( simulation::Region ); @@ -2990,11 +3009,12 @@ opengl_renderer::Render_Alpha( TSubModel *Submodel ) { if( glarelevel > 0.0f ) { // setup - ::glPushAttrib( GL_ENABLE_BIT | GL_CURRENT_BIT | GL_COLOR_BUFFER_BIT ); + ::glPushAttrib( GL_CURRENT_BIT | GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_ENABLE_BIT ); Bind_Texture( m_glaretexture ); ::glColor4f( Submodel->f4Diffuse[ 0 ], Submodel->f4Diffuse[ 1 ], Submodel->f4Diffuse[ 2 ], glarelevel ); ::glDisable( GL_LIGHTING ); + ::glDepthMask( GL_FALSE ); ::glBlendFunc( GL_SRC_ALPHA, GL_ONE ); ::glPushMatrix(); diff --git a/simulation.cpp b/simulation.cpp index 6ef2c32b..0a4a02cd 100644 --- a/simulation.cpp +++ b/simulation.cpp @@ -851,7 +851,9 @@ state_manager::deserialize_sound( cParser &Input, scene::scratch_data &Scratchpa auto *sound = new TTextSound( soundname, Nodedata.range_max, location.x, location.y, location.z, false, false, Nodedata.range_min ); sound->name( Nodedata.name ); #else - auto *sound = new sound_source(); + auto *sound = new sound_source( sound_placement::external, Nodedata.range_max ); + sound->offset( location ); + sound->name( Nodedata.name ); sound->deserialize( Input.getToken(), sound_type::single ); #endif diff --git a/sound.cpp b/sound.cpp index d17912f5..e0cdff2d 100644 --- a/sound.cpp +++ b/sound.cpp @@ -12,6 +12,20 @@ http://mozilla.org/MPL/2.0/. #include "sound.h" #include "parser.h" #include "globals.h" +#include "world.h" +#include "train.h" + +// constructors +sound_source::sound_source( sound_placement const Placement, float const Range ) : + m_placement( Placement ), + m_range( Range ) +{} + +// destructor +sound_source::~sound_source() { + + audio::renderer.erase( this ); +} // restores state of the class from provided data stream sound_source & @@ -27,14 +41,14 @@ sound_source::deserialize( cParser &Input, sound_type const Legacytype, int cons switch( Legacytype ) { case sound_type::single: { // single sample only - m_soundmain = audio::renderer.fetch_buffer( Input.getToken( true, "\n\r\t ,;" ) ); + m_soundmain.buffer = audio::renderer.fetch_buffer( Input.getToken( true, "\n\r\t ,;" ) ); break; } case sound_type::multipart: { // three samples: start, middle, stop - m_soundbegin = audio::renderer.fetch_buffer( Input.getToken( true, "\n\r\t ,;" ) ); - m_soundmain = audio::renderer.fetch_buffer( Input.getToken( true, "\n\r\t ,;" ) ); - m_soundend = audio::renderer.fetch_buffer( Input.getToken( true, "\n\r\t ,;" ) ); + m_soundbegin.buffer = audio::renderer.fetch_buffer( Input.getToken( true, "\n\r\t ,;" ) ); + m_soundmain.buffer = audio::renderer.fetch_buffer( Input.getToken( true, "\n\r\t ,;" ) ); + m_soundend.buffer = audio::renderer.fetch_buffer( Input.getToken( true, "\n\r\t ,;" ) ); break; } default: { @@ -44,12 +58,19 @@ sound_source::deserialize( cParser &Input, sound_type const Legacytype, int cons if( Legacyparameters & sound_parameters::range ) { Input.getTokens( 1, false ); + Input >> m_range; } if( Legacyparameters & sound_parameters::amplitude ) { Input.getTokens( 2, false ); + Input + >> m_amplitudefactor + >> m_amplitudeoffset; } if( Legacyparameters & sound_parameters::frequency ) { Input.getTokens( 2, false ); + Input + >> m_frequencyfactor + >> m_frequencyoffset; } return *this; @@ -64,34 +85,41 @@ sound_source::play( int const Flags ) { // if the sound is disabled altogether or nothing can be emitted from this source, no point wasting time return; } + if( m_range > 0 ) { + auto const cutoffrange{ ( + ( m_soundbegin.buffer != null_handle ) && ( Flags & sound_flags::looping ) ? + m_range * 10 : // larger margin to let the startup sample finish playing at safe distance + m_range * 5 ) }; + if( glm::length2( location() - glm::dvec3{ Global::pCameraPosition } ) > std::min( 2750.f * 2750.f, cutoffrange * cutoffrange ) ) { + // drop sounds from beyond sensible and/or audible range + return; + } + } + + // initialize emitter-specific pitch variation if it wasn't yet set + if( m_pitchvariation == 0.f ) { + m_pitchvariation = 0.01f * static_cast( Random( 95, 105 ) ); + } m_flags = Flags; - switch( m_stage ) { - case stage::none: - case stage::restart: { - if( m_soundbegin != null_handle ) { - std::vector bufferlist{ m_soundbegin, m_soundmain }; - audio::renderer.insert( this, std::begin( bufferlist ), std::end( bufferlist ) ); - } - else { - audio::renderer.insert( this, m_soundmain ); - } - break; + if( false == is_playing() ) { + // dispatch appropriate sound + // TODO: support for parameter-driven sound table + if( m_soundbegin.buffer != null_handle ) { + std::vector bufferlist { m_soundbegin.buffer, m_soundmain.buffer }; + audio::renderer.insert( this, std::begin( bufferlist ), std::end( bufferlist ) ); } - case stage::main: { - // TODO: schedule another main sample playback, or a suitable sample from the table for combined sources - break; + else { + audio::renderer.insert( this, m_soundmain.buffer ); } - case stage::end: { - // schedule stop of current sequence end sample... - m_stage = stage::restart; - // ... and queue startup or main sound again - return play( Flags ); - break; - } - default: { - break; + } + else { + + if( ( m_soundbegin.buffer == null_handle ) + && ( ( m_flags & ( sound_flags::exclusive | sound_flags::looping ) ) == 0 ) ) { + // for single part non-looping samples we allow spawning multiple instances, if not prevented by set flags + audio::renderer.insert( this, m_soundmain.buffer ); } } } @@ -100,30 +128,14 @@ sound_source::play( int const Flags ) { void sound_source::stop() { - if( ( m_stage == stage::none ) - || ( m_stage == stage::end ) ) { - return; - } + if( false == is_playing() ) { return; } - switch( m_stage ) { - case stage::begin: - case stage::main: { - // set the source to kill any currently active sounds - m_stage = stage::cease; - if( m_soundend != null_handle ) { - // and if there's defined sample for the sound end, play it instead - audio::renderer.insert( this, m_soundend ); - } - break; - } - case stage::end: { - // set the source to kill any currently active sounds - m_stage = stage::cease; - break; - } - default: { - break; - } + m_stop = true; + + if( ( m_soundend.buffer != null_handle ) + && ( m_soundend.buffer != m_soundmain.buffer ) ) { // end == main can happen in malformed legacy cases + // spawn potentially defined sound end sample, if the emitter is currently active + audio::renderer.insert( this, m_soundend.buffer ); } } @@ -134,19 +146,28 @@ sound_source::update( audio::openal_source &Source ) { if( true == Source.is_playing ) { // kill the sound if requested - if( m_stage == stage::cease ) { + if( true == m_stop ) { Source.stop(); + update_counter( Source.buffers[ Source.buffer_index ], -1 ); + if( false == is_playing() ) { + m_stop = false; + } return; } - // TODO: positional update - if( m_soundbegin != null_handle ) { - // multipart sound + // check and update if needed current sound properties + update_location(); + update_placement_gain(); + Source.sync_with( m_properties ); + + if( m_soundbegin.buffer != null_handle ) { + // potentially a multipart sound // detect the moment when the sound moves from startup sample to the main - if( ( m_stage == stage::begin ) - && ( Source.buffers[ Source.buffer_index ] == m_soundmain ) ) { + if( ( false == Source.is_looping ) + && ( Source.buffers[ Source.buffer_index ] == m_soundmain.buffer ) ) { // when it happens update active sample flags, and activate the looping Source.loop( true ); - m_stage = stage::main; + --( m_soundbegin.playing ); + ++( m_soundmain.playing ); } } } @@ -155,12 +176,14 @@ sound_source::update( audio::openal_source &Source ) { // we can determine this from number of processed buffers if( Source.buffer_index != Source.buffers.size() ) { auto const buffer { Source.buffers[ Source.buffer_index ] }; - if( buffer == m_soundbegin ) { m_stage = stage::begin; } - // TODO: take ito accound sample table for combined sounds - else if( buffer == m_soundmain ) { m_stage = stage::main; } - else if( buffer == m_soundend ) { m_stage = stage::end; } - // TODO: emitter initialization - if( ( buffer == m_soundmain ) + update_counter( buffer, 1 ); + // emitter initialization + Source.range( m_range ); + Source.pitch( m_pitchvariation ); + update_location(); + update_placement_gain(); + Source.sync_with( m_properties ); + if( ( buffer == m_soundmain.buffer ) && ( true == TestFlag( m_flags, sound_flags::looping ) ) ) { // main sample can be optionally set to loop Source.loop( true ); @@ -169,30 +192,151 @@ sound_source::update( audio::openal_source &Source ) { Source.play(); } else { - // auto const buffer { Source.buffers[ Source.buffer_index - 1 ] }; - if( ( buffer == m_soundend ) - || ( ( m_soundend == null_handle ) - && ( buffer == m_soundmain ) ) ) { - m_stage = stage::none; - } + update_counter( buffer, -1 ); } } } +// sets base volume of the emiter to specified value +sound_source & +sound_source::gain( float const Gain ) { + + m_properties.base_gain = clamp( Gain, 0.f, 2.f ); + return *this; +} + +// returns current base volume of the emitter +float +sound_source::gain() const { + + return m_properties.base_gain; +} + +// sets base pitch of the emitter to specified value +sound_source & +sound_source::pitch( float const Pitch ) { + + m_properties.base_pitch = clamp( Pitch, 0.1f, 10.f ); + return *this; +} + bool sound_source::empty() const { // NOTE: we test only the main sound, won't bother playing potential bookends if this is missing // TODO: take into account presence of sample table, for combined sounds - return ( m_soundmain == null_handle ); + return ( m_soundmain.buffer == null_handle ); } // returns true if the source is emitting any sound bool -sound_source::is_playing() const { +sound_source::is_playing( bool const Includesoundends ) const { - return ( m_stage != stage::none ); + return ( ( m_soundbegin.playing + m_soundmain.playing ) > 0 ); +} + +// returns location of the sound source in simulation region space +glm::dvec3 const +sound_source::location() const { + + if( m_owner == nullptr ) { + // if emitter isn't attached to any vehicle the offset variable defines location in region space + return { m_offset }; + } + // otherwise combine offset with the location of the carrier + return { + m_owner->GetPosition() + + m_owner->VectorLeft() * m_offset.x + + m_owner->VectorUp() * m_offset.y + + m_owner->VectorFront() * m_offset.z }; +} + +void +sound_source::update_counter( audio::buffer_handle const Buffer, int const Value ) { + + if( Buffer == m_soundbegin.buffer ) { m_soundbegin.playing += Value; } + // TODO: take ito accound sample table for combined sounds + else if( Buffer == m_soundmain.buffer ) { m_soundmain.playing += Value; } + else if( Buffer == m_soundend.buffer ) { m_soundend.playing += Value; } +} + +float const EU07_SOUNDGAIN_LOW { 0.2f }; +float const EU07_SOUNDGAIN_MEDIUM { 0.65f }; +float const EU07_SOUNDGAIN_FULL { 1.f }; + +void +sound_source::update_location() { + + m_properties.location = location(); +} + +bool +sound_source::update_placement_gain() { + + // NOTE, HACK: current cab id can vary from -1 to +1 + // we use this as modifier to force re-calculations when moving between compartments + int const activecab = ( + FreeFlyModeFlag ? + 0 : + ( Global::pWorld->train() ? + Global::pWorld->train()->Dynamic()->MoverParameters->ActiveCab : + 0 ) ); + // location-based gain factor: + std::uintptr_t placementstamp = reinterpret_cast( ( + FreeFlyModeFlag ? + nullptr : + ( Global::pWorld->train() ? + Global::pWorld->train()->Dynamic() : + nullptr ) ) ) + + activecab; + + if( placementstamp == m_properties.placement_stamp ) { return false; } + + // listener location has changed, calculate new location-based gain factor + switch( m_placement ) { + case sound_placement::general: { + m_properties.placement_gain = EU07_SOUNDGAIN_FULL; + break; + } + case sound_placement::external: { + m_properties.placement_gain = ( + placementstamp == 0 ? + EU07_SOUNDGAIN_FULL : // listener outside + EU07_SOUNDGAIN_LOW ); // listener in a vehicle + break; + } + case sound_placement::internal: { + m_properties.placement_gain = ( + placementstamp == 0 ? + EU07_SOUNDGAIN_LOW : // listener outside + ( Global::pWorld->train()->Dynamic() != m_owner ? + EU07_SOUNDGAIN_LOW : // in another vehicle + ( activecab == 0 ? + EU07_SOUNDGAIN_LOW : // listener in the engine compartment + EU07_SOUNDGAIN_FULL ) ) ); // listener in the cab of the same vehicle + break; + } + case sound_placement::engine: { + m_properties.placement_gain = ( + placementstamp == 0 ? + EU07_SOUNDGAIN_MEDIUM : // listener outside + ( Global::pWorld->train()->Dynamic() != m_owner ? + EU07_SOUNDGAIN_LOW : // in another vehicle + ( activecab == 0 ? + EU07_SOUNDGAIN_FULL : // listener in the engine compartment + EU07_SOUNDGAIN_LOW ) ) ); // listener in another compartment of the same vehicle + break; + } + default: { + // shouldn't ever land here, but, eh + m_properties.placement_gain = EU07_SOUNDGAIN_FULL; + break; + } + } + + m_properties.placement_stamp = placementstamp; + return true; } //--------------------------------------------------------------------------- diff --git a/sound.h b/sound.h index 7f9ffb29..8a636b20 100644 --- a/sound.h +++ b/sound.h @@ -13,7 +13,11 @@ http://mozilla.org/MPL/2.0/. #include "classes.h" #include "names.h" -enum sound_type { +float const EU07_SOUND_CABCONTROLSCUTOFFRANGE { 7.5f }; +float const EU07_SOUND_BRAKINGCUTOFFRANGE { 100.f }; +float const EU07_SOUND_RUNNINGNOISECUTOFFRANGE { 100.f }; + +enum class sound_type { single, multipart }; @@ -29,27 +33,59 @@ enum sound_flags { exclusive = 0x2 // the source won't dispatch more than one active instance of the sound; implied for multi-sounds }; +enum class sound_placement { + general, // source is equally audible in potential carrier and outside of it + internal, // source is located inside of the carrier, and less audible when the listener is outside + engine, // source is located in the engine compartment, less audible when the listener is outside and even less in the cabs + external // source is located on the outside of the carrier, and less audible when the listener is inside +}; + // mini controller and audio dispatcher; issues play commands for the audio renderer, // updates parameters of created audio emitters for the playback duration // TODO: move to simulation namespace after clean up of owner classes class sound_source { public: +// constructors + sound_source( sound_placement const Placement, float const Range = 50.f ); + +// destructor + ~sound_source(); + // methods // restores state of the class from provided data stream sound_source & - deserialize( cParser &Input, sound_type const Legacytype, int const Legacyparameters = NULL ); + deserialize( cParser &Input, sound_type const Legacytype, int const Legacyparameters = 0 ); sound_source & - deserialize( std::string const &Input, sound_type const Legacytype, int const Legacyparameters = NULL ); + deserialize( std::string const &Input, sound_type const Legacytype, int const Legacyparameters = 0 ); // issues contextual play commands for the audio renderer void - play( int const Flags = NULL ); + play( int const Flags = 0 ); // stops currently active play commands controlled by this emitter void stop(); // adjusts parameters of provided implementation-side sound source void update( audio::openal_source &Source ); + // sets base volume of the emiter to specified value + sound_source & + gain( float const Gain ); + // returns current base volume of the emitter + float + gain() const; + // sets base pitch of the emitter to specified value + sound_source & + pitch( float const Pitch ); + // owner setter/getter + void + owner( TDynamicObject const *Owner ); + TDynamicObject const * + owner() const; + // sound source offset setter/getter + void + offset( glm::vec3 const Offset ); + glm::vec3 const & + offset() const; // sound source name setter/getter void name( std::string Name ); @@ -58,38 +94,62 @@ public: // returns true if there isn't any sound buffer associated with the object, false otherwise bool empty() const; - // returns true if the source is emitting any sound + // returns true if the source is emitting any sound; by default doesn't take into account optional ending soudnds bool - is_playing() const; + is_playing( bool const Includesoundends = false ) const; + // returns location of the sound source in simulation region space + glm::dvec3 const + location() const; + +// members + float m_amplitudefactor { 1.f }; // helper, value potentially used by gain calculation + float m_amplitudeoffset { 0.f }; // helper, value potentially used by gain calculation + float m_frequencyfactor { 1.f }; // helper, value potentially used by pitch calculation + float m_frequencyoffset { 0.f }; // helper, value potentially used by pitch calculation private: // types - enum stage { - none, - begin, - main, - end, - cease, - restart + struct sound_data { + audio::buffer_handle buffer { null_handle }; + int playing { 0 }; // number of currently active sample instances }; +// methods + void + update_counter( audio::buffer_handle const Buffer, int const Value ); + void + update_location(); + // potentially updates area-based gain factor of the source. returns: true if location has changed + bool + update_placement_gain(); + // members - TDynamicObject * m_owner { nullptr }; // optional, the vehicle carrying this sound source + TDynamicObject const * m_owner { nullptr }; // optional, the vehicle carrying this sound source glm::vec3 m_offset; // relative position of the source, either from the owner or the region centre + sound_placement m_placement; + float m_range { 50.f }; // audible range of the emitted sounds std::string m_name; - stage m_stage{ stage::none }; - int m_flags{ NULL }; - audio::buffer_handle m_soundmain { null_handle }; // main sound emitted by the source - audio::buffer_handle m_soundbegin { null_handle }; // optional, sound emitted before the main sound - audio::buffer_handle m_soundend { null_handle }; // optional, sound emitted after the main sound + int m_flags { 0 }; // requested playback parameters + sound_properties m_properties; // current properties of the emitted sounds + float m_pitchvariation { 0.f }; // emitter-specific shift in base pitch + bool m_stop { false }; // indicates active sample instances should be terminated + sound_data m_soundmain; // main sound emitted by the source + sound_data m_soundbegin; // optional, sound emitted before the main sound + sound_data m_soundend; // optional, sound emitted after the main sound // TODO: table of samples with associated values, activated when controlling variable matches the value }; +// owner setter/getter +inline void sound_source::owner( TDynamicObject const *Owner ) { m_owner = Owner; } +inline TDynamicObject const * sound_source::owner() const { return m_owner; } +// sound source offset setter/getter +inline void sound_source::offset( glm::vec3 const Offset ) { m_offset = Offset; } +inline glm::vec3 const & sound_source::offset() const { return m_offset; } // sound source name setter/getter inline void sound_source::name( std::string Name ) { m_name = Name; } inline std::string const & sound_source::name() const { return m_name; } -// collection of generators for power grid present in the scene +// collection of sound sources present in the scene class sound_table : public basic_table { };