foxygit / doom Log in
commit e97bc5db814b844ef2e4ee92f8a683031853dadb
Author:     Simon Howard <fraggle@gmail.com>
AuthorDate: Tue Sep 8 17:56:21 2009 +0000
Commit:     Simon Howard <fraggle@gmail.com>
CommitDate: Tue Sep 8 17:56:21 2009 +0000

    Program two voices for double voice instruments.

    Subversion-branch: /branches/opl-branch
    Subversion-revision: 1660
---
 src/i_oplmusic.c | 164 ++++++++++++++++++++++++++++++-------------------------
 1 file changed, 91 insertions(+), 73 deletions(-)

diff --git a/src/i_oplmusic.c b/src/i_oplmusic.c
index e9fdb395..8ec47b80 100644
--- a/src/i_oplmusic.c
+++ b/src/i_oplmusic.c
@@ -49,7 +49,7 @@

 #define GENMIDI_HEADER          "#OPL_II#"
 #define GENMIDI_FLAG_FIXED      0x0001         /* fixed pitch */
-#define GENMIDI_FLAG_2VOICE     0x0002         /* double voice (OPL3) */
+#define GENMIDI_FLAG_2VOICE     0x0004         /* double voice (OPL3) */

 typedef struct
 {
@@ -76,8 +76,7 @@ typedef struct
     byte fine_tuning;
     byte fixed_note;

-    genmidi_voice_t opl2_voice;
-    genmidi_voice_t opl3_voice;
+    genmidi_voice_t voices[2];
 } PACKEDATTR genmidi_instr_t;

 // Data associated with a channel of a track that is currently playing.
@@ -129,6 +128,11 @@ struct opl_voice_s
     // Currently-loaded instrument data
     genmidi_instr_t *current_instr;

+    // The voice number in the instrument to use.
+    // This is normally set to zero; if this is a double voice
+    // instrument, it may be one.
+    unsigned int current_instr_voice;
+
     // The channel currently using this voice.
     opl_channel_data_t *channel;

@@ -555,20 +559,25 @@ static void LoadOperatorData(int operator, genmidi_op_t *data,

 // Set the instrument for a particular voice.

-static void SetVoiceInstrument(opl_voice_t *voice, genmidi_instr_t *instr)
+static void SetVoiceInstrument(opl_voice_t *voice,
+                               genmidi_instr_t *instr,
+                               unsigned int instr_voice)
 {
     genmidi_voice_t *data;
     unsigned int modulating;

     // Instrument already set for this channel?

-    if (voice->current_instr == instr)
+    if (voice->current_instr == instr
+     && voice->current_instr_voice == instr_voice)
     {
         return;
     }

     voice->current_instr = instr;
-    data = &instr->opl2_voice;
+    voice->current_instr_voice = instr_voice;
+
+    data = &instr->voices[instr_voice];

     // Are we usind modulated feedback mode?

@@ -630,7 +639,7 @@ static void SetVoiceVolume(opl_voice_t *voice, unsigned int volume)

     voice->note_volume = volume;

-    opl_voice = &voice->current_instr->opl2_voice;
+    opl_voice = &voice->current_instr->voices[voice->current_instr_voice];

     // Multiply note volume and channel volume to get the actual volume.

@@ -765,7 +774,7 @@ static boolean I_OPL_InitMusic(void)
             voice = GetFreeVoice();
             instr_num = rand() % 100;

-            SetVoiceInstrument(voice, &main_instrs[instr_num].opl2_voice);
+            SetVoiceInstrument(voice, &main_instrs[instr_num], 0);

             OPL_SetCallback(0, TestCallback, voice);
         }
@@ -785,32 +794,18 @@ static void I_OPL_SetMusicVolume(int volume)
     current_music_volume = volume;
 }

-static opl_voice_t *FindVoiceForKey(opl_channel_data_t *channel, int key)
-{
-    unsigned int i;
-
-    for (i=0; i<OPL_NUM_VOICES; ++i)
-    {
-        if (voices[i].channel == channel && voices[i].key == key)
-        {
-            return &voices[i];
-        }
-    }
-
-    return NULL;
-}
-
-static void VoiceNoteOff(opl_voice_t *voice)
+static void VoiceKeyOff(opl_voice_t *voice)
 {
     WriteRegister(OPL_REGS_FREQ_2 + voice->index, voice->freq >> 8);
 }

 // Get the frequency that we should be using for a voice.

-static void NoteOffEvent(opl_track_data_t *track, midi_event_t *event)
+static void KeyOffEvent(opl_track_data_t *track, midi_event_t *event)
 {
-    opl_voice_t *voice;
     opl_channel_data_t *channel;
+    unsigned int key;
+    unsigned int i;

     printf("note off: channel %i, %i, %i\n",
            event->data.channel.channel,
@@ -818,21 +813,22 @@ static void NoteOffEvent(opl_track_data_t *track, midi_event_t *event)
            event->data.channel.param2);

     channel = &track->channels[event->data.channel.channel];
+    key = event->data.channel.param1;

-    // Find the voice being used to play the note.
-
-    voice = FindVoiceForKey(channel, event->data.channel.param1);
+    // Turn off voices being used to play this key.
+    // If it is a double voice instrument there will be two.

-    if (voice == NULL)
+    for (i=0; i<OPL_NUM_VOICES; ++i)
     {
-        return;
-    }
-
-    VoiceNoteOff(voice);
+        if (voices[i].channel == channel && voices[i].key == key)
+        {
+            VoiceKeyOff(&voices[i]);

-    // Finished with this voice now.
+            // Finished with this voice now.

-    ReleaseVoice(voice);
+            ReleaseVoice(&voices[i]);
+        }
+    }
 }

 static unsigned int FrequencyForVoice(opl_voice_t *voice)
@@ -879,40 +875,17 @@ static void UpdateVoiceFrequency(opl_voice_t *voice)
     }
 }

-static void NoteOnEvent(opl_track_data_t *track, midi_event_t *event)
+// Program a single voice for an instrument.  For a double voice
+// instrument (GENMIDI_FLAG_2VOICE), this is called twice for each
+// key on event.
+
+static void VoiceKeyOn(opl_channel_data_t *channel,
+                       genmidi_instr_t *instrument,
+                       unsigned int instrument_voice,
+                       unsigned int key,
+                       unsigned int volume)
 {
-    genmidi_instr_t *instrument;
     opl_voice_t *voice;
-    opl_channel_data_t *channel;
-    unsigned int key;
-    unsigned int volume;
-
-    printf("note on: channel %i, %i, %i\n",
-           event->data.channel.channel,
-           event->data.channel.param1,
-           event->data.channel.param2);
-
-    // The channel.
-
-    channel = &track->channels[event->data.channel.channel];
-    key = event->data.channel.param1;
-    volume = event->data.channel.param2;
-
-    // Percussion channel (10) is treated differently.
-
-    if (event->data.channel.channel == 9)
-    {
-        if (key < 35 || key > 81)
-        {
-            return;
-        }
-
-        instrument = &percussion_instrs[key - 35];
-    }
-    else
-    {
-        instrument = channel->instrument;
-    }

     // Find a voice to use for this new note.

@@ -920,7 +893,7 @@ static void NoteOnEvent(opl_track_data_t *track, midi_event_t *event)

     if (voice == NULL)
     {
-        printf("\tno free voice\n");
+        printf("\tno free voice for voice %i of instrument\n", instrument_voice);
         return;
     }

@@ -941,7 +914,7 @@ static void NoteOnEvent(opl_track_data_t *track, midi_event_t *event)

     // Program the voice with the instrument data:

-    SetVoiceInstrument(voice, instrument);
+    SetVoiceInstrument(voice, instrument, 0);

     // Set the volume level.

@@ -953,6 +926,51 @@ static void NoteOnEvent(opl_track_data_t *track, midi_event_t *event)
     UpdateVoiceFrequency(voice);
 }

+static void KeyOnEvent(opl_track_data_t *track, midi_event_t *event)
+{
+    genmidi_instr_t *instrument;
+    opl_channel_data_t *channel;
+    unsigned int key;
+    unsigned int volume;
+
+    printf("note on: channel %i, %i, %i\n",
+           event->data.channel.channel,
+           event->data.channel.param1,
+           event->data.channel.param2);
+
+    // The channel.
+
+    channel = &track->channels[event->data.channel.channel];
+    key = event->data.channel.param1;
+    volume = event->data.channel.param2;
+
+    // Percussion channel (10) is treated differently.
+
+    if (event->data.channel.channel == 9)
+    {
+        if (key < 35 || key > 81)
+        {
+            return;
+        }
+
+        instrument = &percussion_instrs[key - 35];
+    }
+    else
+    {
+        instrument = channel->instrument;
+    }
+
+    // Find and program a voice for this instrument.  If this
+    // is a double voice instrument, we must do this twice.
+
+    VoiceKeyOn(channel, instrument, 0, key, volume);
+
+    if ((instrument->flags & GENMIDI_FLAG_2VOICE) != 0)
+    {
+        VoiceKeyOn(channel, instrument, 1, key, volume);
+    }
+}
+
 static void ProgramChangeEvent(opl_track_data_t *track, midi_event_t *event)
 {
     int channel;
@@ -1075,11 +1093,11 @@ static void ProcessEvent(opl_track_data_t *track, midi_event_t *event)
     switch (event->event_type)
     {
         case MIDI_EVENT_NOTE_OFF:
-            NoteOffEvent(track, event);
+            KeyOffEvent(track, event);
             break;

         case MIDI_EVENT_NOTE_ON:
-            NoteOnEvent(track, event);
+            KeyOnEvent(track, event);
             break;

         case MIDI_EVENT_CONTROLLER:
@@ -1284,7 +1302,7 @@ static void I_OPL_StopSong(void)
     {
         if (voices[i].channel != NULL)
         {
-            VoiceNoteOff(&voices[i]);
+            VoiceKeyOff(&voices[i]);
             ReleaseVoice(&voices[i]);
         }
     }