mirror of
https://github.com/libretro/scummvm.git
synced 2025-02-15 00:27:31 +00:00
Merge pull request #279 from lordhoto/mi2-mac-sound
Add support for Monkey Island 2 Mac sound.
This commit is contained in:
commit
4522d182d4
@ -164,7 +164,7 @@ bool IMuseInternal::isMT32(int sound) {
|
||||
return true;
|
||||
|
||||
case MKTAG('M', 'A', 'C', ' '): // Occurs in the Mac version of FOA and MI2
|
||||
return true;
|
||||
return false;
|
||||
|
||||
case MKTAG('G', 'M', 'D', ' '):
|
||||
return false;
|
||||
@ -226,6 +226,45 @@ bool IMuseInternal::isMIDI(int sound) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IMuseInternal::supportsPercussion(int sound) {
|
||||
byte *ptr = g_scumm->_res->_types[rtSound][sound]._address;
|
||||
if (ptr == NULL)
|
||||
return false;
|
||||
|
||||
uint32 tag = READ_BE_UINT32(ptr);
|
||||
switch (tag) {
|
||||
case MKTAG('A', 'D', 'L', ' '):
|
||||
case MKTAG('A', 'S', 'F', 'X'): // Special AD class for old AdLib sound effects
|
||||
case MKTAG('S', 'P', 'K', ' '):
|
||||
return false;
|
||||
|
||||
case MKTAG('A', 'M', 'I', ' '):
|
||||
case MKTAG('R', 'O', 'L', ' '):
|
||||
return true;
|
||||
|
||||
case MKTAG('M', 'A', 'C', ' '): // Occurs in the Mac version of FOA and MI2
|
||||
// This is MIDI, i.e. uses MIDI style program changes, but without a
|
||||
// special percussion channel.
|
||||
return false;
|
||||
|
||||
case MKTAG('G', 'M', 'D', ' '):
|
||||
case MKTAG('M', 'I', 'D', 'I'): // Occurs in Sam & Max
|
||||
return true;
|
||||
}
|
||||
|
||||
// Old style 'RO' has equivalent properties to 'ROL'
|
||||
if (ptr[0] == 'R' && ptr[1] == 'O')
|
||||
return true;
|
||||
// Euphony tracks show as 'SO' and have equivalent properties to 'ADL'
|
||||
// FIXME: Right now we're pretending it's GM.
|
||||
if (ptr[4] == 'S' && ptr[5] == 'O')
|
||||
return true;
|
||||
|
||||
error("Unknown music type: '%c%c%c%c'", (char)tag >> 24, (char)tag >> 16, (char)tag >> 8, (char)tag);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
MidiDriver *IMuseInternal::getBestMidiDriver(int sound) {
|
||||
MidiDriver *driver = NULL;
|
||||
|
||||
|
@ -204,6 +204,7 @@ protected:
|
||||
|
||||
bool _isMT32;
|
||||
bool _isMIDI;
|
||||
bool _supportsPercussion;
|
||||
|
||||
protected:
|
||||
// Player part
|
||||
@ -458,6 +459,7 @@ protected:
|
||||
byte *findStartOfSound(int sound, int ct = (kMThd | kFORM));
|
||||
bool isMT32(int sound);
|
||||
bool isMIDI(int sound);
|
||||
bool supportsPercussion(int sound);
|
||||
int get_queue_sound_status(int sound) const;
|
||||
void handle_marker(uint id, byte data);
|
||||
int get_channel_volume(uint a);
|
||||
|
@ -27,6 +27,7 @@
|
||||
#include "common/util.h"
|
||||
#include "scumm/imuse/imuse_internal.h"
|
||||
#include "scumm/saveload.h"
|
||||
#include "scumm/scumm.h"
|
||||
|
||||
namespace Scumm {
|
||||
|
||||
@ -365,7 +366,17 @@ void Part::set_instrument(uint b) {
|
||||
_bank = (byte)(b >> 8);
|
||||
if (_bank)
|
||||
error("Non-zero instrument bank selection. Please report this");
|
||||
_instrument.program((byte)b, _player->isMT32());
|
||||
// HACK: Horrible hack to allow tracing of program change source.
|
||||
// The Mac version of Monkey Island 2 uses a different program "bank"
|
||||
// when it gets program change events through the iMuse SysEx handler.
|
||||
// We emulate this by introducing a special instrument, which sets
|
||||
// the instrument via sysEx_customInstrument. This seems to be
|
||||
// exclusively used for special sound effects like the "spit" sound.
|
||||
if (g_scumm->_game.id == GID_MONKEY2 && g_scumm->_game.platform == Common::kPlatformMacintosh) {
|
||||
_instrument.macSfx(b);
|
||||
} else {
|
||||
_instrument.program((byte)b, _player->isMT32());
|
||||
}
|
||||
if (clearToTransmit())
|
||||
_instrument.send(_mc);
|
||||
}
|
||||
|
@ -78,6 +78,7 @@ Player::Player() :
|
||||
_speed(128),
|
||||
_isMT32(false),
|
||||
_isMIDI(false),
|
||||
_supportsPercussion(false),
|
||||
_se(0),
|
||||
_vol_chan(0) {
|
||||
}
|
||||
@ -103,6 +104,7 @@ bool Player::startSound(int sound, MidiDriver *midi) {
|
||||
|
||||
_isMT32 = _se->isMT32(sound);
|
||||
_isMIDI = _se->isMIDI(sound);
|
||||
_supportsPercussion = _se->supportsPercussion(sound);
|
||||
|
||||
_parts = NULL;
|
||||
_active = true;
|
||||
@ -386,6 +388,8 @@ void Player::sysEx(const byte *p, uint16 len) {
|
||||
// SysEx manufacturer 0x97 has been spotted in the
|
||||
// Monkey Island 2 AdLib music, so don't make this a
|
||||
// fatal error. See bug #1481383.
|
||||
// The Macintosh version of Monkey Island 2 simply
|
||||
// ignores these SysEx events too.
|
||||
if (a == 0)
|
||||
warning("Unknown SysEx manufacturer 0x00 0x%02X 0x%02X", p[0], p[1]);
|
||||
else
|
||||
@ -1009,6 +1013,7 @@ void Player::fixAfterLoad() {
|
||||
_parser->jumpToTick(_music_tick); // start_seq_sound already switched tracks
|
||||
_isMT32 = _se->isMT32(_id);
|
||||
_isMIDI = _se->isMIDI(_id);
|
||||
_supportsPercussion = _se->supportsPercussion(_id);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -278,6 +278,21 @@ private:
|
||||
byte _instrument[23];
|
||||
};
|
||||
|
||||
class Instrument_MacSfx : public InstrumentInternal {
|
||||
private:
|
||||
byte _program;
|
||||
|
||||
public:
|
||||
Instrument_MacSfx(byte program);
|
||||
Instrument_MacSfx(Serializer *s);
|
||||
void saveOrLoad(Serializer *s);
|
||||
void send(MidiChannel *mc);
|
||||
void copy_to(Instrument *dest) { dest->macSfx(_program); }
|
||||
bool is_valid() {
|
||||
return (_program < 128);
|
||||
}
|
||||
};
|
||||
|
||||
////////////////////////////////////////
|
||||
//
|
||||
// Instrument class members
|
||||
@ -326,6 +341,14 @@ void Instrument::pcspk(const byte *instrument) {
|
||||
_instrument = new Instrument_PcSpk(instrument);
|
||||
}
|
||||
|
||||
void Instrument::macSfx(byte prog) {
|
||||
clear();
|
||||
if (prog > 127)
|
||||
return;
|
||||
_type = itMacSfx;
|
||||
_instrument = new Instrument_MacSfx(prog);
|
||||
}
|
||||
|
||||
void Instrument::saveOrLoad(Serializer *s) {
|
||||
if (s->isSaving()) {
|
||||
s->saveByte(_type);
|
||||
@ -349,6 +372,9 @@ void Instrument::saveOrLoad(Serializer *s) {
|
||||
case itPcSpk:
|
||||
_instrument = new Instrument_PcSpk(s);
|
||||
break;
|
||||
case itMacSfx:
|
||||
_instrument = new Instrument_MacSfx(s);
|
||||
break;
|
||||
default:
|
||||
warning("No known instrument classification #%d", (int)_type);
|
||||
_type = itNone;
|
||||
@ -528,4 +554,38 @@ void Instrument_PcSpk::send(MidiChannel *mc) {
|
||||
mc->sysEx_customInstrument('SPK ', (byte *)&_instrument);
|
||||
}
|
||||
|
||||
////////////////////////////////////////
|
||||
//
|
||||
// Instrument_MacSfx class members
|
||||
//
|
||||
////////////////////////////////////////
|
||||
|
||||
Instrument_MacSfx::Instrument_MacSfx(byte program) :
|
||||
_program(program) {
|
||||
if (program > 127) {
|
||||
_program = 255;
|
||||
}
|
||||
}
|
||||
|
||||
Instrument_MacSfx::Instrument_MacSfx(Serializer *s) {
|
||||
_program = 255;
|
||||
if (!s->isSaving()) {
|
||||
saveOrLoad(s);
|
||||
}
|
||||
}
|
||||
|
||||
void Instrument_MacSfx::saveOrLoad(Serializer *s) {
|
||||
if (s->isSaving()) {
|
||||
s->saveByte(_program);
|
||||
} else {
|
||||
_program = s->loadByte();
|
||||
}
|
||||
}
|
||||
|
||||
void Instrument_MacSfx::send(MidiChannel *mc) {
|
||||
if (_program > 127) {
|
||||
return;
|
||||
}
|
||||
mc->sysEx_customInstrument('MAC ', &_program);
|
||||
}
|
||||
} // End of namespace Scumm
|
||||
|
@ -52,7 +52,8 @@ public:
|
||||
itProgram = 1,
|
||||
itAdLib = 2,
|
||||
itRoland = 3,
|
||||
itPcSpk = 4
|
||||
itPcSpk = 4,
|
||||
itMacSfx = 5
|
||||
};
|
||||
|
||||
Instrument() : _type(0), _instrument(0) { }
|
||||
@ -72,6 +73,7 @@ public:
|
||||
void adlib(const byte *instrument);
|
||||
void roland(const byte *instrument);
|
||||
void pcspk(const byte *instrument);
|
||||
void macSfx(byte program);
|
||||
|
||||
byte getType() { return _type; }
|
||||
bool isValid() { return (_instrument ? _instrument->is_valid() : false); }
|
||||
|
510
engines/scumm/imuse/mac_m68k.cpp
Normal file
510
engines/scumm/imuse/mac_m68k.cpp
Normal file
@ -0,0 +1,510 @@
|
||||
/* 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.
|
||||
*/
|
||||
|
||||
#include "scumm/imuse/mac_m68k.h"
|
||||
|
||||
#include "common/util.h"
|
||||
#include "common/macresman.h"
|
||||
#include "common/stream.h"
|
||||
|
||||
namespace Scumm {
|
||||
|
||||
MacM68kDriver::MacM68kDriver(Audio::Mixer *mixer)
|
||||
: MidiDriver_Emulated(mixer) {
|
||||
}
|
||||
|
||||
MacM68kDriver::~MacM68kDriver() {
|
||||
}
|
||||
|
||||
int MacM68kDriver::open() {
|
||||
if (_isOpen) {
|
||||
return MERR_ALREADY_OPEN;
|
||||
}
|
||||
|
||||
const int error = MidiDriver_Emulated::open();
|
||||
if (error) {
|
||||
return error;
|
||||
}
|
||||
|
||||
for (uint i = 0; i < ARRAYSIZE(_channels); ++i) {
|
||||
_channels[i].init(this, i);
|
||||
}
|
||||
|
||||
memset(_voiceChannels, 0, sizeof(_voiceChannels));
|
||||
_lastUsedVoiceChannel = 0;
|
||||
|
||||
loadAllInstruments();
|
||||
|
||||
_pitchTable[116] = 1664510;
|
||||
_pitchTable[117] = 1763487;
|
||||
_pitchTable[118] = 1868350;
|
||||
_pitchTable[119] = 1979447;
|
||||
_pitchTable[120] = 2097152;
|
||||
_pitchTable[121] = 2221855;
|
||||
_pitchTable[122] = 2353973;
|
||||
_pitchTable[123] = 2493948;
|
||||
_pitchTable[124] = 2642246;
|
||||
_pitchTable[125] = 2799362;
|
||||
_pitchTable[126] = 2965820;
|
||||
_pitchTable[127] = 3142177;
|
||||
for (int i = 115; i >= 0; --i) {
|
||||
_pitchTable[i] = _pitchTable[i + 12] / 2;
|
||||
}
|
||||
|
||||
_volumeTable = new byte[8192];
|
||||
for (int i = 0; i < 32; ++i) {
|
||||
for (int j = 0; j < 256; ++j) {
|
||||
_volumeTable[i * 256 + j] = ((-128 + j) * _volumeBaseTable[i]) / 127 - 128;
|
||||
}
|
||||
}
|
||||
|
||||
_mixBuffer = 0;
|
||||
_mixBufferLength = 0;
|
||||
|
||||
// We set the output sound type to music here to allow sound volume
|
||||
// adjustment. The drawback here is that we can not control the music and
|
||||
// sfx separately here. But the AdLib output has the same issue so it
|
||||
// should not be that bad.
|
||||
_mixer->playStream(Audio::Mixer::kMusicSoundType, &_mixerSoundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void MacM68kDriver::close() {
|
||||
if (!_isOpen) {
|
||||
return;
|
||||
}
|
||||
|
||||
_mixer->stopHandle(_mixerSoundHandle);
|
||||
_isOpen = false;
|
||||
for (InstrumentMap::iterator i = _instruments.begin(); i != _instruments.end(); ++i) {
|
||||
delete[] i->_value.data;
|
||||
}
|
||||
_instruments.clear();
|
||||
delete[] _volumeTable;
|
||||
_volumeTable = 0;
|
||||
delete[] _mixBuffer;
|
||||
_mixBuffer = 0;
|
||||
_mixBufferLength = 0;
|
||||
}
|
||||
|
||||
void MacM68kDriver::send(uint32 d) {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
void MacM68kDriver::sysEx_customInstrument(byte channel, uint32 type, const byte *instr) {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
MidiChannel *MacM68kDriver::allocateChannel() {
|
||||
for (uint i = 0; i < ARRAYSIZE(_channels); ++i) {
|
||||
if (_channels[i].allocate()) {
|
||||
return &_channels[i];
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
MacM68kDriver::Instrument MacM68kDriver::getInstrument(int idx) const {
|
||||
InstrumentMap::const_iterator i = _instruments.find(idx);
|
||||
if (i != _instruments.end()) {
|
||||
return i->_value;
|
||||
} else {
|
||||
return _defaultInstrument;
|
||||
}
|
||||
}
|
||||
|
||||
void MacM68kDriver::generateSamples(int16 *buf, int len) {
|
||||
int silentChannels = 0;
|
||||
|
||||
if (_mixBufferLength < len) {
|
||||
delete[] _mixBuffer;
|
||||
|
||||
_mixBufferLength = len;
|
||||
_mixBuffer = new int[_mixBufferLength];
|
||||
assert(_mixBuffer);
|
||||
}
|
||||
memset(_mixBuffer, 0, sizeof(int) * _mixBufferLength);
|
||||
|
||||
for (int i = 0; i < kChannelCount; ++i) {
|
||||
OutputChannel &out = _voiceChannels[i].out;
|
||||
if (out.isFinished) {
|
||||
++silentChannels;
|
||||
continue;
|
||||
}
|
||||
|
||||
byte *volumeTable = &_volumeTable[(out.volume / 4) * 256];
|
||||
int *buffer = _mixBuffer;
|
||||
|
||||
int samplesLeft = len;
|
||||
while (samplesLeft) {
|
||||
out.subPos += out.pitchModifier;
|
||||
while (out.subPos >= 0x10000) {
|
||||
out.subPos -= 0x10000;
|
||||
out.instrument++;
|
||||
}
|
||||
|
||||
if (out.instrument >= out.end) {
|
||||
if (!out.start) {
|
||||
break;
|
||||
}
|
||||
|
||||
out.instrument = out.start;
|
||||
out.subPos = 0;
|
||||
}
|
||||
|
||||
*buffer++ += volumeTable[*out.instrument];
|
||||
--samplesLeft;
|
||||
}
|
||||
|
||||
if (samplesLeft) {
|
||||
out.isFinished = true;
|
||||
while (samplesLeft--) {
|
||||
*buffer++ += 0x80;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const int *buffer = _mixBuffer;
|
||||
const int silenceAdd = silentChannels << 7;
|
||||
while (len--) {
|
||||
*buf++ = (((*buffer++ + silenceAdd) >> 3) << 8) ^ 0x8000;
|
||||
}
|
||||
}
|
||||
|
||||
void MacM68kDriver::loadAllInstruments() {
|
||||
Common::MacResManager resource;
|
||||
if (resource.open("iMUSE Setups")) {
|
||||
for (int i = 0x3E7; i < 0x468; ++i) {
|
||||
Common::SeekableReadStream *stream = resource.getResource(MKTAG('s', 'n', 'd', ' '), i);
|
||||
if (stream) {
|
||||
addInstrument(i, stream);
|
||||
delete stream;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0x7D0; i < 0x8D0; ++i) {
|
||||
Common::SeekableReadStream *stream = resource.getResource(MKTAG('s', 'n', 'd', ' '), i);
|
||||
if (stream) {
|
||||
addInstrument(i, stream);
|
||||
delete stream;
|
||||
}
|
||||
}
|
||||
|
||||
InstrumentMap::iterator inst = _instruments.find(kDefaultInstrument);
|
||||
if (inst != _instruments.end()) {
|
||||
_defaultInstrument = inst->_value;
|
||||
} else {
|
||||
error("MacM68kDriver::loadAllInstruments: Could not load default instrument");
|
||||
}
|
||||
} else {
|
||||
error("MacM68kDriver::loadAllInstruments: Could not load \"iMUSE Setups\"");
|
||||
}
|
||||
}
|
||||
|
||||
void MacM68kDriver::addInstrument(int idx, Common::SeekableReadStream *data) {
|
||||
// We parse the "SND" files manually here, since we need special data
|
||||
// from their header and need to work on them raw while mixing.
|
||||
data->skip(2);
|
||||
int count = data->readUint16BE();
|
||||
data->skip(2 * (3 * count));
|
||||
count = data->readUint16BE();
|
||||
data->skip(2 * (4 * count));
|
||||
|
||||
Instrument inst;
|
||||
// Skip (optional) pointer to data
|
||||
data->skip(4);
|
||||
inst.length = data->readUint32BE();
|
||||
inst.sampleRate = data->readUint32BE();
|
||||
inst.loopStart = data->readUint32BE();
|
||||
inst.loopEnd = data->readUint32BE();
|
||||
// Skip encoding
|
||||
data->skip(1);
|
||||
inst.baseFrequency = data->readByte();
|
||||
|
||||
inst.data = new byte[inst.length];
|
||||
assert(inst.data);
|
||||
data->read(inst.data, inst.length);
|
||||
_instruments[idx] = inst;
|
||||
}
|
||||
|
||||
void MacM68kDriver::setPitch(OutputChannel *out, int frequency) {
|
||||
out->frequency = frequency;
|
||||
out->isFinished = false;
|
||||
|
||||
const int pitchIdx = (frequency >> 7) + 60 - out->baseFrequency;
|
||||
assert(pitchIdx >= 0);
|
||||
|
||||
const int low7Bits = frequency & 0x7F;
|
||||
if (low7Bits) {
|
||||
out->pitchModifier = _pitchTable[pitchIdx] + (((_pitchTable[pitchIdx + 1] - _pitchTable[pitchIdx]) * low7Bits) >> 7);
|
||||
} else {
|
||||
out->pitchModifier = _pitchTable[pitchIdx];
|
||||
}
|
||||
}
|
||||
|
||||
void MacM68kDriver::VoiceChannel::off() {
|
||||
if (out.start) {
|
||||
out.isFinished = true;
|
||||
}
|
||||
|
||||
part->removeVoice(this);
|
||||
part = 0;
|
||||
}
|
||||
|
||||
void MacM68kDriver::MidiChannel_MacM68k::release() {
|
||||
_allocated = false;
|
||||
while (_voice) {
|
||||
_voice->off();
|
||||
}
|
||||
}
|
||||
|
||||
void MacM68kDriver::MidiChannel_MacM68k::send(uint32 b) {
|
||||
uint8 type = b & 0xF0;
|
||||
uint8 p1 = (b >> 8) & 0xFF;
|
||||
uint8 p2 = (b >> 16) & 0xFF;
|
||||
|
||||
switch (type) {
|
||||
case 0x80:
|
||||
noteOff(p1);
|
||||
break;
|
||||
|
||||
case 0x90:
|
||||
if (p2) {
|
||||
noteOn(p1, p2);
|
||||
} else {
|
||||
noteOff(p1);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0xB0:
|
||||
controlChange(p1, p2);
|
||||
break;
|
||||
|
||||
case 0xE0:
|
||||
pitchBend((p1 | (p2 << 7)) - 0x2000);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void MacM68kDriver::MidiChannel_MacM68k::noteOff(byte note) {
|
||||
for (VoiceChannel *i = _voice; i; i = i->next) {
|
||||
if (i->note == note) {
|
||||
if (_sustain) {
|
||||
i->sustainNoteOff = true;
|
||||
} else {
|
||||
i->off();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MacM68kDriver::MidiChannel_MacM68k::noteOn(byte note, byte velocity) {
|
||||
// Do not start a not unless there is an instrument set up
|
||||
if (!_instrument.data) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Allocate a voice channel
|
||||
VoiceChannel *voice = _owner->allocateVoice(_priority);
|
||||
if (!voice) {
|
||||
return;
|
||||
}
|
||||
addVoice(voice);
|
||||
|
||||
voice->note = note;
|
||||
// This completly ignores the note's volume, but is in accordance
|
||||
// to the original.
|
||||
voice->out.volume = _volume;
|
||||
|
||||
// Set up the instrument data
|
||||
voice->out.baseFrequency = _instrument.baseFrequency;
|
||||
voice->out.soundStart = _instrument.data;
|
||||
voice->out.soundEnd = _instrument.data + _instrument.length;
|
||||
if (_instrument.loopEnd && _instrument.loopEnd - 12 > _instrument.loopStart) {
|
||||
voice->out.loopStart = _instrument.data + _instrument.loopStart;
|
||||
voice->out.loopEnd = _instrument.data + _instrument.loopEnd;
|
||||
} else {
|
||||
voice->out.loopStart = 0;
|
||||
voice->out.loopEnd = voice->out.soundEnd;
|
||||
}
|
||||
|
||||
voice->out.start = voice->out.loopStart;
|
||||
voice->out.end = voice->out.loopEnd;
|
||||
|
||||
// Set up the pitch
|
||||
_owner->setPitch(&voice->out, (note << 7) + _pitchBend);
|
||||
|
||||
// Set up the sample position
|
||||
voice->out.instrument = voice->out.soundStart;
|
||||
voice->out.subPos = 0;
|
||||
}
|
||||
|
||||
void MacM68kDriver::MidiChannel_MacM68k::programChange(byte program) {
|
||||
_instrument = _owner->getInstrument(program + kProgramChangeBase);
|
||||
}
|
||||
|
||||
void MacM68kDriver::MidiChannel_MacM68k::pitchBend(int16 bend) {
|
||||
_pitchBend = (bend * _pitchBendFactor) >> 6;
|
||||
for (VoiceChannel *i = _voice; i; i = i->next) {
|
||||
_owner->setPitch(&i->out, (i->note << 7) + _pitchBend);
|
||||
}
|
||||
}
|
||||
|
||||
void MacM68kDriver::MidiChannel_MacM68k::controlChange(byte control, byte value) {
|
||||
switch (control) {
|
||||
// volume change
|
||||
case 7:
|
||||
_volume = value;
|
||||
for (VoiceChannel *i = _voice; i; i = i->next) {
|
||||
i->out.volume = value;
|
||||
i->out.isFinished = false;
|
||||
}
|
||||
break;
|
||||
|
||||
// sustain
|
||||
case 64:
|
||||
_sustain = value;
|
||||
if (!_sustain) {
|
||||
for (VoiceChannel *i = _voice; i; i = i->next) {
|
||||
if (i->sustainNoteOff) {
|
||||
i->off();
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
// all notes off
|
||||
case 123:
|
||||
for (VoiceChannel *i = _voice; i; i = i->next) {
|
||||
i->off();
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void MacM68kDriver::MidiChannel_MacM68k::pitchBendFactor(byte value) {
|
||||
_pitchBendFactor = value;
|
||||
}
|
||||
|
||||
void MacM68kDriver::MidiChannel_MacM68k::priority(byte value) {
|
||||
_priority = value;
|
||||
}
|
||||
|
||||
void MacM68kDriver::MidiChannel_MacM68k::sysEx_customInstrument(uint32 type, const byte *instr) {
|
||||
assert(instr);
|
||||
if (type == 'MAC ') {
|
||||
_instrument = _owner->getInstrument(*instr + kSysExBase);
|
||||
}
|
||||
}
|
||||
|
||||
void MacM68kDriver::MidiChannel_MacM68k::init(MacM68kDriver *owner, byte channel) {
|
||||
_owner = owner;
|
||||
_number = channel;
|
||||
_allocated = false;
|
||||
}
|
||||
|
||||
bool MacM68kDriver::MidiChannel_MacM68k::allocate() {
|
||||
if (_allocated) {
|
||||
return false;
|
||||
}
|
||||
|
||||
_allocated = true;
|
||||
_voice = 0;
|
||||
_priority = 0;
|
||||
memset(&_instrument, 0, sizeof(_instrument));
|
||||
_pitchBend = 0;
|
||||
_pitchBendFactor = 0;
|
||||
_volume = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
void MacM68kDriver::MidiChannel_MacM68k::addVoice(VoiceChannel *voice) {
|
||||
voice->next = _voice;
|
||||
voice->prev = 0;
|
||||
voice->part = this;
|
||||
if (_voice) {
|
||||
_voice->prev = voice;
|
||||
}
|
||||
_voice = voice;
|
||||
}
|
||||
|
||||
void MacM68kDriver::MidiChannel_MacM68k::removeVoice(VoiceChannel *voice) {
|
||||
VoiceChannel *i = _voice;
|
||||
while (i && i != voice) {
|
||||
i = i->next;
|
||||
}
|
||||
|
||||
if (i) {
|
||||
if (i->next) {
|
||||
i->next->prev = i->prev;
|
||||
}
|
||||
|
||||
if (i->prev) {
|
||||
i->prev->next = i->next;
|
||||
} else {
|
||||
_voice = i->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MacM68kDriver::VoiceChannel *MacM68kDriver::allocateVoice(int priority) {
|
||||
VoiceChannel *channel = 0;
|
||||
for (int i = 0; i < kChannelCount; ++i) {
|
||||
if (++_lastUsedVoiceChannel == kChannelCount) {
|
||||
_lastUsedVoiceChannel = 0;
|
||||
}
|
||||
|
||||
VoiceChannel *cur = &_voiceChannels[_lastUsedVoiceChannel];
|
||||
if (!cur->part) {
|
||||
memset(cur, 0, sizeof(*cur));
|
||||
return cur;
|
||||
} else if (!cur->next) {
|
||||
if (cur->part->_priority <= priority) {
|
||||
priority = cur->part->_priority;
|
||||
channel = cur;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (channel) {
|
||||
channel->off();
|
||||
memset(channel, 0, sizeof(*channel));
|
||||
}
|
||||
|
||||
return channel;
|
||||
}
|
||||
|
||||
const int MacM68kDriver::_volumeBaseTable[32] = {
|
||||
0, 0, 1, 1, 2, 3, 5, 6,
|
||||
8, 11, 13, 16, 19, 22, 26, 30,
|
||||
34, 38, 43, 48, 53, 58, 64, 70,
|
||||
76, 83, 89, 96, 104, 111, 119, 127
|
||||
};
|
||||
|
||||
} // End of namespace Scumm
|
177
engines/scumm/imuse/mac_m68k.h
Normal file
177
engines/scumm/imuse/mac_m68k.h
Normal file
@ -0,0 +1,177 @@
|
||||
/* 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 SCUMM_IMUSE_MAC_M68K_H
|
||||
#define SCUMM_IMUSE_MAC_M68K_H
|
||||
|
||||
#include "audio/softsynth/emumidi.h"
|
||||
|
||||
#include "common/hashmap.h"
|
||||
|
||||
namespace Common {
|
||||
class SeekableReadStream;
|
||||
}
|
||||
|
||||
namespace Scumm {
|
||||
|
||||
class MacM68kDriver : public MidiDriver_Emulated {
|
||||
friend class MidiChannel_MacM68k;
|
||||
public:
|
||||
MacM68kDriver(Audio::Mixer *mixer);
|
||||
~MacM68kDriver();
|
||||
|
||||
virtual int open();
|
||||
virtual void close();
|
||||
|
||||
virtual void send(uint32 d);
|
||||
virtual void sysEx_customInstrument(byte channel, uint32 type, const byte *instr);
|
||||
|
||||
virtual MidiChannel *allocateChannel();
|
||||
virtual MidiChannel *getPercussionChannel() { return 0; }
|
||||
|
||||
virtual bool isStereo() const { return false; }
|
||||
virtual int getRate() const {
|
||||
// The original is using a frequency of approx. 22254.54546 here.
|
||||
// To be precise it uses the 16.16 fixed point value 0x56EE8BA3.
|
||||
return 22254;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void generateSamples(int16 *buf, int len);
|
||||
virtual void onTimer() {}
|
||||
|
||||
private:
|
||||
int *_mixBuffer;
|
||||
int _mixBufferLength;
|
||||
|
||||
struct Instrument {
|
||||
uint length;
|
||||
uint sampleRate;
|
||||
uint loopStart;
|
||||
uint loopEnd;
|
||||
int baseFrequency;
|
||||
|
||||
byte *data;
|
||||
};
|
||||
|
||||
enum {
|
||||
kDefaultInstrument = 0x3E7,
|
||||
kProgramChangeBase = 0x3E8,
|
||||
kSysExBase = 0x7D0
|
||||
};
|
||||
|
||||
Instrument getInstrument(int idx) const;
|
||||
typedef Common::HashMap<int, Instrument> InstrumentMap;
|
||||
InstrumentMap _instruments;
|
||||
Instrument _defaultInstrument;
|
||||
void loadAllInstruments();
|
||||
void addInstrument(int idx, Common::SeekableReadStream *data);
|
||||
|
||||
struct OutputChannel {
|
||||
int pitchModifier;
|
||||
|
||||
const byte *instrument;
|
||||
uint subPos;
|
||||
|
||||
const byte *start;
|
||||
const byte *end;
|
||||
|
||||
const byte *soundStart;
|
||||
const byte *soundEnd;
|
||||
const byte *loopStart;
|
||||
const byte *loopEnd;
|
||||
|
||||
int frequency;
|
||||
int volume;
|
||||
|
||||
bool isFinished;
|
||||
|
||||
int baseFrequency;
|
||||
};
|
||||
|
||||
void setPitch(OutputChannel *out, int frequency);
|
||||
int _pitchTable[128];
|
||||
|
||||
byte *_volumeTable;
|
||||
static const int _volumeBaseTable[32];
|
||||
|
||||
class MidiChannel_MacM68k;
|
||||
|
||||
struct VoiceChannel {
|
||||
MidiChannel_MacM68k *part;
|
||||
VoiceChannel *prev, *next;
|
||||
int channel;
|
||||
int note;
|
||||
bool sustainNoteOff;
|
||||
OutputChannel out;
|
||||
|
||||
void off();
|
||||
};
|
||||
|
||||
class MidiChannel_MacM68k : public MidiChannel {
|
||||
friend class MacM68kDriver;
|
||||
public:
|
||||
virtual MidiDriver *device() { return _owner; }
|
||||
virtual byte getNumber() { return _number; }
|
||||
virtual void release();
|
||||
|
||||
virtual void send(uint32 b);
|
||||
virtual void noteOff(byte note);
|
||||
virtual void noteOn(byte note, byte velocity);
|
||||
virtual void programChange(byte program);
|
||||
virtual void pitchBend(int16 bend);
|
||||
virtual void controlChange(byte control, byte value);
|
||||
virtual void pitchBendFactor(byte value);
|
||||
virtual void priority(byte value);
|
||||
virtual void sysEx_customInstrument(uint32 type, const byte *instr);
|
||||
|
||||
void init(MacM68kDriver *owner, byte channel);
|
||||
bool allocate();
|
||||
|
||||
void addVoice(VoiceChannel *voice);
|
||||
void removeVoice(VoiceChannel *voice);
|
||||
private:
|
||||
MacM68kDriver *_owner;
|
||||
bool _allocated;
|
||||
int _number;
|
||||
|
||||
VoiceChannel *_voice;
|
||||
int _priority;
|
||||
int _sustain;
|
||||
Instrument _instrument;
|
||||
int _pitchBend;
|
||||
int _pitchBendFactor;
|
||||
int _volume;
|
||||
};
|
||||
|
||||
MidiChannel_MacM68k _channels[32];
|
||||
|
||||
enum {
|
||||
kChannelCount = 8
|
||||
};
|
||||
VoiceChannel _voiceChannels[kChannelCount];
|
||||
int _lastUsedVoiceChannel;
|
||||
VoiceChannel *allocateVoice(int priority);
|
||||
};
|
||||
|
||||
} // End of namespace Scumm
|
||||
|
||||
#endif
|
@ -71,7 +71,7 @@ void sysexHandler_Scumm(Player *player, const byte *msg, uint16 len) {
|
||||
part->set_pri(buf[2]);
|
||||
part->volume(buf[3]);
|
||||
part->set_pan(buf[4]);
|
||||
part->_percussion = player->_isMIDI ? ((buf[5] & 0x80) > 0) : false;
|
||||
part->_percussion = player->_supportsPercussion ? ((buf[5] & 0x80) > 0) : false;
|
||||
part->set_transpose(buf[5]);
|
||||
part->set_detune(buf[6]);
|
||||
part->pitchBendFactor(buf[7]);
|
||||
|
@ -27,6 +27,7 @@ MODULE_OBJS := \
|
||||
imuse/imuse_part.o \
|
||||
imuse/imuse_player.o \
|
||||
imuse/instrument.o \
|
||||
imuse/mac_m68k.o \
|
||||
imuse/pcspk.o \
|
||||
imuse/sysex_samnmax.o \
|
||||
imuse/sysex_scumm.o \
|
||||
|
@ -47,7 +47,7 @@ namespace Scumm {
|
||||
* only saves/loads those which are valid for the version of the savegame
|
||||
* which is being loaded/saved currently.
|
||||
*/
|
||||
#define CURRENT_VER 92
|
||||
#define CURRENT_VER 93
|
||||
|
||||
/**
|
||||
* An auxillary macro, used to specify savegame versions. We use this instead
|
||||
|
@ -73,6 +73,7 @@
|
||||
#include "scumm/util.h"
|
||||
#include "scumm/verbs.h"
|
||||
#include "scumm/imuse/pcspk.h"
|
||||
#include "scumm/imuse/mac_m68k.h"
|
||||
|
||||
#include "backends/audiocd/audiocd.h"
|
||||
|
||||
@ -1835,17 +1836,31 @@ void ScummEngine::setupMusic(int midi) {
|
||||
} else if (_game.version >= 3 && _game.heversion <= 62) {
|
||||
MidiDriver *nativeMidiDriver = 0;
|
||||
MidiDriver *adlibMidiDriver = 0;
|
||||
bool multi_midi = ConfMan.getBool("multi_midi") && _sound->_musicType != MDT_NONE && _sound->_musicType != MDT_PCSPK && (midi & MDT_ADLIB);
|
||||
bool useOnlyNative = false;
|
||||
|
||||
if (_sound->_musicType != MDT_ADLIB && _sound->_musicType != MDT_TOWNS && _sound->_musicType != MDT_PCSPK)
|
||||
if (_game.platform == Common::kPlatformMacintosh && _game.id == GID_MONKEY2) {
|
||||
// We setup this driver as native MIDI driver to avoid playback
|
||||
// of the Mac music via a selected MIDI device.
|
||||
nativeMidiDriver = new MacM68kDriver(_mixer);
|
||||
// The Mac driver is never MT-32.
|
||||
_native_mt32 = false;
|
||||
// Ignore non-native drivers. This also ignores the multi MIDI setting.
|
||||
useOnlyNative = true;
|
||||
} else if (_sound->_musicType != MDT_ADLIB && _sound->_musicType != MDT_TOWNS && _sound->_musicType != MDT_PCSPK) {
|
||||
nativeMidiDriver = MidiDriver::createMidi(dev);
|
||||
}
|
||||
|
||||
if (nativeMidiDriver != NULL && _native_mt32)
|
||||
nativeMidiDriver->property(MidiDriver::PROP_CHANNEL_MASK, 0x03FE);
|
||||
bool multi_midi = ConfMan.getBool("multi_midi") && _sound->_musicType != MDT_NONE && _sound->_musicType != MDT_PCSPK && (midi & MDT_ADLIB);
|
||||
if (_sound->_musicType == MDT_ADLIB || _sound->_musicType == MDT_TOWNS || multi_midi) {
|
||||
adlibMidiDriver = MidiDriver::createMidi(MidiDriver::detectDevice(_sound->_musicType == MDT_TOWNS ? MDT_TOWNS : MDT_ADLIB));
|
||||
adlibMidiDriver->property(MidiDriver::PROP_OLD_ADLIB, (_game.features & GF_SMALL_HEADER) ? 1 : 0);
|
||||
} else if (_sound->_musicType == MDT_PCSPK) {
|
||||
adlibMidiDriver = new PcSpkDriver(_mixer);
|
||||
|
||||
if (!useOnlyNative) {
|
||||
if (_sound->_musicType == MDT_ADLIB || _sound->_musicType == MDT_TOWNS || multi_midi) {
|
||||
adlibMidiDriver = MidiDriver::createMidi(MidiDriver::detectDevice(_sound->_musicType == MDT_TOWNS ? MDT_TOWNS : MDT_ADLIB));
|
||||
adlibMidiDriver->property(MidiDriver::PROP_OLD_ADLIB, (_game.features & GF_SMALL_HEADER) ? 1 : 0);
|
||||
} else if (_sound->_musicType == MDT_PCSPK) {
|
||||
adlibMidiDriver = new PcSpkDriver(_mixer);
|
||||
}
|
||||
}
|
||||
|
||||
_imuse = IMuse::create(_system, nativeMidiDriver, adlibMidiDriver);
|
||||
|
@ -248,7 +248,10 @@ void Sound::playSound(int soundID) {
|
||||
_mixer->playStream(Audio::Mixer::kSFXSoundType, NULL, stream, soundID);
|
||||
}
|
||||
// Support for sampled sound effects in Monkey Island 1 and 2
|
||||
else if (_vm->_game.platform != Common::kPlatformFMTowns && READ_BE_UINT32(ptr) == MKTAG('S','B','L',' ')) {
|
||||
else if (_vm->_game.platform != Common::kPlatformFMTowns
|
||||
// The Macintosh version of MI2 just ignores SBL effects.
|
||||
&& (_vm->_game.platform != Common::kPlatformMacintosh && _vm->_game.id != GID_MONKEY2)
|
||||
&& READ_BE_UINT32(ptr) == MKTAG('S','B','L',' ')) {
|
||||
debugC(DEBUG_SOUND, "Using SBL sound effect");
|
||||
|
||||
// SBL resources essentially contain VOC sound data.
|
||||
|
Loading…
x
Reference in New Issue
Block a user