summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon Howard2009-03-10 21:21:16 +0000
committerSimon Howard2009-03-10 21:21:16 +0000
commitca7f823d48bb78eda9048750909cdb315836125c (patch)
tree702b90741a1470f8e51ef487cc0ce8858b7503b4
parent3efee3d5e2b662df97aabc4c8fd275b60b6f08f4 (diff)
downloadchocolate-doom-ca7f823d48bb78eda9048750909cdb315836125c.tar.gz
chocolate-doom-ca7f823d48bb78eda9048750909cdb315836125c.tar.bz2
chocolate-doom-ca7f823d48bb78eda9048750909cdb315836125c.zip
Initialise OPL registers on startup, initialise voices.
Subversion-branch: /branches/opl-branch Subversion-revision: 1457
-rw-r--r--opl/opl.h3
-rw-r--r--src/i_oplmusic.c178
2 files changed, 181 insertions, 0 deletions
diff --git a/opl/opl.h b/opl/opl.h
index 62b507f7..352e6696 100644
--- a/opl/opl.h
+++ b/opl/opl.h
@@ -36,6 +36,7 @@ typedef enum
#define OPL_NUM_OPERATORS 21
#define OPL_NUM_VOICES 9
+#define OPL_REG_WAVEFORM_ENABLE 0x01
#define OPL_REG_TIMER1 0x02
#define OPL_REG_TIMER2 0x03
#define OPL_REG_TIMER_CTRL 0x04
@@ -47,11 +48,13 @@ typedef enum
#define OPL_REGS_LEVEL 0x40
#define OPL_REGS_ATTACK 0x60
#define OPL_REGS_SUSTAIN 0x80
+#define OPL_REGS_WAVEFORM 0xE0
// Voice registers (9 of each):
#define OPL_REGS_FREQ_1 0xA0
#define OPL_REGS_FREQ_2 0xB0
+#define OPL_REGS_FEEDBACK 0xC0
// Initialise the OPL subsystem.
diff --git a/src/i_oplmusic.c b/src/i_oplmusic.c
index 98170284..5b3ae041 100644
--- a/src/i_oplmusic.c
+++ b/src/i_oplmusic.c
@@ -77,14 +77,45 @@ typedef struct
genmidi_voice_t opl3_voice;
} PACKEDATTR genmidi_instr_t;
+typedef struct opl_voice_s opl_voice_t;
+
+struct opl_voice_s
+{
+ // Index of this voice:
+ int index;
+
+ // The operators used by this voice:
+ int op1, op2;
+
+ // Currently-loaded instrument data
+ genmidi_instr_t *current_instr;
+
+ // Next in freelist
+ opl_voice_t *next;
+};
+
+// Operators used by the different voices.
+
+static const int voice_operators[2][OPL_NUM_VOICES] = {
+ { 0x00, 0x01, 0x02, 0x08, 0x09, 0x0a, 0x10, 0x11, 0x12 },
+ { 0x03, 0x04, 0x05, 0x0b, 0x0c, 0x0d, 0x13, 0x14, 0x15 }
+};
+
static boolean music_initialised = false;
//static boolean musicpaused = false;
static int current_music_volume;
+// GENMIDI lump instrument data:
+
static genmidi_instr_t *main_instrs;
static genmidi_instr_t *percussion_instrs;
+// Voices:
+
+static opl_voice_t voices[OPL_NUM_VOICES];
+static opl_voice_t *voice_free_list;
+
// Configuration file variable, containing the port number for the
// adlib chip.
@@ -158,6 +189,47 @@ static boolean DetectOPL(void)
&& (result2 & 0xe0) == 0xc0;
}
+// Initialise registers on startup
+
+static void InitRegisters(void)
+{
+ int r;
+
+ // Initialise level registers
+
+ for (r=OPL_REGS_LEVEL; r < OPL_REGS_LEVEL + OPL_NUM_OPERATORS; ++r)
+ {
+ WriteRegister(r, 0x3f);
+ }
+
+ // Initialise other registers
+ // These two loops write to registers that actually don't exist,
+ // but this is what Doom does ...
+
+ for (r=OPL_REGS_ATTACK; r < OPL_REGS_WAVEFORM + OPL_NUM_OPERATORS; ++r)
+ {
+ WriteRegister(r, 0x00);
+ }
+
+ // More registers ...
+
+ for (r=0; r < OPL_REGS_LEVEL; ++r)
+ {
+ WriteRegister(r, 0x00);
+ }
+
+ // Re-initialise the low registers:
+
+ // Reset both timers and enable interrupts:
+ WriteRegister(OPL_REG_TIMER_CTRL, 0x60);
+ WriteRegister(OPL_REG_TIMER_CTRL, 0x80);
+
+ // "Allow FM chips to control the waveform of each operator":
+ WriteRegister(OPL_REG_WAVEFORM_ENABLE, 0x20);
+
+ // Keyboard split point on (?)
+ WriteRegister(OPL_REG_FM_MODE, 0x40);
+}
// Load instrument table from GENMIDI lump:
@@ -182,6 +254,109 @@ static boolean LoadInstrumentTable(void)
return true;
}
+// Get the next available voice from the freelist.
+
+static opl_voice_t *GetFreeVoice(void)
+{
+ opl_voice_t *result;
+
+ // None available?
+
+ if (voice_free_list == NULL)
+ {
+ return NULL;
+ }
+
+ result = voice_free_list;
+ voice_free_list = voice_free_list->next;
+
+ return result;
+}
+
+// Release a voice back to the freelist.
+
+static void ReleaseVoice(opl_voice_t *voice)
+{
+ opl_voice_t **rover;
+
+ // Search to the end of the freelist (This is how Doom behaves!)
+
+ rover = &voice_free_list;
+
+ while (*rover != NULL)
+ {
+ rover = &(*rover)->next;
+ }
+
+ *rover = voice;
+}
+
+// Load data to the specified operator
+
+static void LoadOperatorData(int operator, genmidi_op_t *data,
+ boolean max_level)
+{
+ 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;
+ }
+
+ WriteRegister(OPL_REGS_LEVEL + operator, level);
+ WriteRegister(OPL_REGS_TREMOLO + operator, data->tremolo);
+ WriteRegister(OPL_REGS_ATTACK + operator, data->attack);
+ WriteRegister(OPL_REGS_SUSTAIN + operator, data->sustain);
+ WriteRegister(OPL_REGS_WAVEFORM + operator, data->waveform);
+}
+
+// Set the instrument for a particular voice.
+
+static void SetVoiceInstrument(opl_voice_t *voice, genmidi_voice_t *data)
+{
+ // Doom loads the second operator first, then the first.
+
+ LoadOperatorData(voice->op2, &data->carrier, true);
+ LoadOperatorData(voice->op1, &data->modulator, false);
+
+ // 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.
+
+ WriteRegister(OPL_REGS_FEEDBACK + voice->index,
+ data->feedback | 0x30);
+}
+
+// Initialise the voice table and freelist
+
+static void InitVoices(void)
+{
+ int i;
+
+ // Start with an empty free list.
+
+ voice_free_list = NULL;
+
+ // Initialise each voice.
+
+ for (i=0; i<OPL_NUM_VOICES; ++i)
+ {
+ voices[i].index = i;
+ voices[i].op1 = voice_operators[0][i];
+ voices[i].op2 = voice_operators[1][i];
+ voices[i].current_instr = NULL;
+
+ // Add this voice to the freelist.
+
+ ReleaseVoice(&voices[i]);
+ }
+}
+
// Shutdown music
static void I_OPL_ShutdownMusic(void)
@@ -224,6 +399,9 @@ static boolean I_OPL_InitMusic(void)
return false;
}
+ InitRegisters();
+ InitVoices();
+
music_initialised = true;
return true;