shithub: choc

Download patch

ref: d051bd5d55a998f2ff4f74f718e23c98cbf49b88
parent: 6cbe5972d39b882a0fc8e5ddfc83b3f72a8b7031
parent: 37aae66760717b106acf602c180140096d95d036
author: Simon Howard <[email protected]>
date: Sat Nov 1 17:03:32 EDT 2014

Merge pull request #465 from khokh2001/opl-fix

opl additive voice volume calculation fix

Adjust how the OPL volume register values are calculated based on the
channel, note and global MIDI volume, to better match how the Doom
DMX library performs these calculations (thanks Alexey Khokholov /
khokh2001).

--- a/src/i_oplmusic.c
+++ b/src/i_oplmusic.c
@@ -515,46 +515,45 @@
 static void SetVoiceVolume(opl_voice_t *voice, unsigned int volume)
 {
     genmidi_voice_t *opl_voice;
+    unsigned int midi_volume;
     unsigned int full_volume;
-    unsigned int op_volume;
-    unsigned int reg_volume;
-
+    unsigned int car_volume;
+    unsigned int mod_volume;
+    
     voice->note_volume = volume;
-
+    
     opl_voice = &voice->current_instr->voices[voice->current_instr_voice];
-
+    
     // Multiply note volume and channel volume to get the actual volume.
-
-    full_volume = (volume_mapping_table[voice->note_volume]
-                   * volume_mapping_table[voice->channel->volume]
-                   * volume_mapping_table[current_music_volume]) / (127 * 127);
-
-    // The volume of each instrument can be controlled via GENMIDI:
-
-    op_volume = 0x3f - opl_voice->carrier.level;
-
+    
+    midi_volume = 2 * (volume_mapping_table[(voice->channel->volume
+                  * current_music_volume) / 127] + 1);
+    
+    full_volume = (volume_mapping_table[voice->note_volume] * midi_volume) >> 9;
+    
     // The volume value to use in the register:
-
-    reg_volume = (op_volume * full_volume) / 128;
-    reg_volume = (0x3f - reg_volume) | opl_voice->carrier.scale;
-
+    car_volume = 0x3f - full_volume;
+    
     // Update the volume register(s) if necessary.
-
-    if (reg_volume != voice->reg_volume)
+    
+    if (car_volume != voice->reg_volume)
     {
-        voice->reg_volume = reg_volume;
-
-        OPL_WriteRegister(OPL_REGS_LEVEL + voice->op2, reg_volume);
-
+        voice->reg_volume = car_volume | (opl_voice->carrier.scale & 0xc0);
+        
+        OPL_WriteRegister(OPL_REGS_LEVEL + voice->op2, voice->reg_volume);
+        
         // If we are using non-modulated feedback mode, we must set the
         // volume for both voices.
-        // Note that the same register volume value is written for
-        // both voices, always calculated from the carrier's level
-        // value.
-
-        if ((opl_voice->feedback & 0x01) != 0)
+        
+        if ((opl_voice->feedback & 0x01) != 0 && opl_voice->modulator.level != 0x3f)
         {
-            OPL_WriteRegister(OPL_REGS_LEVEL + voice->op1, reg_volume);
+            mod_volume = 0x3f - opl_voice->modulator.level;
+            if (mod_volume >= car_volume)
+            {
+                mod_volume = car_volume;
+            }
+            OPL_WriteRegister(OPL_REGS_LEVEL + voice->op1,
+                              mod_volume | (opl_voice->modulator.scale & 0xc0));
         }
     }
 }