Preliminary preparation for new YM2612 FM emulator.
All the hooks are in, but actual implementation needs to be checked for portability. svn-id: r10615
This commit is contained in:
parent
7174a32c8e
commit
a722d0601e
16 changed files with 154 additions and 60 deletions
1
README
1
README
|
@ -570,6 +570,7 @@ choices of output, depending on your operating system and configuration.
|
|||
adlib - Uses internal Adlib Emulation (default)
|
||||
pcjr - Uses internal PCjr Emulation
|
||||
pcspk - Uses internal PC Speaker Emulation
|
||||
towns - Uses FM-Towns YM2612 Emulation
|
||||
windows - Windows MIDI. Uses built-in sequencer, for Windows users
|
||||
seq - Uses /dev/sequencer for MIDI, *nix users. See below.
|
||||
qt - Quicktime sound, for Macintosh users.
|
||||
|
|
31
backends/midi/ym2612.cpp
Normal file
31
backends/midi/ym2612.cpp
Normal file
|
@ -0,0 +1,31 @@
|
|||
/* ScummVM - Scumm Interpreter
|
||||
* Copyright (C) 2001-2003 The ScummVM project
|
||||
*
|
||||
* YM2612 tone generation code written by Tomoaki Hayasaka.
|
||||
* Used under the terms of the GNU General Public License.
|
||||
* Adpated to ScummVM by Jamieson Christian.
|
||||
*
|
||||
* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*
|
||||
* $Header$
|
||||
*/
|
||||
|
||||
// Real implementation coming soon! :)
|
||||
|
||||
#include "sound/mididrv.h"
|
||||
|
||||
MidiDriver *MidiDriver_YM2612_create(SoundMixer *mixer) {
|
||||
return 0;
|
||||
}
|
|
@ -11,7 +11,8 @@ MODULE_OBJS := \
|
|||
backends/midi/quicktime.o \
|
||||
backends/midi/seq.o \
|
||||
backends/midi/alsa.o \
|
||||
backends/midi/windows.o
|
||||
backends/midi/windows.o \
|
||||
backends/midi/ym2612.o
|
||||
|
||||
MODULE_DIRS += \
|
||||
backends \
|
||||
|
|
|
@ -676,12 +676,15 @@ bool GameDetector::detectMain() {
|
|||
if (_game.midi & MDT_PREFER_NATIVE)
|
||||
_midi_driver = getMidiDriverType();
|
||||
else
|
||||
_midi_driver = MD_ADLIB;
|
||||
_midi_driver = MD_TOWNS;
|
||||
}
|
||||
bool nativeMidiDriver =
|
||||
(_midi_driver != MD_NULL && _midi_driver != MD_ADLIB &&
|
||||
_midi_driver != MD_PCSPK && _midi_driver != MD_PCJR);
|
||||
_midi_driver != MD_PCSPK && _midi_driver != MD_PCJR &&
|
||||
_midi_driver != MD_TOWNS);
|
||||
if (nativeMidiDriver && !(_game.midi & MDT_NATIVE))
|
||||
_midi_driver = MD_TOWNS;
|
||||
if (_midi_driver == MD_TOWNS && !(_game.midi & MDT_TOWNS))
|
||||
_midi_driver = MD_ADLIB;
|
||||
if (_midi_driver == MD_ADLIB && !(_game.midi & MDT_ADLIB))
|
||||
_midi_driver = MD_PCJR;
|
||||
|
@ -770,12 +773,15 @@ MidiDriver *GameDetector::createMidi() {
|
|||
|
||||
switch(drv) {
|
||||
case MD_NULL: return MidiDriver_NULL_create();
|
||||
|
||||
// In the case of Adlib, we won't specify anything.
|
||||
// IMuse is designed to set up its own Adlib driver
|
||||
// if need be, and we only have to specify a native
|
||||
// driver.
|
||||
case MD_ADLIB: return NULL;
|
||||
|
||||
case MD_TOWNS: return MidiDriver_YM2612_create(g_engine->_mixer);
|
||||
|
||||
// Right now PC Speaker and PCjr are handled
|
||||
// outside the MidiDriver architecture, so
|
||||
// don't create anything for now.
|
||||
|
|
|
@ -70,8 +70,9 @@ enum MidiDriverType {
|
|||
MDT_NONE = 0,
|
||||
MDT_PCSPK = 1, // MD_PCSPK and MD_PCJR
|
||||
MDT_ADLIB = 2, // MD_ADLIB
|
||||
MDT_NATIVE = 4, // Everything else
|
||||
MDT_PREFER_NATIVE = 8
|
||||
MDT_TOWNS = 4, // MD_TOWNS
|
||||
MDT_NATIVE = 8, // Everything else
|
||||
MDT_PREFER_NATIVE = 16
|
||||
};
|
||||
|
||||
struct TargetSettings {
|
||||
|
|
|
@ -540,6 +540,10 @@ SOURCE=..\..\backends\midi\null.cpp
|
|||
|
||||
SOURCE=..\..\backends\midi\windows.cpp
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\..\backends\midi\ym2612.cpp
|
||||
# End Source File
|
||||
# End Group
|
||||
# End Group
|
||||
# End Group
|
||||
|
|
|
@ -355,6 +355,9 @@
|
|||
<File
|
||||
RelativePath="..\..\backends\midi\windows.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\backends\midi\ym2612.cpp">
|
||||
</File>
|
||||
</Filter>
|
||||
</Filter>
|
||||
<Filter
|
||||
|
|
|
@ -54,6 +54,7 @@ _initialized(false),
|
|||
_tempoFactor(0),
|
||||
_player_limit(ARRAYSIZE(_players)),
|
||||
_recycle_players(false),
|
||||
_direct_passthrough(false),
|
||||
_queue_end(0),
|
||||
_queue_pos(0),
|
||||
_queue_sound(0),
|
||||
|
@ -250,7 +251,7 @@ bool IMuseInternal::startSound(int sound) {
|
|||
return false;
|
||||
|
||||
player->clear();
|
||||
return player->startSound(sound, driver);
|
||||
return player->startSound(sound, driver, _direct_passthrough);
|
||||
}
|
||||
|
||||
|
||||
|
@ -1117,10 +1118,14 @@ uint32 IMuseInternal::property(int prop, uint32 value) {
|
|||
break;
|
||||
|
||||
case IMuse::PROP_RECYCLE_PLAYERS:
|
||||
if (value > 0 && value <= ARRAYSIZE(_players))
|
||||
_recycle_players = (value != 0);
|
||||
break;
|
||||
|
||||
case IMuse::PROP_DIRECT_PASSTHROUGH:
|
||||
_direct_passthrough = (value != 0);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -51,12 +51,13 @@ public:
|
|||
~IMuse();
|
||||
|
||||
enum {
|
||||
PROP_TEMPO_BASE = 1,
|
||||
PROP_NATIVE_MT32 = 2,
|
||||
PROP_MULTI_MIDI = 3,
|
||||
PROP_OLD_ADLIB_INSTRUMENTS = 4,
|
||||
PROP_LIMIT_PLAYERS = 5,
|
||||
PROP_RECYCLE_PLAYERS = 6
|
||||
PROP_TEMPO_BASE,
|
||||
PROP_NATIVE_MT32,
|
||||
PROP_MULTI_MIDI,
|
||||
PROP_OLD_ADLIB_INSTRUMENTS,
|
||||
PROP_LIMIT_PLAYERS,
|
||||
PROP_RECYCLE_PLAYERS,
|
||||
PROP_DIRECT_PASSTHROUGH
|
||||
};
|
||||
|
||||
void on_timer(MidiDriver *midi);
|
||||
|
|
|
@ -60,10 +60,6 @@ class ScummEngine;
|
|||
|
||||
#define TICKS_PER_BEAT 480
|
||||
|
||||
#define IMUSE_SYSEX_ID 0x7D
|
||||
#define ROLAND_SYSEX_ID 0x41
|
||||
#define PERCUSSION_CHANNEL 9
|
||||
|
||||
#define TRIGGER_ID 0
|
||||
#define COMMAND_ID 1
|
||||
|
||||
|
@ -158,6 +154,7 @@ protected:
|
|||
protected:
|
||||
MidiDriver *_midi;
|
||||
MidiParser *_parser;
|
||||
bool _passThrough; // Only respond to EOT, all else direct to MidiDriver
|
||||
|
||||
Part *_parts;
|
||||
bool _active;
|
||||
|
@ -259,7 +256,7 @@ public:
|
|||
void setSpeed(byte speed);
|
||||
int setTranspose(byte relative, int b);
|
||||
int setVolume(byte vol);
|
||||
bool startSound(int sound, MidiDriver *midi);
|
||||
bool startSound(int sound, MidiDriver *midi, bool passThrough);
|
||||
int getMusicTimer() const;
|
||||
|
||||
public:
|
||||
|
@ -368,6 +365,7 @@ protected:
|
|||
|
||||
int _player_limit; // Limits how many simultaneous music tracks are played
|
||||
bool _recycle_players; // Can we stop a player in order to start another one?
|
||||
bool _direct_passthrough; // Pass data direct to MidiDriver (no interactivity)
|
||||
|
||||
uint _queue_end, _queue_pos, _queue_sound;
|
||||
byte _queue_adding;
|
||||
|
|
|
@ -39,6 +39,11 @@ namespace Scumm {
|
|||
//
|
||||
////////////////////////////////////////
|
||||
|
||||
#define IMUSE_SYSEX_ID 0x7D
|
||||
#define YM2612_SYSEX_ID 0x7C
|
||||
#define ROLAND_SYSEX_ID 0x41
|
||||
#define PERCUSSION_CHANNEL 9
|
||||
|
||||
extern MidiParser *MidiParser_createRO();
|
||||
extern MidiParser *MidiParser_createEUP();
|
||||
|
||||
|
@ -85,7 +90,7 @@ Player::~Player() {
|
|||
}
|
||||
}
|
||||
|
||||
bool Player::startSound(int sound, MidiDriver *midi) {
|
||||
bool Player::startSound(int sound, MidiDriver *midi, bool passThrough) {
|
||||
void *ptr;
|
||||
int i;
|
||||
|
||||
|
@ -111,6 +116,7 @@ bool Player::startSound(int sound, MidiDriver *midi) {
|
|||
_pan = 0;
|
||||
_transpose = 0;
|
||||
_detune = 0;
|
||||
_passThrough = passThrough;
|
||||
|
||||
for (i = 0; i < ARRAYSIZE(_parameterFaders); ++i)
|
||||
_parameterFaders[i].init();
|
||||
|
@ -152,8 +158,11 @@ void Player::clear() {
|
|||
debug (0, "Stopping music %d", _id);
|
||||
#endif
|
||||
|
||||
if (_parser)
|
||||
if (_parser) {
|
||||
_parser->unloadMusic();
|
||||
delete _parser;
|
||||
_parser = 0;
|
||||
}
|
||||
uninit_parts();
|
||||
_se->ImFireAllTriggers(_id);
|
||||
_active = false;
|
||||
|
@ -224,6 +233,11 @@ void Player::setSpeed(byte speed) {
|
|||
}
|
||||
|
||||
void Player::send(uint32 b) {
|
||||
if (_passThrough) {
|
||||
_midi->send (b);
|
||||
return;
|
||||
}
|
||||
|
||||
byte cmd = (byte)(b & 0xF0);
|
||||
byte chan = (byte)(b & 0x0F);
|
||||
byte param1 = (byte)((b >> 8) & 0xFF);
|
||||
|
@ -333,8 +347,12 @@ void Player::sysEx(byte *p, uint16 len) {
|
|||
byte buf[128];
|
||||
Part *part;
|
||||
|
||||
if (_passThrough) {
|
||||
_midi->sysEx (p, len);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check SysEx manufacturer.
|
||||
// Roland is 0x41
|
||||
a = *p++;
|
||||
--len;
|
||||
if (a != IMUSE_SYSEX_ID) {
|
||||
|
@ -346,6 +364,9 @@ void Player::sysEx(byte *p, uint16 len) {
|
|||
if (part->clearToTransmit())
|
||||
part->_instrument.send(part->_mc);
|
||||
}
|
||||
} else if (a == YM2612_SYSEX_ID) {
|
||||
// FM-Towns custom instrument definition
|
||||
_midi->sysEx_customInstrument (p[0], 'EUP ', p + 1);
|
||||
} else {
|
||||
warning("Unknown SysEx manufacturer 0x%02X", (int) a);
|
||||
}
|
||||
|
@ -1124,11 +1145,9 @@ uint32 Player::getBaseTempo() {
|
|||
}
|
||||
|
||||
void Player::metaEvent(byte type, byte *msg, uint16 len) {
|
||||
if (type == 0x2F) {
|
||||
_parser->unloadMusic();
|
||||
if (type == 0x2F)
|
||||
clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -32,6 +32,8 @@ namespace Scumm {
|
|||
*/
|
||||
class MidiParser_EUP : public MidiParser {
|
||||
protected:
|
||||
byte _instruments[6][50]; // Two extra bytes for SysEx ID and channel #
|
||||
byte _channel_instr[16];
|
||||
struct {
|
||||
byte *enable;
|
||||
int8 *channel;
|
||||
|
@ -69,14 +71,22 @@ void MidiParser_EUP::parseNextEvent (EventInfo &info) {
|
|||
// program changes to get a reasonable "one-size-
|
||||
// fits-all" sound until we actually support the
|
||||
// FM synthesis capabilities of FM Towns.
|
||||
if (_presend) {
|
||||
--_presend;
|
||||
for (; _presend < 32; ++_presend) {
|
||||
if (_channel_instr[_presend >> 1] == 0xFF) continue;
|
||||
info.start = pos;
|
||||
info.delta = 0;
|
||||
info.event = ((_presend & 1) ? 0xB0 : 0xC0) | (_presend >> 1);
|
||||
info.basic.param1 = ((_presend & 1) ? 7 : 0x38);
|
||||
info.basic.param2 = ((_presend & 1) ? 127 : 0);
|
||||
_presend = (_presend + 2) % 32;
|
||||
if (_presend & 1) {
|
||||
info.event = 0xB0;
|
||||
info.basic.param1 = 7;
|
||||
info.basic.param2 = 127;
|
||||
} else {
|
||||
byte *data = &_instruments[_channel_instr[_presend >> 1]][0];
|
||||
data[1] = _presend >> 1;
|
||||
info.event = 0xF0;
|
||||
info.ext.data = data;
|
||||
info.length = 48;
|
||||
}
|
||||
++_presend;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -89,7 +99,17 @@ void MidiParser_EUP::parseNextEvent (EventInfo &info) {
|
|||
channel = cmd & 0x0F;
|
||||
uint16 tick = (pos[2] | ((uint16) pos[3] << 7)) + _base_tick;
|
||||
int note = (int) pos[4] + _presets.transpose[preset];
|
||||
int volume = (int) pos[5] + _presets.volume[preset];
|
||||
int volume = (int) pos[5];
|
||||
// HACK: Loom-Towns distaff tracks seem to
|
||||
// contain zero-volume note events, so change
|
||||
// those to full volume.
|
||||
if (!volume)
|
||||
volume = 127;
|
||||
volume += _presets.volume[preset];
|
||||
if (volume > 127)
|
||||
volume = 127;
|
||||
else if (volume < 0)
|
||||
volume = 0;
|
||||
pos += 6;
|
||||
if (_presets.enable[preset]) {
|
||||
uint16 duration = pos[1] | (pos[2] << 4);
|
||||
|
@ -148,7 +168,12 @@ bool MidiParser_EUP::loadMusic (byte *data, uint32 size) {
|
|||
}
|
||||
|
||||
byte numInstruments = pos[16];
|
||||
pos += (16 + 2 + numInstruments * 48);
|
||||
pos += 16 + 2;
|
||||
for (int i = 0; i < numInstruments; ++i) {
|
||||
_instruments[i][0] = 0x7C;
|
||||
memcpy (&_instruments[i][2], pos, 48);
|
||||
pos += 48;
|
||||
}
|
||||
|
||||
// Load the prest pointers
|
||||
_presets.enable = pos;
|
||||
|
@ -161,6 +186,10 @@ bool MidiParser_EUP::loadMusic (byte *data, uint32 size) {
|
|||
pos += 32;
|
||||
|
||||
pos += 8; // Unknown bytes
|
||||
for (i = 0; i < 16; ++i)
|
||||
_channel_instr[i] = 0xFF;
|
||||
for (i = 0; i < 6; ++i)
|
||||
_channel_instr[pos[i]] = i;
|
||||
pos += 6; // Instrument-to-channel mapping (not supported yet)
|
||||
pos += 4; // Skip the music size for now.
|
||||
pos++; // Unknown byte
|
||||
|
@ -183,7 +212,7 @@ bool MidiParser_EUP::loadMusic (byte *data, uint32 size) {
|
|||
|
||||
void MidiParser_EUP::resetTracking() {
|
||||
MidiParser::resetTracking();
|
||||
_presend = 1;
|
||||
_presend = 0;
|
||||
_base_tick = 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -92,15 +92,15 @@ static const TargetSettings scumm_settings[] = {
|
|||
/* Scumm Version 3 */
|
||||
{"indy3EGA", "Indiana Jones and the Last Crusade", GID_INDY3, 3, MDT_PCSPK | MDT_ADLIB,
|
||||
GF_SMALL_HEADER | GF_SMALL_NAMES | GF_NO_SCALING | GF_USE_KEY | GF_16COLOR | GF_OLD_BUNDLE, "00.LFL"},
|
||||
{"indy3Towns", "Indiana Jones and the Last Crusade (FM Towns)", GID_INDY3, 3, MDT_ADLIB,
|
||||
{"indy3Towns", "Indiana Jones and the Last Crusade (FM Towns)", GID_INDY3, 3, MDT_TOWNS,
|
||||
GF_SMALL_HEADER | GF_SMALL_NAMES | GF_NO_SCALING | GF_OLD256 | GF_FEW_LOCALS | GF_FMTOWNS | GF_AUDIOTRACKS, "00.LFL"},
|
||||
{"indy3", "Indiana Jones and the Last Crusade (256)", GID_INDY3, 3, MDT_PCSPK | MDT_ADLIB,
|
||||
GF_SMALL_HEADER | GF_SMALL_NAMES | GF_NO_SCALING | GF_OLD256 | GF_FEW_LOCALS, "00.LFL"},
|
||||
{"zak256", "Zak McKracken and the Alien Mindbenders (256)", GID_ZAK256, 3, MDT_ADLIB,
|
||||
{"zak256", "Zak McKracken and the Alien Mindbenders (256)", GID_ZAK256, 3, MDT_TOWNS,
|
||||
GF_SMALL_HEADER | GF_SMALL_NAMES | GF_NO_SCALING | GF_OLD256 | GF_FMTOWNS | GF_AUDIOTRACKS, "00.LFL"},
|
||||
{"loom", "Loom", GID_LOOM, 3, MDT_PCSPK | MDT_ADLIB | MDT_NATIVE,
|
||||
GF_SMALL_HEADER | GF_SMALL_NAMES | GF_NO_SCALING | GF_USE_KEY | GF_16COLOR | GF_OLD_BUNDLE, "00.LFL"},
|
||||
{"loomTowns", "Loom (FM Towns)", GID_LOOM, 3, MDT_ADLIB,
|
||||
{"loomTowns", "Loom (FM Towns)", GID_LOOM, 3, MDT_TOWNS,
|
||||
GF_SMALL_HEADER | GF_SMALL_NAMES | GF_NO_SCALING | GF_OLD256 | GF_FMTOWNS | GF_AUDIOTRACKS, "00.LFL"},
|
||||
|
||||
/* Scumm Version 4 */
|
||||
|
@ -696,6 +696,8 @@ ScummEngine::ScummEngine(GameDetector *detector, OSystem *syst)
|
|||
_imuse->property(IMuse::PROP_LIMIT_PLAYERS, 1);
|
||||
_imuse->property(IMuse::PROP_RECYCLE_PLAYERS, 1);
|
||||
}
|
||||
if (_features & GF_FMTOWNS)
|
||||
_imuse->property(IMuse::PROP_DIRECT_PASSTHROUGH, 1);
|
||||
_imuse->set_music_volume(_sound->_sound_volume_music);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -54,6 +54,7 @@ static const struct MidiDriverDescription midiDrivers[] = {
|
|||
{"adlib", "Adlib", MD_ADLIB},
|
||||
{"pcspk", "PC Speaker", MD_PCSPK},
|
||||
{"pcjr", "IBM PCjr", MD_PCJR},
|
||||
{"towns", "FM Towns", MD_TOWNS},
|
||||
#endif
|
||||
|
||||
#if defined(__PALM_OS__)
|
||||
|
|
|
@ -44,8 +44,8 @@ enum {
|
|||
MD_ADLIB = 10,
|
||||
MD_PCSPK = 11,
|
||||
MD_PCJR = 12,
|
||||
|
||||
MD_YPA1 = 100 // palmos
|
||||
MD_TOWNS = 13,
|
||||
MD_YPA1 = 14 // PalmOS
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -144,16 +144,16 @@ public:
|
|||
|
||||
// Control Change messages
|
||||
virtual void controlChange (byte control, byte value) = 0;
|
||||
virtual void modulationWheel (byte value) = 0;
|
||||
virtual void volume (byte value) = 0;
|
||||
virtual void panPosition (byte value) = 0;
|
||||
virtual void modulationWheel (byte value) { controlChange (1, value); }
|
||||
virtual void volume (byte value) { controlChange (7, value); }
|
||||
virtual void panPosition (byte value) { controlChange (10, value); }
|
||||
virtual void pitchBendFactor (byte value) = 0;
|
||||
virtual void detune (byte value) = 0;
|
||||
virtual void priority (byte value) = 0;
|
||||
virtual void sustain (bool value) = 0;
|
||||
virtual void effectLevel (byte value) = 0;
|
||||
virtual void chorusLevel (byte value) = 0;
|
||||
virtual void allNotesOff() = 0;
|
||||
virtual void detune (byte value) { controlChange (17, value); }
|
||||
virtual void priority (byte value) { controlChange (18, value); }
|
||||
virtual void sustain (bool value) { controlChange (64, value ? 1 : 0); }
|
||||
virtual void effectLevel (byte value) { controlChange (91, value); }
|
||||
virtual void chorusLevel (byte value) { controlChange (93, value); }
|
||||
virtual void allNotesOff() { controlChange (123, 0); }
|
||||
|
||||
// SysEx messages
|
||||
virtual void sysEx_customInstrument (uint32 type, byte *instr) = 0;
|
||||
|
@ -169,6 +169,7 @@ extern MidiDriver *MidiDriver_QT_create();
|
|||
extern MidiDriver *MidiDriver_CORE_create();
|
||||
extern MidiDriver *MidiDriver_ETUDE_create();
|
||||
extern MidiDriver *MidiDriver_ALSA_create();
|
||||
extern MidiDriver *MidiDriver_YM2612_create(SoundMixer *mixer);
|
||||
extern MidiDriver *MidiDriver_YamahaPa1_create();
|
||||
|
||||
#endif
|
||||
|
|
|
@ -59,16 +59,7 @@ public:
|
|||
|
||||
// Control Change messages
|
||||
void controlChange (byte control, byte value);
|
||||
void modulationWheel (byte value) { controlChange (1, value); }
|
||||
void volume (byte value) { controlChange (7, value); }
|
||||
void panPosition (byte value) { controlChange (10, value); }
|
||||
void pitchBendFactor (byte value);
|
||||
void detune (byte value) { controlChange (17, value); }
|
||||
void priority (byte value) { controlChange (18, value); }
|
||||
void sustain (bool value) { controlChange (64, value ? 1 : 0); }
|
||||
void effectLevel (byte value) { controlChange (91, value); }
|
||||
void chorusLevel (byte value) { controlChange (93, value); }
|
||||
void allNotesOff() { controlChange (123, 0); }
|
||||
|
||||
// SysEx messages
|
||||
void sysEx_customInstrument (uint32 type, byte *instr);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue