scummvm/audio/adlib_ms.h
Coen Rampen d8491ecea4 AUDIO: Add default for OPL channel volume
This adds an option to the AdLib multisource driver to set the MIDI channel
volume that will be used to initialize to OPL 4x registers.
2021-10-09 16:59:44 +02:00

1049 lines
37 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#ifndef AUDIO_ADLIB_MS_H
#define AUDIO_ADLIB_MS_H
#include "audio/mididrv_ms.h"
#include "audio/fmopl.h"
/**
* Data for one operator of an OPL instrument definition.
*/
struct OplInstrumentOperatorDefinition {
/**
* 2x register: frequency multiplier, key scaling rate, envelope gain type,
* vibrato and modulation.
*/
uint8 freqMultMisc;
/**
* 4x register: level and key scaling level.
*/
uint8 level;
/**
* 6x register: decay and attack.
*/
uint8 decayAttack;
/**
* 8x register: release and sustain.
*/
uint8 releaseSustain;
/**
* Ex register: waveform select.
*/
uint8 waveformSelect;
};
/**
* Instrument definition for an OPL2 or OPL3 chip. Contains the data for all
* registers belonging to an OPL channel, except the Ax and Bx registers (these
* determine the frequency and are derived from the note played).
*/
struct OplInstrumentDefinition {
/**
* Indicates if this instrument uses 2 or 4 operators.
*/
bool fourOperator;
/**
* Operator data. 2 operator instruments use operators 0 and 1 only.
*/
OplInstrumentOperatorDefinition operator0;
OplInstrumentOperatorDefinition operator1;
OplInstrumentOperatorDefinition operator2;
OplInstrumentOperatorDefinition operator3;
/**
* Cx register: connection and feedback.
* Note: panning is determined by a MIDI controller and not part of the
* instrument definition.
*/
uint8 connectionFeedback0;
/**
* Second Cx register (used by 4 operator instruments).
*/
uint8 connectionFeedback1;
/**
* Notes played on a MIDI rhythm channel indicate which rhythm instrument
* should be played, not which note should be played. This field indicates
* the pitch (MIDI note) which should be used to play this rhythm
* instrument. Not used for melodic instruments.
*/
uint8 rhythmNote;
/**
* Check if this instrument definition contains any data.
*
* @return True if this instrument is empty; false otherwise.
*/
bool isEmpty();
/**
* Returns the number of operators used by this instrument definition.
*
* @return The number of operators (2 or 4).
*/
uint8 getNumberOfOperators();
/**
* Returns the definition data for the operator with the specified number.
* Specify 0 or 1 for 2 operator instruments or 0-3 for 4 operator
* instruments.
*
* @param operatorNum The operator for which the data should be returned.
* @return Pointer to the definition data for the specified operator.
*/
OplInstrumentOperatorDefinition &getOperatorDefinition(uint8 operatorNum);
};
#include "common/pack-start.h" // START STRUCT PACKING
/**
* Data for one operator of an OPL instrument definition in the AdLib BNK
* format.
*/
struct AdLibBnkInstrumentOperatorDefinition {
/**
* Individual fields for each setting in the 2x-8x registers.
* Note that waveform select is not part of the operator data in this
* format; it is included in the instrument data as a separate field.
*/
uint8 keyScalingLevel;
uint8 frequencyMultiplier;
uint8 feedback; // ignored for operator 1
uint8 attack;
uint8 sustain;
uint8 envelopeGainType; // 0x00: not sustained, >= 0x01: sustained
uint8 decay;
uint8 release;
uint8 level;
uint8 amplitudeModulation; // 0x00: off, >= 0x01: on
uint8 vibrato; // 0x00: off, >= 0x01: on
uint8 keyScalingRate; // 0x00: low, >= 0x01: high
uint8 connection; // 0x00: additive, >= 0x01: FM; ignored for operator 1
/**
* Copies the data in this AdLib BNK operator definition to the specified
* OplInstrumentOperatorDefinition struct.
*
* @param operatorDef The operator definition to which the data should be
* copied.
* @param waveformSelect The value of the waveform select parameter for
* this operator.
*/
void toOplInstrumentOperatorDefinition(OplInstrumentOperatorDefinition &operatorDef, uint8 waveformSelect);
} PACKED_STRUCT;
/**
* Instrument definition for an OPL2 chip in the format used by the AdLib BNK
* instrument bank file format.
*/
struct AdLibBnkInstrumentDefinition {
/**
* The type of instrument (0x00: melodic, 0x01: rhythm).
*/
uint8 instrumentType;
/**
* TODO Unclear what this represents; might be the same as rhythmNote.
*/
uint8 rhythmVoiceNumber;
/**
* Operator data.
*/
AdLibBnkInstrumentOperatorDefinition operator0;
AdLibBnkInstrumentOperatorDefinition operator1;
/**
* Waveform select parameter for each operator.
*/
uint8 waveformSelect0;
uint8 waveformSelect1;
/**
* Copies the data in this AdLib BNK instrument definition to the specified
* OplInstrumentDefinition struct.
*
* @param instrumentDef The instrument definition to which the data should
* be copied.
*/
void toOplInstrumentDefinition(OplInstrumentDefinition &instrumentDef);
} PACKED_STRUCT;
#include "common/pack-end.h" // END STRUCT PACKING
/**
* MIDI driver for AdLib / OPL2 and OPL3 emulators and devices with support for
* multiple simultaneous sources of MIDI data.
*
* This driver converts MIDI events to OPL chip register writes. When opened it
* will initialize an OPL emulator or device using the specified OPL type. It
* tracks the MIDI state of each source separately to avoid conflicts.
* The default behavior of the driver plays General MIDI data with the same
* output as the SoudBlaster 16 Windows 95 driver. It can be subclassed and
* customized to match the specific behavior of a game.
*
* Customization
*
* Depending on the platform and the type of music data the game uses, you can
* customize the driver to match this behavior:
* - Windows: If the game uses the standard Windows APIs to play General MIDI
* data, the default behavior of the driver should give identical output.
* - DOS, General MIDI: The default behavior of the driver should give you a
* decent starting point, but because there is no standard way to handle GM
* on OPL chips in DOS, it is probably not accurate. The instruments used by
* the game can be set in the _instrumentBank and _rhythmBank fields.
* You can subclass the driver to override more behavior, such as the
* calculateFrequency, calculatePitchBend, calculateUnscaledVolume and
* allocateOplChannel functions.
* - DOS, other type of MIDI: Additionally, you will need to override the
* functions that handle the various MIDI events and controllers when they do
* not match the General MIDI standard. You can override determineInstrument
* if the game uses some other way than instrument banks to set instruments.
* - DOS, does not use MIDI: Write new code to access the OPL registers
* directly instead of using this driver.
*
* TODO Dual OPL2 and 4 operator instrument support is unfinished.
*/
class MidiDriver_ADLIB_Multisource : public MidiDriver_Multisource {
public:
/**
* The available accuracy modes for frequency and volume calculation.
*/
enum AccuracyMode {
/**
* Accurate to the behavior of the Windows 95 SB16 driver.
*/
ACCURACY_MODE_SB16_WIN95,
/**
* Accurate to the General MIDI and MIDI specifications.
*/
ACCURACY_MODE_GM
};
/**
* The available modes for OPL channel allocation.
*/
enum ChannelAllocationMode {
/**
* Dynamic channel allocation (new OPL channel allocated to each note
* played).
*/
ALLOCATION_MODE_DYNAMIC,
/**
* Static channel allocation (fixed OPL channel allocated to each MIDI
* channel).
*/
ALLOCATION_MODE_STATIC
};
/**
* The available modes for the OPL note select setting.
*/
enum NoteSelectMode {
NOTE_SELECT_MODE_0,
NOTE_SELECT_MODE_1
};
/**
* The available modes for the OPL modulation depth setting.
*/
enum ModulationDepth {
/**
* Low modulation depth (1 dB).
*/
MODULATION_DEPTH_LOW,
/**
* High modulation depth (4.8 dB).
*/
MODULATION_DEPTH_HIGH
};
/**
* The available modes for the OPL vibrato depth setting.
*/
enum VibratoDepth {
/**
* Low vibrato depth (7 %).
*/
VIBRATO_DEPTH_LOW,
/**
* High vibrato depth (14 %).
*/
VIBRATO_DEPTH_HIGH
};
/**
* The number of available channels on each OPL chip.
*/
static const uint8 OPL2_NUM_CHANNELS = 9;
static const uint8 OPL3_NUM_CHANNELS = 18;
/**
* OPL test and timer registers.
*/
static const uint8 OPL_REGISTER_TEST = 0x01;
static const uint8 OPL_REGISTER_TIMER1 = 0x02;
static const uint8 OPL_REGISTER_TIMER2 = 0x03;
static const uint8 OPL_REGISTER_TIMERCONTROL = 0x04;
/**
* OPL global setting registers.
*/
static const uint8 OPL_REGISTER_NOTESELECT_CSM = 0x08;
static const uint8 OPL_REGISTER_RHYTHM = 0xBD;
/**
* OPL operator base registers.
*/
static const uint8 OPL_REGISTER_BASE_FREQMULT_MISC = 0x20;
static const uint8 OPL_REGISTER_BASE_LEVEL = 0x40;
static const uint8 OPL_REGISTER_BASE_DECAY_ATTACK = 0x60;
static const uint8 OPL_REGISTER_BASE_RELEASE_SUSTAIN = 0x80;
static const uint8 OPL_REGISTER_BASE_WAVEFORMSELECT = 0xE0;
/**
* OPL channel base registers.
*/
static const uint8 OPL_REGISTER_BASE_FNUMLOW = 0xA0;
static const uint8 OPL_REGISTER_BASE_FNUMHIGH_BLOCK_KEYON = 0xB0;
static const uint8 OPL_REGISTER_BASE_CONNECTION_FEEDBACK_PANNING = 0xC0;
/**
* OPL3-specific global setting registers.
*/
static const uint16 OPL3_REGISTER_CONNECTIONSELECT = 0x104;
static const uint16 OPL3_REGISTER_NEW = 0x105;
/**
* Offset to the second register set (for dual OPL2 and OPL3).
*/
static const uint16 OPL_REGISTER_SET_2_OFFSET = 0x100;
/**
* Bitmasks for various parameters in the OPL registers.
*/
static const uint8 OPL_MASK_LEVEL = 0x3F;
static const uint8 OPL_MASK_FNUMHIGH_BLOCK = 0x1F;
static const uint8 OPL_MASK_KEYON = 0x20;
static const uint8 OPL_MASK_PANNING = 0x30;
/**
* Settings for the panning bits in the OPL Cx registers.
*/
static const uint8 OPL_PANNING_CENTER = 0x30;
static const uint8 OPL_PANNING_LEFT = 0x10;
static const uint8 OPL_PANNING_RIGHT = 0x20;
/**
* The default melodic instrument definitions.
*/
static OplInstrumentDefinition OPL_INSTRUMENT_BANK[];
/**
* The default rhythm instrument definitions.
*/
static OplInstrumentDefinition OPL_RHYTHM_BANK[];
protected:
/**
* Default setting for OPL channel volume (level).
*/
static const uint8 OPL_LEVEL_DEFAULT = 0x3F;
/**
* The lowest MIDI panning controller value interpreted as left panning.
*/
static const uint8 OPL_MIDI_PANNING_LEFT_LIMIT = 0x2F;
/**
* The highest MIDI panning controller value interpreted as right panning.
*/
static const uint8 OPL_MIDI_PANNING_RIGHT_LIMIT = 0x51;
/**
* OPL frequency (F-num) value for each octave semitone. The values assume
* octave 5.
*/
static const uint16 OPL_NOTE_FREQUENCIES[];
/**
* OPL volume lookup array for a MIDI volume value shifted from 7 to 5 bits.
*/
static const uint8 OPL_VOLUME_LOOKUP[];
/**
* Contains the current controller settings for a MIDI channel.
*/
struct MidiChannelControlData {
uint8 program;
uint8 channelPressure;
uint16 pitchBend; // 14 bit value; 0x2000 is neutral
uint8 modulation;
uint8 volume;
uint8 panning; // 0x40 is center
uint8 expression;
bool sustain;
uint16 rpn; // Two 7 bit values stored in 8 bits each
uint8 pitchBendSensitivity; // Semitones
uint8 pitchBendSensitivityCents;
uint16 masterTuningFine; // 14 bit value; 0x2000 is neutral
uint8 masterTuningCoarse; // Semitones; 0x40 is neutral
MidiChannelControlData();
/**
* Initializes the controller settings to default values.
*/
void init();
};
/**
* Contains information on the currently active note on an OPL channel.
*/
struct ActiveNote {
/**
* True if a note is currently playing (including if it is sustained,
* but not if it is in the "release" phase).
*/
bool noteActive;
/**
* True if the currently playing note is sustained, i.e. note has been
* turned off but is kept active due to the sustain controller.
*/
bool noteSustained;
/**
* The MIDI note value as it appeared in the note on event.
*/
uint8 note;
/**
* The MIDI velocity value of the note on event.
*/
uint8 velocity;
/**
* The MIDI channel that played the current/last note (0xFF if no note
* has been played since initialization).
*/
uint8 channel;
/**
* The source that played the current/last note (0xFF if no note has
* been played since initialization).
*/
uint8 source;
/**
* The MIDI note value that is actually played. This is the same as the
* note field for melodic instruments, but on the MIDI rhythm channel
* the note indicates which rhythm instrument should be played instead
* of the pitch. In that case this field is different
* (@see determineInstrument).
*/
uint8 oplNote;
/**
* The OPL frequency (F-num) and octave (block) (in Ax (low byte) and
* Bx (high byte) register format) that was calculated to play the MIDI
* note.
*/
uint16 oplFrequency;
/**
* The value of the note counter when a note was last turned on or off
* on this OPL channel.
*/
uint32 noteCounterValue;
/**
* A unique identifier of the instrument that is used to play the note.
* In the default implementation this is the MIDI program number for
* melodic instruments and the rhythm channel note number + 0x80 for
* rhythm instruments (@see determineInstrument).
*/
uint8 instrumentId;
/**
* Pointer to the instrument definition used to play the note.
*/
OplInstrumentDefinition *instrumentDef;
/**
* True if this OPL channel has been allocated to a MIDI channel.
* Note that in the default driver implementation only the static
* channel allocation algorithm uses this field.
*/
bool channelAllocated;
ActiveNote();
/**
* Initializes the active note data to default values.
*/
void init();
};
/**
* OPL instrument data for playing a note.
*/
struct InstrumentInfo {
/**
* MIDI note value to use for playing this instrument
* (@see ActiveNote.oplNote).
*/
uint8 oplNote;
/**
* Pointer to the instrument definition.
*/
OplInstrumentDefinition *instrumentDef;
/**
* Unique identifer for this instrument (@see ActiveNote.instrumentId).
*/
uint8 instrumentId;
};
public:
/**
* Checks if the specified type of OPL chip is supported by the OPL
* emulator or hardware that is used.
*
* @param oplType The type of OPL chip that should be detected.
* @return True if the specified type of OPL chip is supported by the OPL
* emulator/hardware; false otherwise.
*/
static bool detectOplType(OPL::Config::OplType oplType);
/**
* Constructs a new AdLib multisource MIDI driver using the specified type
* of OPL chip.
*
* @param oplType The type of OPL chip that should be used.
*/
MidiDriver_ADLIB_Multisource(OPL::Config::OplType oplType);
~MidiDriver_ADLIB_Multisource();
/**
* Prepares the driver for processing MIDI data and initializes the OPL
* emulator or hardware.
*
* @return 0 if the driver was opened successfully; a MidiDriver error code
* otherwise.
*/
int open() override;
bool isOpen() const override;
void close() override;
uint32 property(int prop, uint32 param) override;
uint32 getBaseTempo() override;
/**
* This driver does not use MidiChannel objects, so this function returns 0.
*
* @return 0
*/
MidiChannel *allocateChannel() override;
/**
* This driver does not use MidiChannel objects, so this function returns 0.
*
* @return 0
*/
MidiChannel *getPercussionChannel() override;
void send(int8 source, uint32 b) override;
void sysEx(const byte *msg, uint16 length) override;
void metaEvent(int8 source, byte type, byte *data, uint16 length) override;
void stopAllNotes(bool stopSustainedNotes = false) override;
void stopAllNotes(uint8 source, uint8 channel) override;
void deinitSource(uint8 source) override;
protected:
void applySourceVolume(uint8 source) override;
/**
* Initializes the OPL registers to their default values.
*/
virtual void initOpl();
/**
* Processes a MIDI note off event.
*
* @param channel The MIDI channel on which the note is active.
* @param note The MIDI note that should be turned off.
* @param velocity The release velocity (not implemented).
* @param source The source sending the note off event.
*/
virtual void noteOff(uint8 channel, uint8 note, uint8 velocity, uint8 source);
/**
* Processes a MIDI note on event.
*
* @param channel The MIDI channel on which the note is played.
* @param note The MIDI note that should be turned on.
* @param velocity The MIDI velocity of the played note.
* @param source The source sending the note on event.
*/
virtual void noteOn(uint8 channel, uint8 note, uint8 velocity, uint8 source);
/**
* Processes a MIDI polyphonic aftertouch event.
* Note: this event has no default implementation because it is not
* implemented in the Win95 SB16 driver.
*
* @param channel The MIDI channel on which the event is sent.
* @param note The MIDI note on which aftertouch should be applied.
* @param pressure The aftertouch amount that should be applied.
* @param source The source sending the aftertouch event.
*/
virtual void polyAftertouch(uint8 channel, uint8 note, uint8 pressure, uint8 source);
/**
* Processes a MIDI control change event. The individual controllers are
* handled by separate functions (@see modulation etc.).
*
* @param channel The MIDI channel on which the event is sent.
* @param controller The MIDI controller whose value should be changed.
* @param value The value that should be applied to the controller.
* @param source The source sending the conrol change event.
*/
virtual void controlChange(uint8 channel, uint8 controller, uint8 value, uint8 source);
/**
* Processes a MIDI program (instrument) change event.
*
* @param channel The MIDI channel on which the instrument should be set.
* @param program The instrument that should be set on the channel.
* @param source The source sending the program change event.
*/
virtual void programChange(uint8 channel, uint8 program, uint8 source);
/**
* Processes a MIDI channel aftertouch event.
* Note: this event has no default implementation because it is not
* implemented in the Win95 SB16 driver.
*
* @param channel The MIDI channel on which aftertouch should be applied.
* @param pressure The aftertouch amount that should be applied.
* @param source The source sending the aftertouch event.
*/
virtual void channelAftertouch(uint8 channel, uint8 pressure, uint8 source);
/**
* Processes a MIDI pitch bend event.
* Note that MIDI pitch bend is a 14 bit value sent as 2 7 bit values, with
* the LSB sent first.
*
* @param channel The MIDI channel on which pitch bend should be applied.
* @param pitchBendLsb The LSB of the pitch bend value.
* @param pitchBendMsb The MSB of the pitch bend value.
* @param source The source sending the pitch bend event.
*/
virtual void pitchBend(uint8 channel, uint8 pitchBendLsb, uint8 pitchBendMsb, uint8 source);
/**
* Processes a MIDI modulation control change event.
* Note: this event has no default implementation because it is not
* implemented in the Win95 SB16 driver.
*
* @param channel The MIDI channel on which modulation should be applied.
* @param modulation The modulation amount that should be applied.
* @param source The source sending the control change event.
*/
virtual void modulation(uint8 channel, uint8 modulation, uint8 source);
/**
* Processes a MIDI data entry control change event. This sets the MSB
* and/or LSB of the currently selected registered parameter number.
* Note that a MIDI data entry event contains either the MSB or LSB;
* specify 0xFF for the other data byte to leave it unchanged.
* RPNs pitch bend sensitivity, master tuning fine and coarse are supported
* in accuracy mode GM only.
*
* @param channel The MIDI channel on which the RPN data byte should be set.
* @param dataMsb The MSB of the RPN data value; 0xFF to not set the MSB.
* @param dataLsb The LSB of the RPN data value; 0xFF to not set the LSB.
* @param source The source sending the control change event.
*/
virtual void dataEntry(uint8 channel, uint8 dataMsb, uint8 dataLsb, uint8 source);
/**
* Process a MIDI volume control change event.
*
* @param channel The MIDI channel on which volume should be set.
* @param volume The volume level that should be set.
* @param source The source sending the control change event.
*/
virtual void volume(uint8 channel, uint8 volume, uint8 source);
/**
* Process a MIDI panning control change event.
* Note that panning is not supported on an OPL2 chip because it has mono
* output.
*
* @param channel The MIDI channel on which panning should be set.
* @param panning The panning value that should be set.
* @param source The source sending the control change event.
*/
virtual void panning(uint8 channel, uint8 panning, uint8 source);
/**
* Process a MIDI expression control change event.
*
* @param channel The MIDI channel on which expression should be set.
* @param expression The expression value that should be set.
* @param source The source sending the control change event.
*/
virtual void expression(uint8 channel, uint8 expression, uint8 source);
/**
* Process a MIDI sustain control change event.
*
* @param channel The MIDI channel on which sustain should be set.
* @param sustain The sustain value that should be set.
* @param source The source sending the control change event.
*/
virtual void sustain(uint8 channel, uint8 sustain, uint8 source);
/**
* Process a MIDI registered parameter number control change event. This
* sets the currently active RPN; subsequent data entry control change
* events will set the value for the selected RPN.
* Note that a MIDI PRN event contains either the MSB or LSB; specify 0xFF
* for the other rpn byte to leave it unchanged.
* RPNs pitch bend sensitivity, master tuning fine and coarse are supported
* in accuracy mode GM only.
*
* @param channel The MIDI channel on which the active PRN should be set.
* @param rpnMsb The MSB of the RPN number; 0xFF to not set the MSB.
* @param rpnLsb The LSB of the RPN number; 0xFF to not set the LSB.
* @param source The source sending the control change event.
*/
virtual void registeredParameterNumber(uint8 channel, uint8 rpnMsb, uint8 rpnLsb, uint8 source);
/**
* Process a MIDI all sound off channel mode event.
* Note that this should immediately stop all sound, but it is not possible
* to abort the "release" phase of a note on an OPL chip. So this will
* function like an all notes off event, except it will also stop sustained
* notes.
*
* @param channel The MIDI channel on which the all sound off channel mode
* event is sent.
* @param source The source sending the control change event.
*/
virtual void allSoundOff(uint8 channel, uint8 source);
/**
* Process a MIDI reset all controllers channel mode event. This will reset
* the following controllers to their default values:
* - modulation
* - expression
* - sustain
* - active RPN
* - pitch bend
* - channel aftertouch
* It should also reset polyphonic aftertouch, but this is not implemented.
*
* @param channel The MIDI channel on which the reset all controllers
* channel mode event is sent.
* @param source The source sending the control change event.
*/
virtual void resetAllControllers(uint8 channel, uint8 source);
/**
* Process a MIDI all notes off channel mode event. This will turn off all
* non-sustained notes or sustain all notes if the sustain controller is on.
*
* @param channel The MIDI channel on which the all notes off channel mode
* event is sent.
* @param source The source sending the control change event.
*/
virtual void allNotesOff(uint8 channel, uint8 source);
/**
* Recalculates and writes the frequencies of the active notes on the
* specified MIDI channel and source.
*
* @param channel The MIDI channel on which the note frequencies should be
* recalculated.
* @param source The source for which the note frequencies should be
* recalculated.
*/
virtual void recalculateFrequencies(uint8 channel, uint8 source);
/**
* Recalculates and writes the volumes of the active notes on the specified
* MIDI channel and source. 0xFF can be specified to recalculate volumes of
* notes on all MIDI channels and/or sources.
*
* @param channel The MIDI channel on which the note volumes should be
* recalculated; 0xFF to recalculate volumes for all channels.
* @param source The source for which the note volumes should be
* recalculated; 0xFF to recalculate volumes for all sources.
*/
virtual void recalculateVolumes(uint8 channel, uint8 source);
/**
* Determines the instrument data necessary to play the specified note on
* the specified MIDI channel and source. This will determine the
* instrument definition to use, the note that should be played and an
* instrument ID for use by the dynamic channel allocation algorithm.
*
* @param channel The MIDI channel on which the note is played.
* @param source The source playing the note.
* @param note The MIDI note which is played.
* @return The instrument data for playing the note, or an empty struct if
* the note cannot be played.
*/
virtual InstrumentInfo determineInstrument(uint8 channel, uint8 source, uint8 note);
/**
* Allocates an OPL channel to play a note on the specified MIDI channel
* and source with the specified instrument ID. Allocation behavior depends
* on the active channel allocation mode:
* - Dynamic: allocates an unused channel, a channel playing a note using
* the same instrument or the channel playing the oldest note. This will
* always allocate a channel to play the note. This is the same behavior
* as the Win95 SB16 driver.
* - Static: allocates an unused OPL channel and assigns it to the MIDI
* channel playing the note. All subsequent notes on this MIDI channel
* will be played using this OPL channel. If there are no free channels,
* it will fail to allocate a channel. The MIDI data must play one note
* at a time on each channel and not use more MIDI channels than there
* are OPL channels for this algorithm to work properly.
*
* @param channel The MIDI channel on which the note is played.
* @param source The source playing the note.
* @param instrumentId The ID of the instrument playing the note. Not used
* by the static channel allocation mode.
* @return The number of the allocated OPL channel; 0xFF if allocation
* failed (not possible using the dynamic channel allocation mode).
*/
virtual uint8 allocateOplChannel(uint8 channel, uint8 source, uint8 instrumentId);
/**
* Calculates the OPL frequency (F-num) and octave (block) to play the
* specified note on the specified MIDI channel and source, taking into
* account the MIDI controllers pitch bend and (on accuracy mode GM) pitch
* bend sensitivity and master tuning. The result is returned in the format
* of the Ax (low byte) and Bx (high byte) OPL registers.
* Note that the MIDI note range exceeds the frequency range of an OPL
* chip, so the highest MIDI notes will be shifted down one or two octaves.
* The SB16 Win95 accuracy mode calculates the same frequencies as the
* Windows 95 SB16 driver. The GM accuracy mode is more accurate, but the
* calculations are more CPU intensive. This mode also supports pitch bend
* sensitivity (which is fixed at 2 semitones in SB16 Win95 mode) and
* master tuning.
*
* @param channel The MIDI channel on which the note is played.
* @param source The source playing the note.
* @param note The MIDI note which is played.
* @return The F-num and block to play the note on the OPL chip.
*/
virtual uint16 calculateFrequency(uint8 channel, uint8 source, uint8 note);
/**
* Calculates the pitch bend value to apply to the specified OPL frequency
* (F-num) on the specified MIDI channel and source. If the accuracy mode
* is GM, pitch bend sensitivity and master tuning settings are also
* applied. The result is an adjustment which can be added to the OPL
* frequency to get the pitch bent note.
*
* @param channel The MIDI channel for which pitch bend should be
* calculated.
* @param source The source for which pitch bend should be calculated.
* @param oplFrequency The OPL frequency for which pitch bend should be
* calculated.
* @return The calculated pitch bend (OPL frequency adjustment).
*/
virtual int32 calculatePitchBend(uint8 channel, uint8 source, uint16 oplFrequency);
/**
* Calculates the volume for the specified operator of a note on the
* specified MIDI channel and source, using the specified MIDI velocity and
* instrument definition.
* This function will check if the operator will need to have volume
* applied to it or if the operator volume from the instrument definition
* should be used without adjustment (this depends on the connection type).
* If volume should be applied, unscaled volume is calculated
* (@see calculateUnscaledVolume) and volume is scaled to source and user
* volume. The volume is returned as an OPL 2x register volume (level)
* value, i.e. 0 = maximum volume, 3F = minimum volume.
*
* @param channel The MIDI channel on which the note is played.
* @param source The source playing the note.
* @param velocity The MIDI velocity of the note for which volume should be
* calculated.
* @param instrumentDef The instrument definition used to play the note.
* @param operatorNum The number of the operator for which volume should be
* calculated; 0-1 for 2 operator instruments, 0-3 for 4 operator
* instruments.
* @return The calculated operator volume (level).
*/
virtual uint8 calculateVolume(uint8 channel, uint8 source, uint8 velocity, OplInstrumentDefinition &instrumentDef, uint8 operatorNum);
/**
* Calculates the unscaled volume for the specified operator of a note on
* the specified MIDI channel and source, using the specified MIDI velocity
* and instrument definition.
* The SB16 Win95 accuracy mode calculates the same values as the Windows
* 95 SB16 driver. The GM accuracy mode is more accurate to the volume
* curve in the General MIDI specification and supports the expression
* controller, but the calculation is more CPU intensive.
* The volume is returned as an OPL 2x register volume (level) value,
* i.e. 0 = maximum volume, 3F = minimum volume.
*
* @param channel The MIDI channel on which the note is played.
* @param source The source playing the note.
* @param velocity The MIDI velocity of the note for which volume should be
* calculated.
* @param instrumentDef The instrument definition used to play the note.
* @param operatorNum The number of the operator for which volume should be
* calculated; 0-1 for 2 operator instruments, 0-3 for 4 operator
* instruments.
* @return The calculated unscaled operator volume (level).
*/
virtual uint8 calculateUnscaledVolume(uint8 channel, uint8 source, uint8 velocity, OplInstrumentDefinition &instrumentDef, uint8 operatorNum);
/**
* Determines the panning that should be applied to notes played on the
* specified MIDI channel and source.
* This will convert the MIDI panning controller value to simple left,
* right and center panning and return the result as a Cx register panning
* value (in bits 4 and 5) for an OPL3 chip.
*
* @param channel The MIDI channel for which panning should be calculated.
* @param source The source for which panning should be calculated.
* @return The calculated panning.
*/
virtual uint8 calculatePanning(uint8 channel, uint8 source);
/**
* Determines the number of channels available on the OPL chip used by the
* driver.
*
* @return The number of OPL channels (9 or 18).
*/
uint8 determineNumOplChannels();
/**
* Determines the offset from a base register for the specified operator of
* the specified OPL channel.
* Add the offset to the base register to get the correct register for this
* operator and channel.
*
* @param oplChannel The OPL channel for which to determine the offset.
* @param operatorNum The operator for which to determine the offset;
* 0-1 for 2 operator instruments, 0-3 for 4 operator instruments.
* @param fourOperator True if the instrument used is a 4 operator
* instrument; false if it is a 2 operator instrument.
* @return The offset to the base register for this operator.
*/
uint16 determineOperatorRegisterOffset(uint8 oplChannel, uint8 operatorNum, bool fourOperator = false);
/**
* Determines the offset from a base register for the specified OPL channel.
* Add the offset to the base register to get the correct register for this
* channel.
*
* @param oplChannel The OPL channel for which to determine the offset.
* @param fourOperator True if the instrument used is a 4 operator
* instrument; false if it is a 2 operator instrument.
* @return The offset to the base register for this channel.
*/
uint16 determineChannelRegisterOffset(uint8 oplChannel, bool fourOperator = false);
/**
* Sets the key on bit to false on the specified OPL channel and updates
* _activeNotes with the new status.
* Specify forceWrite to force the OPL register to be written, even if the
* key on bit is already false according to the shadow registers.
*
* @param oplChannel The OPL channel on which the key on bit should be set
* to false.
* @param forceWrite True if the OPL register write should be forced; false
* otherwise.
*/
void writeKeyOff(uint8 oplChannel, bool forceWrite = false);
/**
* Calculates the volume for the specified OPL channel and operator
* (@see calculateVolume) and writes the new value to the OPL registers.
*
* @param oplChannel The OPL channel for which volume should be calculated
* and written.
* @param operatorNum The operator for which volume should be calculated
* and written.
*/
void writeVolume(uint8 oplChannel, uint8 operatorNum);
/**
* Calculates the panning for the specified OPL channel
* (@see calculatePanning) and writes the new value to the OPL registers.
*
* @param oplChannel The OPL channel for which panning should be calculated
* and written.
*/
void writePanning(uint8 oplChannel);
/**
* Calculates the frequency for the active note on the specified OPL
* channel (@see calculateFrequency) and writes the new value to the OPL
* registers.
*
* @param oplChannel The OPL channel for which the frequency should be
* calculated and written.
*/
void writeFrequency(uint8 oplChannel);
/**
* Writes the specified value to the specified OPL register.
* If the specified value is the same as the current value according to the
* shadow registers, the value is not written unless forceWrite is
* specified.
*
* @param reg The OPL register where the value should be written
* (>= 0x100 for the second register set).
* @param value The value to write in the register.
* @param forceWrite True if the register write should be forced; false
* otherwise.
*/
void writeRegister(uint16 reg, uint8 value, bool forceWrite = false);
// The type of OPL chip to use.
OPL::Config::OplType _oplType;
// The OPL emulator / hardware interface.
OPL::OPL *_opl;
// True if the driver has been successfully opened.
bool _isOpen;
// Controls the behavior for calculating note frequency and volume.
AccuracyMode _accuracyMode;
// Controls the OPL channel allocation behavior.
ChannelAllocationMode _allocationMode;
// The default MIDI channel volume (set when opening the driver).
uint8 _defaultChannelVolume;
// OPL global settings. Set these, then call oplInit or open to apply the
// new values.
NoteSelectMode _noteSelect;
ModulationDepth _modulationDepth;
VibratoDepth _vibratoDepth;
// Pointer to the melodic instrument definitions.
OplInstrumentDefinition *_instrumentBank;
// Pointer to the rhythm instrument definitions.
OplInstrumentDefinition *_rhythmBank;
// The MIDI note value of the first rhythm instrument in the bank.
uint8 _rhythmBankFirstNote;
// The MIDI note value of the last rhythm instrument in the bank.
uint8 _rhythmBankLastNote;
// The current MIDI controller values for each MIDI channel and source.
MidiChannelControlData _controlData[MAXIMUM_SOURCES][MIDI_CHANNEL_COUNT];
// The active note data for each OPL channel.
ActiveNote _activeNotes[OPL3_NUM_CHANNELS];
// The OPL channel allocated to each MIDI channel and source; 0xFF if a
// MIDI channel has no OPL channel allocated. Note that this is only used by
// the static channel allocation mode.
uint8 _channelAllocations[MAXIMUM_SOURCES][MIDI_CHANNEL_COUNT];
// The amount of notes played since the driver was opened / reset.
uint32 _noteCounter;
// Factor to convert a frequency in Hertz to the format used by the OPL
// registers (F - num).
float _oplFrequencyConversionFactor;
// The values last written to each OPL register.
uint8 _shadowRegisters[0x200];
Common::Mutex _allocationMutex; // For operations on channel allocations
Common::Mutex _activeNotesMutex; // For operations on active notes
};
#endif