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]);
}
}