dethrace-labs / dethrace

Reverse engineering the 1997 game "Carmageddon"
https://twitter.com/dethrace_labs
GNU General Public License v3.0
673 stars 38 forks source link

Game does not obey sound effects volume level set in sound settings. #399

Closed b-kurczynski closed 1 month ago

b-kurczynski commented 1 month ago

Version Code: v0.7.1-19-gec04a9c Platform: Windows

Symptoms If the game is started with --sound-options the volume level for effects does nothing. This applies to in-game spatial effects as well as menu "tick" when user navigates through options.

Code Analysis The issue is observed in game running on Windows platform. However, all the platforms seem to be affected.

  1. The "tick" in all menus

It's due to incorrect order of calls inside S3PlaySample() function.

The following change addresses the issue:

int S3PlaySample(tS3_channel* chan) {
    tS3_sample* sound_data;

    if (chan->type_struct_sample == NULL) {
        return 0;
    }

-   S3SyncSampleVolumeAndPan(chan);
-   S3SyncSampleRate(chan);

    sound_data = (tS3_sample*)chan->descriptor->sound_data;
    if (AudioBackend_PlaySample(chan->type_struct_sample,
            sound_data->channels,
            sound_data->dataptr,
            sound_data->size,
            sound_data->rate,
            chan->repetitions == 0)
        != eAB_success) {
        return 0;
    }

+   S3SyncSampleVolumeAndPan(chan);
+   S3SyncSampleRate(chan);
  1. In-game spatial effects

It's due to incorrect transformation of volume specified in "Carmageddon decibels" (which are linear) into linear range <0.0f..1.0f>. Miniaudio ma_volume_db_to_linear() function expects value expressed in "real decibels" which are exponential. Using it for "Carmageddon decibels" produce either too loud or too quiet sound.

The best solution I have found is to recalculate "Carmageddon decibels" into <0.0f..1.0f> range explicitly

tAudioBackend_error_code AudioBackend_SetVolume(void* type_struct_sample, int volume_db) {
    tMiniaudio_sample* miniaudio;
    float linear_volume;

    miniaudio = (tMiniaudio_sample*)type_struct_sample;
    assert(miniaudio != NULL);

-   // convert from directsound -10000 - 0 decibel volume scale
-   linear_volume = ma_volume_db_to_linear(volume_db / 100.0f);
+   // convert from Carmageddon "decibel" scale to linear <0.0f..1.0f>
+   linear_volume = (volume_db + 350.0f) / -5.0f;
+   if (linear_volume == 0.0f) {
+       linear_volume = 1.0f;
+   }
+   linear_volume = 1.0f / linear_volume;

    ma_sound_set_volume(&miniaudio->sound, linear_volume);
    return eAB_success;
}