shithub: choc

Download patch

ref: e97bc5db814b844ef2e4ee92f8a683031853dadb
parent: d372d65b3de579fe40207a308c3f7c4bf3076c0a
author: Simon Howard <fraggle@gmail.com>
date: Tue Sep 8 13:56:21 EDT 2009

Program two voices for double voice instruments.

Subversion-branch: /branches/opl-branch
Subversion-revision: 1660

--- 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 @@
     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 @@
     // 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,7 +559,9 @@
 
 // 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;
@@ -562,14 +568,17 @@
 
     // 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?
 
     modulating = (data->feedback & 0x01) == 0;
@@ -630,7 +639,7 @@
 
     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 @@
             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 @@
     current_music_volume = volume;
 }
 
-static opl_voice_t *FindVoiceForKey(opl_channel_data_t *channel, int key)
+static void VoiceKeyOff(opl_voice_t *voice)
 {
-    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)
-{
     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 @@
            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.
+    // Turn off voices being used to play this key.
+    // If it is a double voice instrument there will be two.
 
-    voice = FindVoiceForKey(channel, event->data.channel.param1);
-
-    if (voice == NULL)
+    for (i=0; i<OPL_NUM_VOICES; ++i)
     {
-        return;
-    }
+        if (voices[i].channel == channel && voices[i].key == key)
+        {
+            VoiceKeyOff(&voices[i]);
 
-    VoiceNoteOff(voice);
+            // Finished with this voice now.
 
-    // Finished with this voice now.
-
-    ReleaseVoice(voice);
+            ReleaseVoice(&voices[i]);
+        }
+    }
 }
 
 static unsigned int FrequencyForVoice(opl_voice_t *voice)
@@ -879,41 +875,18 @@
     }
 }
 
-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.
 
     voice = GetFreeVoice();
@@ -920,7 +893,7 @@
 
     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 @@
 
     // Program the voice with the instrument data:
 
-    SetVoiceInstrument(voice, instrument);
+    SetVoiceInstrument(voice, instrument, 0);
 
     // Set the volume level.
 
@@ -953,6 +926,51 @@
     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 @@
     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 @@
     {
         if (voices[i].channel != NULL)
         {
-            VoiceNoteOff(&voices[i]);
+            VoiceKeyOff(&voices[i]);
             ReleaseVoice(&voices[i]);
         }
     }