Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
e291d23
Play maximum 2 simultaneous walk sounds (one till it ends and other o…
Districh-ru Jun 16, 2023
67c737c
Add the sound fade ability (for SDL2) to avoid possible clicks while …
Districh-ru Jun 16, 2023
2d908a4
Update copyright year
Districh-ru Jun 16, 2023
6442aec
Fix SDL1 compilation
Districh-ru Jun 16, 2023
6d6bfdd
Fix cut of single step move, increase sound fade time to reduce possi…
Districh-ru Jun 18, 2023
127779d
Apply the same logic for playing fly sounds for flying creatures, fix…
Districh-ru Jun 18, 2023
371e87e
Remove extra debug print
Districh-ru Jun 18, 2023
1cc2149
ReduceSDL_mixer 'chunkSize' 1024 and slightly increase sound fade tim…
Districh-ru Jun 21, 2023
3a94123
Do not fade the last played walk sound, for the fast movement do not …
Districh-ru Jul 16, 2023
fffc44b
Merge branch 'ihhub:master' into walk_sound_limit
Districh-ru Jul 16, 2023
652cf4c
Fix audio playback issues.
Districh-ru Jul 22, 2023
abfcf44
Remove "echo" effect after the last step
Districh-ru Jul 22, 2023
a7bbe14
Apply the same move sound rules for the flying creatures
Districh-ru Jul 22, 2023
3df7a1e
Revert change of std::vector to std::set for '_channelsToCleanup'
Districh-ru Jul 22, 2023
69fadb7
Change approach for creature walk sounds limit: Play every sound full…
Districh-ru Jul 23, 2023
91120d2
Remove the unused variable 'pathSize'
Districh-ru Jul 23, 2023
a6cd09f
Make Clang-Tidy a little happier
Districh-ru Jul 23, 2023
dabd4df
Fully revert the changes in audio.h/cpp
Districh-ru Jul 23, 2023
c1912f8
Address @oleg-derevenetz comment
Districh-ru Jul 23, 2023
fb68633
Make std::scoped_lock const to correspond #7458
Districh-ru Jul 23, 2023
0929ccb
Fix division by zero: do not lower the frameDelay if is it already 1 ms.
Districh-ru Jul 23, 2023
2fe87b6
Address @Branikolog comment: Change logic for move sound delay to dep…
Districh-ru Jul 23, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions src/fheroes2/audio/audio_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -906,6 +906,16 @@ namespace AudioManager
g_asyncSoundManager.pushSound( m82, Settings::Get().SoundVolume() );
}

double getSoundDurationMs( const int m82 )
{
const std::scoped_lock<std::recursive_mutex> lock( g_asyncSoundManager.resourceMutex() );

const std::vector<uint8_t> & v = GetWAV( m82 );

// Sound duration is: (wavSize - headerSize) / sampleRateKHz / channelCount / byesPerSample.
return ( static_cast<double>( v.size() ) - 44. ) / 22.050;
}

bool isExternalMusicFileAvailable( const int trackId )
{
return !getExternalMusicFile( trackId ).empty();
Expand Down
3 changes: 3 additions & 0 deletions src/fheroes2/audio/audio_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,9 @@ namespace AudioManager
int PlaySound( const int m82 );
void PlaySoundAsync( const int m82 );

// Returns specified sound duration in milliseconds.
double getSoundDurationMs( const int m82 );

// Returns true if an external music file is available for the music track with the specified ID, otherwise returns false.
bool isExternalMusicFileAvailable( const int trackId );

Expand Down
69 changes: 58 additions & 11 deletions src/fheroes2/battle/battle_interface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3834,12 +3834,15 @@ void Battle::Interface::RedrawActionMove( Unit & unit, const Indexes & path )

Cursor::Get().SetThemes( Cursor::WAR_POINTER );

Indexes::const_iterator dst = path.begin();
Bridge * bridge = Arena::GetBridge();

const int walkSoundId = unit.M82Move();

// Get the time to animate movement for one cell.
uint32_t frameDelay = Game::ApplyBattleSpeed( unit.animation.getMoveSpeed() );
if ( unit.Modes( SP_HASTE ) ) {
const int cellCountForOneSound = static_cast<int>( std::round( AudioManager::getSoundDurationMs( walkSoundId ) / frameDelay ) ) - 1;

if ( unit.Modes( SP_HASTE ) && frameDelay > 1 ) {
frameDelay = frameDelay * 65 / 100; // by 35% faster
}
else if ( unit.Modes( SP_SLOW ) ) {
Expand All @@ -3848,6 +3851,7 @@ void Battle::Interface::RedrawActionMove( Unit & unit, const Indexes & path )

// Set the delay between movement animation frames. This delay will be used for all types of movement animations.
unit.SwitchAnimation( Monster_Info::MOVING );

Game::setCustomUnitMovementDelay( frameDelay / unit.animation.animationLength() );

std::string msg = _( "Moved %{monster}: from [%{src}] to [%{dst}]." );
Expand All @@ -3863,7 +3867,10 @@ void Battle::Interface::RedrawActionMove( Unit & unit, const Indexes & path )
const bool canFly = unit.isAbilityPresent( fheroes2::MonsterAbilityType::FLYING );
// If it is a wide creature (cache this boolean to use in the loop).
const bool isWide = unit.isWide();

Indexes::const_iterator dst = path.begin();
const Indexes::const_iterator pathEnd = path.end();
const Indexes::const_iterator finalStep = pathEnd - 1;

const bool isOneStepPath = [&unit, &path]() {
if ( path.size() == 1 ) {
Expand Down Expand Up @@ -3952,15 +3959,22 @@ void Battle::Interface::RedrawActionMove( Unit & unit, const Indexes & path )
}
}

// For Battle speed 9 and 10 we play sound by sound without any simultaneous playbacks
// and do not use calculated 'cellCountForOneSound' as the real count may differ as
// some monitors may use 50 Hz refresh rate with V-sync on (or a low CPU may not allow so high FPS).
const bool playSoundBySound = Settings::Get().BattleSpeed() > 8;
int soundStatus = -1;

while ( dst != pathEnd ) {
// Check if a wide unit changes its horizontal direction.
if ( isWide && unit.GetTailIndex() == *dst ) {
// We must not reflect the flyers at the and of the path (just before the landing).
if ( !canFly || ( dst != ( pathEnd - 1 ) ) ) {
if ( !canFly || ( dst != finalStep ) ) {
unit.SetReflection( !unit.isReflect() );
}
// After changing the direction go to the next step in the path.
++dst;

continue;
}

Expand All @@ -3977,7 +3991,7 @@ void Battle::Interface::RedrawActionMove( Unit & unit, const Indexes & path )
unit.SwitchAnimation( Monster_Info::STAND_STILL );
bridge->ActionDown();
_movingUnit = &unit;
if ( dst == ( pathEnd - 1 ) ) {
if ( dst == finalStep ) {
// There is only one cell left to move after standing.
unit.SwitchAnimation( Monster_Info::MOVE_QUICK );
}
Expand All @@ -3992,9 +4006,20 @@ void Battle::Interface::RedrawActionMove( Unit & unit, const Indexes & path )
_movingPos.x -= CELLW;
}

// Render the unit movement with the movement sound.
// TODO: adjust sounds calls and synchronize them with frames. Take into account that some sounds (like for Cavalry) consists of a sequence of steps.
AudioManager::PlaySound( unit.M82Move() );
// Limit the simultaneous walk sounds.
if ( playSoundBySound ) {
if ( soundStatus < 0 || !Mixer::isPlaying( soundStatus ) ) {
soundStatus = AudioManager::PlaySound( walkSoundId );
}
}
else {
if ( soundStatus < 1 ) {
AudioManager::PlaySound( walkSoundId );
soundStatus = cellCountForOneSound;
}
--soundStatus;
}

AnimateUnitWithDelay( unit );
unit.SetPosition( *dst );

Expand All @@ -4014,7 +4039,7 @@ void Battle::Interface::RedrawActionMove( Unit & unit, const Indexes & path )
unit.SwitchAnimation( Monster_Info::STAND_STILL );
bridge->ActionUp();
_movingUnit = &unit;
if ( dst == ( pathEnd - 1 ) ) {
if ( dst == finalStep ) {
// There is only one cell left to move after standing.
unit.SwitchAnimation( Monster_Info::MOVE_QUICK );
}
Expand All @@ -4023,7 +4048,7 @@ void Battle::Interface::RedrawActionMove( Unit & unit, const Indexes & path )
unit.SwitchAnimation( Monster_Info::MOVE_START );
}
}
else if ( dst == ( pathEnd - 1 ) ) {
else if ( dst == finalStep ) {
// There is only one cell left to move.
unit.SwitchAnimation( Monster_Info::MOVE_END );
}
Expand Down Expand Up @@ -4087,7 +4112,10 @@ void Battle::Interface::RedrawActionFly( Unit & unit, const Position & pos )

const uint32_t step = unit.animation.getFlightSpeed();
uint32_t frameDelay = Game::ApplyBattleSpeed( unit.animation.getMoveSpeed() );
if ( unit.Modes( SP_HASTE ) ) {
const int flySoundId = unit.M82Move();
const int cellCountForOneSound = static_cast<int>( std::round( AudioManager::getSoundDurationMs( flySoundId ) / frameDelay ) ) - 1;

if ( unit.Modes( SP_HASTE ) && frameDelay > 1 ) {
frameDelay = frameDelay * 8 / 10; // 20% faster
}
else if ( unit.Modes( SP_SLOW ) ) {
Expand Down Expand Up @@ -4129,11 +4157,30 @@ void Battle::Interface::RedrawActionFly( Unit & unit, const Position & pos )
++currentPoint;
}

// For Battle speed 9 and 10 we play sound by sound without any simultaneous playbacks
// and do not use calculated 'cellCountForOneSound' as the real count may differ as
// some monitors may use 50 Hz refresh rate with V-sync on (or a low CPU may not allow so high FPS).
const bool playSoundBySound = Settings::Get().BattleSpeed() > 8;
int soundStatus = -1;

unit.SwitchAnimation( Monster_Info::MOVING );
while ( currentPoint != points.end() ) {
_movingPos = *currentPoint;

AudioManager::PlaySound( unit.M82Move() );
// Limit the simultaneous fly sounds.
if ( playSoundBySound ) {
if ( soundStatus < 0 || !Mixer::isPlaying( soundStatus ) ) {
soundStatus = AudioManager::PlaySound( flySoundId );
}
}
else {
if ( soundStatus < 1 ) {
AudioManager::PlaySound( flySoundId );
soundStatus = cellCountForOneSound;
}
--soundStatus;
}

unit.animation.restartAnimation();
AnimateUnitWithDelay( unit );

Expand Down