cog/Plugins/MIDI/MIDI/synthlib_doom/i_oplmusic.cpp

973 lines
23 KiB
C++

//
// Copyright(C) 1993-1996 Id Software, Inc.
// Copyright(C) 2005-2014 Simon Howard
// Copyright(C) 2014-2015 Alexey Khokholov
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// DESCRIPTION:
// System interface for music.
//
#include "i_oplmusic.h"
void DoomOPL::OPL_WriteRegister(unsigned int reg, unsigned char data) {
opl->fm_writereg(reg, data);
}
void DoomOPL::OPL_InitRegisters(bool opl_new) {
unsigned int r;
// Initialize level registers
for(r = OPL_REGS_LEVEL; r <= OPL_REGS_LEVEL + OPL_NUM_OPERATORS; ++r) {
OPL_WriteRegister(r, 0x3f);
}
// Initialize other registers
// These two loops write to registers that actually don't exist,
// but this is what Doom does ...
// Similarly, the <= is also intenational.
for(r = OPL_REGS_ATTACK; r <= OPL_REGS_WAVEFORM + OPL_NUM_OPERATORS; ++r) {
OPL_WriteRegister(r, 0x00);
}
// More registers ...
for(r = 1; r < OPL_REGS_LEVEL; ++r) {
OPL_WriteRegister(r, 0x00);
}
if(opl_new) {
OPL_WriteRegister(OPL_REG_NEW_MODE, 0x01);
// Initialize level registers
for(r = OPL_REGS_LEVEL; r <= OPL_REGS_LEVEL + OPL_NUM_OPERATORS; ++r) {
OPL_WriteRegister(r | 0x100, 0x3f);
}
// Initialize other registers
// These two loops write to registers that actually don't exist,
// but this is what Doom does ...
// Similarly, the <= is also intenational.
for(r = OPL_REGS_ATTACK; r <= OPL_REGS_WAVEFORM + OPL_NUM_OPERATORS; ++r) {
OPL_WriteRegister(r | 0x100, 0x00);
}
// More registers ...
for(r = 1; r < OPL_REGS_LEVEL; ++r) {
OPL_WriteRegister(r | 0x100, 0x00);
}
}
// Re-initialize the low registers:
// Reset both timers and enable interrupts:
OPL_WriteRegister(OPL_REG_TIMER_CTRL, 0x60);
OPL_WriteRegister(OPL_REG_TIMER_CTRL, 0x80);
// "Allow FM chips to control the waveform of each operator":
OPL_WriteRegister(OPL_REG_WAVEFORM_ENABLE, 0x20);
// Keyboard split point on (?)
OPL_WriteRegister(OPL_REG_FM_MODE, 0x40);
if(opl_new) {
OPL_WriteRegister(OPL_REG_NEW_MODE, 0x01);
}
if(opl_extp) {
OPL_WriteRegister(0x106, 0x17);
}
}
// Load instrument table from GENMIDI lump:
namespace DoomOPL_inst {
#include "inst/dmx_dmx.h"
#include "inst/dmx_doom1.h"
#include "inst/dmx_doom2.h"
#include "inst/dmx_raptor.h"
#include "inst/dmx_strife.h"
#include "inst/dmxopl.h"
} // namespace DoomOPL_inst
bool DoomOPL::LoadInstrumentTable(unsigned int bank) {
const byte *lump;
switch(bank) {
default:
case 0:
lump = DoomOPL_inst::dmx_dmx;
break;
case 1:
lump = DoomOPL_inst::dmx_doom1;
break;
case 2:
lump = DoomOPL_inst::dmx_doom2;
break;
case 3:
lump = DoomOPL_inst::dmx_raptor;
break;
case 4:
lump = DoomOPL_inst::dmx_strife;
break;
case 5:
lump = DoomOPL_inst::dmxopl;
break;
}
main_instrs = (const genmidi_instr_t *)(lump + strlen(GENMIDI_HEADER));
percussion_instrs = main_instrs + GENMIDI_NUM_INSTRS;
return true;
}
// Release a voice back to the freelist.
void DoomOPL::ReleaseVoice(unsigned int id) {
opl_voice_t *voice;
unsigned int i;
bool doublev;
// Doom 2 1.666 OPL crash emulation.
if(id >= voice_alloced_num) {
voice_alloced_num = 0;
voice_free_num = 0;
return;
}
voice = voice_alloced_list[id];
VoiceKeyOff(voice);
voice->channel = NULL;
voice->note = 0;
doublev = voice->current_instr_voice != 0;
// Remove from alloced list.
voice_alloced_num--;
for(i = id; i < voice_alloced_num; i++) {
voice_alloced_list[i] = voice_alloced_list[i + 1];
}
// Search to the end of the freelist (This is how Doom behaves!)
voice_free_list[voice_free_num++] = voice;
if(doublev && opl_drv_ver < opl_doom_1_9) {
ReleaseVoice(id);
}
}
// Load data to the specified operator
void DoomOPL::LoadOperatorData(int slot, const genmidi_op_t *data,
bool max_level, unsigned int *volume) {
int level;
// The scale and level fields must be combined for the level register.
// For the carrier wave we always set the maximum level.
level = (data->scale & 0xc0) | (data->level & 0x3f);
if(max_level) {
level |= 0x3f;
} else {
level |= data->level;
}
*volume = level;
OPL_WriteRegister(OPL_REGS_LEVEL + slot, level);
OPL_WriteRegister(OPL_REGS_TREMOLO + slot, data->tremolo);
OPL_WriteRegister(OPL_REGS_ATTACK + slot, data->attack);
OPL_WriteRegister(OPL_REGS_SUSTAIN + slot, data->sustain);
OPL_WriteRegister(OPL_REGS_WAVEFORM + slot, data->waveform);
}
// Set the instrument for a particular voice.
void DoomOPL::SetVoiceInstrument(opl_voice_t *voice,
const genmidi_instr_t *instr,
unsigned int instr_voice) {
const genmidi_voice_t *data;
unsigned int modulating;
// Instrument already set for this channel?
if(voice->current_instr == instr && voice->current_instr_voice == instr_voice) {
return;
}
voice->current_instr = instr;
voice->current_instr_voice = instr_voice;
data = &instr->voices[instr_voice];
// Are we usind modulated feedback mode?
modulating = (data->feedback & 0x01) == 0;
// Doom loads the second operator first, then the first.
// The carrier is set to minimum volume until the voice volume
// is set in SetVoiceVolume (below). If we are not using
// modulating mode, we must set both to minimum volume.
LoadOperatorData(voice->op2 | voice->array, &data->carrier, true, &voice->car_volume);
LoadOperatorData(voice->op1 | voice->array, &data->modulator, !modulating, &voice->mod_volume);
// Set feedback register that control the connection between the
// two operators. Turn on bits in the upper nybble; I think this
// is for OPL3, where it turns on channel A/B.
OPL_WriteRegister((OPL_REGS_FEEDBACK + voice->index) | voice->array,
data->feedback | voice->reg_pan);
// Calculate voice priority.
voice->priority = 0x0f - (data->carrier.attack >> 4) + 0x0f - (data->carrier.sustain & 0x0f);
}
void DoomOPL::SetVoiceVolume(opl_voice_t *voice, unsigned int volume) {
const genmidi_voice_t *opl_voice;
unsigned int midi_volume;
unsigned int full_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.
midi_volume = 2 * (volume_mapping_table[voice->channel->volume] + 1);
full_volume = (volume_mapping_table[voice->note_volume] * midi_volume) >> 9;
// The volume value to use in the register:
car_volume = 0x3f - full_volume;
// Update the volume register(s) if necessary.
if(car_volume != (voice->car_volume & 0x3f)) {
voice->car_volume = car_volume | (voice->car_volume & 0xc0);
OPL_WriteRegister((OPL_REGS_LEVEL + voice->op2) | voice->array, voice->car_volume);
// If we are using non-modulated feedback mode, we must set the
// volume for both voices.
if((opl_voice->feedback & 0x01) != 0 && opl_voice->modulator.level != 0x3f) {
mod_volume = opl_voice->modulator.level;
if(mod_volume < car_volume) {
mod_volume = car_volume;
}
mod_volume |= voice->mod_volume & 0xc0;
if(mod_volume != voice->mod_volume) {
voice->mod_volume = mod_volume;
OPL_WriteRegister((OPL_REGS_LEVEL + voice->op1) | voice->array,
voice->mod_volume);
}
}
}
}
void DoomOPL::SetVoicePan(opl_voice_t *voice, unsigned int pan) {
const genmidi_voice_t *opl_voice;
voice->reg_pan = pan;
opl_voice = &voice->current_instr->voices[voice->current_instr_voice];
OPL_WriteRegister((OPL_REGS_FEEDBACK + voice->index) | voice->array,
opl_voice->feedback | pan);
}
void DoomOPL::SetVoicePanEx(opl_voice_t *voice, unsigned int pan) {
const genmidi_voice_t *opl_voice;
opl_voice = &voice->current_instr->voices[voice->current_instr_voice];
OPL_WriteRegister(0x107, voice->index + (voice->array * 9 / 256));
OPL_WriteRegister(0x108, pan * 2);
}
// Initialize the voice table and freelist
void DoomOPL::InitVoices(void) {
unsigned int i;
voice_free_num = opl_voices;
voice_alloced_num = 0;
// Initialize each voice.
for(i = 0; i < opl_voices; ++i) {
voices[i].index = i % OPL_NUM_VOICES;
voices[i].op1 = voice_operators[0][i % OPL_NUM_VOICES];
voices[i].op2 = voice_operators[1][i % OPL_NUM_VOICES];
voices[i].array = (i / OPL_NUM_VOICES) << 8;
voices[i].current_instr = NULL;
voice_free_list[i] = &voices[i];
}
}
void DoomOPL::VoiceKeyOff(opl_voice_t *voice) {
OPL_WriteRegister((OPL_REGS_FREQ_2 + voice->index) | voice->array, voice->freq >> 8);
}
opl_channel_data_t *DoomOPL::TrackChannelForEvent(unsigned char channel_num) {
channel_num = channel_map_table[channel_num];
return &channels[channel_num];
}
// Get the frequency that we should be using for a voice.
void DoomOPL::KeyOffEvent(unsigned char channel_num, unsigned char key) {
opl_channel_data_t *channel;
unsigned int i;
/*
printf("note off: channel %i, %i, %i\n",
event->data.channel.channel,
event->data.channel.param1,
event->data.channel.param2);
*/
channel = TrackChannelForEvent(channel_num);
// Turn off voices being used to play this key.
// If it is a double voice instrument there will be two.
for(i = 0; i < voice_alloced_num;) {
if(voice_alloced_list[i]->channel == channel && voice_alloced_list[i]->key == key) {
// Finished with this voice now.
ReleaseVoice(i);
continue;
}
i++;
}
}
// When all voices are in use, we must discard an existing voice to
// play a new note. Find and free an existing voice. The channel
// passed to the function is the channel for the new note to be
// played.
void DoomOPL::ReplaceExistingVoice() {
unsigned int i;
unsigned int result;
// Check the allocated voices, if we find an instrument that is
// of a lower priority to the new instrument, discard it.
// If a voice is being used to play the second voice of an instrument,
// use that, as second voices are non-essential.
// Lower numbered MIDI channels implicitly have a higher priority
// than higher-numbered channels, eg. MIDI channel 1 is never
// discarded for MIDI channel 2.
result = 0;
for(i = 0; i < voice_alloced_num; i++) {
if(voice_alloced_list[i]->current_instr_voice != 0 || voice_alloced_list[i]->channel >= voice_alloced_list[result]->channel) {
result = i;
}
}
ReleaseVoice(result);
}
// Alternate versions of ReplaceExistingVoice() used when emulating old
// versions of the DMX library used in Doom 1.666, Heretic and Hexen.
void DoomOPL::ReplaceExistingVoiceDoom1(void) {
int i;
int result;
result = 0;
for(i = 0; i < voice_alloced_num; i++) {
if(voice_alloced_list[i]->channel > voice_alloced_list[result]->channel) {
result = i;
}
}
ReleaseVoice(result);
}
void DoomOPL::ReplaceExistingVoiceDoom2(opl_channel_data_t *channel) {
unsigned int i;
unsigned int result;
unsigned int priority;
result = 0;
priority = 0x8000;
for(i = 0; i < voice_alloced_num - 3; i++) {
if(voice_alloced_list[i]->priority < priority && voice_alloced_list[i]->channel >= channel) {
priority = voice_alloced_list[i]->priority;
result = i;
}
}
ReleaseVoice(result);
}
unsigned int DoomOPL::FrequencyForVoice(opl_voice_t *voice) {
const genmidi_voice_t *gm_voice;
signed int freq_index;
unsigned int octave;
unsigned int sub_index;
signed int note;
note = voice->note;
// Apply note offset.
// Don't apply offset if the instrument is a fixed note instrument.
gm_voice = &voice->current_instr->voices[voice->current_instr_voice];
if((voice->current_instr->flags & GENMIDI_FLAG_FIXED) == 0) {
note += (signed short)gm_voice->base_note_offset;
}
// Avoid possible overflow due to base note offset:
while(note < 0) {
note += 12;
}
while(note > 95) {
note -= 12;
}
freq_index = 64 + 32 * note + voice->channel->bend;
// If this is the second voice of a double voice instrument, the
// frequency index can be adjusted by the fine tuning field.
if(voice->current_instr_voice != 0) {
freq_index += (voice->current_instr->fine_tuning / 2) - 64;
}
if(freq_index < 0) {
freq_index = 0;
}
// The first 7 notes use the start of the table, while
// consecutive notes loop around the latter part.
if(freq_index < 284) {
return frequency_curve[freq_index];
}
sub_index = (freq_index - 284) % (12 * 32);
octave = (freq_index - 284) / (12 * 32);
// Once the seventh octave is reached, things break down.
// We can only go up to octave 7 as a maximum anyway (the OPL
// register only has three bits for octave number), but for the
// notes in octave 7, the first five bits have octave=7, the
// following notes have octave=6. This 7/6 pattern repeats in
// following octaves (which are technically impossible to
// represent anyway).
if(octave >= 7) {
octave = 7;
}
// Calculate the resulting register value to use for the frequency.
return frequency_curve[sub_index + 284] | (octave << 10);
}
// Update the frequency that a voice is programmed to use.
void DoomOPL::UpdateVoiceFrequency(opl_voice_t *voice) {
unsigned int freq;
// Calculate the frequency to use for this voice and update it
// if neccessary.
freq = FrequencyForVoice(voice);
if(voice->freq != freq) {
OPL_WriteRegister((OPL_REGS_FREQ_1 + voice->index) | voice->array, freq & 0xff);
OPL_WriteRegister((OPL_REGS_FREQ_2 + voice->index) | voice->array, (freq >> 8) | 0x20);
voice->freq = freq;
}
}
// Program a single voice for an instrument. For a double voice
// instrument (GENMIDI_FLAG_2VOICE), this is called twice for each
// key on event.
void DoomOPL::VoiceKeyOn(opl_channel_data_t *channel,
const genmidi_instr_t *instrument,
unsigned int instrument_voice,
unsigned int note,
unsigned int key,
unsigned int volume) {
opl_voice_t *voice;
unsigned int i = 0;
// Find a voice to use for this new note.
if(voice_free_num == 0) {
return;
}
voice = voice_free_list[0];
voice_free_num--;
for(i = 0; i < voice_free_num; ++i) {
voice_free_list[i] = voice_free_list[i + 1];
}
voice_alloced_list[voice_alloced_num++] = voice;
if(!opl_new && opl_drv_ver == opl_doom1_1_666) {
instrument_voice = 0;
}
voice->channel = channel;
voice->key = key;
// Work out the note to use. This is normally the same as
// the key, unless it is a fixed pitch instrument.
if((instrument->flags & GENMIDI_FLAG_FIXED) != 0) {
voice->note = instrument->fixed_note;
} else {
voice->note = note;
}
voice->reg_pan = channel->pan;
// Program the voice with the instrument data:
SetVoiceInstrument(voice, instrument, instrument_voice);
// Set the volume level.
SetVoiceVolume(voice, volume);
// Set the extended panning, if necessary
if(opl_extp)
SetVoicePanEx(voice, channel->panex);
// Write the frequency value to turn the note on.
voice->freq = 0;
UpdateVoiceFrequency(voice);
}
void DoomOPL::KeyOnEvent(unsigned char channel_num, unsigned char key, unsigned char volume) {
const genmidi_instr_t *instrument;
opl_channel_data_t *channel;
unsigned int note, voicenum;
bool doublev;
/*
printf("note on: channel %i, %i, %i\n",
event->data.channel.channel,
event->data.channel.param1,
event->data.channel.param2);
*/
note = key;
// A volume of zero means key off. Some MIDI tracks, eg. the ones
// in AV.wad, use a second key on with a volume of zero to mean
// key off.
if(volume <= 0) {
KeyOffEvent(channel_num, key);
return;
}
// The channel.
channel = TrackChannelForEvent(channel_num);
// Percussion channel is treated differently.
if(channel_num == 9) {
if(key < 35 || key > 81) {
return;
}
instrument = &percussion_instrs[key - 35];
note = 60;
} else {
instrument = channel->instrument;
}
doublev = ((short)(instrument->flags) & GENMIDI_FLAG_2VOICE) != 0;
switch(opl_drv_ver) {
case opl_doom1_1_666:
voicenum = doublev + 1;
if(!opl_new) {
voicenum = 1;
}
while(voice_alloced_num > opl_voices - voicenum) {
ReplaceExistingVoiceDoom1();
}
// Find and program a voice for this instrument. If this
// is a double voice instrument, we must do this twice.
if(doublev) {
VoiceKeyOn(channel, instrument, 1, note, key, volume);
}
VoiceKeyOn(channel, instrument, 0, note, key, volume);
break;
case opl_doom2_1_666:
if(voice_alloced_num == opl_voices) {
ReplaceExistingVoiceDoom2(channel);
}
if(voice_alloced_num == opl_voices - 1 && doublev) {
ReplaceExistingVoiceDoom2(channel);
}
// Find and program a voice for this instrument. If this
// is a double voice instrument, we must do this twice.
if(doublev) {
VoiceKeyOn(channel, instrument, 1, note, key, volume);
}
VoiceKeyOn(channel, instrument, 0, note, key, volume);
break;
default:
case opl_doom_1_9:
if(voice_free_num == 0) {
ReplaceExistingVoice();
}
// Find and program a voice for this instrument. If this
// is a double voice instrument, we must do this twice.
VoiceKeyOn(channel, instrument, 0, note, key, volume);
if(doublev) {
VoiceKeyOn(channel, instrument, 1, note, key, volume);
}
break;
}
}
void DoomOPL::ProgramChangeEvent(unsigned char channel_num, unsigned char instrument) {
opl_channel_data_t *channel;
// Set the instrument used on this channel.
channel = TrackChannelForEvent(channel_num);
channel->instrument = &main_instrs[instrument];
// TODO: Look through existing voices that are turned on on this
// channel, and change the instrument.
}
void DoomOPL::SetChannelVolume(opl_channel_data_t *channel, unsigned int volume) {
unsigned int i;
channel->volume = volume;
// Update all voices that this channel is using.
for(i = 0; i < voice_alloced_num; ++i) {
if(voice_alloced_list[i]->channel == channel) {
SetVoiceVolume(voice_alloced_list[i], voice_alloced_list[i]->note_volume);
}
}
}
void DoomOPL::SetChannelPan(opl_channel_data_t *channel, unsigned int pan) {
unsigned int reg_pan;
unsigned int i;
if(opl_new) {
if(opl_extp) {
if(channel->panex != pan) {
channel->panex = pan;
for(i = 0; i < voice_alloced_num; i++) {
if(voice_alloced_list[i]->channel == channel) {
SetVoicePanEx(voice_alloced_list[i], pan);
}
}
}
} else {
if(pan >= 96) {
reg_pan = 0x10;
} else if(pan <= 48) {
reg_pan = 0x20;
} else {
reg_pan = 0x30;
}
if(channel->pan != reg_pan) {
channel->pan = reg_pan;
for(i = 0; i < voice_alloced_num; i++) {
if(voice_alloced_list[i]->channel == channel) {
SetVoicePan(voice_alloced_list[i], reg_pan);
}
}
}
}
}
}
// Handler for the MIDI_CONTROLLER_ALL_NOTES_OFF channel event.
void DoomOPL::AllNotesOff(opl_channel_data_t *channel, unsigned int param) {
unsigned int i;
for(i = 0; i < voice_alloced_num;) {
if(voice_alloced_list[i]->channel == channel) {
// Finished with this voice now.
ReleaseVoice(i);
continue;
}
i++;
}
}
void DoomOPL::ControllerEvent(unsigned char channel_num, unsigned char controller, unsigned char param) {
opl_channel_data_t *channel;
/*
printf("change controller: channel %i, %i, %i\n",
event->data.channel.channel,
event->data.channel.param1,
event->data.channel.param2);
*/
channel = TrackChannelForEvent(channel_num);
switch(controller) {
case MIDI_CONTROLLER_MAIN_VOLUME:
SetChannelVolume(channel, param);
break;
case MIDI_CONTROLLER_PAN:
SetChannelPan(channel, param);
break;
case MIDI_CONTROLLER_ALL_NOTES_OFF:
AllNotesOff(channel, param);
break;
default:
break;
}
}
// Process a pitch bend event.
void DoomOPL::PitchBendEvent(unsigned char channel_num, unsigned char bend) {
opl_channel_data_t *channel;
unsigned int i;
opl_voice_t *voice_updated_list[OPL_NUM_VOICES * 2];
unsigned int voice_updated_num = 0;
opl_voice_t *voice_not_updated_list[OPL_NUM_VOICES * 2];
unsigned int voice_not_updated_num = 0;
// Update the channel bend value. Only the MSB of the pitch bend
// value is considered: this is what Doom does.
channel = TrackChannelForEvent(channel_num);
channel->bend = bend - 64;
// Update all voices for this channel.
for(i = 0; i < voice_alloced_num; ++i) {
if(voice_alloced_list[i]->channel == channel) {
UpdateVoiceFrequency(voice_alloced_list[i]);
voice_updated_list[voice_updated_num++] = voice_alloced_list[i];
} else {
voice_not_updated_list[voice_not_updated_num++] = voice_alloced_list[i];
}
}
for(i = 0; i < voice_not_updated_num; i++) {
voice_alloced_list[i] = voice_not_updated_list[i];
}
for(i = 0; i < voice_updated_num; i++) {
voice_alloced_list[i + voice_not_updated_num] = voice_updated_list[i];
}
}
// Process a MIDI event from a track.
void DoomOPL::midi_write(unsigned int data) {
unsigned char event_type = data & 0xf0;
unsigned char channel_num = data & 0x0f;
unsigned char key = (data >> 8) & 0xff;
unsigned char volume = (data >> 16) & 0xff;
if(key > 0x7f) {
key = 0x7f;
}
if(volume > 0x7f) {
volume = 0x7f;
}
switch(event_type) {
case MIDI_EVENT_NOTE_OFF:
KeyOffEvent(channel_num, key);
break;
case MIDI_EVENT_NOTE_ON:
KeyOnEvent(channel_num, key, volume);
break;
case MIDI_EVENT_CONTROLLER:
ControllerEvent(channel_num, key, volume);
break;
case MIDI_EVENT_PROGRAM_CHANGE:
ProgramChangeEvent(channel_num, key);
break;
case MIDI_EVENT_PITCH_BEND:
PitchBendEvent(channel_num, volume);
break;
default:
break;
}
}
// Initialize a channel.
void DoomOPL::InitChannel(opl_channel_data_t *channel) {
// TODO: Work out sensible defaults?
channel->instrument = &main_instrs[0];
channel->volume = 127;
channel->pan = 0x30;
channel->panex = 64;
channel->bend = 0;
}
int DoomOPL::midi_init(unsigned int rate, unsigned int bank, unsigned int extp) {
/*char *env;*/
unsigned int i;
opl = getchip();
if(!opl || !opl->fm_init(rate)) {
return 0;
}
opl_extp = !!extp;
memset(channels, 0, sizeof(channels));
main_instrs = NULL;
percussion_instrs = NULL;
memset(voices, 0, sizeof(voices));
voice_alloced_num = 0;
voice_free_num = 0;
opl_new = 0;
opl_voices = OPL_NUM_VOICES;
opl_drv_ver = opl_doom_1_9;
/*env = getenv("DMXOPTION");
if (env)
{
if (strstr(env, "-opl3"))*/
{
opl_new = 1;
opl_voices = OPL_NUM_VOICES * 2;
} /*
if (strstr(env, "-doom1"))
{
opl_drv_ver = opl_doom1_1_666;
}
if (strstr(env, "-doom2"))
{
opl_drv_ver = opl_doom2_1_666;
}
}*/
OPL_InitRegisters(opl_new);
// Load instruments from GENMIDI lump:
if(!LoadInstrumentTable(bank)) {
return 0;
}
for(i = 0; i < MIDI_CHANNELS_PER_TRACK; i++) {
InitChannel(&channels[i]);
}
InitVoices();
return 1;
}
void DoomOPL::midi_generate(signed short *buffer, unsigned int length) {
opl->fm_generate(buffer, length);
}
const char *DoomOPL::midi_synth_name(void) {
return "DoomOPL";
}
unsigned int DoomOPL::midi_bank_count(void) {
return 6;
}
const char *DoomOPL::midi_bank_name(unsigned int bank) {
switch(bank) {
default:
case 0:
return "DMX Default";
case 1:
return "DMX Doom 1";
case 2:
return "DMX Doom 2";
case 3:
return "DMX Raptor";
case 4:
return "DMX Strife";
case 5:
return "DMXOPL";
}
}
midisynth *getsynth_doom() {
DoomOPL *synth = new DoomOPL;
return synth;
}