From 34f96595fdceb1df76b62d653b2847911beb4540 Mon Sep 17 00:00:00 2001 From: tmj-fstate Date: Thu, 30 Nov 2017 16:37:25 +0100 Subject: [PATCH] audio source reinitialization fix, sound volume level tweaks --- Driver.cpp | 52 +++++------ DynObj.cpp | 12 +-- Train.cpp | 214 ++++++++++++++++++++++++---------------------- Train.h | 4 +- audio.cpp | 13 ++- audiorenderer.cpp | 23 +++-- audiorenderer.h | 3 +- sound.cpp | 50 +++++++---- sound.h | 11 ++- 9 files changed, 222 insertions(+), 160 deletions(-) diff --git a/Driver.cpp b/Driver.cpp index 24c9e81e..cb11fa8f 100644 --- a/Driver.cpp +++ b/Driver.cpp @@ -2987,7 +2987,8 @@ void TController::Doors(bool what) if (mvOccupied->DoorOpenCtrl == 1) { // jeśli drzwi sterowane z kabiny if( AIControllFlag ) { - if( mvOccupied->DoorLeftOpened || mvOccupied->DoorRightOpened ) { // AI zamyka drzwi przed odjazdem + if( mvOccupied->DoorLeftOpened || mvOccupied->DoorRightOpened ) { + // AI zamyka drzwi przed odjazdem if( ( true == mvOccupied->DoorClosureWarning ) && ( false == mvOccupied->DepartureSignal ) && ( true == TestFlag( iDrivigFlags, moveDoorOpened ) ) ) { @@ -3092,8 +3093,8 @@ bool TController::PutCommand( std::string NewCommand, double NewValue1, double N asNextStop = TrainParams->NextStop(); iDrivigFlags |= movePrimary; // skoro dostał rozkład, to jest teraz głównym NewCommand = Global::asCurrentSceneryPath + NewCommand + ".wav"; // na razie jeden - if (FileExists(NewCommand)) - { // wczytanie dźwięku odjazdu podawanego bezpośrenido + if (FileExists(NewCommand)) { + // wczytanie dźwięku odjazdu podawanego bezpośrenido #ifdef EU07_USE_OLD_SOUNDCODE tsGuardSignal = new TTextSound( @@ -3101,19 +3102,15 @@ 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 = new sound_source( sound_placement::external, 75.f ); 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 } - else - { + else { NewCommand = NewCommand.insert(NewCommand.find_last_of("."),"radio"); // wstawienie przed kropkč - if (FileExists(NewCommand)) - { // wczytanie dźwięku odjazdu w wersji radiowej (słychać tylko w kabinie) + if (FileExists(NewCommand)) { + // wczytanie dźwięku odjazdu w wersji radiowej (słychać tylko w kabinie) #ifdef EU07_USE_OLD_SOUNDCODE tsGuardSignal = new TTextSound( @@ -3121,12 +3118,8 @@ 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 = new sound_source( sound_placement::internal, 2 * EU07_SOUND_CABCONTROLSCUTOFFRANGE ); 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; } @@ -4487,20 +4480,21 @@ TController::UpdateSituation(double dt) { } if (VelDesired > 0.0) if( ( ( iDrivigFlags & moveStopHere ) == 0 ) - || ( ( SemNextIndex != -1 ) - && ( SemNextIndex < sSpeedTable.size() ) // BUG: index can point at non-existing slot. investigate reason(s) - && ( sSpeedTable[SemNextIndex].fVelNext != 0.0 ) ) ) { + || ( ( SemNextIndex != -1 ) + && ( SemNextIndex < sSpeedTable.size() ) // BUG: index can point at non-existing slot. investigate reason(s) + && ( sSpeedTable[SemNextIndex].fVelNext != 0.0 ) ) ) { // 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 iDrivigFlags &= ~moveGuardSignal; // tylko raz nadać - +/* if( ( iDrivigFlags & moveDoorOpened ) && ( false == mvOccupied->DoorOpenCtrl ) ) { // jeśli drzwi otwarte, niesterowane przez maszynistę Doors( false ); // a EZT zamknie dopiero po odegraniu komunikatu kierownika } +*/ if( tsGuardSignal != nullptr ) { #ifdef EU07_USE_OLD_SOUNDCODE tsGuardSignal->Stop(); @@ -4513,11 +4507,14 @@ TController::UpdateSituation(double dt) { // 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 ) { + if( iGuardRadio == 0 ) { // 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->owner( pVehicle ); + // place virtual conductor some distance away + tsGuardSignal->offset( { pVehicle->MoverParameters->Dim.W * -0.75f, 1.7f, std::min( -20.0, -0.2 * fLength ) } ); tsGuardSignal->play( sound_flags::exclusive ); #endif } @@ -4526,15 +4523,18 @@ TController::UpdateSituation(double dt) { // if (!FreeFlyModeFlag) //obserwator musi być w środku pojazdu // (albo może mieć radio przenośne) - kierownik mógłby powtarzać // przy braku reakcji +#ifdef EU07_USE_OLD_SOUNDCODE 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 } +#else + // TODO: proper system for sending/receiving radio messages + // place the sound in appropriate cab of the manned vehicle + tsGuardSignal->owner( pVehicle ); + tsGuardSignal->offset( { 0.f, 2.f, pVehicle->MoverParameters->Dim.L * 0.4f * ( pVehicle->MoverParameters->ActiveCab < 0 ? -1 : 1 ) } ); + tsGuardSignal->play( sound_flags::exclusive ); +#endif } } } diff --git a/DynObj.cpp b/DynObj.cpp index 59941932..f2ff0bc0 100644 --- a/DynObj.cpp +++ b/DynObj.cpp @@ -3605,6 +3605,8 @@ void TDynamicObject::RenderSounds() { double volume { 1.0 }; double const dt { Timer::GetDeltaRenderTime() }; + if( dt == 0.0 ) { return; } + // engine sounds if( MoverParameters->Power > 0 ) { @@ -3889,7 +3891,7 @@ void TDynamicObject::RenderSounds() { } else { eng_turbo = std::max( goalpitch, eng_turbo - changerate * 0.5 ); - volume = std::max( 0.0, sTurbo.gain() - 10.0 * dt ); + volume = std::max( 0.0, sTurbo.gain() - 2.0 * dt ); if( volume > 0.05 ) { sTurbo .pitch( 0.4 + eng_turbo * 0.4 ) @@ -3943,7 +3945,7 @@ void TDynamicObject::RenderSounds() { // brake system and braking sounds: if( m_lastbrakepressure != -1.f ) { - // calculate rate of pressure change in brake cylinder, once it's been initialized + // calculate rate of pressure drop 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 ); } @@ -3951,7 +3953,7 @@ void TDynamicObject::RenderSounds() { 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 ] ) ) + .gain( static_cast( 1.25 * std::max( MoverParameters->BrakePress, 0.0 ) / MoverParameters->MaxBrakePress[ 3 ] ) ) .play( sound_flags::exclusive | sound_flags::looping ); } else { @@ -4164,13 +4166,13 @@ void TDynamicObject::RenderSounds() { } // scale volume by track quality volume *= ( 20.0 + MyTrack->iDamageFlag ) / 21; - // scale volume with curve radius and vehicle speed + // scale volume with 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, + MoverParameters->Vel / 60.0, 0.0, 1.0 ) ); rsRunningNoise .pitch( clamp( frequency, 0.5, 1.15 ) ) // arbitrary limits to prevent the pitch going out of whack diff --git a/Train.cpp b/Train.cpp index db9f5a56..213c9683 100644 --- a/Train.cpp +++ b/Train.cpp @@ -5130,120 +5130,128 @@ bool TTrain::Update( double const Deltatime ) void TTrain::update_sounds( double const Deltatime ) { -#ifdef EU07_USE_OLD_SOUNDCODE - double vol = 0; - // int freq=1; - double dfreq; + + if( Deltatime == 0.0 ) { return; } + + double volume { 0.0 }; + double const brakevolumescale { 0.5 }; // McZapkie-280302 - syczenie - if( ( mvOccupied->BrakeHandle == FV4a ) || ( mvOccupied->BrakeHandle == FVel6 ) ) { - if( rsHiss.AM != 0 ) // upuszczanie z PG - { - fPPress = ( 1 * fPPress + mvOccupied->Handle->GetSound( s_fv4a_b ) ) / ( 2 ); - if( fPPress > 0 ) { - vol = 2.0 * rsHiss.AM * fPPress; - } - if( vol > 0.001 ) { - rsHiss.Play( vol, DSBPLAY_LOOPING, true, DynamicObject->GetPosition() ); - } - else { - rsHiss.Stop(); - } + // TODO: softer volume reduction than plain abrupt stop, perhaps as reusable wrapper? + if( ( mvOccupied->BrakeHandle == FV4a ) + || ( mvOccupied->BrakeHandle == FVel6 ) ) { + // upuszczanie z PG + fPPress = interpolate( fPPress, mvOccupied->Handle->GetSound( s_fv4a_b ), 0.05 ); + volume = ( + fPPress > 0 ? + rsHiss.m_amplitudefactor * fPPress * 0.25 : + 0 ); + if( volume * brakevolumescale > 0.05 ) { + rsHiss + .gain( volume * brakevolumescale ) + .play( sound_flags::exclusive | sound_flags::looping ); } - if( rsHissU.AM != 0 ) // upuszczanie z PG - { - fNPress = ( 1 * fNPress + mvOccupied->Handle->GetSound( s_fv4a_u ) ) / ( 2 ); - if( fNPress > 0 ) { - vol = rsHissU.AM * fNPress; - } - if( vol > 0.001 ) { - rsHissU.Play( vol, DSBPLAY_LOOPING, true, DynamicObject->GetPosition() ); - } - else { - rsHissU.Stop(); - } + else { + rsHiss.stop(); } - if( rsHissE.AM != 0 ) // upuszczanie przy naglym - { - vol = mvOccupied->Handle->GetSound( s_fv4a_e ) * rsHissE.AM; - if( vol > 0.001 ) { - rsHissE.Play( vol, DSBPLAY_LOOPING, true, DynamicObject->GetPosition() ); - } - else { - rsHissE.Stop(); - } + // napelnianie PG + fNPress = interpolate( fNPress, mvOccupied->Handle->GetSound( s_fv4a_u ), 0.25 ); + volume = ( + fNPress > 0 ? + rsHissU.m_amplitudefactor * fNPress : + 0 ); + if( volume * brakevolumescale > 0.05 ) { + rsHissU + .gain( volume * brakevolumescale ) + .play( sound_flags::exclusive | sound_flags::looping ); } - if( rsHissX.AM != 0 ) // upuszczanie sterujacego fala - { - vol = mvOccupied->Handle->GetSound( s_fv4a_x ) * rsHissX.AM; - if( vol > 0.001 ) { - rsHissX.Play( vol, DSBPLAY_LOOPING, true, DynamicObject->GetPosition() ); - } - else { - rsHissX.Stop(); - } + else { + rsHissU.stop(); } - if( rsHissT.AM != 0 ) // upuszczanie z czasowego - { - vol = mvOccupied->Handle->GetSound( s_fv4a_t ) * rsHissT.AM; - if( vol > 0.001 ) { - rsHissT.Play( vol, DSBPLAY_LOOPING, true, DynamicObject->GetPosition() ); - } - else { - rsHissT.Stop(); - } + // upuszczanie przy naglym + volume = mvOccupied->Handle->GetSound( s_fv4a_e ) * rsHissE.m_amplitudefactor; + if( volume * brakevolumescale > 0.05 ) { + rsHissE + .gain( volume * brakevolumescale ) + .play( sound_flags::exclusive | sound_flags::looping ); + } + else { + rsHissE.stop(); + } + // upuszczanie sterujacego fala + volume = mvOccupied->Handle->GetSound( s_fv4a_x ) * rsHissX.m_amplitudefactor; + if( volume * brakevolumescale > 0.05 ) { + rsHissX + .gain( volume * brakevolumescale ) + .play( sound_flags::exclusive | sound_flags::looping ); + } + else { + rsHissX.stop(); + } + // upuszczanie z czasowego + volume = mvOccupied->Handle->GetSound( s_fv4a_t ) * rsHissT.m_amplitudefactor; + if( volume * brakevolumescale > 0.05 ) { + rsHissT + .gain( volume * brakevolumescale ) + .play( sound_flags::exclusive | sound_flags::looping ); + } + else { + rsHissT.stop(); } - } // koniec FV4a - else // jesli nie FV4a - { - if( rsHiss.AM != 0.0 ) // upuszczanie z PG - { - fPPress = ( 4.0f * fPPress + std::max( mvOccupied->dpLocalValve, mvOccupied->dpMainValve ) ) / ( 4.0f + 1.0f ); - if( fPPress > 0.0f ) { - vol = 2.0 * rsHiss.AM * fPPress; - } - if( vol > 0.01 ) { - rsHiss.Play( vol, DSBPLAY_LOOPING, true, DynamicObject->GetPosition() ); - } - else { - rsHiss.Stop(); - } + } else { + // jesli nie FV4a + // upuszczanie z PG + fPPress = ( 4.0f * fPPress + std::max( mvOccupied->dpLocalValve, mvOccupied->dpMainValve ) ) / ( 4.0f + 1.0f ); + volume = ( + fPPress > 0.0f ? + 2.0 * rsHiss.m_amplitudefactor * fPPress : + 0.0 ); + if( volume > 0.05 ) { + rsHiss + .gain( volume ) + .play( sound_flags::exclusive | sound_flags::looping ); } - if( rsHissU.AM != 0.0 ) // napelnianie PG - { - fNPress = ( 4.0f * fNPress + Min0R( mvOccupied->dpLocalValve, mvOccupied->dpMainValve ) ) / ( 4.0f + 1.0f ); - if( fNPress < 0.0f ) { - vol = -1.0 * rsHissU.AM * fNPress; - } - if( vol > 0.01 ) { - rsHissU.Play( vol, DSBPLAY_LOOPING, true, DynamicObject->GetPosition() ); - } - else { - rsHissU.Stop(); - } + else { + rsHiss.stop(); + } + // napelnianie PG + fNPress = ( 4.0f * fNPress + Min0R( mvOccupied->dpLocalValve, mvOccupied->dpMainValve ) ) / ( 4.0f + 1.0f ); + volume = ( + fNPress < 0.0f ? + -1.0 * rsHissU.m_amplitudefactor * fNPress : + 0.0 ); + if( volume > 0.01 ) { + rsHissU + .gain( volume ) + .play( sound_flags::exclusive | sound_flags::looping ); + } + else { + rsHissU.stop(); } } // koniec nie FV4a // Winger-160404 - syczenie pomocniczego (luzowanie) - /* if (rsSBHiss.AM!=0) - { - fSPPress=(mvOccupied->LocalBrakeRatio())-(mvOccupied->LocalBrakePos); - if (fSPPress>0) - { - vol=2*rsSBHiss.AM*fSPPress; - } - if (vol>0.1) - { - rsSBHiss.Play(vol,DSBPLAY_LOOPING,true,DynamicObject->GetPosition()); - } - else - { - rsSBHiss.Stop(); - } - } - */ -#endif + if( m_lastlocalbrakepressure != -1.f ) { + // calculate rate of pressure drop in local brake cylinder, once it's been initialized + auto const brakepressuredifference { m_lastlocalbrakepressure - mvOccupied->LocBrakePress }; + m_localbrakepressurechange = interpolate( m_localbrakepressurechange, 10 * ( brakepressuredifference / Deltatime ), 0.1f ); + } + m_lastlocalbrakepressure = mvOccupied->LocBrakePress; + if( ( m_localbrakepressurechange > 0.05f ) + && ( mvOccupied->LocBrakePress > mvOccupied->BrakePress - 0.05 ) ) { + rsSBHiss + .gain( clamp( 0.05 * m_localbrakepressurechange, 0.0, 1.5 ) ) + .play( sound_flags::exclusive | sound_flags::looping ); + } + else { + // don't stop the sound too abruptly + volume = std::max( 0.0, rsSBHiss.gain() - 0.1 * Deltatime ); + rsSBHiss.gain( volume ); + if( volume < 0.05 ) { + rsSBHiss.stop(); + } + } // ambient sound // since it's typically ticking of the clock we can center it on tachometer or on middle of compartment bounding area @@ -5400,6 +5408,10 @@ bool TTrain::LoadMMediaFile(std::string const &asFileName) // syk: rsHiss.deserialize( parser, sound_type::single, sound_parameters::amplitude ); rsHiss.owner( DynamicObject ); + if( true == rsSBHiss.empty() ) { + // fallback for vehicles without defined local brake hiss sound + rsSBHiss = rsHiss; + } } else if (token == "airsound2:") { diff --git a/Train.h b/Train.h index e60f3667..968917cd 100644 --- a/Train.h +++ b/Train.h @@ -410,6 +410,8 @@ public: // reszta może by?publiczna 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 + float m_lastlocalbrakepressure { -1.f }; // helper, cached level of pressure in local brake cylinder + float m_localbrakepressurechange { 0.f }; // recent change of pressure in local brake cylinder sound_source rsFadeSound { sound_placement::internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE }; sound_source dsbHasler { sound_placement::internal, EU07_SOUND_CABCONTROLSCUTOFFRANGE }; @@ -461,7 +463,7 @@ private: bool bHeat[8]; // grzanie // McZapkie: do syczenia float fPPress, fNPress; - float fSPPress, fSNPress; +// float fSPPress, fSNPress; int iSekunda; // Ra: sekunda aktualizacji pr?dko?ci int iRadioChannel; // numer aktualnego kana?u radiowego TPythonScreens pyScreens; diff --git a/audio.cpp b/audio.cpp index 3e0c8f70..e3b4f6e1 100644 --- a/audio.cpp +++ b/audio.cpp @@ -128,7 +128,18 @@ buffer_manager::create( std::string const &Filename ) { return emplace( filelookup ); } } - // if dynamic-specific lookup finds nothing, try the default sound folder + if( filename.find( '/' ) != std::string::npos ) { + // if the filename includes path, try to use it directly + lookup = find_buffer( filename ); + if( lookup != null_handle ) { + return lookup; + } + filelookup = find_file( filename ); + if( false == filelookup.empty() ) { + return emplace( filelookup ); + } + } + // if dynamic-specific and/or direct lookups find nothing, try the default sound folder lookup = find_buffer( szSoundPath + filename ); if( lookup != null_handle ) { return lookup; diff --git a/audiorenderer.cpp b/audiorenderer.cpp index 55b97197..5bb4985d 100644 --- a/audiorenderer.cpp +++ b/audiorenderer.cpp @@ -33,6 +33,12 @@ void openal_source::stop() { loop( false ); + // NOTE: workaround for potential edge cases where ::alSourceStop() doesn't set source which wasn't yet started to AL_STOPPED + int state; + ::alGetSourcei( id, AL_SOURCE_STATE, &state ); + if( state == AL_INITIAL ) { + play(); + } ::alSourceStop( id ); is_playing = false; } @@ -43,7 +49,6 @@ 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 ); // for multipart sounds trim away processed sources until only one remains, the last one may be set to looping by the controller ALuint bufferid; @@ -53,9 +58,11 @@ openal_source::update( double const Deltatime ) { buffers.erase( std::begin( buffers ) ); --buffer_index; } + int state; ::alGetSourcei( id, AL_SOURCE_STATE, &state ); is_playing = ( state == AL_PLAYING ); + // request instructions from the controller controller->update( *this ); } @@ -75,6 +82,7 @@ openal_source::sync_with( sound_properties const &State ) { if( glm::length2( sourceoffset ) > std::max( ( sound_range * sound_range ), ( EU07_SOUND_CUTOFFRANGE * EU07_SOUND_CUTOFFRANGE ) ) ) { // range cutoff check stop(); + is_synced = false; // flag sync failure for the controller return; } if( sound_range >= 0 ) { @@ -101,6 +109,7 @@ openal_source::sync_with( sound_properties const &State ) { ::alSourcef( id, AL_PITCH, properties.base_pitch * pitch_variation ); } + is_synced = true; } // sets max audible distance for sounds emitted by the source @@ -145,20 +154,18 @@ openal_source::loop( bool const State ) { // NOTE: doesn't release allocated implementation-side source void openal_source::clear() { - - controller = nullptr; // unqueue bound buffers: // ensure no buffer is in use... stop(); // ...prepare space for returned ids of unqueued buffers (not that we need that info)... std::vector bufferids; bufferids.resize( buffers.size() ); - // ...release the buffers and update source data to match + // ...release the buffers... ::alSourceUnqueueBuffers( id, bufferids.size(), bufferids.data() ); - buffers.clear(); - buffer_index = 0; - // reset properties - properties = sound_properties(); + // ...and reset reset the properties, except for the id of the allocated source + auto const sourceid { id }; + *this = openal_source(); + id = sourceid; } diff --git a/audiorenderer.h b/audiorenderer.h index 220be99f..79f7facb 100644 --- a/audiorenderer.h +++ b/audiorenderer.h @@ -36,9 +36,10 @@ struct openal_source { ALuint id { audio::null_resource }; // associated AL resource sound_source *controller { nullptr }; // source controller buffer_sequence buffers; // sequence of samples the source will emit - int buffer_index; // currently queued sample from the buffer sequence + int buffer_index { 0 }; // currently queued sample from the buffer sequence bool is_playing { false }; bool is_looping { false }; + bool is_synced { true }; // set to false only if a sync attempt fails sound_properties properties; // methods diff --git a/sound.cpp b/sound.cpp index e0cdff2d..69da3b1a 100644 --- a/sound.cpp +++ b/sound.cpp @@ -108,10 +108,10 @@ sound_source::play( int const Flags ) { // 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 ) ); + insert( std::begin( bufferlist ), std::end( bufferlist ) ); } else { - audio::renderer.insert( this, m_soundmain.buffer ); + insert( m_soundmain.buffer ); } } else { @@ -119,7 +119,7 @@ sound_source::play( int const Flags ) { 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 ); + insert( m_soundmain.buffer ); } } } @@ -135,7 +135,7 @@ sound_source::stop() { 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 ); + insert( m_soundend.buffer ); } } @@ -154,10 +154,6 @@ sound_source::update( audio::openal_source &Source ) { } return; } - // 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 @@ -170,26 +166,41 @@ sound_source::update( audio::openal_source &Source ) { ++( m_soundmain.playing ); } } + + // check and update if needed current sound properties + update_location(); + update_placement_gain(); + Source.sync_with( m_properties ); + if( false == Source.is_synced ) { + // if the sync went wrong we let the renderer kill its part of the emitter, and update our playcounter(s) to match + update_counter( Source.buffers[ Source.buffer_index ], -1 ); + } + } else { // if the emitter isn't playing it's either done or wasn't yet started // we can determine this from number of processed buffers if( Source.buffer_index != Source.buffers.size() ) { auto const buffer { Source.buffers[ Source.buffer_index ] }; - 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 ); } - // all set, start playback - Source.play(); + Source.range( m_range ); + Source.pitch( m_pitchvariation ); + update_location(); + update_placement_gain(); + Source.sync_with( m_properties ); + if( true == Source.is_synced ) { + // all set, start playback + Source.play(); + } + else { + // if the initial sync went wrong we skip the activation so the renderer can clean the emitter on its end + update_counter( buffer, -1 ); + } } else { auto const buffer { Source.buffers[ Source.buffer_index - 1 ] }; @@ -339,4 +350,11 @@ sound_source::update_placement_gain() { return true; } +void +sound_source::insert( audio::buffer_handle Buffer ) { + + std::vector buffers { Buffer }; + return insert( std::begin( buffers ), std::end( buffers ) ); +} + //--------------------------------------------------------------------------- diff --git a/sound.h b/sound.h index 8a636b20..ee916c9a 100644 --- a/sound.h +++ b/sound.h @@ -15,7 +15,7 @@ http://mozilla.org/MPL/2.0/. float const EU07_SOUND_CABCONTROLSCUTOFFRANGE { 7.5f }; float const EU07_SOUND_BRAKINGCUTOFFRANGE { 100.f }; -float const EU07_SOUND_RUNNINGNOISECUTOFFRANGE { 100.f }; +float const EU07_SOUND_RUNNINGNOISECUTOFFRANGE { 200.f }; enum class sound_type { single, @@ -122,6 +122,15 @@ private: // potentially updates area-based gain factor of the source. returns: true if location has changed bool update_placement_gain(); + void + insert( audio::buffer_handle Buffer ); + template + void + insert( Iterator_ First, Iterator_ Last ) { + + audio::renderer.insert( this, First, Last ); + update_counter( *First, 1 ); + } // members TDynamicObject const * m_owner { nullptr }; // optional, the vehicle carrying this sound source