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:
Jamieson Christian 2003-10-05 15:36:52 +00:00
parent 7174a32c8e
commit a722d0601e
16 changed files with 154 additions and 60 deletions

3
README
View file

@ -567,9 +567,10 @@ emulation. MIDI may not be available on all operating systems or may need
manual configuration. If you ARE using MIDI, you have several different
choices of output, depending on your operating system and configuration.
adlib - Uses internal Adlib Emulation (default)
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
View 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;
}

View file

@ -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 \

View file

@ -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.

View file

@ -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 {

View file

@ -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

View file

@ -355,6 +355,9 @@
<File
RelativePath="..\..\backends\midi\windows.cpp">
</File>
<File
RelativePath="..\..\backends\midi\ym2612.cpp">
</File>
</Filter>
</Filter>
<Filter

View file

@ -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);
_recycle_players = (value != 0);
break;
case IMuse::PROP_DIRECT_PASSTHROUGH:
_direct_passthrough = (value != 0);
break;
}
return 0;
}

View file

@ -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);

View file

@ -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:
@ -366,8 +363,9 @@ protected:
int _tempoFactor;
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?
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;

View file

@ -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,10 +1145,8 @@ uint32 Player::getBaseTempo() {
}
void Player::metaEvent(byte type, byte *msg, uint16 len) {
if (type == 0x2F) {
_parser->unloadMusic();
if (type == 0x2F)
clear();
}
}

View file

@ -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;
}

View file

@ -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);
}
}

View file

@ -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__)

View file

@ -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

View file

@ -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);