Skip to content

bugfix(audiomanager): Fix dummy audio manager implementation for failing audio length script conditions in headless mode#2313

Open
Caball009 wants to merge 5 commits intoTheSuperHackers:mainfrom
Caball009:fix_dummy_audio_manager
Open

bugfix(audiomanager): Fix dummy audio manager implementation for failing audio length script conditions in headless mode#2313
Caball009 wants to merge 5 commits intoTheSuperHackers:mainfrom
Caball009:fix_dummy_audio_manager

Conversation

@Caball009
Copy link

@Caball009 Caball009 commented Feb 16, 2026

Our current implementation of dummy audio manager is incomplete, because it cannot return the actual length of audio files. Replays will mismatch if they contain scripts that rely on this.

TODO:

  • Make sure the Github CI can access the audio files.
  • Replicate in Generals.

@Caball009 Caball009 added Audio Is audio related Bug Something is not working right, typically is user facing Major Severity: Minor < Major < Critical < Blocker ThisProject The issue was introduced by this project, or this task is specific to this project Script Is related to Script Engine, SCB labels Feb 16, 2026
@Caball009 Caball009 changed the title bugfix(audiomanager): Fix audio manager implementation for failing script conditions in headless mode bugfix(audiomanager): Fix dummy audio manager implementation for failing script audio length conditions in headless mode Feb 16, 2026
@Caball009 Caball009 changed the title bugfix(audiomanager): Fix dummy audio manager implementation for failing script audio length conditions in headless mode bugfix(audiomanager): Fix dummy audio manager implementation for failing audio length script conditions in headless mode Feb 16, 2026
@Caball009 Caball009 force-pushed the fix_dummy_audio_manager branch from 7751f9e to 477d80b Compare February 28, 2026 16:19
@Caball009 Caball009 marked this pull request as ready for review February 28, 2026 16:21
@greptile-apps
Copy link

greptile-apps bot commented Feb 28, 2026

Greptile Summary

Fixed dummy audio manager for headless mode by changing inheritance from AudioManager to MilesAudioManager, enabling actual audio file length queries required by certain scripts. The new MilesAudioManagerDummy deliberately does not override openDevice(), closeDevice(), or getFileLengthMS() to leverage the parent's Miles Sound System integration for querying audio file metadata without playback.

Key Changes:

  • Removed AudioManagerDummy from shared Core header
  • Added MilesAudioManagerDummy in Miles-specific header inheriting from MilesAudioManager
  • Refactored GeneralsMD audio manager factory to accept headless parameter
  • Updated GeneralsMD initialization to use factory pattern

Critical Issue:

  • Generals (vanilla) build is broken - Generals/Code/GameEngine/Source/Common/GameEngine.cpp:437 still references removed AudioManagerDummy class

Minor Issue:

  • Commented-out code in MilesAudioManagerDummy could be removed since line 335 comment explains design intent

Confidence Score: 2/5

  • Cannot merge - breaks Generals build due to removed AudioManagerDummy reference
  • GeneralsMD changes are architecturally sound and the approach correctly enables audio file length queries in headless mode, but the shared Core header change breaks the Generals build which still references the removed class. PR's own TODO acknowledges this ("Replicate in Generals"), but the current state leaves one codebase broken.
  • Pay attention to Core/GameEngine/Include/Common/GameAudio.h - removal breaks Generals build. Either revert this change temporarily, or include corresponding Generals updates in this PR.

Important Files Changed

Filename Overview
Core/GameEngine/Include/Common/GameAudio.h Removed AudioManagerDummy class from shared header, breaking Generals build which still references it
Core/GameEngineDevice/Include/MilesAudioDevice/MilesAudioManager.h Added MilesAudioManagerDummy inheriting from MilesAudioManager to enable audio file length queries in headless mode; contains commented-out code for documentation
GeneralsMD/Code/GameEngineDevice/Include/Win32Device/Common/Win32GameEngine.h Implemented createAudioManager(Bool headless) to return MilesAudioManagerDummy when headless, otherwise MilesAudioManager

Class Diagram

%%{init: {'theme': 'neutral'}}%%
classDiagram
    class AudioManager {
        <<interface>>
        +getFileLengthMS(string) Real
        +openDevice() void
        +closeDevice() void
        +getDevice() void*
    }
    
    class MilesAudioManager {
        -m_digitalHandle
        +openDevice() void
        +closeDevice() void
        +getDevice() void*
        +getFileLengthMS(string) Real
    }
    
    class MilesAudioManagerDummy {
        +stopAudio() void
        +pauseAudio() void
        +isMusicPlaying() bool
        Note: Inherits openDevice, closeDevice, getFileLengthMS from parent
    }
    
    class AudioManagerDummy_OLD {
        <<removed>>
        +getFileLengthMS() returns -1
    }
    
    AudioManager <|-- MilesAudioManager
    MilesAudioManager <|-- MilesAudioManagerDummy
    AudioManager <|.. AudioManagerDummy_OLD : "Before (removed)"
    
    note for MilesAudioManagerDummy "Uses parent's Miles device initialization\nto query actual audio file lengths"
Loading

Last reviewed commit: 487a865

Copy link

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

5 files reviewed, 4 comments

Edit Code Review Agent Settings | Greptile

Comment on lines +351 to +353
//virtual void openDevice() {}
//virtual void closeDevice() {}
//virtual void* getDevice() { return nullptr; }
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove commented-out code. The existing comment at line 335 already explains why these methods aren't overridden.

Prompt To Fix With AI
This is a comment left during a code review.
Path: Core/GameEngineDevice/Include/MilesAudioDevice/MilesAudioManager.h
Line: 351-353

Comment:
Remove commented-out code. The existing comment at line 335 already explains why these methods aren't overridden.

How can I resolve this? If you propose a fix, please make it concise.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there's value in providing a complete implementation of AudioManager. The functions that are commented out show clearly, more so than the comment, which functions are not overridden for this dummy implementation.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a fair point. Commented-out method signatures can serve as inline documentation showing what's intentionally not overridden, especially in a class that overrides most other virtual methods.

However, this approach has a trade-off: it creates ambiguity about whether the code is temporarily disabled or intentionally documented. A clearer alternative would be to add these method names to the existing comment at line 335, making it explicit which methods are intentionally not overridden without leaving commented code.

For example:

// Methods intentionally not overridden: openDevice(), closeDevice(), getDevice()

This gives you the same clarity without the "is this dead code?" question. But if you prefer the visual clarity of seeing the signatures, keeping them commented is also reasonable—just be aware that most linters and style guides flag commented-out code as a smell.

Your call on which approach better serves future maintainers.

virtual Radar *createRadar(); ///< Factory for radar
virtual WebBrowser *createWebBrowser(); ///< Factory for embedded browser
virtual AudioManager *createAudioManager(); ///< Factory for audio device
virtual AudioManager *createAudioManager(Bool headless); ///< Factory for audio device
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe do Bool dummy ? Because it may not just be headless that want's to use a dummy in the future, aka the Dummy class should not care in what context it is used.


};

// TheSuperHackers @feature helmutbuhler 17/05/2025 AudioManager that does almost nothing. Used for Headless Mode.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Useful for headless mode"

@@ -77,7 +77,7 @@ class Win32GameEngine : public GameEngine
virtual NetworkInterface *createNetwork(); ///< Factory for the network
virtual Radar *createRadar(); ///< Factory for radar
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps mirror the implemention for createRadar?

It currently also does TheGlobalData->m_headless ? NEW RadarDummy : createRadar()

};

// TheSuperHackers @feature helmutbuhler 17/05/2025 AudioManager that does almost nothing. Used for Headless Mode.
// @bugfix Caball009 16/02/2026 Scripts may require the actual audio file length to function properly.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps clarify that this is CRC relevant

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Audio Is audio related Bug Something is not working right, typically is user facing Major Severity: Minor < Major < Critical < Blocker Script Is related to Script Engine, SCB ThisProject The issue was introduced by this project, or this task is specific to this project

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Incomplete implementation of dummy audio manager can cause mismatches in headless mode

2 participants