scummvm/audio/adlib_ms.h
2023-12-26 20:23:59 +01:00

1202 lines
44 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef AUDIO_ADLIB_MS_H
#define AUDIO_ADLIB_MS_H
#include "audio/mididrv_ms.h"
#include "audio/fmopl.h"
/**
* Rhythm instrument types used by the OPL2 and OPL3 rhythm mode.
*/
enum OplInstrumentRhythmType {
RHYTHM_TYPE_UNDEFINED,
RHYTHM_TYPE_HI_HAT,
RHYTHM_TYPE_CYMBAL,
RHYTHM_TYPE_TOM_TOM,
RHYTHM_TYPE_SNARE_DRUM,
RHYTHM_TYPE_BASS_DRUM
};
/**
* 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;
/**
* Check if this operator definition contains any data.
*
* @return True if this operator is empty; false otherwise.
*/
bool isEmpty();
};
/**
* 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;
/**
* The type of OPL rhythm instrument that this definition should be used
* with. Type undefined indicates that this definition should not be used
* with rhythm mode.
*/
OplInstrumentRhythmType rhythmType;
/**
* 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 writing the instrument definition to a channel.
*/
enum InstrumentWriteMode {
/**
* Will write the instrument definition before each note on event.
* Works with both dynamic and static channel allocation modes, but
* is less efficient and resets all parameters of the instrument when
* a note is played.
*/
INSTRUMENT_WRITE_MODE_NOTE_ON,
/**
* Will write the instrument definition after a program change event.
* This will only work with a static channel allocation mode. It will
* write the instrument only once for many notes and allows parameters
* of the instrument to be changed for the following notes.
*/
INSTRUMENT_WRITE_MODE_PROGRAM_CHANGE
};
/**
* 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;
/**
* The melodic channel numbers available on an OPL2 chip with rhythm mode
* disabled.
*/
static const uint8 MELODIC_CHANNELS_OPL2[9];
/**
* The melodic channel numbers available on an OPL2 chip with rhythm mode
* enabled.
*/
static const uint8 MELODIC_CHANNELS_OPL2_RHYTHM[6];
/**
* The melodic channel numbers available on an OPL3 chip with rhythm mode
* disabled.
*/
static const uint8 MELODIC_CHANNELS_OPL3[18];
/**
* The melodic channel numbers available on an OPL3 chip with rhythm mode
* enabled.
*/
static const uint8 MELODIC_CHANNELS_OPL3_RHYTHM[15];
/**
* The number of rhythm instruments available in OPL rhythm mode.
*/
static const uint8 OPL_NUM_RHYTHM_INSTRUMENTS = 5;
/**
* The OPL channels used by the rhythm instruments, in order:
* hi-hat, cymbal, tom tom, snare drum, bass drum.
*/
static const uint8 OPL_RHYTHM_INSTRUMENT_CHANNELS[OPL_NUM_RHYTHM_INSTRUMENTS];
/**
* 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;
/**
* Offsets for the rhythm mode instrument registers.
*/
static const uint8 OPL_REGISTER_RHYTHM_OFFSETS[];
/**
* 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.
* @param timerFrequency The number of timer callbacks per second that
* should be generated.
*/
MidiDriver_ADLIB_Multisource(OPL::Config::OplType oplType, int timerFrequency = OPL::OPL::kDefaultCallbackFrequency);
~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 nullptr.
*
* @return nullptr
*/
MidiChannel *allocateChannel() override;
/**
* This driver does not use MidiChannel objects, so this function returns nullptr.
*
* @return nullptr
*/
MidiChannel *getPercussionChannel() override;
using MidiDriver_Multisource::send;
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);
/**
* Applies the controller default settings to the controller data for the
* specified source.
* This will set all supported default values specified on _controllerDefaults
* except sustain, which is set by deinitSource.
*
* @param source The source triggering the default settings, or 0xFF to
* apply controller defaults for all sources.
*/
virtual void applyControllerDefaults(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);
/**
* Determines which melodic channels are available based on the OPL chip
* type and rhythm mode setting and sets _melodicChannels and
* _numMelodicChannels to the determined values.
* This is called after constructing the driver with the OPL chip type and
* after calls to setRhythmMode.
*/
void determineMelodicChannels();
/**
* 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);
/**
* Activates or deactivates the rhythm mode setting of the OPL chip. This
* setting uses 3 OPL channels to make 5 preset rhythm instruments
* available. Rhythm mode is turned off by default.
* Activating rhythm mode will deallocate and end active notes on channels
* 6 to 8. Deactivating rhythm mode will end active rhythm notes.
* If the specified setting is the same as the current setting, this method
* does nothing.
*
* @param rhythmMode True if rhythm mode should be turned on; false if it
* should be turned off.
*/
virtual void setRhythmMode(bool rhythmMode);
/**
* Determines the offset from a base register for the specified operator of
* the specified OPL channel or rhythm instrument.
* Add the offset to the base register to get the correct register for this
* operator and channel or rhythm instrument.
*
* @param oplChannel The OPL channel for which to determine the offset.
* Ignored if a rhythm type is specified.
* @param operatorNum The operator for which to determine the offset;
* 0-1 for 2 operator instruments, 0-3 for 4 operator instruments. Ignored
* for rhythm instruments other than bass drum.
* @param rhythmType The rhythm instrument for which to determine the
* offset. Specify type undefined to determine the offset for a melodic
* instrument on the specified channel.
* @param fourOperator True if the instrument used is a 4 operator
* instrument; false if it is a 2 operator instrument. Ignored if a rhythm
* instrument type is specified.
* @return The offset to the base register for this operator.
*/
uint16 determineOperatorRegisterOffset(uint8 oplChannel, uint8 operatorNum, OplInstrumentRhythmType rhythmType = RHYTHM_TYPE_UNDEFINED, 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);
/**
* Writes the specified instrument definition to the specified OPL channel.
* It will calculate volume and panning if necessary.
*
* @param oplChannel The OPL channel on which to write the instrument.
* @param instrument The data of the instrument to write.
*/
void writeInstrument(uint8 oplChannel, InstrumentInfo instrument);
/**
* Sets the key on bit to false for the specified OPL channel or rhythm
* instrument and updates _activeNotes or _activeRhythmNotes 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. Ignored if a rhythm type is specified.
* @param rhythmType The rhythm instrument for 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, OplInstrumentRhythmType rhythmType = RHYTHM_TYPE_UNDEFINED, bool forceWrite = false);
/**
* Determines the value for the rhythm register (0xBD) and writes the new
* value to the OPL chip. This register controls rhythm mode, the rhythm
* instruments and the vibrato and modulation depth settings.
*/
void writeRhythm(bool forceWrite = false);
/**
* Calculates the volume for the specified OPL channel or rhythm instrument
* 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. Ignored if a rhythm type is specified.
* @param operatorNum The operator for which volume should be calculated
* and written.
* @param rhythmType The rhythm instrument for which volume should be
* calculated and written. Use type undefined to calculate volume for a
* melodic instrument.
*/
virtual void writeVolume(uint8 oplChannel, uint8 operatorNum, OplInstrumentRhythmType rhythmType = RHYTHM_TYPE_UNDEFINED);
/**
* Calculates the panning for the specified OPL channel or rhythm type
* (@see calculatePanning) and writes the new value to the OPL registers.
*
* @param oplChannel The OPL channel for which panning should be calculated
* and written. Ignored if a rhythm type is specified.
* @param rhythmType The rhythm instrument for which panning should be
* calculated and written. Use type undefined to calculate panning for a
* melodic instrument.
*/
virtual void writePanning(uint8 oplChannel, OplInstrumentRhythmType rhythmType = RHYTHM_TYPE_UNDEFINED);
/**
* Calculates the frequency for the active note on the specified OPL
* channel or of the specified rhythm type (@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. Ignored if a rhythm type is specified.
* @param rhythmType The rhythm instrument for which the frequency should
* be calculated and written. Use type undefined to calculate the frequency
* for a melodic instrument.
*/
virtual void writeFrequency(uint8 oplChannel, OplInstrumentRhythmType rhythmType = RHYTHM_TYPE_UNDEFINED);
/**
* 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;
// The number of timer callbacks per second.
int _timerFrequency;
// Controls the behavior for calculating note frequency and volume.
AccuracyMode _accuracyMode;
// Controls the OPL channel allocation behavior.
ChannelAllocationMode _allocationMode;
// Controls when the instrument definitions are written.
InstrumentWriteMode _instrumentWriteMode;
// Controls response to rhythm note off events when rhythm mode is active.
bool _rhythmModeIgnoreNoteOffs;
// 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;
// Current OPL rhythm mode setting. Use setRhythmMode to set and activate.
bool _rhythmMode;
// 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 active note data for the OPL rhythm instruments.
ActiveNote _activeRhythmNotes[5];
// 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];
// Array containing the numbers of the available melodic channels.
const uint8 *_melodicChannels;
// The number of available melodic channels (length of _melodicChannels).
uint8 _numMelodicChannels;
// 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