[Scummvm-git-logs] scummvm master -> b3baa636ee7ad549961a9341e2f840d6ce759c85
athrxx
noreply at scummvm.org
Sat Nov 23 22:15:52 UTC 2024
This automated email contains information about 6 new commits which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .
Summary:
ae86ea42e8 SCUMM: (IMS/Mac) - replace sound driver with more accurate version
39aff274af SCUMM: (IMS/Mac) - allow sfx volume control
673e338135 SCUMM: (IMS/Mac) - prevent playback of ADL resources
7e875c273e SCUMM: (IMS/Mac) - add DOTT sound support
f06279c8b2 SCUMM: (IMS) - get rid of Mac sfx instrument type
b3baa636ee SCUMM: whitespace
Commit: ae86ea42e8e9287f648927ce3e15435d3de1a5ca
https://github.com/scummvm/scummvm/commit/ae86ea42e8e9287f648927ce3e15435d3de1a5ca
Author: athrxx (athrxx at scummvm.org)
Date: 2024-11-23T23:07:25+01:00
Commit Message:
SCUMM: (IMS/Mac) - replace sound driver with more accurate version
The quality improvement seems to be rather subtle. But at least we now
have the quality slider and some fixed sound effects.
Changed paths:
A engines/scumm/imuse/drivers/macintosh.cpp
A engines/scumm/imuse/drivers/macintosh.h
A engines/scumm/players/mac_sound_lowlevel.h
R engines/scumm/imuse/drivers/mac_m68k.cpp
R engines/scumm/imuse/drivers/mac_m68k.h
engines/scumm/detection_internal.h
engines/scumm/imuse/drivers/midi.cpp
engines/scumm/imuse/drivers/midi.h
engines/scumm/imuse/imuse.cpp
engines/scumm/imuse/imuse.h
engines/scumm/imuse/imuse_part.cpp
engines/scumm/imuse/instrument.cpp
engines/scumm/macgui/macgui_v5.cpp
engines/scumm/metaengine.cpp
engines/scumm/module.mk
engines/scumm/players/player_mac_indy3.cpp
engines/scumm/players/player_mac_intern.h
engines/scumm/players/player_mac_loom_monkey.cpp
engines/scumm/players/player_mac_new.cpp
engines/scumm/saveload.cpp
engines/scumm/scumm.cpp
engines/scumm/scumm.h
engines/scumm/sound.cpp
engines/scumm/vars.cpp
diff --git a/engines/scumm/detection_internal.h b/engines/scumm/detection_internal.h
index f50b41979c9..7b70d0f6a2e 100644
--- a/engines/scumm/detection_internal.h
+++ b/engines/scumm/detection_internal.h
@@ -862,14 +862,22 @@ static bool testGame(const GameSettings *g, const DescMap &fileMD5Map, const Com
static Common::String customizeGuiOptions(const DetectorResult &res) {
Common::String guiOptions = res.game.guioptions;
+ static const uint mtypes[] = {MT_PCSPK, MT_CMS, MT_PCJR, MT_ADLIB, MT_C64, MT_AMIGA, MT_APPLEIIGS, MT_TOWNS, MT_PC98, MT_SEGACD, 0, 0, 0, 0, MT_MACINTOSH};
int midiflags = res.game.midi;
+
// These games often have no detection entries of their own and therefore come with all the DOS audio options.
- // We clear them here to avoid confusion and add the appropriate default sound option below. The games from
- // version 5 onwards seem to have correct sound options in the detection tables.
- if (res.game.version < 5 && (res.game.platform == Common::kPlatformAmiga || (res.game.platform == Common::kPlatformMacintosh && strncmp(res.extra, "Steam", 6)) || res.game.platform == Common::kPlatformC64))
+ // We clear them here to avoid confusion and add the appropriate default sound option below.
+ if (res.game.platform == Common::kPlatformAmiga || (res.game.platform == Common::kPlatformMacintosh && strncmp(res.extra, "Steam", 6)) || res.game.platform == Common::kPlatformC64) {
midiflags = MDT_NONE;
-
- static const uint mtypes[] = {MT_PCSPK, MT_CMS, MT_PCJR, MT_ADLIB, MT_C64, MT_AMIGA, MT_APPLEIIGS, MT_TOWNS, MT_PC98, MT_SEGACD, 0, 0, 0, 0, MT_MACINTOSH};
+ // Remove invalid types from options string
+ for (int i = 0; i < ARRAYSIZE(mtypes); ++i) {
+ if (!mtypes[i])
+ continue;
+ uint pos = guiOptions.findFirstOf(MidiDriver::musicType2GUIO(mtypes[i]));
+ if (pos != Common::String::npos)
+ guiOptions.erase(pos, 1);
+ }
+ }
for (int i = 0; i < ARRAYSIZE(mtypes); ++i) {
if (mtypes[i] && (midiflags & (1 << i)))
diff --git a/engines/scumm/imuse/drivers/mac_m68k.cpp b/engines/scumm/imuse/drivers/mac_m68k.cpp
deleted file mode 100644
index 5f9fe34794f..00000000000
--- a/engines/scumm/imuse/drivers/mac_m68k.cpp
+++ /dev/null
@@ -1,526 +0,0 @@
-/* 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/>.
- *
- */
-
-#include "scumm/imuse/drivers/mac_m68k.h"
-
-#include "common/util.h"
-#include "common/macresman.h"
-#include "common/stream.h"
-
-namespace Scumm {
-
-IMuseDriver_MacM68k::IMuseDriver_MacM68k(Audio::Mixer *mixer)
- : MidiDriver_Emulated(mixer), _mixBuffer(nullptr), _mixBufferLength(0), _volumeTable(nullptr), _lastUsedVoiceChannel(0), _defaultInstrument() {
- memset(_channels, 0, sizeof(_channels));
- memset(_pitchTable, 0, sizeof(_pitchTable));
- memset(_voiceChannels, 0, sizeof(_voiceChannels));
-}
-
-IMuseDriver_MacM68k::~IMuseDriver_MacM68k() {
- for (uint i = 0; i < ARRAYSIZE(_channels); ++i)
- delete _channels[i];
-}
-
-int IMuseDriver_MacM68k::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) {
- delete _channels[i];
- _channels[i] = new MidiChannel_MacM68k(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 = nullptr;
- _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 IMuseDriver_MacM68k::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 = nullptr;
- delete[] _mixBuffer;
- _mixBuffer = nullptr;
- _mixBufferLength = 0;
-}
-
-void IMuseDriver_MacM68k::send(uint32 d) {
- assert(false);
-}
-
-MidiChannel *IMuseDriver_MacM68k::allocateChannel() {
- for (uint i = 0; i < ARRAYSIZE(_channels); ++i) {
- if (_channels[i]->allocate()) {
- return _channels[i];
- }
- }
-
- return nullptr;
-}
-
-IMuseDriver_MacM68k::Instrument IMuseDriver_MacM68k::getInstrument(int idx) const {
- InstrumentMap::const_iterator i = _instruments.find(idx);
- if (i != _instruments.end()) {
- return i->_value;
- } else {
- return _defaultInstrument;
- }
-}
-
-void IMuseDriver_MacM68k::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 IMuseDriver_MacM68k::loadAllInstruments() {
- Common::MacResManager resource;
- if (resource.open("iMUSE Setups")) {
- if (!resource.hasResFork()) {
- error("MacM68kDriver::loadAllInstruments: \"iMUSE Setups\" loaded, but no resource fork present");
- }
-
- 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 IMuseDriver_MacM68k::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 IMuseDriver_MacM68k::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 IMuseDriver_MacM68k::VoiceChannel::off() {
- if (out.start) {
- out.isFinished = true;
- }
-
- part->removeVoice(this);
- part = nullptr;
-}
-
-void IMuseDriver_MacM68k::MidiChannel_MacM68k::release() {
- _allocated = false;
- while (_voice) {
- _voice->off();
- }
-}
-
-void IMuseDriver_MacM68k::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 IMuseDriver_MacM68k::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 IMuseDriver_MacM68k::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 = nullptr;
- 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 + _transpose) << 7) + ((_pitchBend * _pitchBendFactor) >> 6) + _detune);
-
- // Set up the sample position
- voice->out.instrument = voice->out.soundStart;
- voice->out.subPos = 0;
-}
-
-void IMuseDriver_MacM68k::MidiChannel_MacM68k::programChange(byte program) {
- _instrument = _owner->getInstrument(program + kProgramChangeBase);
-}
-
-void IMuseDriver_MacM68k::MidiChannel_MacM68k::pitchBend(int16 bend) {
- _pitchBend = bend;
- for (VoiceChannel *i = _voice; i; i = i->next) {
- _owner->setPitch(&i->out, ((i->note + _transpose) << 7) + ((_pitchBend * _pitchBendFactor) >> 6) + _detune);
- }
-}
-
-void IMuseDriver_MacM68k::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 IMuseDriver_MacM68k::MidiChannel_MacM68k::pitchBendFactor(byte value) {
- _pitchBendFactor = value;
-}
-
-
-void IMuseDriver_MacM68k::MidiChannel_MacM68k::transpose(int8 value) {
- _transpose = value;
- for (VoiceChannel *i = _voice; i; i = i->next) {
- _owner->setPitch(&i->out, ((i->note + _transpose) << 7) + ((_pitchBend * _pitchBendFactor) >> 6) + _detune);
- }
-}
-
-void IMuseDriver_MacM68k::MidiChannel_MacM68k::detune(int16 value) {
- _detune = (int8)value;
- for (VoiceChannel *i = _voice; i; i = i->next) {
- _owner->setPitch(&i->out, ((i->note + _transpose) << 7) + ((_pitchBend * _pitchBendFactor) >> 6) + _detune);
- }
-}
-
-void IMuseDriver_MacM68k::MidiChannel_MacM68k::priority(byte value) {
- _priority = value;
-}
-
-void IMuseDriver_MacM68k::MidiChannel_MacM68k::sysEx_customInstrument(uint32 type, const byte *instr, uint32 dataSize) {
- assert(instr);
- if (type == 'MAC ' && dataSize == sizeof(byte))
- _instrument = _owner->getInstrument(*instr + kSysExBase);
- else if (type != 'MAC ')
- warning("MidiChannel_MacM68k: Receiving '%c%c%c%c' instrument data. Probably loading a savegame with that sound setting", (type >> 24) & 0xFF, (type >> 16) & 0xFF, (type >> 8) & 0xFF, type & 0xFF);
-}
-
-bool IMuseDriver_MacM68k::MidiChannel_MacM68k::allocate() {
- if (_allocated) {
- return false;
- }
-
- _allocated = true;
- _voice = nullptr;
- _priority = 0;
- _instrument.reset();
- _pitchBend = 0;
- _pitchBendFactor = 2;
- _volume = 0;
- return true;
-}
-
-void IMuseDriver_MacM68k::MidiChannel_MacM68k::addVoice(VoiceChannel *voice) {
- voice->next = _voice;
- voice->prev = nullptr;
- voice->part = this;
- if (_voice) {
- _voice->prev = voice;
- }
- _voice = voice;
-}
-
-void IMuseDriver_MacM68k::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;
- }
- }
-}
-
-IMuseDriver_MacM68k::VoiceChannel *IMuseDriver_MacM68k::allocateVoice(int priority) {
- VoiceChannel *channel = nullptr;
- 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 IMuseDriver_MacM68k::_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
diff --git a/engines/scumm/imuse/drivers/mac_m68k.h b/engines/scumm/imuse/drivers/mac_m68k.h
deleted file mode 100644
index 8c7b665d7a6..00000000000
--- a/engines/scumm/imuse/drivers/mac_m68k.h
+++ /dev/null
@@ -1,191 +0,0 @@
-/* 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 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 IMuseDriver_MacM68k : public MidiDriver_Emulated {
- friend class MidiChannel_MacM68k;
-public:
- IMuseDriver_MacM68k(Audio::Mixer *mixer);
- ~IMuseDriver_MacM68k() override;
-
- int open() override;
- void close() override;
-
- void send(uint32 d) override;
-
- MidiChannel *allocateChannel() override;
- MidiChannel *getPercussionChannel() override { return nullptr; }
-
- bool isStereo() const override { return false; }
- int getRate() const override {
- // 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:
- void generateSamples(int16 *buf, int len) override;
- void onTimer() override {}
-
-private:
- int *_mixBuffer;
- int _mixBufferLength;
-
- struct Instrument {
- Instrument() { reset(); }
- uint length;
- uint sampleRate;
- uint loopStart;
- uint loopEnd;
- int baseFrequency;
-
- byte *data;
-
- void reset() {
- length = 0;
- sampleRate = 0;
- loopStart = 0;
- loopEnd = 0;
- baseFrequency = 0;
- data = nullptr;
- }
- };
-
- 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 IMuseDriver_MacM68k;
- public:
- MidiChannel_MacM68k(IMuseDriver_MacM68k *driver, byte number) : MidiChannel(), _owner(driver), _number(number), _allocated(false),
- _priority(0), _sustain(0), _pitchBend(0), _pitchBendFactor(2), _transpose(0), _detune(0), _volume(0), _voice(nullptr), _instrument() {}
- MidiDriver *device() override { return _owner; }
- byte getNumber() override { return _number; }
- void release() override;
-
- void send(uint32 b) override;
- void noteOff(byte note) override;
- void noteOn(byte note, byte velocity) override;
- void programChange(byte program) override;
- void pitchBend(int16 bend) override;
- void controlChange(byte control, byte value) override;
- void pitchBendFactor(byte value) override;
- void transpose(int8 value) override;
- void detune(int16 value) override;
- void priority(byte value) override;
- void sysEx_customInstrument(uint32 type, const byte *instr, uint32 dataSize) override;
-
- bool allocate();
-
- void addVoice(VoiceChannel *voice);
- void removeVoice(VoiceChannel *voice);
- private:
- IMuseDriver_MacM68k *_owner;
- const byte _number;
- bool _allocated;
-
- VoiceChannel *_voice;
- int _priority;
- int _sustain;
- Instrument _instrument;
- int _pitchBend;
- int _pitchBendFactor;
- int8 _detune;
- int8 _transpose;
- int _volume;
- };
-
- MidiChannel_MacM68k *_channels[32];
-
- enum {
- kChannelCount = 8
- };
- VoiceChannel _voiceChannels[kChannelCount];
- int _lastUsedVoiceChannel;
- VoiceChannel *allocateVoice(int priority);
-};
-
-} // End of namespace Scumm
-
-#endif
diff --git a/engines/scumm/imuse/drivers/macintosh.cpp b/engines/scumm/imuse/drivers/macintosh.cpp
new file mode 100644
index 00000000000..7fbe8988f7f
--- /dev/null
+++ b/engines/scumm/imuse/drivers/macintosh.cpp
@@ -0,0 +1,796 @@
+/* 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/>.
+ *
+ */
+
+
+#include "common/macresman.h"
+#include "common/punycode.h"
+#include "scumm/players/mac_sound_lowlevel.h"
+#include "scumm/players/player_mac_new.h"
+#include "scumm/imuse/drivers/macintosh.h"
+#include "scumm/imuse/imuse.h"
+
+
+namespace IMSMacintosh {
+using namespace Scumm;
+
+class DJMSoundChannel {
+public:
+ DJMSoundChannel(const uint32 *pitchTable) : _pitchTable(pitchTable), _frequency(0), _phase(0), _end(nullptr), _pos(nullptr), _smpBuffStart(nullptr), _smpBuffEnd(nullptr), _loopStart(nullptr), _loopEnd(nullptr), _pitch(0), _volume(0), _mute(0), _instr(nullptr), _baseFreq(0) {}
+ ~DJMSoundChannel() {}
+
+ void recalcFrequency();
+
+ uint32 _frequency;
+ uint32 _phase;
+ const byte *_end;
+ const byte *_pos;
+ const byte *_smpBuffStart;
+ const byte *_smpBuffEnd;
+ const byte *_loopStart;
+ const byte *_loopEnd;
+ uint16 _pitch;
+ byte _volume;
+ byte _mute;
+ byte _baseFreq;
+ Common::SharedPtr<MacSndInstrument> _instr;
+ const uint32 *_pitchTable;
+};
+
+class DJMSoundSystem : public VblTaskClientDriver, public MacLowLevelPCMDriver::CallbackClient {
+public:
+ DJMSoundSystem(Audio::Mixer *mixer);
+ ~DJMSoundSystem();
+
+ bool init(bool internal16Bit);
+ void deinit();
+ bool start();
+ void stop();
+
+ void setQuality(int qual);
+
+ void setInstrument(byte chan, Common::SharedPtr<MacSndInstrument> &instr);
+ void noteOn(byte chan, uint16 pitch, byte volume);
+ void noteOff(byte chan);
+ void setVolume(byte chan, byte volume);
+ void setPitch(byte chan, uint16 pitch);
+
+ void setTimerCallback(void *timerParam, Common::TimerManager::TimerProc timerProc);
+
+ void vblCallback() override;
+ void generateData(int8 *dst, uint32 byteSize, Audio::Mixer::SoundType type, bool expectStereo) const override;
+ const MacSoundDriver::Status &getDriverStatus(Audio::Mixer::SoundType sndType) const override;
+
+ void dblBuffCallback(MacLowLevelPCMDriver::DoubleBuffer *dblBuffer) override;
+
+private:
+ void fillPitchTable();
+
+ byte _quality;
+ uint16 _feedBufferSize;
+ uint32 *_pitchTable;
+ byte *_ampTable;
+ int16 *_mixBuffer16Bit;
+
+ DJMSoundChannel **_channels;
+
+ MacPlayerAudioStream *_macstr;
+ MacLowLevelPCMDriver *_sdrv;
+ Audio::SoundHandle _soundHandle;
+ MacPlayerAudioStream::CallbackProc _vblTskProc;
+ MacLowLevelPCMDriver::ChanHandle _chan;
+ MacLowLevelPCMDriver::DBCallback _dbCbProc;
+
+ Audio::Mixer *_mixer;
+ void *_timerParam;
+ Common::TimerManager::TimerProc _timerProc;
+ bool _internal16Bit;
+ const byte _numChannels;
+};
+
+struct ChannelNode;
+class IMuseChannel_Mac_DJM final : public MidiChannel {
+public:
+ IMuseChannel_Mac_DJM(IMuseDriver_Mac_DJM *drv, int number);
+ ~IMuseChannel_Mac_DJM() override;
+
+ MidiDriver *device() override { return _drv; }
+ byte getNumber() override { return _number; }
+
+ bool allocate();
+ void release() override { _allocated = false; }
+
+ void send(uint32 b) override { if (_drv) _drv->send((b & ~0x0F) | _number); }
+
+ // Regular messages
+ void noteOff(byte note) override;
+ void noteOn(byte note, byte velocity) override;
+ void controlChange(byte control, byte value) override;
+ void programChange(byte program) override;
+ void pitchBend(int16 bend) override;
+
+ // Control Change and SCUMM specific functions
+ void pitchBendFactor(byte value) override { pitchBend(0); _pitchBendSensitivity = value; }
+ void transpose(int8 value) override { _transpose = value; pitchBend(_pitchBendSet); }
+ void detune(int16 value) override { _detune = value; pitchBend(_pitchBendSet); }
+ void priority(byte value) override { _prio = value; }
+ void sustain(bool value) override;
+ void allNotesOff() override;
+ void sysEx_customInstrument(uint32 type, const byte *instr, uint32 dataSize) override {}
+
+private:
+ bool _allocated;
+
+ byte _prio;
+ byte _bank;
+ byte _volume;
+ int16 _detune;
+ int8 _transpose;
+ int16 _pitchBendSet;
+ byte _pitchBendSensitivity;
+ int16 _pitchBendEff;
+ bool _sustain;
+ Common::SharedPtr<MacSndInstrument> _prog;
+ const byte _number;
+
+ ChannelNode *allocateDeviceChannel(int prio);
+
+ ChannelNode **_channels;
+ ChannelNode *_out;
+ static byte _allocCur;
+
+ IMuseDriver_Mac_DJM *_drv;
+ DJMSoundSystem *_device;
+
+ Common::Array<Common::SharedPtr<MacSndInstrument> > *_instruments;
+ const byte _numChannels;
+};
+
+struct ChannelNode {
+ ChannelNode(int num) : _prev(nullptr), _next(nullptr), _in(nullptr), _number(num), _note(0), _sustain(false) {}
+ ChannelNode *_prev;
+ ChannelNode *_next;
+ IMuseChannel_Mac_DJM *_in;
+ const byte _number;
+ byte _note;
+ bool _sustain;
+};
+
+void connect(ChannelNode *&chain, ChannelNode *node) {
+ if (!node || node->_prev || node->_next)
+ return;
+ if ((node->_next = chain))
+ chain->_prev = node;
+ chain = node;
+}
+
+void disconnect(ChannelNode *&chain, ChannelNode *node) {
+ if (!node || !chain)
+ return;
+
+ const ChannelNode *ch = chain;
+ while (ch && ch != node)
+ ch = ch->_next;
+ if (!ch)
+ return;
+
+ if (node->_next)
+ node->_next->_prev = node->_prev;
+
+ if (node->_prev)
+ node->_prev->_next = node->_next;
+ else
+ chain = node->_next;
+
+ node->_in = nullptr;
+ node->_next = node->_prev = nullptr;
+}
+
+void DJMSoundChannel::recalcFrequency() {
+ int pos = (_pitch >> 7) + 60 - _baseFreq;
+ assert(pos>=0);
+ if (_pitch & 0x7F)
+ _frequency = _pitchTable[pos] + (((_pitchTable[pos + 1] - _pitchTable[pos]) * (_pitch & 0x7F)) >> 7);
+ else
+ _frequency = _pitchTable[pos];
+}
+
+DJMSoundSystem::DJMSoundSystem(Audio::Mixer *mixer) : VblTaskClientDriver(), _mixer(mixer), _macstr(nullptr), _sdrv(nullptr), _vblTskProc(this, &VblTaskClientDriver::vblCallback),
+ _dbCbProc(this, &MacLowLevelPCMDriver::CallbackClient::dblBuffCallback), _chan(0), _quality(22), _feedBufferSize(1024), _channels(nullptr),
+ _timerParam(nullptr), _timerProc(nullptr), _pitchTable(nullptr), _ampTable(nullptr), _numChannels(8) {
+ _pitchTable = new uint32[128]();
+ assert(_pitchTable);
+ _channels = new DJMSoundChannel*[_numChannels];
+ assert(_channels);
+ for (int i = 0; i < _numChannels; ++i)
+ _channels[i] = new DJMSoundChannel(_pitchTable);
+ _mixBuffer16Bit = new int16[_feedBufferSize]();
+ assert(_mixBuffer16Bit);
+ fillPitchTable();
+ _ampTable = new byte[8192]();
+ assert(_ampTable);
+}
+
+DJMSoundSystem::~DJMSoundSystem() {
+ deinit();
+ if (_channels) {
+ for (int i = 0; i < _numChannels; ++i)
+ delete _channels[i];
+ delete[] _channels;
+ }
+ delete[] _pitchTable;
+ delete[] _mixBuffer16Bit;
+}
+
+bool DJMSoundSystem::init(bool internal16Bit) {
+ _internal16Bit = internal16Bit;
+
+ static const byte ampTable[32] = {
+ 0x00, 0x00, 0x01, 0x01, 0x02, 0x03, 0x05, 0x06,
+ 0x08, 0x0b, 0x0d, 0x10, 0x13, 0x16, 0x1a, 0x1e,
+ 0x22, 0x26, 0x2b, 0x30, 0x35, 0x3a, 0x40, 0x46,
+ 0x4c, 0x53, 0x59, 0x60, 0x68, 0x6f, 0x77, 0x7f
+ };
+
+ byte *dst = _ampTable;
+ byte offs = _internal16Bit ? 0 : 128;
+ for (int i = 0; i < 32; ++i) {
+ for (int ii = 0; ii < 256; ++ii)
+ *dst++ = (((ii - 128) * ampTable[i]) / 127) + offs;
+ }
+
+ _macstr = new MacPlayerAudioStream(this, _mixer->getOutputRate(), false, false, internal16Bit);
+ if (!_macstr)
+ return false;
+
+ // We can start this with the ScummVM mixer output rate instead of the ASC rate. The Mac sample rate converter can handle it (at
+ // least for up to 48 KHz, I haven't tried higher settings) and we don't have to do another rate conversion in the ScummVM mixer.
+ _sdrv = new MacLowLevelPCMDriver(_mixer->mutex(), _mixer->getOutputRate() << 16/*ASC_DEVICE_RATE*/, internal16Bit);
+ if (!_sdrv)
+ return false;
+
+ _macstr->initDrivers();
+ _macstr->initBuffers(1024);
+ _macstr->setVblCallback(&_vblTskProc);
+
+ _mixer->playStream(Audio::Mixer::kPlainSoundType, &_soundHandle, _macstr, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
+
+ return true;
+}
+
+void DJMSoundSystem::deinit() {
+ Common::StackLock lock(_mixer->mutex());
+ stop();
+ _mixer->stopHandle(_soundHandle);
+ delete _macstr;
+ _macstr = nullptr;
+ delete _sdrv;
+ _sdrv = nullptr;
+ delete[] _ampTable;
+ _ampTable = nullptr;
+}
+
+bool DJMSoundSystem::start() {
+ _chan = _sdrv->createChannel(Audio::Mixer::kPlainSoundType, MacLowLevelPCMDriver::kSampledSynth, 0x8C, nullptr);
+ if (!_chan)
+ return false;
+
+ uint32 rate = 0;
+ switch (_quality) {
+ case 5:
+ rate = 0x15BBA2E8;
+ break;
+ case 7:
+ rate = 0x1CFA2E8B;
+ break;
+ case 11:
+ rate = 0x2B7745D1;
+ break;
+ case 22:
+ rate = 0x56EE8BA3;
+ break;
+ default:
+ warning("%s(): Invalid quality setting %d", __FUNCTION__, _quality);
+ return false;
+ }
+
+ return _sdrv->playDoubleBuffer(_chan, 1, _internal16Bit ? 16 : 8, rate, &_dbCbProc, _internal16Bit ? 8 : 1);
+}
+
+void DJMSoundSystem::stop() {
+ Common::StackLock lock(_mixer->mutex());
+ if (_sdrv)
+ _sdrv->disposeChannel(_chan);
+}
+
+void DJMSoundSystem::setQuality(int qual) {
+ Common::StackLock lock(_mixer->mutex());
+
+ qual = qual > 1 ? 22 : 11;
+ if (qual == _quality)
+ return;
+
+ _quality = qual;
+ _feedBufferSize = (qual == 22) ? 1024 : 512;
+ fillPitchTable();
+
+ for (int i = 0; i < _numChannels; ++i)
+ _channels[i]->recalcFrequency();
+
+ stop();
+ if (!start())
+ error("%s(): Unknown error", __FUNCTION__);
+}
+
+void DJMSoundSystem::setInstrument(byte chan, Common::SharedPtr<MacSndInstrument> &instr) {
+ Common::StackLock lock(_mixer->mutex());
+ if (chan > 7)
+ return;
+ DJMSoundChannel &c = *_channels[chan];
+
+ if (c._instr == instr)
+ return;
+
+ const MacLowLevelPCMDriver::PCMSound *s = instr.get()->data();
+ c._instr = instr;
+ c._baseFreq = s->baseFreq;
+ c._smpBuffStart = s->data.get();
+ c._smpBuffEnd = c._smpBuffStart + s->len;
+ if (s->loopst >= s->loopend - 12) {
+ c._loopStart = nullptr;
+ c._loopEnd = c._smpBuffEnd;
+ } else {
+ c._loopStart = c._smpBuffStart + s->loopst;
+ c._loopEnd = c._smpBuffStart + s->loopend;
+ }
+}
+
+void DJMSoundSystem::noteOn(byte chan, uint16 pitch, byte volume) {
+ Common::StackLock lock(_mixer->mutex());
+ if (chan > 7)
+ return;
+ DJMSoundChannel &c = *_channels[chan];
+ c._pitch = pitch;
+ c._volume = volume;
+ c._mute = 0;
+ c._end = c._loopEnd;
+ c._pos = c._smpBuffStart;
+ c._phase = 0;
+ c.recalcFrequency();
+}
+
+void DJMSoundSystem::noteOff(byte chan) {
+ Common::StackLock lock(_mixer->mutex());
+ if (chan > 7)
+ return;
+ DJMSoundChannel &c = *_channels[chan];
+ if (c._loopStart)
+ c._mute = 1;
+}
+
+void DJMSoundSystem::setVolume(byte chan, byte volume) {
+ Common::StackLock lock(_mixer->mutex());
+ if (chan > 7)
+ return;
+ DJMSoundChannel &c = *_channels[chan];
+ c._volume = volume;
+ c._mute = 0;
+}
+
+void DJMSoundSystem::setPitch(byte chan, uint16 pitch) {
+ Common::StackLock lock(_mixer->mutex());
+ if (chan > 7)
+ return;
+ DJMSoundChannel &c = *_channels[chan];
+ c._pitch = pitch;
+ c._mute = 0;
+ c.recalcFrequency();
+}
+
+void DJMSoundSystem::setTimerCallback(void *timerParam, Common::TimerManager::TimerProc timerProc) {
+ _timerParam = timerParam;
+ _timerProc = timerProc;
+}
+
+void DJMSoundSystem::vblCallback() {
+ if (_timerProc)
+ _timerProc(_timerParam);
+}
+
+void DJMSoundSystem::generateData(int8 *dst, uint32 len, Audio::Mixer::SoundType type, bool expectStereo) const {
+ assert(dst);
+ memset(dst, 0, len);
+ _sdrv->feed(dst, len, type, expectStereo);
+}
+
+const MacSoundDriver::Status &DJMSoundSystem::getDriverStatus(Audio::Mixer::SoundType) const {
+ return _sdrv->getStatus(Audio::Mixer::kPlainSoundType);
+}
+
+void DJMSoundSystem::dblBuffCallback(MacLowLevelPCMDriver::DoubleBuffer *dblBuffer) {
+ uint16 sil = 0;
+ memset(_mixBuffer16Bit, 0, _feedBufferSize * sizeof(uint16));
+ byte b = _internal16Bit ? 0 : 0x80;
+
+ for (int i = 0; i < _numChannels; ++i) {
+ DJMSoundChannel &c = *_channels[i];
+ if (c._mute) {
+ ++sil;
+ continue;
+ }
+
+ const byte *a = &_ampTable[(c._volume & ~3) << 6];
+ int16 *t = _mixBuffer16Bit;
+
+ for (int ii = 0; ii < _feedBufferSize; ++ii) {
+ if (!c._pos) {
+ *t++ += b;
+ continue;
+ }
+ c._phase += c._frequency;
+ if (c._phase >> 16) {
+ c._pos += (c._phase >> 16);
+ c._phase &= 0xFFFF;
+ if (c._loopEnd <= c._pos) {
+ c._pos = c._loopStart;
+ c._phase = 0;
+ if (!c._pos) {
+ c._mute = 1;
+ --ii;
+ continue;
+ }
+ }
+ }
+ *t++ += _internal16Bit ? static_cast<int8>(a[*c._pos]) : a[*c._pos];
+ }
+ }
+
+ const int16 *s = _mixBuffer16Bit;
+ byte *d = dblBuffer->data;
+
+ if (_internal16Bit) {
+ memcpy(d, s, _feedBufferSize * sizeof(int16));
+ } else {
+ sil <<= 7;
+ for (int i = 0; i < _feedBufferSize; ++i)
+ *d++ = (sil + *s++) >> 3;
+ }
+
+ dblBuffer->numFrames = _feedBufferSize;
+ dblBuffer->flags |= MacLowLevelPCMDriver::DoubleBuffer::kBufferReady;
+}
+
+void DJMSoundSystem::fillPitchTable() {
+ static const float fltbl[12] = {
+ 1664510.62f, 1763487.62f, 1868350.00f, 1979447.88f, 2097152.00f, 2221855.25f,
+ 2353973.50f, 2493948.00f, 2642246.00f, 2799362.00f, 2965820.75f, 3142177.75f
+ };
+ for (int i = 0; i < 12; ++i)
+ _pitchTable[116 + i] = (uint32)fltbl[i];
+ for (int i = 115; i >= 0; --i)
+ _pitchTable[i] = _pitchTable[i + 12] >> 1;
+ if (_quality != 11)
+ return;
+ for (int i = 0; i < 128; ++i)
+ _pitchTable[i] <<= 1;
+}
+
+byte IMuseChannel_Mac_DJM::_allocCur = 0;
+
+IMuseChannel_Mac_DJM::IMuseChannel_Mac_DJM(IMuseDriver_Mac_DJM *drv, int number) : MidiChannel(), _drv(drv), _number(number), _allocated(false), _sustain(false), _bank(0),
+ _pitchBendEff(0), _prio(0x80), _detune(0), _transpose(0), _pitchBendSet(0), _pitchBendSensitivity(2), _volume(0), _channels(drv ? drv->_channels : nullptr), _prog(nullptr),
+ _out(nullptr), _device(drv ? drv->_device : nullptr), _instruments(drv ? &drv->_instruments : nullptr), _numChannels(8) {
+ assert(_drv);
+ assert(_channels);
+ assert(_device);
+ assert(_instruments);
+ _allocCur = 0;
+}
+
+IMuseChannel_Mac_DJM::~IMuseChannel_Mac_DJM() {
+}
+
+bool IMuseChannel_Mac_DJM::allocate() {
+ if (_allocated)
+ return false;
+
+ return (_allocated = true);
+}
+
+void IMuseChannel_Mac_DJM::noteOff(byte note) {
+ for (ChannelNode *node = _out; node; node = node->_next) {
+ if (node->_note == note) {
+ if (_sustain) {
+ node->_sustain = true;
+ } else {
+ debug(5, "NOTE OFF: chan '%d', note '%d'", node->_number, note);
+ _device->noteOff(node->_number);
+ disconnect(_out, node);
+ }
+ }
+ }
+}
+
+void IMuseChannel_Mac_DJM::noteOn(byte note, byte velocity) {
+ ChannelNode *node = allocateDeviceChannel(_prio);
+ if (node == nullptr || _prog == nullptr)
+ return;
+ connect(_out, node);
+ node->_in = this;
+ node->_note = note;
+ node->_sustain = false;
+
+ _device->setInstrument(node->_number, _prog);
+ debug(4, "NOTE ON: chan '%d', note '%d', instr '%s'", node->_number, note, _prog.get()->name());
+ _device->noteOn(node->_number, ((note + _transpose) << 7) + _pitchBendEff, _volume); // ignoring velocity
+}
+
+void IMuseChannel_Mac_DJM::controlChange(byte control, byte value) {
+ switch (control) {
+ case 7:
+ _volume = value;
+ for (ChannelNode *node = _out; node; node = node->_next)
+ _device->setVolume(node->_number, value);
+ break;
+ case 32:
+ _bank = value; // The original code doesn't have that. It will just call a different programChange() method from the sysex handler.
+ break;
+ case 64:
+ _sustain = (value != 0);
+ if (value == 0) {
+ for (ChannelNode *node = _out; node; node = node->_next) {
+ if (node->_sustain) {
+ _device->noteOff(node->_number);
+ disconnect(_out, node);
+ }
+ }
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+void IMuseChannel_Mac_DJM::programChange(byte program) {
+ _prog.reset();
+ uint16 base = _bank ? 2000 : 1000;
+ Common::SharedPtr<MacSndInstrument> *def = nullptr;
+ for (Common::Array<Common::SharedPtr<MacSndInstrument> >::iterator it = _instruments->begin(); _prog == nullptr && it != _instruments->end(); ++it) {
+ uint16 id = (*it)->id();
+ if (id == program + base)
+ _prog = *it;
+ else if (id == 999u)
+ def = it;
+ }
+ if (_prog == nullptr) {
+ if (def != nullptr)
+ _prog = *def;
+ else
+ error("%s(): Failure (program %d)", __FUNCTION__, program);
+ }
+}
+
+void IMuseChannel_Mac_DJM::pitchBend(int16 bend) {
+ _pitchBendSet = bend;
+ //if (_newSystem) {
+ // SAMNMAX formula (same for Roland MT-32 and GM)
+ //bend = (((bend * _pitchBendSensitivity) >> 5) + _detune + (_transpose << 8)) << 1;
+ //} else {
+ // DOTT, INDY4 and MI2 formula
+ bend = CLIP<int>(((bend * _pitchBendSensitivity) >> 6) + _detune, -2048, 2047);
+ //}
+
+ _pitchBendEff = bend;
+
+ debug(4, "PITCH BEND: chan %d, eff pitch '%d'", _number, _pitchBendEff);
+ for (ChannelNode *node = _out; node; node = node->_next)
+ _device->setPitch(node->_number, ((node->_note + _transpose) << 7) + _pitchBendEff);
+}
+
+void IMuseChannel_Mac_DJM::sustain(bool value) {
+ _sustain = value;
+}
+
+void IMuseChannel_Mac_DJM::allNotesOff() {
+ for (ChannelNode *node = _out; node; node = node->_next) {
+ _device->noteOff(node->_number);
+ disconnect(_out, node);
+ }
+}
+
+ChannelNode *IMuseChannel_Mac_DJM::allocateDeviceChannel(int prio) {
+ ChannelNode *res = nullptr;
+ for (byte i = 0; i < _numChannels; ++i) {
+ _allocCur = (_allocCur + 1) % _numChannels;
+ ChannelNode *node = _channels[_allocCur];
+ if (node->_in == nullptr)
+ return node;
+ if (!node->_next && node->_in && node->_in->_prio <= prio) {
+ res = node;
+ prio = node->_in->_prio;
+ }
+ }
+
+ if (res) {
+ _device->noteOff(res->_number);
+ disconnect(res->_in->_out, res);
+ }
+ return res;
+}
+
+} // End of namespace IMSMacintosh
+
+namespace Scumm {
+using namespace IMSMacintosh;
+
+IMuseDriver_Mac_DJM::IMuseDriver_Mac_DJM(Audio::Mixer *mixer) : MidiDriver(), _isOpen(false), _device(nullptr), _imsParts(nullptr), _channels(nullptr),
+ _numParts(24), _numChannels(8), _baseTempo(16667), _quality(1) {
+ _device = new DJMSoundSystem(mixer);
+}
+
+IMuseDriver_Mac_DJM::~IMuseDriver_Mac_DJM() {
+ close();
+ delete _device;
+}
+
+int IMuseDriver_Mac_DJM::open() {
+ if (_isOpen)
+ return MERR_ALREADY_OPEN;
+
+ createChannels();
+
+ static const char *fileNames[] = {
+ "iMUSE Setups",
+ nullptr
+ };
+
+ if (!loadDefaultInstruments(fileNames, ARRAYSIZE(fileNames)) || !_device->init(true) || !_device->start())
+ return MERR_DEVICE_NOT_AVAILABLE;
+
+ _isOpen = true;
+
+ return 0;
+}
+
+void IMuseDriver_Mac_DJM::close() {
+ if (!_isOpen)
+ return;
+
+ _isOpen = false;
+
+ _device->deinit();
+ releaseChannels();
+}
+
+uint32 IMuseDriver_Mac_DJM::property(int prop, uint32 param) {
+ uint32 res = 0;
+ switch (prop) {
+ case IMuse::PROP_QUALITY:
+ res = _quality;
+ if (param != (uint32)-1 && param != _quality) {
+ _quality = param;
+ if (_device)
+ _device->setQuality(param == MacSound::kQualityAuto || param == MacSound::kQualityHighest ? 2: 0);
+ }
+ break;
+ default:
+ break;
+ }
+ return res;
+}
+
+void IMuseDriver_Mac_DJM::setTimerCallback(void *timerParam, Common::TimerManager::TimerProc timerProc) {
+ if (_device)
+ _device->setTimerCallback(timerParam, timerProc);
+}
+
+MidiChannel *IMuseDriver_Mac_DJM::allocateChannel() {
+ if (!_isOpen)
+ return nullptr;
+
+ for (int i = 0; i < _numParts; ++i) {
+ IMuseChannel_Mac_DJM *ch = _imsParts[i];
+ if (ch && ch->allocate())
+ return ch;
+ }
+
+ return nullptr;
+}
+
+MidiChannel *IMuseDriver_Mac_DJM::getPercussionChannel() {
+ return nullptr;
+}
+
+void IMuseDriver_Mac_DJM::createChannels() {
+ releaseChannels();
+
+ _channels = new ChannelNode*[_numChannels];
+ assert(_channels);
+ for (int i = 0; i < _numChannels; ++i) {
+ _channels[i] = new ChannelNode(i);
+ assert(_channels[i]);
+ }
+
+ _imsParts = new IMuseChannel_Mac_DJM*[_numParts];
+ assert(_imsParts);
+ for (int i = 0; i < _numParts; ++i)
+ _imsParts[i] = new IMuseChannel_Mac_DJM(this, i);
+}
+
+void IMuseDriver_Mac_DJM::releaseChannels() {
+ if (_imsParts) {
+ for (int i = 0; i < _numParts; ++i)
+ delete _imsParts[i];
+ delete[] _imsParts;
+ _imsParts = nullptr;
+ }
+
+ if (_channels) {
+ for (int i = 0; i < _numChannels; ++i)
+ delete _channels[i];
+ delete[] _channels;
+ _channels = nullptr;
+ }
+}
+
+bool IMuseDriver_Mac_DJM::loadDefaultInstruments(const char *const *tryFileNames, uint16 numTryFileNames) {
+ const Common::CodePage tryCodePages[] = {
+ Common::kMacRoman,
+ Common::kISO8859_1
+ };
+
+ Common::MacResManager resMan;
+ Common::Path resFile;
+
+ for (int i = 0; resFile.empty() && i < numTryFileNames && tryFileNames[i] != nullptr; ++i) {
+ for (int ii = 0; resFile.empty() && ii < ARRAYSIZE(tryCodePages); ++ii) {
+ Common::U32String fn(tryFileNames[i], tryCodePages[ii]);
+ resFile = Common::Path(fn.encode(Common::kUtf8));
+ if (!resMan.exists(resFile) || !resMan.open(resFile) || !resMan.hasResFork()) {
+ resMan.close();
+ resFile = Common::Path(Common::punycode_encodefilename(fn));
+ if (!resMan.exists(resFile) || !resMan.open(resFile) || !resMan.hasResFork()) {
+ resMan.close();
+ resFile.clear();
+ }
+ }
+ }
+ }
+
+ if (resFile.empty()) {
+ warning("%s(): Resource fork not found", __FUNCTION__);
+ return false;
+ }
+
+ _instruments.clear();
+
+ Common::MacResIDArray ids = resMan.getResIDArray(MKTAG('s', 'n', 'd', ' '));
+ for (Common::MacResIDArray::const_iterator i = ids.begin(); i != ids.end(); ++i) {
+ Common::SeekableReadStream *str = resMan.getResource(MKTAG('s', 'n', 'd', ' '), *i);
+ uint16 type = str ? str->readUint16BE() : 0;
+ if (type == 1 || type == 2)
+ _instruments.push_back(Common::SharedPtr<MacSndInstrument>(new MacSndInstrument(*i, str, resMan.getResName(MKTAG('s', 'n', 'd', ' '), *i))));
+ delete str;
+ }
+
+ return !_instruments.empty();
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/imuse/drivers/macintosh.h b/engines/scumm/imuse/drivers/macintosh.h
new file mode 100644
index 00000000000..68d53346b7b
--- /dev/null
+++ b/engines/scumm/imuse/drivers/macintosh.h
@@ -0,0 +1,79 @@
+/* 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 SCUMM_IMUSE_DRV_MAC_H
+#define SCUMM_IMUSE_DRV_MAC_H
+
+#include "audio/mididrv.h"
+#include "scumm/players/mac_sound_lowlevel.h"
+
+namespace Audio {
+class Mixer;
+}
+
+namespace IMSMacintosh {
+class IMuseChannel_Mac_DJM;
+class DJMSoundSystem;
+struct ChannelNode;
+} // End of namespace IMSMacintosh
+
+namespace Scumm {
+
+class IMuseDriver_Mac_DJM final : public MidiDriver {
+ friend class IMSMacintosh::IMuseChannel_Mac_DJM;
+public:
+ IMuseDriver_Mac_DJM(Audio::Mixer *mixer);
+ virtual ~IMuseDriver_Mac_DJM() override;
+
+ int open() override;
+ void close() override;
+
+ bool isOpen() const override { return _isOpen; }
+ uint32 property(int prop, uint32 param) override;
+ void setTimerCallback(void *timerParam, Common::TimerManager::TimerProc timerProc) override;
+ uint32 getBaseTempo() override { return _baseTempo; }
+ void send(uint32 b) override { error("%s():: Not implemented", __FUNCTION__); }
+
+ // Channel allocation functions
+ MidiChannel *allocateChannel() override;
+ MidiChannel *getPercussionChannel() override;
+
+private:
+ void createChannels();
+ void releaseChannels();
+ bool loadDefaultInstruments(const char *const *tryFileNames, uint16 numTryFileNames);
+
+ bool _isOpen;
+ uint32 _quality;
+
+ IMSMacintosh::DJMSoundSystem *_device;
+ IMSMacintosh::IMuseChannel_Mac_DJM **_imsParts;
+ IMSMacintosh::ChannelNode **_channels;
+ Common::Array<Common::SharedPtr<MacSndInstrument> > _instruments;
+
+ const byte _numChannels;
+ const byte _numParts;
+ const uint32 _baseTempo;
+};
+
+} // End of namespace Scumm
+
+#endif
diff --git a/engines/scumm/imuse/drivers/midi.cpp b/engines/scumm/imuse/drivers/midi.cpp
index 56bd2bb60ae..257fbb5666f 100644
--- a/engines/scumm/imuse/drivers/midi.cpp
+++ b/engines/scumm/imuse/drivers/midi.cpp
@@ -28,7 +28,8 @@
// dynamic allocation (instead of playing on fixed channels).
//#define FORCE_NEWSTYLE_CHANNEL_ALLOCATION
-namespace Scumm {
+namespace IMSMidi {
+using namespace Scumm;
/*******************************
* General Midi driver
@@ -379,6 +380,11 @@ void IMuseChannel_Midi::sendNoteOn(byte note, byte velocity) {
sendMidi(0x90, note, velocity);
}
+} // End of namespace IMSMidi
+
+namespace Scumm {
+using namespace IMSMidi;
+
IMuseDriver_GMidi::IMuseDriver_GMidi(MidiDriver::DeviceHandle dev, bool rolandGSMode, bool newSystem) : MidiDriver(), _drv(nullptr), _gsMode(rolandGSMode),
_imsParts(nullptr), _newSystem(newSystem), _numChannels(16), _notesPlaying(nullptr), _notesSustained(nullptr), _idleChain(nullptr), _activeChain(nullptr), _numVoices(12) {
_drv = MidiDriver::createMidi(dev);
@@ -633,6 +639,10 @@ void IMuseDriver_GMidi::deinitDevice() {
}
}
+} // End of namespace Scumm
+
+namespace IMSMidi {
+
/**************************
* MT-32 driver
***************************/
@@ -846,6 +856,10 @@ void IMuseChannel_MT32::sendSysexTimbreData(const byte *data, uint32 dataSize) c
_mt32Drv->sendMT32Sysex(_sysexTimbreAddrBase, data, dataSize);
}
+} // End of namespace IMSMidi
+
+namespace Scumm {
+
IMuseDriver_MT32::IMuseDriver_MT32(MidiDriver::DeviceHandle dev, bool newSystem) : IMuseDriver_GMidi(dev, false, newSystem), _programsMapping(nullptr), _hwRealChain(nullptr) {
#ifdef FORCE_NEWSTYLE_CHANNEL_ALLOCATION
_numChannels = 16;
diff --git a/engines/scumm/imuse/drivers/midi.h b/engines/scumm/imuse/drivers/midi.h
index e2714ebf549..9c91622fa53 100644
--- a/engines/scumm/imuse/drivers/midi.h
+++ b/engines/scumm/imuse/drivers/midi.h
@@ -24,14 +24,16 @@
#include "audio/mididrv.h"
-namespace Scumm {
-
+namespace IMSMidi {
class IMuseChannel_Midi;
class IMuseChannel_MT32;
struct ChannelNode;
+} // End of namespace IMSMidi
+
+namespace Scumm {
class IMuseDriver_GMidi : public MidiDriver {
- friend class IMuseChannel_Midi;
+ friend class IMSMidi::IMuseChannel_Midi;
public:
IMuseDriver_GMidi(MidiDriver::DeviceHandle dev, bool rolandGSMode, bool newSystem);
virtual ~IMuseDriver_GMidi() override;
@@ -53,7 +55,7 @@ public:
MidiChannel *getPercussionChannel() override;
protected:
- IMuseChannel_Midi *getPart(int number);
+ IMSMidi::IMuseChannel_Midi *getPart(int number);
virtual void createChannels();
virtual void createParts();
virtual void releaseChannels();
@@ -62,7 +64,7 @@ protected:
const bool _newSystem;
byte _numChannels;
byte _numVoices;
- IMuseChannel_Midi **_imsParts;
+ IMSMidi::IMuseChannel_Midi **_imsParts;
private:
virtual void initDevice();
@@ -78,15 +80,15 @@ private:
const bool _gsMode;
- ChannelNode *_idleChain;
- ChannelNode *_activeChain;
+ IMSMidi::ChannelNode *_idleChain;
+ IMSMidi::ChannelNode *_activeChain;
uint16 *_notesPlaying;
uint16 *_notesSustained;
};
class IMuseDriver_MT32 final : public IMuseDriver_GMidi {
- friend class IMuseChannel_MT32;
+ friend class IMSMidi::IMuseChannel_MT32;
public:
IMuseDriver_MT32(MidiDriver::DeviceHandle dev, bool newSystem);
~IMuseDriver_MT32() override {}
@@ -101,7 +103,7 @@ private:
// Convenience function that allows to send the sysex message with the exact same arguments as they are used in the original drivers.
void sendMT32Sysex(uint32 addr, const byte *data, uint32 dataSize);
- ChannelNode *_hwRealChain;
+ IMSMidi::ChannelNode *_hwRealChain;
const byte *_programsMapping;
};
diff --git a/engines/scumm/imuse/imuse.cpp b/engines/scumm/imuse/imuse.cpp
index b2e068c1d3a..356ef705644 100644
--- a/engines/scumm/imuse/imuse.cpp
+++ b/engines/scumm/imuse/imuse.cpp
@@ -254,8 +254,6 @@ bool IMuseInternal::supportsPercussion(int sound) {
return _soundType != MDT_AMIGA && _soundType != MDT_MACINTOSH;
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', ' '):
@@ -269,8 +267,6 @@ bool IMuseInternal::supportsPercussion(int sound) {
// 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;
@@ -488,6 +484,11 @@ uint32 IMuseInternal::property(int prop, uint32 value) {
_recycle_players = (value != 0);
break;
+ case IMuse::PROP_QUALITY:
+ if (_midi_native)
+ _midi_native->property(IMuse::PROP_QUALITY, value);
+ break;
+
default:
break;
}
diff --git a/engines/scumm/imuse/imuse.h b/engines/scumm/imuse/imuse.h
index 64251880a96..e4396798919 100644
--- a/engines/scumm/imuse/imuse.h
+++ b/engines/scumm/imuse/imuse.h
@@ -52,7 +52,8 @@ public:
enum {
PROP_TEMPO_BASE,
PROP_LIMIT_PLAYERS,
- PROP_RECYCLE_PLAYERS
+ PROP_RECYCLE_PLAYERS,
+ PROP_QUALITY
};
public:
@@ -68,8 +69,8 @@ public:
public:
virtual void startSoundWithNoteOffset(int sound, int offset) = 0;
- // MusicEngine base class methods.
- // Not actually redefined here because none are implemented.
+ // MusicEngine base class methods. Only this one is implemented:
+ void setQuality(int qual) override { property(PROP_QUALITY, qual); }
public:
// Factory methods
diff --git a/engines/scumm/imuse/imuse_part.cpp b/engines/scumm/imuse/imuse_part.cpp
index 7440752c61c..cb912318e2a 100644
--- a/engines/scumm/imuse/imuse_part.cpp
+++ b/engines/scumm/imuse/imuse_part.cpp
@@ -434,17 +434,12 @@ void Part::set_instrument(uint b) {
_bank = (byte)(b >> 8);
if (_bank)
error("Non-zero instrument bank selection. Please report this");
- // HACK: Horrible hack to allow tracing of program change source.
- // The Mac m68k versions of MI2 and Indy4 use 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->isMacM68kIMuse()) {
+
+ if (_se->_soundType == MDT_MACINTOSH)
_instrument.macSfx(b);
- } else {
+ else
_instrument.program((byte)b, _player->isMT32());
- }
+
if (clearToTransmit())
_instrument.send(_mc);
}
diff --git a/engines/scumm/imuse/instrument.cpp b/engines/scumm/imuse/instrument.cpp
index d96ca2dac18..38dcd80bce4 100644
--- a/engines/scumm/imuse/instrument.cpp
+++ b/engines/scumm/imuse/instrument.cpp
@@ -287,7 +287,7 @@ public:
void send(MidiChannel *mc) override;
void copy_to(Instrument *dest) override { dest->macSfx(_program); }
bool is_valid() override {
- return (_program < 128);
+ return (true);
}
};
@@ -337,8 +337,6 @@ void Instrument::pcspk(const byte *instrument) {
void Instrument::macSfx(byte prog) {
clear();
- if (prog > 127)
- return;
_type = itMacSfx;
_instrument = new Instrument_MacSfx(prog);
}
@@ -402,10 +400,13 @@ void Instrument_Program::saveLoadWithSerializer(Common::Serializer &s) {
s.syncAsByte(_program);
if (s.isSaving()) {
s.syncAsByte(_soundTypeMT32);
+ s.syncAsByte(_nativeMT32Device);
} else {
byte tmp;
s.syncAsByte(tmp);
_soundTypeMT32 = (tmp > 0);
+ s.syncAsByte(tmp, VER(122));
+ _nativeMT32Device = (tmp > 0);
}
}
@@ -534,9 +535,6 @@ void Instrument_PcSpk::send(MidiChannel *mc) {
Instrument_MacSfx::Instrument_MacSfx(byte program) :
_program(program) {
- if (program > 127) {
- _program = 255;
- }
}
Instrument_MacSfx::Instrument_MacSfx(Common::Serializer &s) {
@@ -551,9 +549,10 @@ void Instrument_MacSfx::saveLoadWithSerializer(Common::Serializer &s) {
}
void Instrument_MacSfx::send(MidiChannel *mc) {
- if (_program > 127) {
+ if (!mc)
return;
- }
- mc->sysEx_customInstrument('MAC ', &_program, sizeof(_program));
+ mc->controlChange(0x20, 1);
+ mc->programChange(_program);
+ mc->controlChange(0x20, 0);
}
} // End of namespace Scumm
diff --git a/engines/scumm/macgui/macgui_v5.cpp b/engines/scumm/macgui/macgui_v5.cpp
index c33a707c586..a5080c47af8 100644
--- a/engines/scumm/macgui/macgui_v5.cpp
+++ b/engines/scumm/macgui/macgui_v5.cpp
@@ -845,7 +845,7 @@ bool MacV5Gui::runOptionsDialog() {
int textSpeed = _vm->_defaultTextSpeed;
int musicQuality = ConfMan.hasKey("mac_snd_quality") ? ConfMan.getInt("mac_snd_quality") : 0;
int musicQualityOption = (musicQuality == 0) ? 1 : (musicQuality - 1) % 3;
- musicQuality = (musicQuality == 0) ? (_vm->VAR(_vm->VAR_SOUNDCARD) == 10 ? 0 : 2) : (musicQuality - 1) / 3;
+ musicQuality = (musicQuality == 0) ? 2 : (musicQuality - 1) / 3;
if (_vm->_game.id == GID_MONKEY) {
checkboxMusic = (MacCheckbox *)window->getWidget(kWidgetCheckbox, 1);
diff --git a/engines/scumm/metaengine.cpp b/engines/scumm/metaengine.cpp
index fa354c6849a..a33595a500c 100644
--- a/engines/scumm/metaengine.cpp
+++ b/engines/scumm/metaengine.cpp
@@ -200,10 +200,6 @@ Common::Path ScummEngine_v70he::generateFilename(const int room) const {
return Common::Path(result, Common::Path::kNoSeparator);
}
-bool ScummEngine::isMacM68kIMuse() const {
- return _game.platform == Common::kPlatformMacintosh && (_game.id == GID_MONKEY2 || _game.id == GID_INDY4) && !(_game.features & GF_MAC_CONTAINER);
-}
-
} // End of namespace Scumm
#pragma mark -
@@ -411,10 +407,13 @@ Common::Error ScummMetaEngine::createInstance(OSystem *syst, Engine **engine,
res.language = Common::parseLanguage(ConfMan.get("language"));
// V3 FM-TOWNS games *always* should use the corresponding music driver,
- // anything else makes no sense for them.
+ // anything else makes no sense for them. Same for Mac (but not limited to V3).
// TODO: Maybe allow the null driver, too?
if (res.game.platform == Common::kPlatformFMTowns && res.game.version == 3)
res.game.midi = MDT_TOWNS;
+ else if (res.game.platform == Common::kPlatformMacintosh && res.game.version < 6)
+ res.game.midi = MDT_MACINTOSH;
+
// Finally, we have massaged the GameDescriptor to our satisfaction, and can
// instantiate the appropriate game engine. Hooray!
switch (res.game.version) {
diff --git a/engines/scumm/module.mk b/engines/scumm/module.mk
index 8b862ea1be2..6783741c648 100644
--- a/engines/scumm/module.mk
+++ b/engines/scumm/module.mk
@@ -35,7 +35,7 @@ MODULE_OBJS := \
imuse/drivers/amiga.o \
imuse/drivers/fmtowns.o \
imuse/drivers/midi.o \
- imuse/drivers/mac_m68k.o \
+ imuse/drivers/macintosh.o \
imuse/drivers/pcspk.o \
input.o \
ks_check.o \
diff --git a/engines/scumm/players/mac_sound_lowlevel.h b/engines/scumm/players/mac_sound_lowlevel.h
new file mode 100644
index 00000000000..f1bc45a7b8b
--- /dev/null
+++ b/engines/scumm/players/mac_sound_lowlevel.h
@@ -0,0 +1,255 @@
+/* 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 SCUMM_PLAYERS_MAC_SOUND_LOWLEVEL_H
+#define SCUMM_PLAYERS_MAC_SOUND_LOWLEVEL_H
+
+#include "audio/audiostream.h"
+#include "audio/mixer.h"
+#include "common/array.h"
+#include "common/func.h"
+
+namespace Scumm {
+
+class MacSoundDriver {
+public:
+ MacSoundDriver(Common::Mutex &mutex, uint32 deviceRate, int activeChannels, bool canInterpolate, bool internal16Bit) : _mutex(mutex),
+ _smpSize(internal16Bit ? 2 : 1), _smpMin(internal16Bit ? -32768 : -128), _smpMax(internal16Bit ? 32767 : 127) {
+ for (int i = 0; i < 4; ++i) {
+ _status[i].deviceRate = deviceRate;
+ _status[i].numExternalMixChannels = activeChannels;
+ _status[i].allowInterPolation = canInterpolate;
+ _status[i].flags = 0;
+ }
+ }
+ virtual ~MacSoundDriver() {}
+ virtual void feed(int8 *dst, uint32 byteSize, Audio::Mixer::SoundType type, bool expectStereo) = 0;
+
+ struct Status {
+ Status() : deviceRate(0), numExternalMixChannels(0), allowInterPolation(false), flags(0) {}
+ uint32 deviceRate;
+ int numExternalMixChannels;
+ bool allowInterPolation;
+ uint8 flags;
+ };
+ const Status &getStatus(Audio::Mixer::SoundType sndType = Audio::Mixer::kPlainSoundType) const { return _status[sndType]; }
+
+ enum StatusFlag : uint8 {
+ kStatusPlaying = 1 << 0,
+ kStatusOverflow = 1 << 1,
+ kStatusStartup = 1 << 2,
+ kStatusDone = 1 << 3,
+ kStatusDisabled = 1 << 7
+ };
+
+ void clearFlags(uint8 flags, Audio::Mixer::SoundType sndType = Audio::Mixer::kPlainSoundType) { _status[sndType].flags &= ~flags; }
+ void setFlags(uint8 flags, Audio::Mixer::SoundType sndType = Audio::Mixer::kPlainSoundType) { _status[sndType].flags |= flags; }
+
+protected:
+ Common::Mutex &_mutex;
+ const int _smpSize;
+ const int16 _smpMin;
+ const int16 _smpMax;
+ Status _status[4];
+};
+
+class MacSndChannel;
+class MacLowLevelPCMDriver final : public MacSoundDriver {
+public:
+ struct PCMSound {
+ PCMSound() : len(0), rate(0), loopst(0), loopend(0), baseFreq(0), stereo(false), enc(0) {}
+ PCMSound(Common::SharedPtr<const byte> a, uint32 b, uint32 c, uint32 d, uint32 e, byte f, byte g, bool h) : data(a), len(b), rate(c), loopst(d), loopend(e), baseFreq(f), enc(g), stereo(h) {}
+ Common::SharedPtr<const byte> data;
+ uint32 len;
+ uint32 rate;
+ uint32 loopst;
+ uint32 loopend;
+ byte baseFreq;
+ byte enc;
+ bool stereo;
+ };
+
+ enum SynthType : byte {
+ kSquareWaveSynth = 1,
+ kWaveTableSynth = 3,
+ kSampledSynth = 5,
+ kIgnoreSynth = 0xff
+ };
+
+ enum ChanAttrib : byte {
+ kInitChanLeft = 2,
+ kInitChanRight = 3,
+ kWaveInitChannel0 = 4,
+ kWaveInitChannel1 = 5,
+ kWaveInitChannel2 = 6,
+ kWaveInitChannel3 = 7,
+ kNoInterp = 4,
+ kInitNoDrop = 8,
+ kInitMono = 0x80,
+ kInitStereo = 0xC0
+ };
+
+ enum ExecMode : byte {
+ kImmediate,
+ kEnqueue,
+ };
+
+ typedef int ChanHandle;
+
+ struct DoubleBuffer {
+ enum Flags : uint32 {
+ kBufferReady = 1,
+ kLastBufferLast = 4
+ };
+ DoubleBuffer() : numFrames(0), flags(0), data(0) {}
+ ~DoubleBuffer() { delete[] data; }
+ uint32 numFrames;
+ uint32 flags;
+ byte *data;
+ };
+
+ class CallbackClient {
+ public:
+ virtual ~CallbackClient() {}
+ virtual void sndChannelCallback(uint16 arg1, const void *arg2) {}
+ virtual void dblBuffCallback(DoubleBuffer *dblBuffer) {}
+ };
+ typedef Common::Functor2Mem<uint16, const void*, void, CallbackClient> ChanCallback;
+ typedef Common::Functor1Mem<DoubleBuffer*, void, CallbackClient> DBCallback;
+
+public:
+ MacLowLevelPCMDriver(Common::Mutex &mutex, uint32 deviceRate, bool internal16Bit);
+ ~MacLowLevelPCMDriver() override;
+
+ void feed(int8 *dst, uint32 byteSize, Audio::Mixer::SoundType type, bool expectStereo) override;
+
+ ChanHandle createChannel(Audio::Mixer::SoundType sndType, SynthType synthType, byte attributes, ChanCallback *callback);
+ void disposeChannel(ChanHandle handle);
+
+ void playSamples(ChanHandle handle, ExecMode mode, const PCMSound *snd);
+ void playNote(ChanHandle handle, ExecMode mode, uint8 note, uint16 duration);
+ void quiet(ChanHandle handle, ExecMode mode);
+ void flush(ChanHandle handle, ExecMode mode);
+ void wait(ChanHandle handle, ExecMode mode, uint16 duration);
+ void loadWaveTable(ChanHandle handle, ExecMode mode, const byte *data, uint16 dataSize);
+ void loadInstrument(ChanHandle handle, ExecMode mode, const PCMSound *snd);
+ void setTimbre(ChanHandle handle, ExecMode mode, uint16 timbre);
+ void callback(ChanHandle handle, ExecMode mode, uint16 arg1, const void *arg2);
+
+ bool playDoubleBuffer(ChanHandle handle, byte numChan, byte bitsPerSample, uint32 rate, DBCallback *callback, byte externalMixNumChan = 1);
+
+ uint8 getChannelStatus(ChanHandle handle) const;
+ void clearChannelFlags(ChanHandle handle, uint8 flags);
+
+private:
+ void updateStatus(Audio::Mixer::SoundType sndType);
+ MacSndChannel *findAndCheckChannel(ChanHandle h, const char *caller, byte reqSynthType) const;
+ MacSndChannel *findChannel(ChanHandle h) const;
+ Common::Array<MacSndChannel*> _channels;
+ int _numInternalMixChannels;
+ int32 *_mixBuffer = 0;
+ uint32 _mixBufferSize;
+};
+
+class VblTaskClientDriver {
+public:
+ virtual ~VblTaskClientDriver() {}
+ virtual void vblCallback() = 0;
+ virtual void generateData(int8 *dst, uint32 len, Audio::Mixer::SoundType type, bool expectStereo) const = 0;
+ virtual const MacSoundDriver::Status &getDriverStatus(Audio::Mixer::SoundType sndType) const = 0;
+};
+
+class MacPlayerAudioStream : public Audio::AudioStream {
+public:
+ MacPlayerAudioStream(VblTaskClientDriver *drv, uint32 scummVMOutputrate, bool stereo, bool interpolate, bool internal16Bit);
+ ~MacPlayerAudioStream() override;
+
+ void initBuffers(uint32 feedBufferSize);
+ void initDrivers();
+ void addVolumeGroup(Audio::Mixer::SoundType type);
+ typedef Common::Functor0Mem<void, VblTaskClientDriver> CallbackProc;
+ void setVblCallback(const CallbackProc *proc);
+ void clearBuffer();
+
+ void setMasterVolume(Audio::Mixer::SoundType type, uint16 vol);
+
+ // AudioStream interface
+ int readBuffer(int16 *buffer, const int numSamples) override;
+ bool isStereo() const override { return _isStereo; }
+ int getRate() const override { return _outputRate; }
+ bool endOfData() const override { return false; }
+
+private:
+ void generateData(int8 *dst, uint32 byteSize, Audio::Mixer::SoundType, bool expectStereo) const;
+ void runVblTask();
+
+ VblTaskClientDriver *_drv;
+ int _numGroups;
+
+ uint32 _vblSmpQty;
+ uint32 _vblSmpQtyRem;
+ uint32 _vblCountDown;
+ uint32 _vblCountDownRem;
+ const CallbackProc *_vblCbProc;
+
+ struct SmpBuffer {
+ SmpBuffer() : start(0), pos(0), end(0), volume(0), lastL(0), lastR(0), size(0), rateConvInt(0), rateConvFrac(0), rateConvAcc(-1), group(Audio::Mixer::kPlainSoundType) {}
+ int8 *start;
+ int8 *pos;
+ const int8 *end;
+ uint32 volume;
+ int32 lastL;
+ int32 lastR;
+ uint32 size;
+ uint32 rateConvInt;
+ uint32 rateConvFrac;
+ int32 rateConvAcc;
+ Audio::Mixer::SoundType group;
+ } *_buffers;
+
+ const uint32 _outputRate;
+ const uint8 _frameSize;
+ const bool _interp;
+ const int _smpInternalSize;
+
+ const bool _isStereo;
+};
+
+struct MacSndInstrument {
+public:
+ MacSndInstrument(uint32 id, Common::SeekableReadStream *&in, Common::String &&name);
+ ~MacSndInstrument() {}
+ const MacLowLevelPCMDriver::PCMSound *data() const { return &_snd; }
+ uint32 id() const { return _id; }
+ const char* name() { return _name.c_str(); }
+
+private:
+ uint32 _id;
+ Common::String _name;
+ MacLowLevelPCMDriver::PCMSound _snd;
+};
+
+extern const uint8 _fourToneSynthWaveForm[256];
+extern const uint32 _fourToneSynthWaveFormSize;
+
+} // End of namespace Scumm
+
+#endif
diff --git a/engines/scumm/players/player_mac_indy3.cpp b/engines/scumm/players/player_mac_indy3.cpp
index 6b65f99acb0..06d680da301 100644
--- a/engines/scumm/players/player_mac_indy3.cpp
+++ b/engines/scumm/players/player_mac_indy3.cpp
@@ -39,9 +39,14 @@ extern const uint32 g_pv2ModTblSize;
class LegacyMusicDriver : public MacSoundDriver {
public:
- LegacyMusicDriver(uint16 numChannels, Common::Mutex &mutex, bool canInterpolate, bool internal16Bit) :
+ LegacyMusicDriver(uint16 numChannels, Audio::Mixer::SoundType sndType, Common::Mutex &mutex, bool canInterpolate, bool internal16Bit) :
MacSoundDriver(mutex, ASC_DEVICE_RATE, internal16Bit ? numChannels : 1, canInterpolate, internal16Bit),
- _numChan(numChannels), _sndType(Audio::Mixer::kMusicSoundType) {}
+ _numChan(numChannels), _sndType(sndType) {
+ for (int i = 0; i < ARRAYSIZE(_status); ++i) {
+ if (sndType != i)
+ _status[i].flags |= kStatusDisabled;
+ }
+ }
virtual void start() = 0;
virtual void stop() = 0;
@@ -78,7 +83,7 @@ protected:
class FourToneSynthDriver final : public LegacyMusicDriver {
public:
- FourToneSynthDriver(Common::Mutex &mutex, bool internal16bit);
+ FourToneSynthDriver(Audio::Mixer::SoundType sndType, Common::Mutex &mutex, bool internal16bit);
~FourToneSynthDriver() override;
void feed(int8 *dst, uint32 byteSize, Audio::Mixer::SoundType type, bool expectStereo) override;
@@ -106,7 +111,7 @@ private:
class SquareWaveSynthDriver final : public LegacyMusicDriver {
public:
- SquareWaveSynthDriver(Common::Mutex &mutex, bool internal16Bit);
+ SquareWaveSynthDriver(Audio::Mixer::SoundType sndType, Common::Mutex &mutex, bool internal16Bit);
~SquareWaveSynthDriver() override {};
void feed(int8 *dst, uint32 byteSize, Audio::Mixer::SoundType type, bool expectStereo) override;
@@ -135,7 +140,7 @@ private:
uint32 _phase;
};
-FourToneSynthDriver::FourToneSynthDriver(Common::Mutex &mutex, bool internal16Bit) : LegacyMusicDriver(4, mutex, false, internal16Bit), _duration(0), _pos(0), _chan(nullptr) {
+FourToneSynthDriver::FourToneSynthDriver(Audio::Mixer::SoundType sndType, Common::Mutex &mutex, bool internal16Bit) : LegacyMusicDriver(4, sndType, mutex, false, internal16Bit), _duration(0), _pos(0), _chan(nullptr) {
_chan = new Channel[_numChan];
}
@@ -239,8 +244,8 @@ void FourToneSynthDriver::setRate(uint8 chan, uint16 rate) {
_chan[chan].rate = rate ? (0x5060000 / (rate >> ((rate < 1600) ? 4 : 6))) : 0;
}
-SquareWaveSynthDriver::SquareWaveSynthDriver(Common::Mutex &mutex, bool internal16Bit) :
- LegacyMusicDriver(1, mutex, false, internal16Bit), _count(0xffff), _duration(0), _amplitude(0), _phase(0), _pos(0) {
+SquareWaveSynthDriver::SquareWaveSynthDriver(Audio::Mixer::SoundType sndType, Common::Mutex &mutex, bool internal16Bit) :
+ LegacyMusicDriver(1, sndType, mutex, false, internal16Bit), _count(0xffff), _duration(0), _amplitude(0), _phase(0), _pos(0) {
}
void SquareWaveSynthDriver::feed(int8 *dst, uint32 byteSize, Audio::Mixer::SoundType type, bool expectStereo) {
@@ -395,7 +400,7 @@ bool Indy3MacSnd::startDevices(uint32 outputRate, uint32 pcmDeviceRate, uint32 f
return false;
_sdrv = new MacLowLevelPCMDriver(_mixer->mutex(), pcmDeviceRate, internal16Bit);
- FourToneSynthDriver *mdrv = new FourToneSynthDriver(_mixer->mutex(), internal16Bit);
+ FourToneSynthDriver *mdrv = new FourToneSynthDriver(Audio::Mixer::kMusicSoundType, _mixer->mutex(), internal16Bit);
if (!mdrv || !_sdrv)
return false;
@@ -406,12 +411,14 @@ bool Indy3MacSnd::startDevices(uint32 outputRate, uint32 pcmDeviceRate, uint32 f
_mdrv = mdrv;
_sfxChan = _sdrv->createChannel(Audio::Mixer::kSFXSoundType, MacLowLevelPCMDriver::kSampledSynth, 0, nullptr);
+ _sdrv->setFlags(MacSoundDriver::kStatusDisabled, Audio::Mixer::kMusicSoundType);
_drivers.push_back(_mdrv);
_drivers.push_back(_sdrv);
- _macstr->initDrivers();
_macstr->initBuffers(feedBufferSize);
+ _macstr->addVolumeGroup(Audio::Mixer::kMusicSoundType);
+ _macstr->addVolumeGroup(Audio::Mixer::kSFXSoundType);
_macstr->setVblCallback(&_nextTickProc);
_mixer->playStream(Audio::Mixer::kPlainSoundType, &_soundHandle, _macstr, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
@@ -494,14 +501,14 @@ void Indy3MacSnd::setQuality(int qual) {
_qmode = qual;
if (isHiQuality()) {
- FourToneSynthDriver *mdrv = new FourToneSynthDriver(_mixer->mutex(), _16bit);
+ FourToneSynthDriver *mdrv = new FourToneSynthDriver(Audio::Mixer::kMusicSoundType, _mixer->mutex(), _16bit);
assert(mdrv);
for (int i = 0; i < mdrv->numChannels(); ++i)
mdrv->send(LegacyMusicDriver::kChanWaveform, i, _fourToneSynthWaveForm, sizeof(_fourToneSynthWaveForm));
_mdrv = mdrv;
_qualHi = true;
} else {
- _mdrv = new SquareWaveSynthDriver(_mixer->mutex(), _16bit);
+ _mdrv = new SquareWaveSynthDriver(Audio::Mixer::kMusicSoundType, _mixer->mutex(), _16bit);
_qualHi = false;
assert(_mdrv);
}
@@ -577,10 +584,19 @@ void Indy3MacSnd::generateData(int8 *dst, uint32 len, Audio::Mixer::SoundType ty
(*i)->feed(dst, len, type, expectStereo);
}
-const MacSoundDriver::Status &Indy3MacSnd::getDriverStatus(uint8 drvID, Audio::Mixer::SoundType type) const {
- if (drvID >= _drivers.size())
- error("Indy3MacSnd::getDriverCaps(): Invalid driver id %d", drvID);
- return _drivers[drvID]->getStatus(type);
+const MacSoundDriver::Status &Indy3MacSnd::getDriverStatus(Audio::Mixer::SoundType type) const {
+ Common::Array<MacSoundDriver*>::const_iterator res = _drivers.end();
+ for (Common::Array<MacSoundDriver*>::const_iterator i = _drivers.begin(); i != _drivers.end(); ++i) {
+ if ((*i)->getStatus(type).flags & MacSoundDriver::kStatusDisabled)
+ continue;
+ if (res != _drivers.end())
+ warning("%s(): Multiple drivers for sound type %d", __FUNCTION__, type);
+ res = i;
+ }
+ if (res == _drivers.end())
+ error("%s(): No sound driver for type %d", __FUNCTION__, type);
+
+ return (*res)->getStatus(type);
}
void Indy3MacSnd::startSong(int id) {
diff --git a/engines/scumm/players/player_mac_intern.h b/engines/scumm/players/player_mac_intern.h
index de1538c8723..15c39b4073e 100644
--- a/engines/scumm/players/player_mac_intern.h
+++ b/engines/scumm/players/player_mac_intern.h
@@ -22,198 +22,13 @@
#ifndef SCUMM_PLAYERS_PLAYER_MAC_INTERN_H
#define SCUMM_PLAYERS_PLAYER_MAC_INTERN_H
-#include "audio/audiostream.h"
-#include "audio/mixer.h"
-#include "common/array.h"
-#include "common/func.h"
+#include "engines/scumm/players/mac_sound_lowlevel.h"
+#include "common/serializer.h"
namespace Scumm {
-class MacSoundDriver {
-public:
- MacSoundDriver(Common::Mutex &mutex, uint32 deviceRate, int activeChannels, bool canInterpolate, bool internal16Bit) : _mutex(mutex),
- _smpSize(internal16Bit ? 2 : 1), _smpMin(internal16Bit ? -32768 : -128), _smpMax(internal16Bit ? 32767 : 127) {
- for (int i = 0; i < 4; ++i) {
- _status[i].deviceRate = deviceRate;
- _status[i].numExternalMixChannels = activeChannels;
- _status[i].allowInterPolation = canInterpolate;
- _status[i].flags = 0;
- }
- }
- virtual ~MacSoundDriver() {}
- virtual void feed(int8 *dst, uint32 byteSize, Audio::Mixer::SoundType type, bool expectStereo) = 0;
-
- struct Status {
- Status() : deviceRate(0), numExternalMixChannels(0), allowInterPolation(false), flags(0) {}
- uint32 deviceRate;
- int numExternalMixChannels;
- bool allowInterPolation;
- uint8 flags;
- };
- const Status &getStatus(Audio::Mixer::SoundType sndType = Audio::Mixer::kPlainSoundType) const { return _status[sndType]; }
-
- enum StatusFlag : uint8 {
- kStatusPlaying = 1 << 0,
- kStatusOverflow = 1 << 1,
- kStatusStartup = 1 << 2,
- kStatusDone = 1 << 3
- };
-
- void clearFlags(uint8 flags, Audio::Mixer::SoundType sndType = Audio::Mixer::kPlainSoundType) { _status[sndType].flags &= ~flags; }
- void setFlags(uint8 flags, Audio::Mixer::SoundType sndType = Audio::Mixer::kPlainSoundType) { _status[sndType].flags |= flags; }
-
-protected:
- Common::Mutex &_mutex;
- const int _smpSize;
- const int16 _smpMin;
- const int16 _smpMax;
- Status _status[4];
-};
-
-class MacSndChannel;
-class MacLowLevelPCMDriver final : public MacSoundDriver {
-public:
- struct PCMSound {
- PCMSound() : len(0), rate(0), loopst(0), loopend(0), baseFreq(0), stereo(false), enc(0) {}
- PCMSound(Common::SharedPtr<const byte> a, uint32 b, uint32 c, uint32 d, uint32 e, byte f, byte g, bool h) : data(a), len(b), rate(c), loopst(d), loopend(e), baseFreq(f), enc(g), stereo(h) {}
- Common::SharedPtr<const byte> data;
- uint32 len;
- uint32 rate;
- uint32 loopst;
- uint32 loopend;
- byte baseFreq;
- byte enc;
- bool stereo;
- };
-
- enum SynthType : byte {
- kSquareWaveSynth = 1,
- kWaveTableSynth = 3,
- kSampledSynth = 5,
- kIgnoreSynth = 0xff
- };
-
- enum ChanAttrib : byte {
- kInitChanLeft = 2,
- kInitChanRight = 3,
- kWaveInitChannel0 = 4,
- kWaveInitChannel1 = 5,
- kWaveInitChannel2 = 6,
- kWaveInitChannel3 = 7,
- kNoInterp = 4,
- kInitNoDrop = 8,
- kInitMono = 0x80,
- kInitStereo = 0xC0
- };
-
- enum ExecMode : byte {
- kImmediate,
- kEnqueue,
- };
-
- typedef int ChanHandle;
-
- class CallbackClient {
- public:
- virtual ~CallbackClient() {}
- virtual void sndChannelCallback(uint16 arg1, const void *arg2) = 0;
- };
- typedef Common::Functor2Mem<uint16, const void*, void, CallbackClient> ChanCallback;
-
-public:
- MacLowLevelPCMDriver(Common::Mutex &mutex, uint32 deviceRate, bool internal16Bit);
- ~MacLowLevelPCMDriver() override;
-
- void feed(int8 *dst, uint32 byteSize, Audio::Mixer::SoundType type, bool expectStereo) override;
-
- ChanHandle createChannel(Audio::Mixer::SoundType sndType, SynthType synthType, byte attributes, ChanCallback *callback);
- void disposeChannel(ChanHandle handle);
-
- void playSamples(ChanHandle handle, ExecMode mode, const PCMSound *snd);
- void playNote(ChanHandle handle, ExecMode mode, uint8 note, uint16 duration);
- void quiet(ChanHandle handle, ExecMode mode);
- void flush(ChanHandle handle, ExecMode mode);
- void wait(ChanHandle handle, ExecMode mode, uint16 duration);
- void loadWaveTable(ChanHandle handle, ExecMode mode, const byte *data, uint16 dataSize);
- void loadInstrument(ChanHandle handle, ExecMode mode, const PCMSound *snd);
- void setTimbre(ChanHandle handle, ExecMode mode, uint16 timbre);
- void callback(ChanHandle handle, ExecMode mode, uint16 arg1, const void *arg2);
-
- uint8 getChannelStatus(ChanHandle handle) const;
- void clearChannelFlags(ChanHandle handle, uint8 flags);
-
-private:
- void updateStatus(Audio::Mixer::SoundType sndType);
- MacSndChannel *findAndCheckChannel(ChanHandle h, const char *caller, byte reqSynthType) const;
- MacSndChannel *findChannel(ChanHandle h) const;
- Common::Array<MacSndChannel*> _channels;
- int _numInternalMixChannels;
- int32 *_mixBuffer = 0;
- uint32 _mixBufferSize;
-};
-
-class VblTaskClientDriver {
-public:
- virtual ~VblTaskClientDriver() {}
- virtual void vblCallback() = 0;
- virtual void generateData(int8 *dst, uint32 len, Audio::Mixer::SoundType type, bool expectStereo) const = 0;
- virtual const MacSoundDriver::Status &getDriverStatus(uint8 drvID, Audio::Mixer::SoundType sndType) const = 0;
-};
-
-class MacPlayerAudioStream : public Audio::AudioStream {
-public:
- MacPlayerAudioStream(VblTaskClientDriver *drv, uint32 scummVMOutputrate, bool stereo, bool interpolate, bool internal16Bit);
- ~MacPlayerAudioStream() override;
-
- void initBuffers(uint32 feedBufferSize);
- void initDrivers();
- typedef Common::Functor0Mem<void, VblTaskClientDriver> CallbackProc;
- void setVblCallback(const CallbackProc *proc);
- void clearBuffer();
-
- void setMasterVolume(Audio::Mixer::SoundType type, uint16 vol);
-
- // AudioStream interface
- int readBuffer(int16 *buffer, const int numSamples) override;
- bool isStereo() const override { return _isStereo; }
- int getRate() const override { return _outputRate; }
- bool endOfData() const override { return false; }
-
-private:
- void generateData(int8 *dst, uint32 byteSize, Audio::Mixer::SoundType, bool expectStereo) const;
- void runVblTask();
-
- VblTaskClientDriver *_drv;
-
- uint32 _vblSmpQty;
- uint32 _vblSmpQtyRem;
- uint32 _vblCountDown;
- uint32 _vblCountDownRem;
- const CallbackProc *_vblCbProc;
-
- struct SmpBuffer {
- SmpBuffer() : start(0), pos(0), end(0), volume(0x10000), lastL(0), lastR(0), size(0), rateConvInt(0), rateConvFrac(0), rateConvAcc(-1) {}
- int8 *start;
- int8 *pos;
- const int8 *end;
- uint32 volume;
- int32 lastL;
- int32 lastR;
- uint32 size;
- uint32 rateConvInt;
- uint32 rateConvFrac;
- int32 rateConvAcc;
- } _buffers[2];
-
- const uint32 _outputRate;
- const uint8 _frameSize;
- const bool _interp;
- const int _smpInternalSize;
-
- const bool _isStereo;
-};
-
class LegacyMusicDriver;
+class ScummEngine;
class Indy3MacSnd final : public VblTaskClientDriver {
private:
Indy3MacSnd(ScummEngine *vm, Audio::Mixer *mixer);
@@ -237,7 +52,7 @@ public:
void vblCallback() override;
void generateData(int8 *dst, uint32 byteSize, Audio::Mixer::SoundType type, bool expectStereo) const override;
- const MacSoundDriver::Status &getDriverStatus(uint8 drvID, Audio::Mixer::SoundType sndType) const override;
+ const MacSoundDriver::Status &getDriverStatus(Audio::Mixer::SoundType sndType) const override;
private:
void startSong(int id);
@@ -394,7 +209,7 @@ public:
void vblCallback() override;
void generateData(int8 *dst, uint32 byteSize, Audio::Mixer::SoundType type, bool expectStereo) const override;
- const MacSoundDriver::Status &getDriverStatus(uint8, Audio::Mixer::SoundType sndType) const override;
+ const MacSoundDriver::Status &getDriverStatus(Audio::Mixer::SoundType sndType) const override;
void sndChannelCallback(uint16 arg1, const void *arg2) override;
@@ -449,9 +264,6 @@ private:
byte _chanPlaying;
};
-extern const uint8 _fourToneSynthWaveForm[256];
-extern const uint32 _fourToneSynthWaveFormSize;
-
} // End of namespace Scumm
#endif
diff --git a/engines/scumm/players/player_mac_loom_monkey.cpp b/engines/scumm/players/player_mac_loom_monkey.cpp
index 389edc1a747..a5990f8746c 100644
--- a/engines/scumm/players/player_mac_loom_monkey.cpp
+++ b/engines/scumm/players/player_mac_loom_monkey.cpp
@@ -62,21 +62,7 @@ public:
protected:
bool loadInstruments(const char *const *tryFileNames, uint16 numTryFileNames, uint16 numInstruments);
- struct Instrument {
- public:
- Instrument(uint32 id, Common::SeekableReadStream *&in, Common::String &&name);
- ~Instrument() { _res.reset(); }
- const MacLowLevelPCMDriver::PCMSound *data() const { return &_snd; }
- uint32 id() const { return _id; }
-
- private:
- uint32 _id;
- Common::SharedPtr<const byte> _res;
- Common::String _name;
- MacLowLevelPCMDriver::PCMSound _snd;
- };
-
- Common::Array<Common::SharedPtr<Instrument> > _instruments;
+ Common::Array<Common::SharedPtr<MacSndInstrument> > _instruments;
byte _sndRes6;
byte _isMusic;
@@ -89,8 +75,8 @@ protected:
const byte *_chanSndData[5];
uint32 _chanNumEvents[5];
uint32 _chanCurEvent[5];
- const Common::SharedPtr<Instrument> *_chanInstr[5];
- const Common::SharedPtr<Instrument> *_chanInstr2[5];
+ const Common::SharedPtr<MacSndInstrument> *_chanInstr[5];
+ const Common::SharedPtr<MacSndInstrument> *_chanInstr2[5];
const bool _useInstrTag;
};
@@ -113,7 +99,7 @@ public:
bool restartSoundAfterLoad() const override { return isMusic(); }
bool ignoreMachineRating() const override { return false; }
private:
- const Common::SharedPtr<Instrument> *fetchInstrument(uint16 id) const;
+ const Common::SharedPtr<MacSndInstrument> *fetchInstrument(uint16 id) const;
};
class MonkeyMacSndLoader final : public MacSndLoader {
@@ -134,31 +120,12 @@ public:
bool restartSoundAfterLoad() const override { return _isMusic && _loop; }
bool ignoreMachineRating() const override { return true; }
private:
- const Common::SharedPtr<Instrument> *fetchInstrument(const byte *data, uint32 dataSize, uint32 tagOrOffset);
+ const Common::SharedPtr<MacSndInstrument> *fetchInstrument(const byte *data, uint32 dataSize, uint32 tagOrOffset);
bool _blockSfx;
byte _transpose;
const byte _numInstrumentsMax;
};
-MacSndLoader::Instrument::Instrument(uint32 id, Common::SeekableReadStream *&in, Common::String &&name) : _id(id), _name(Common::move(name)) {
- in->seek(2);
- uint16 numTypes = in->readUint16BE();
- in->seek(numTypes * 6 + 4);
- in->seek(in->readUint16BE() * 8 + numTypes * 6 + 10);
-
- _snd.len = in->readUint32BE();
- _snd.rate = in->readUint32BE();
- _snd.loopst = in->readUint32BE();
- _snd.loopend = in->readUint32BE();
- _snd.enc = in->readByte();
- _snd.baseFreq = in->readByte();
-
- byte *buff = new byte[_snd.len];
- if (in->read(buff, _snd.len) != _snd.len)
- error("MacSndLoader::Instrument::Instrument(): Data error");
- _snd.data = Common::SharedPtr<const byte>(buff, Common::ArrayDeleter<const byte>());
-}
-
MacSndLoader::MacSndLoader(bool useInstrTag) : _sndRes6(0), _isMusic(0), _sndRes9(0), _sndRes10(0), _chanSetup(0),
_timbre(0), _useInstrTag(useInstrTag), _synth(0), _loop(false) {
memset(_chanInstr, 0, sizeof(_chanInstr));
@@ -214,7 +181,7 @@ bool MacSndLoader::loadInstruments(const char *const *tryFileNames, uint16 numTr
Common::String nm(resMan.getResName(MKTAG('s', 'n', 'd', ' '), ids[i]));
memcpy(&tag, nm.c_str(), MIN<uint>(nm.size(), sizeof(tag)));
uint32 id = _useInstrTag ? FROM_BE_32(tag) : ids[i];
- _instruments.push_back(Common::SharedPtr<Instrument>(new Instrument(id, str, Common::move(nm))));
+ _instruments.push_back(Common::SharedPtr<MacSndInstrument>(new MacSndInstrument(id, str, Common::move(nm))));
delete str;
}
@@ -278,9 +245,9 @@ bool LoomMacSndLoader::parseNextEvent(uint16 chan, uint16 &duration, uint8 ¬e
return true;
}
-const Common::SharedPtr<MacSndLoader::Instrument> *LoomMacSndLoader::fetchInstrument(uint16 id) const {
- Common::Array<Common::SharedPtr<Instrument> >::const_iterator instr = _instruments.end();
- for (Common::Array<Common::SharedPtr<Instrument> >::const_iterator i = _instruments.begin(); i != _instruments.end(); ++i) {
+const Common::SharedPtr<MacSndInstrument> *LoomMacSndLoader::fetchInstrument(uint16 id) const {
+ Common::Array<Common::SharedPtr<MacSndInstrument> >::const_iterator instr = _instruments.end();
+ for (Common::Array<Common::SharedPtr<MacSndInstrument> >::const_iterator i = _instruments.begin(); i != _instruments.end(); ++i) {
if ((*i)->id() == id)
return i;
else if ((*i)->id() == 0x2D1C)
@@ -389,10 +356,10 @@ bool MonkeyMacSndLoader::parseNextEvent(uint16 chan, uint16 &duration, uint8 &no
return true;
}
-const Common::SharedPtr<MacSndLoader::Instrument> *MonkeyMacSndLoader::fetchInstrument(const byte *data, uint32 dataSize, uint32 tagOrOffset) {
- Common::Array<Common::SharedPtr<Instrument> >::const_iterator instr = _instruments.end();
+const Common::SharedPtr<MacSndInstrument> *MonkeyMacSndLoader::fetchInstrument(const byte *data, uint32 dataSize, uint32 tagOrOffset) {
+ Common::Array<Common::SharedPtr<MacSndInstrument> >::const_iterator instr = _instruments.end();
if (tagOrOffset & ~0x7fffff) {
- for (Common::Array<Common::SharedPtr<Instrument> >::const_iterator i = _instruments.begin(); i != _instruments.end(); ++i) {
+ for (Common::Array<Common::SharedPtr<MacSndInstrument> >::const_iterator i = _instruments.begin(); i != _instruments.end(); ++i) {
if ((*i)->id() == tagOrOffset)
return i;
else if ((*i)->id() == MKTAG('s', 'i', 'l', 'e'))
@@ -402,7 +369,7 @@ const Common::SharedPtr<MacSndLoader::Instrument> *MonkeyMacSndLoader::fetchInst
Common::SeekableReadStream *str = new Common::MemoryReadStream(&data[tagOrOffset + 8], READ_BE_UINT32(data + tagOrOffset + 4), DisposeAfterUse::NO);
if (_instruments.size() == _numInstrumentsMax)
_instruments.pop_back();
- _instruments.push_back(Common::SharedPtr<Instrument>(new Instrument(READ_BE_UINT32(&data[tagOrOffset]), str, Common::String())));
+ _instruments.push_back(Common::SharedPtr<MacSndInstrument>(new MacSndInstrument(READ_BE_UINT32(&data[tagOrOffset]), str, Common::String())));
delete str;
instr = _instruments.end() - 1;
}
@@ -479,8 +446,9 @@ bool LoomMonkeyMacSnd::startDevice(uint32 outputRate, uint32 pcmDeviceRate, uint
_effectiveChanConfig = 9;
_16bit = internal16Bit;
- _macstr->initDrivers();
_macstr->initBuffers(feedBufferSize);
+ _macstr->addVolumeGroup(Audio::Mixer::kMusicSoundType);
+ _macstr->addVolumeGroup(Audio::Mixer::kSFXSoundType);
_macstr->setVblCallback(&_vblTskProc);
_mixer->playStream(Audio::Mixer::kPlainSoundType, &_soundHandle, _macstr, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
@@ -684,7 +652,7 @@ void LoomMonkeyMacSnd::generateData(int8 *dst, uint32 len, Audio::Mixer::SoundTy
_sdrv->feed(dst, len, type, expectStereo);
}
-const MacSoundDriver::Status &LoomMonkeyMacSnd::getDriverStatus(uint8, Audio::Mixer::SoundType sndType) const {
+const MacSoundDriver::Status &LoomMonkeyMacSnd::getDriverStatus(Audio::Mixer::SoundType sndType) const {
return _sdrv->getStatus(sndType);
}
diff --git a/engines/scumm/players/player_mac_new.cpp b/engines/scumm/players/player_mac_new.cpp
index 1063defcd0c..9f76d17eca0 100644
--- a/engines/scumm/players/player_mac_new.cpp
+++ b/engines/scumm/players/player_mac_new.cpp
@@ -37,6 +37,7 @@ namespace Scumm {
#define RATECNV_BIT_PRECSN 24
+class DoubleBufferIntern;
class MacSndChannel {
public:
MacSndChannel(MacLowLevelPCMDriver *drv, Audio::Mixer::SoundType sndtp, int synth, bool interp, bool enableL, bool enableR, MacLowLevelPCMDriver::ChanCallback *callback);
@@ -54,6 +55,8 @@ public:
void setTimbre(uint16 timbre);
void callback(uint16 p1, const void *p2);
+ bool playDoubleBuffer(byte numChan, byte bitsPerSample, uint32 rate, MacLowLevelPCMDriver::DBCallback *callback);
+
struct SoundCommand {
SoundCommand() : cmd(0), arg1(0), arg2(0), ptr(nullptr) {}
SoundCommand(uint8 c, uint16 p1, uint32 p2) : cmd(c), arg1(p1), arg2(p2), ptr(nullptr) {}
@@ -93,7 +96,9 @@ private:
Common::SharedPtr<const int8> _res;
const int8 *_data;
- int8 _lastSmp[2];
+ DoubleBufferIntern *_dblBuff;
+
+ int16 _lastSmp[2];
bool _enable[2];
uint32 _len;
uint16 _rmH;
@@ -143,10 +148,33 @@ uint32 fixMul(uint32 fx1, uint32 fx2) {
return (a << 16) + b + c + (d >> 16);
}
+class DoubleBufferIntern {
+public:
+ DoubleBufferIntern(uint32 numFrames, byte bitsPerSample, byte numChannels, MacLowLevelPCMDriver::DBCallback *cb);
+ ~DoubleBufferIntern();
+ void callback();
+ void update();
+
+ const int8 *data() const { return _data; }
+ uint32 bufferSize() const { return _processSize; }
+ uint32 flags() const { return _buff.flags; }
+ byte bitsPerSample() const { return _bitsPerSample; }
+
+private:
+ MacLowLevelPCMDriver::DoubleBuffer _buff;
+ MacLowLevelPCMDriver::DBCallback *_callback;
+ int8 *_data;
+ byte _bitsPerSample;
+ byte _numChan;
+ uint32 _bufferSize;
+ uint32 _processSize;
+};
+
MacPlayerAudioStream::MacPlayerAudioStream(VblTaskClientDriver *drv, uint32 scummVMOutputrate, bool stereo, bool interpolate, bool internal16Bit) : Audio::AudioStream(), _drv(drv),
_vblSmpQty(0), _vblSmpQtyRem(0), _frameSize((stereo ? 2 : 1) * (internal16Bit ? 2 : 1)), _vblCountDown(0), _vblCountDownRem(0), _outputRate(scummVMOutputrate),
- _vblCbProc(nullptr), _isStereo(stereo), _interp(interpolate), _smpInternalSize(internal16Bit ? 2 : 1) {
+ _vblCbProc(nullptr), _numGroups(1), _isStereo(stereo), _interp(interpolate), _smpInternalSize(internal16Bit ? 2 : 1) {
assert(_drv);
+ _buffers = new SmpBuffer[2];
_vblSmpQty = (_outputRate << 16) / VBL_UPDATE_RATE;
_vblSmpQtyRem = (_outputRate << 16) % VBL_UPDATE_RATE;
_vblCountDown = _vblSmpQty;
@@ -156,6 +184,7 @@ MacPlayerAudioStream::MacPlayerAudioStream(VblTaskClientDriver *drv, uint32 scum
MacPlayerAudioStream::~MacPlayerAudioStream() {
for (int i = 0; i < 2; ++i)
delete[] _buffers[i].start;
+ delete[] _buffers;
}
void MacPlayerAudioStream::initBuffers(uint32 feedBufferSize) {
@@ -168,19 +197,38 @@ void MacPlayerAudioStream::initBuffers(uint32 feedBufferSize) {
_buffers[i].end = &_buffers[i].start[_buffers[i].size];
}
clearBuffer();
+ setMasterVolume(Audio::Mixer::kPlainSoundType, 0x100);
}
void MacPlayerAudioStream::initDrivers() {
- for (int i = 0; i < 2; ++i) {
+ for (int i = 0; i < _numGroups; ++i) {
if (!_drv)
error("MacPlayerAudioStream::initDrivers(): Failed to query device rate for device %d", i);
- uint64 irt = (uint64)_drv->getDriverStatus(i, Audio::Mixer::kPlainSoundType).deviceRate * (1 << RATECNV_BIT_PRECSN) / _outputRate;
+ uint64 irt = (uint64)_drv->getDriverStatus(_buffers[i].group).deviceRate * (1 << RATECNV_BIT_PRECSN) / _outputRate;
_buffers[i].rateConvInt = irt >> (RATECNV_BIT_PRECSN + 16);
_buffers[i].rateConvFrac = (irt >> 16) & ((1 << RATECNV_BIT_PRECSN) - 1);
_buffers[i].rateConvAcc = 0;
}
}
+void MacPlayerAudioStream::addVolumeGroup(Audio::Mixer::SoundType type) {
+ if (type == Audio::Mixer::kPlainSoundType || type == Audio::Mixer::kSpeechSoundType) {
+ static const char *msg[4] = { "'kPlainSoundType' cannot be added, since this is the default, when no groups are defined", "", "", "'kSpeechSoundType' is not supported." };
+ warning("%s(): Group %s", __FUNCTION__, msg[type]);
+ return;
+ }
+
+ if (_buffers[0].group == Audio::Mixer::kPlainSoundType)
+ --_numGroups;
+
+ for (int i = 0; i < 2; ++i) {
+ if (_buffers[i].group == type)
+ return;
+ }
+ _buffers[_numGroups++].group = type;
+ initDrivers();
+}
+
void MacPlayerAudioStream::setVblCallback(const CallbackProc *proc) {
_vblCbProc = proc;
}
@@ -193,22 +241,23 @@ void MacPlayerAudioStream::clearBuffer() {
}
void MacPlayerAudioStream::setMasterVolume(Audio::Mixer::SoundType type, uint16 vol) {
- if (type == Audio::Mixer::kMusicSoundType || type == Audio::Mixer::kPlainSoundType)
- _buffers[0].volume = vol * vol;
- if (type == Audio::Mixer::kSFXSoundType || type == Audio::Mixer::kPlainSoundType)
- _buffers[1].volume = vol * vol;
+ if (vol > 256) {
+ warning("%s(): Volume %d out of range (%d, %d)", __FUNCTION__, vol, 0, 256);
+ vol = 256;
+ }
+ for (int i = 0; i < _numGroups; ++i) {
+ if (type == _buffers[i].group)
+ _buffers[i].volume = powf(vol, 1.25f);
+ }
}
int MacPlayerAudioStream::readBuffer(int16 *buffer, const int numSamples) {
- static const Audio::Mixer::SoundType stype[2] = {
- Audio::Mixer::kMusicSoundType,
- Audio::Mixer::kSFXSoundType
- };
-
static const char errFnNames[2][8] = {"Buffers", "Drivers"};
- int errNo = (!_buffers[0].size || !_buffers[1].size) ? 0 : ((_buffers[0].rateConvAcc == -1 || _buffers[1].rateConvAcc == -1) ? 1 : -1);
+ int errNo = -1;
+ for (int i = 0; i < _numGroups && errNo == -1; ++i)
+ errNo = !_buffers[i].size ? 0 : (_buffers[i].rateConvAcc == -1 ? 1 : -1);
if (errNo != -1)
- error("MacPlayerAudioStream::readBuffer(): init%s() must be called before playback", errFnNames[errNo]);
+ error("%s(): init%s() must be called before playback", __FUNCTION__, errFnNames[errNo]);
for (int i = _isStereo ? numSamples >> 1 : numSamples; i; --i) {
if (!--_vblCountDown) {
@@ -223,9 +272,10 @@ int MacPlayerAudioStream::readBuffer(int16 *buffer, const int numSamples) {
int32 smpL = 0;
int32 smpR = 0;
- for (int ii = 0; ii < 2; ++ii) {
- int numch = _drv->getDriverStatus(ii, stype[ii]).numExternalMixChannels;
- bool interp = _interp && _drv->getDriverStatus(ii, stype[ii]).allowInterPolation;
+
+ for (int ii = 0; ii < _numGroups; ++ii) {
+ int numch = _drv->getDriverStatus(_buffers[ii].group).numExternalMixChannels;
+ bool interp = _interp && _drv->getDriverStatus(_buffers[ii].group).allowInterPolation;
if (!numch)
continue;
@@ -244,7 +294,7 @@ int MacPlayerAudioStream::readBuffer(int16 *buffer, const int numSamples) {
}
}
- for (int ii = 0; ii < 2; ++ii) {
+ for (int ii = 0; ii < _numGroups; ++ii) {
uint32 incr = (_buffers[ii].rateConvInt * _frameSize);
_buffers[ii].rateConvAcc += _buffers[ii].rateConvFrac;
if (_buffers[ii].rateConvAcc & ~((1 << RATECNV_BIT_PRECSN) - 1)) {
@@ -272,14 +322,14 @@ int MacPlayerAudioStream::readBuffer(int16 *buffer, const int numSamples) {
int refreshSize = MIN<int>(_vblCountDown * _frameSize, _buffers[ii].size);
_buffers[ii].pos -= refreshSize;
assert(_buffers[ii].pos + refreshSize < _buffers[ii].end + PCM_BUFFER_RESERVE);
- generateData(_buffers[ii].pos, refreshSize, stype[ii], _isStereo);
+ generateData(_buffers[ii].pos, refreshSize, _buffers[ii].group, _isStereo);
}
}
}
- *buffer++ = CLIP<int32>(smpL >> 8, -32768, 32767);
+ *buffer++ = CLIP<int32>((smpL / _numGroups) >> 2, -32768, 32767);
if (_isStereo)
- *buffer++ = CLIP<int32>(smpR >> 8, -32768, 32767);
+ *buffer++ = CLIP<int32>((smpL / _numGroups) >> 2, -32768, 32767);
}
return numSamples;
}
@@ -343,7 +393,7 @@ void MacLowLevelPCMDriver::feed(int8 *dst, uint32 byteSize, Audio::Mixer::SoundT
MacLowLevelPCMDriver::ChanHandle MacLowLevelPCMDriver::createChannel(Audio::Mixer::SoundType sndType, SynthType synthType, byte attributes, ChanCallback *callback) {
Common::StackLock lock(_mutex);
- MacSndChannel *ch = new MacSndChannel(this, sndType, synthType, synthType == kSampledSynth && !(attributes & kNoInterp), !(synthType == kSampledSynth && ((attributes & 3) == kInitChanRight)), !(synthType == kSampledSynth && ((attributes & 3) == kInitChanLeft)), callback);
+ MacSndChannel *ch = new MacSndChannel(this, sndType, synthType, synthType == kSampledSynth && !(attributes & kNoInterp), !(synthType == kSampledSynth && ((attributes & 3) == kInitChanRight)), !(synthType == kSampledSynth && (((attributes & 3) == kInitChanLeft) || ((attributes & 0x80) == kInitMono))), callback);
assert(ch);
_channels.push_back(ch);
@@ -456,6 +506,22 @@ void MacLowLevelPCMDriver::callback(ChanHandle handle, ExecMode mode, uint16 arg
ch->enqueueSndCmd(13, arg1, arg2, 0, mode);
}
+bool MacLowLevelPCMDriver::playDoubleBuffer(ChanHandle handle, byte numChan, byte bitsPerSample, uint32 rate, DBCallback *callback, byte externalMixNumChan) {
+ Common::StackLock lock(_mutex);
+ MacSndChannel *ch = findAndCheckChannel(handle, __FUNCTION__, kSampledSynth);
+ if (!ch)
+ return false;
+
+ if (!callback) {
+ warning("%s(): callback argument required", __FUNCTION__);
+ return false;
+ }
+
+ _status[ch->_sndType].numExternalMixChannels = bitsPerSample > 8 ? externalMixNumChan : 1;
+
+ return ch->playDoubleBuffer(numChan, bitsPerSample, rate, callback);
+}
+
uint8 MacLowLevelPCMDriver::getChannelStatus(ChanHandle handle) const {
MacSndChannel *ch = findChannel(handle); return ch ? ch->_flags : 0;
}
@@ -466,12 +532,15 @@ void MacLowLevelPCMDriver::clearChannelFlags(ChanHandle handle, uint8 flags) {
void MacLowLevelPCMDriver::updateStatus(Audio::Mixer::SoundType sndType) {
_numInternalMixChannels = _smpSize > 1 ? 1 : _channels.size();
- _status[sndType].numExternalMixChannels = _smpSize > 1 ? _channels.size() : 1;
_status[sndType].allowInterPolation = true;
+ int count = 0;
for (Common::Array<MacSndChannel*>::const_iterator ch = _channels.begin(); ch != _channels.end(); ++ch) {
+ if ((*ch)->_sndType == sndType)
+ ++count;
if (!(*ch)->_interpolate)
_status[sndType].allowInterPolation = false;
}
+ _status[sndType].numExternalMixChannels = _smpSize > 1 ? count : 1;
}
MacSndChannel *MacLowLevelPCMDriver::findAndCheckChannel(ChanHandle h, const char *callingProcName, byte reqSynthType) const {
@@ -482,7 +551,7 @@ MacSndChannel *MacLowLevelPCMDriver::findAndCheckChannel(ChanHandle h, const cha
}
if (reqSynthType != kIgnoreSynth && reqSynthType != ch->_synth) {
- warning("%s(): Wrong channel type", callingProcName);
+ warning("%s(): Wrong channel synth type '%d' (required: '%d')", callingProcName, ch->_synth, reqSynthType);
return nullptr;
}
@@ -499,7 +568,7 @@ MacSndChannel *MacLowLevelPCMDriver::findChannel(ChanHandle handle) const {
MacSndChannel::MacSndChannel(MacLowLevelPCMDriver *drv, Audio::Mixer::SoundType sndtp, int synth, bool interp, bool enableL, bool enableR, MacLowLevelPCMDriver::ChanCallback *callback) : _drv(drv), _sndType(sndtp),
_synth(synth), _interpolate(interp), _frameSize(1), _len(0), _rmH(0), _rmL(0), _smpWtAcc(0), _loopSt2(0), _loopEnd(0), _loopLen(0), _loopSt(0), _baseFreq(0), _rcPos(0),
- _flags(0), _tmrPos(0), _tmrInc(0), _timbre(0), _data(nullptr), _srate(0), _phase(0), _duration(0), _tmrRate(0), _callback(callback) {
+ _flags(0), _tmrPos(0), _tmrInc(0), _timbre(0), _data(nullptr), _srate(0), _phase(0), _duration(0), _tmrRate(0), _callback(callback), _dblBuff(nullptr) {
_lastSmp[0] = _lastSmp[1] = 0;
_enable[0] = enableL;
_enable[1] = enableR;
@@ -560,6 +629,8 @@ void MacSndChannel::quiet() {
_data = nullptr;
_tmrInc = 0;
_duration = 0;
+ delete _dblBuff;
+ _dblBuff = nullptr;
}
void MacSndChannel::wait(uint32 duration) {
@@ -580,6 +651,9 @@ void MacSndChannel::flush() {
_sndCmdQueue.clear();
_tmrInc = 0;
_duration = 0;
+
+ delete _dblBuff;
+ _dblBuff = nullptr;
}
void MacSndChannel::loadWaveTable(const byte *data, uint16 dataSize) {
@@ -619,6 +693,37 @@ void MacSndChannel::callback(uint16 p1, const void *p2) {
(*_callback)(p1, p2);
}
+bool MacSndChannel::playDoubleBuffer(byte numChan, byte bitsPerSample, uint32 rate, MacLowLevelPCMDriver::DBCallback *callback) {
+ if (!numChan || !bitsPerSample || !rate || !callback) {
+ warning("%s(): Invalid argument", __FUNCTION__);
+ return false;
+ }
+
+ if (_dblBuff) {
+ warning("%s(): Double buffer already in use", __FUNCTION__);
+ return false;
+ }
+
+ _dblBuff = new DoubleBufferIntern(1024, bitsPerSample, numChan, callback);
+ setupRateConv(_drv->getStatus().deviceRate, 0x10000, rate, false);
+ _duration = _tmrInc = _tmrPos = _loopSt = _loopEnd = _rcPos = _smpWtAcc = _phase = 0;
+
+ _srate = rate;
+ _data = _dblBuff->data();
+ _len = _loopLen = _dblBuff->bufferSize();
+ _frameSize = (bitsPerSample >> 3) * numChan;
+
+ _lastSmp[0] = (bitsPerSample == 16) ? reinterpret_cast<const int16*>(_data)[0] : _data[0];
+ if (_len >= _frameSize)
+ _lastSmp[1] = (bitsPerSample == 16) ? reinterpret_cast<const int16*>(_data)[1] : _data[1];
+
+ _drv->clearFlags(MacLowLevelPCMDriver::kStatusDone);
+ clearFlags(MacLowLevelPCMDriver::kStatusDone);
+ setFlags(MacLowLevelPCMDriver::kStatusPlaying);
+
+ return true;
+}
+
void MacSndChannel::enqueueSndCmd(uint8 c, uint16 p1, uint32 p2, byte mode) {
if (mode == MacLowLevelPCMDriver::kImmediate) {
switch (c) {
@@ -683,20 +788,22 @@ void MacSndChannel::feed(int32 *dst, uint32 dstSize, byte dstFrameSize) {
for (const int32 *end = &dst[dstSize]; dst < end; ) {
processSndCmdQueue();
- int8 in = 0;
- for (int i = 0; i < dstFrameSize; ++i) {
- if (_data != nullptr && i < _frameSize) {
+ int16 in = 0;
+ byte smpSize = _dblBuff ? _dblBuff->bitsPerSample() >> 3 : 1;
+ for (int i = 0, si = 0; i < dstFrameSize; ++i) {
+ if (_data != nullptr && si < _frameSize) {
if (_synth != MacLowLevelPCMDriver::kSampledSynth) {
in = _data[(_phase >> 16) & 0xff];
} else {
- in = _data[_rcPos + i];
+ in = (smpSize == 2) ? *reinterpret_cast<const int16*>(_data + _rcPos + si) : _data[_rcPos + i];
if (interp && in != _lastSmp[i]) {
diff = in - _lastSmp[i];
diff = (diff * (_smpWtAcc >> 1)) >> 15;
- in = (_lastSmp[i] + diff) & 0xff;
+ in = _lastSmp[i] + diff;
}
}
}
+ si += smpSize;
if (_enable[i])
*dst += in;
dst++;
@@ -723,8 +830,13 @@ void MacSndChannel::feed(int32 *dst, uint32 dstSize, byte dstFrameSize) {
continue;
if (interp) {
- for (int i = 0; i < _frameSize; ++i)
- _lastSmp[i] = _data[_loopSt + ((_rcPos - _frameSize + i - _loopSt) % _loopLen)];
+ if (smpSize == 2) {
+ for (int i = 0; i < _frameSize; i += 2)
+ _lastSmp[i] = *reinterpret_cast<const int16*>(_data + _loopSt + (_rcPos - _frameSize + i - _loopSt) % _loopLen);
+ } else {
+ for (int i = 0; i < _frameSize; ++i)
+ _lastSmp[i] = _data[_loopSt + (_rcPos - _frameSize + i - _loopSt) % _loopLen];
+ }
}
if (_rcPos >= _loopSt + _loopLen) {
@@ -733,9 +845,33 @@ void MacSndChannel::feed(int32 *dst, uint32 dstSize, byte dstFrameSize) {
_loopLen = _loopEnd - _loopSt2;
if (interp) {
for (int i = 0; i < _frameSize; ++i)
- _lastSmp[i] = _data[_loopSt + ((_rcPos - _frameSize + i - _loopSt) % _loopLen)];
+ _lastSmp[i] = _data[_loopSt + (_rcPos - _frameSize + i - _loopSt) % _loopLen];
}
_rcPos = _loopSt + ((_rcPos - _loopSt) % _loopLen);
+ } else if (_dblBuff) {
+ if (_dblBuff->flags() & MacLowLevelPCMDriver::DoubleBuffer::kLastBufferLast) {
+ delete _dblBuff;
+ _dblBuff = nullptr;
+ _data = nullptr;
+ } else {
+ _dblBuff->callback();
+ _dblBuff->update();
+ _data = _dblBuff->data();
+ uint32 plen = _loopLen;
+ _loopLen = _dblBuff->bufferSize();
+ if (interp) {
+ if (_loopLen != plen)
+ _rcPos = _frameSize;
+ if (smpSize == 2) {
+ for (int i = 0; i < _frameSize; i += 2)
+ _lastSmp[i] = *reinterpret_cast<const int16*>(_data + (_rcPos - _frameSize + i) % _loopLen);
+ } else {
+ for (int i = 0; i < _frameSize; ++i)
+ _lastSmp[i] = _data[(_rcPos - _frameSize + i) % _loopLen];
+ }
+ }
+ _rcPos = (_loopLen == plen) ? _rcPos % _loopLen : 0;
+ }
} else {
_data = nullptr;
}
@@ -1057,6 +1193,68 @@ void MacSndChannel::makeSquareWave(int8 *dstBuff, uint16 dstSize, byte timbre) {
*d2++ = *d1++ ^ 0xff;
}
+DoubleBufferIntern::DoubleBufferIntern(uint32 numFrames, byte bitsPerSample, byte numChannels, MacLowLevelPCMDriver::DBCallback *cb) : _bitsPerSample(bitsPerSample), _numChan(numChannels), _callback(cb), _data(nullptr), _bufferSize(0), _processSize(0) {
+ _buff.numFrames = numFrames;
+ update();
+}
+
+DoubleBufferIntern::~DoubleBufferIntern() {
+ delete[] _data;
+}
+
+void DoubleBufferIntern::callback() {
+ if (_callback && _callback->isValid())
+ (*_callback)(&_buff);
+}
+
+void DoubleBufferIntern::update() {
+ _processSize = _buff.numFrames * (_bitsPerSample >> 3) * _numChan;
+
+ if (_buff.flags & MacLowLevelPCMDriver::DoubleBuffer::kBufferReady) {
+ assert(_buff.data);
+ assert(_data);
+ byte *s = _buff.data;
+ uint32 len = MIN<uint32>(_bufferSize, _processSize);
+ if (_bitsPerSample == 8) {
+ for (uint32 i = 0; i < len; ++i)
+ _data[i] = *s++ ^ 0x80;
+ } else {
+ memcpy(_data, s, len);
+ }
+ _buff.flags &= ~MacLowLevelPCMDriver::DoubleBuffer::kBufferReady;
+ }
+
+ if (_processSize > _bufferSize) {
+ delete[] _buff.data;
+ _buff.data = new byte[_processSize]();
+ int8 *newData = new int8[_processSize]();
+ if (_data && _bufferSize)
+ memcpy(newData, _data, _bufferSize);
+ _bufferSize = _processSize;
+ delete[] _data;
+ _data = newData;
+ }
+}
+
+MacSndInstrument::MacSndInstrument(uint32 id, Common::SeekableReadStream *&in, Common::String &&name) : _id(id), _name(Common::move(name)) {
+ in->seek(2);
+ uint16 numTypes = in->readUint16BE();
+ in->seek(numTypes * 6 + 4);
+ in->seek(in->readUint16BE() * 8 + numTypes * 6 + 10);
+
+ _snd.len = in->readUint32BE();
+ _snd.rate = in->readUint32BE();
+ _snd.loopst = in->readUint32BE();
+ _snd.loopend = in->readUint32BE();
+ _snd.enc = in->readByte();
+ _snd.baseFreq = in->readByte();
+
+ byte *buff = new byte[_snd.len];
+ if (in->read(buff, _snd.len) != _snd.len)
+ error("%s(): Data error", __FUNCTION__);
+ _snd.data = Common::SharedPtr<const byte>(buff, Common::ArrayDeleter<const byte>());
+}
+
const uint8 _fourToneSynthWaveForm[256] = {
0x80, 0x7a, 0x74, 0x6e, 0x69, 0x63, 0x5d, 0x57, 0x52, 0x4c, 0x47, 0x42, 0x3e, 0x3b, 0x38, 0x35,
0x34, 0x33, 0x34, 0x35, 0x37, 0x3a, 0x3e, 0x43, 0x49, 0x4e, 0x54, 0x5b, 0x61, 0x67, 0x6c, 0x71,
diff --git a/engines/scumm/saveload.cpp b/engines/scumm/saveload.cpp
index 487b7cd6270..408f3948192 100644
--- a/engines/scumm/saveload.cpp
+++ b/engines/scumm/saveload.cpp
@@ -70,7 +70,7 @@ struct SaveInfoSection {
#define SaveInfoSectionSize (4+4+4 + 4+4 + 4+2)
-#define CURRENT_VER 121
+#define CURRENT_VER 122
#define INFOSECTION_VERSION 2
#pragma mark -
diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp
index b672013b3a8..dc819c34394 100644
--- a/engines/scumm/scumm.cpp
+++ b/engines/scumm/scumm.cpp
@@ -85,9 +85,9 @@
#include "scumm/util.h"
#include "scumm/verbs.h"
#include "scumm/imuse/drivers/pcspk.h"
-#include "scumm/imuse/drivers/mac_m68k.h"
#include "scumm/imuse/drivers/amiga.h"
#include "scumm/imuse/drivers/fmtowns.h"
+#include "scumm/imuse/drivers/macintosh.h"
#include "scumm/imuse/drivers/midi.h"
#include "scumm/detection_steam.h"
@@ -2166,6 +2166,9 @@ void ScummEngine::setupMusic(int midi) {
case MT_APPLEIIGS:
_sound->_musicType = MDT_APPLEIIGS;
break;
+ case MT_MACINTOSH:
+ _sound->_musicType = MDT_MACINTOSH;
+ break;
default:
_sound->_musicType = MDT_MIDI;
break;
@@ -2250,7 +2253,7 @@ void ScummEngine::setupMusic(int midi) {
// Adding AdLib capabilities to the player may still be a good
// idea, because there are plenty of sound resources that exist
// only as ADL and SPK.
- _sound->_musicType = MDT_MIDI;
+ //_sound->_musicType = MDT_MIDI;
}
/* Bind the mixer to the system => mixer will be invoked
@@ -2331,16 +2334,13 @@ void ScummEngine::setupMusic(int midi) {
bool enable_gs = (_game.id == GID_TENTACLE || _game.id == GID_SAMNMAX) ? false : (ConfMan.getBool("enable_gs") && MidiDriver::getMusicType(dev) != MT_MT32);
bool newSystem = (_game.id == GID_SAMNMAX);
- if (isMacM68kIMuse()) {
- // We setup this driver as native MIDI driver to avoid playback
- // of the Mac music via a selected MIDI device.
- nativeMidiDriver = new IMuseDriver_MacM68k(_mixer);
- // The Mac driver is never MT-32.
+ if (_sound->_musicType == MDT_AMIGA) {
+ nativeMidiDriver = new IMuseDriver_Amiga(_mixer);
_native_mt32 = enable_gs = false;
- // Ignore non-native drivers. This also ignores the multi MIDI setting.
useOnlyNative = true;
- } else if (_sound->_musicType == MDT_AMIGA) {
- nativeMidiDriver = new IMuseDriver_Amiga(_mixer);
+ } else if (_sound->_musicType == MDT_MACINTOSH) {
+ //nativeMidiDriver = new IMuseDriver_MacM68k(_mixer);
+ nativeMidiDriver = new IMuseDriver_Mac_DJM(_mixer);
_native_mt32 = enable_gs = false;
useOnlyNative = true;
} else if (_sound->_musicType != MDT_ADLIB && _sound->_musicType != MDT_TOWNS && _sound->_musicType != MDT_PCSPK) {
@@ -2363,9 +2363,9 @@ void ScummEngine::setupMusic(int midi) {
}
}
- _imuse = IMuse::create(this, nativeMidiDriver, adlibMidiDriver, isMacM68kIMuse() ? MDT_MACINTOSH : _sound->_musicType, _native_mt32);
+ _imuse = IMuse::create(this, nativeMidiDriver, adlibMidiDriver, _sound->_musicType, _native_mt32);
- if (_game.platform == Common::kPlatformFMTowns) {
+ if (_sound->_musicType == MDT_TOWNS) {
_musicEngine = _townsPlayer = new Player_Towns_v2(this, _mixer, _imuse, true);
if (!_townsPlayer->init())
error("ScummEngine::setupMusic(): Failed to initialize FM-Towns audio driver");
@@ -2373,6 +2373,9 @@ void ScummEngine::setupMusic(int midi) {
_musicEngine = _imuse;
}
+ if (_sound->_musicType == MDT_MACINTOSH && ConfMan.hasKey("mac_snd_quality"))
+ _musicEngine->setQuality(ConfMan.getInt("mac_snd_quality"));
+
if (_imuse) {
_imuse->addSysexHandler
(/*IMUSE_SYSEX_ID*/ 0x7D,
diff --git a/engines/scumm/scumm.h b/engines/scumm/scumm.h
index 802cb25260f..f2ff324684d 100644
--- a/engines/scumm/scumm.h
+++ b/engines/scumm/scumm.h
@@ -1090,9 +1090,6 @@ public:
bool openFile(BaseScummFile &file, const Common::Path &filename, bool resourceFile = false);
- /** Is this game a Mac m68k v5 game with iMuse? */
- bool isMacM68kIMuse() const;
-
protected:
int _resourceHeaderSize = 8;
byte _resourceMapper[128];
diff --git a/engines/scumm/sound.cpp b/engines/scumm/sound.cpp
index 62771592c90..fff13bd64e4 100644
--- a/engines/scumm/sound.cpp
+++ b/engines/scumm/sound.cpp
@@ -407,8 +407,7 @@ void Sound::triggerSound(int soundID) {
}
// Support for sampled sound effects in Monkey Island 1 and 2
else if (_vm->_game.platform != Common::kPlatformFMTowns
- // The Macintosh m68k versions of MI2/Indy4 just ignore SBL effects.
- && !_vm->isMacM68kIMuse()
+ && _vm->_game.platform != Common::kPlatformMacintosh
&& READ_BE_UINT32(ptr) == MKTAG('S','B','L',' ')) {
debugC(DEBUG_SOUND, "Using SBL sound effect");
diff --git a/engines/scumm/vars.cpp b/engines/scumm/vars.cpp
index 9eaa29c942f..634daccffbd 100644
--- a/engines/scumm/vars.cpp
+++ b/engines/scumm/vars.cpp
@@ -871,8 +871,8 @@ void ScummEngine::setSoundCardVarToCurrentConfig() {
VAR(VAR_SOUNDCARD) = (ConfMan.hasKey("mac_snd_quality") && ConfMan.getInt("mac_snd_quality") > 0 && ConfMan.getInt("mac_snd_quality") < 4) ? 10 : 11;
else if (_game.id == GID_MONKEY)
VAR(VAR_SOUNDCARD) = 0xffff;
- else
- VAR(VAR_SOUNDCARD) = 3;
+ else // GID_MONKEY2 || GID_INDY4
+ VAR(VAR_SOUNDCARD) = 4;
break;
case MDT_NONE:
case MDT_PCSPK:
Commit: 39aff274af1e92141453784576bb2eb5d0a35d8e
https://github.com/scummvm/scummvm/commit/39aff274af1e92141453784576bb2eb5d0a35d8e
Author: athrxx (athrxx at scummvm.org)
Date: 2024-11-23T23:07:29+01:00
Commit Message:
SCUMM: (IMS/Mac) - allow sfx volume control
It works on less sfx than I expected, since many of them
use instruments from the musical sound bank rather than
ones from the second sound bank (which seems to contain
the 'SBL' sounds).
Changed paths:
engines/scumm/imuse/drivers/macintosh.cpp
engines/scumm/imuse/drivers/macintosh.h
engines/scumm/imuse/imuse.cpp
engines/scumm/imuse/imuse.h
engines/scumm/imuse/imuse_internal.h
engines/scumm/players/mac_sound_lowlevel.h
engines/scumm/players/player_mac_new.cpp
diff --git a/engines/scumm/imuse/drivers/macintosh.cpp b/engines/scumm/imuse/drivers/macintosh.cpp
index 7fbe8988f7f..adb8f7f664a 100644
--- a/engines/scumm/imuse/drivers/macintosh.cpp
+++ b/engines/scumm/imuse/drivers/macintosh.cpp
@@ -33,7 +33,7 @@ using namespace Scumm;
class DJMSoundChannel {
public:
- DJMSoundChannel(const uint32 *pitchTable) : _pitchTable(pitchTable), _frequency(0), _phase(0), _end(nullptr), _pos(nullptr), _smpBuffStart(nullptr), _smpBuffEnd(nullptr), _loopStart(nullptr), _loopEnd(nullptr), _pitch(0), _volume(0), _mute(0), _instr(nullptr), _baseFreq(0) {}
+ DJMSoundChannel(const uint32 *pitchTable) : _pitchTable(pitchTable), _frequency(0), _phase(0), _end(nullptr), _pos(nullptr), _smpBuffStart(nullptr), _smpBuffEnd(nullptr), _loopStart(nullptr), _loopEnd(nullptr), _pitch(0), _volume(0), _mute(1), _instr(nullptr), _baseFreq(0) {}
~DJMSoundChannel() {}
void recalcFrequency();
@@ -76,10 +76,12 @@ public:
void vblCallback() override;
void generateData(int8 *dst, uint32 byteSize, Audio::Mixer::SoundType type, bool expectStereo) const override;
- const MacSoundDriver::Status &getDriverStatus(Audio::Mixer::SoundType sndType) const override;
+ const MacSoundDriver::Status &getDriverStatus(Audio::Mixer::SoundType type) const override;
void dblBuffCallback(MacLowLevelPCMDriver::DoubleBuffer *dblBuffer) override;
+ void setMasterVolume(Audio::Mixer::SoundType type, uint16 volume);
+
private:
void fillPitchTable();
@@ -95,7 +97,8 @@ private:
MacLowLevelPCMDriver *_sdrv;
Audio::SoundHandle _soundHandle;
MacPlayerAudioStream::CallbackProc _vblTskProc;
- MacLowLevelPCMDriver::ChanHandle _chan;
+ MacLowLevelPCMDriver::ChanHandle _musicChan;
+ MacLowLevelPCMDriver::ChanHandle _sfxChan;
MacLowLevelPCMDriver::DBCallback _dbCbProc;
Audio::Mixer *_mixer;
@@ -213,7 +216,7 @@ void DJMSoundChannel::recalcFrequency() {
}
DJMSoundSystem::DJMSoundSystem(Audio::Mixer *mixer) : VblTaskClientDriver(), _mixer(mixer), _macstr(nullptr), _sdrv(nullptr), _vblTskProc(this, &VblTaskClientDriver::vblCallback),
- _dbCbProc(this, &MacLowLevelPCMDriver::CallbackClient::dblBuffCallback), _chan(0), _quality(22), _feedBufferSize(1024), _channels(nullptr),
+ _dbCbProc(this, &MacLowLevelPCMDriver::CallbackClient::dblBuffCallback), _musicChan(0), _sfxChan(0), _quality(22), _feedBufferSize(1024), _channels(nullptr),
_timerParam(nullptr), _timerProc(nullptr), _pitchTable(nullptr), _ampTable(nullptr), _numChannels(8) {
_pitchTable = new uint32[128]();
assert(_pitchTable);
@@ -266,8 +269,10 @@ bool DJMSoundSystem::init(bool internal16Bit) {
if (!_sdrv)
return false;
- _macstr->initDrivers();
_macstr->initBuffers(1024);
+ _macstr->addVolumeGroup(Audio::Mixer::kMusicSoundType);
+ _macstr->addVolumeGroup(Audio::Mixer::kSFXSoundType);
+ _macstr->scaleVolume(2);
_macstr->setVblCallback(&_vblTskProc);
_mixer->playStream(Audio::Mixer::kPlainSoundType, &_soundHandle, _macstr, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
@@ -288,8 +293,9 @@ void DJMSoundSystem::deinit() {
}
bool DJMSoundSystem::start() {
- _chan = _sdrv->createChannel(Audio::Mixer::kPlainSoundType, MacLowLevelPCMDriver::kSampledSynth, 0x8C, nullptr);
- if (!_chan)
+ _musicChan = _sdrv->createChannel(Audio::Mixer::kMusicSoundType, MacLowLevelPCMDriver::kSampledSynth, 0x8C, nullptr);
+ _sfxChan = _sdrv->createChannel(Audio::Mixer::kSFXSoundType, MacLowLevelPCMDriver::kSampledSynth, 0x8C, nullptr);
+ if (!_musicChan || !_sfxChan)
return false;
uint32 rate = 0;
@@ -311,13 +317,16 @@ bool DJMSoundSystem::start() {
return false;
}
- return _sdrv->playDoubleBuffer(_chan, 1, _internal16Bit ? 16 : 8, rate, &_dbCbProc, _internal16Bit ? 8 : 1);
+ return _sdrv->playDoubleBuffer(_musicChan, 1, _internal16Bit ? 16 : 8, rate, &_dbCbProc, _internal16Bit ? 8 : 1) &&
+ _sdrv->playDoubleBuffer(_sfxChan, 1, _internal16Bit ? 16 : 8, rate, &_dbCbProc, _internal16Bit ? 8 : 1);
}
void DJMSoundSystem::stop() {
Common::StackLock lock(_mixer->mutex());
- if (_sdrv)
- _sdrv->disposeChannel(_chan);
+ if (_sdrv) {
+ _sdrv->disposeChannel(_musicChan);
+ _sdrv->disposeChannel(_sfxChan);
+ }
}
void DJMSoundSystem::setQuality(int qual) {
@@ -420,8 +429,8 @@ void DJMSoundSystem::generateData(int8 *dst, uint32 len, Audio::Mixer::SoundType
_sdrv->feed(dst, len, type, expectStereo);
}
-const MacSoundDriver::Status &DJMSoundSystem::getDriverStatus(Audio::Mixer::SoundType) const {
- return _sdrv->getStatus(Audio::Mixer::kPlainSoundType);
+const MacSoundDriver::Status &DJMSoundSystem::getDriverStatus(Audio::Mixer::SoundType type) const {
+ return _sdrv->getStatus(type);
}
void DJMSoundSystem::dblBuffCallback(MacLowLevelPCMDriver::DoubleBuffer *dblBuffer) {
@@ -431,9 +440,13 @@ void DJMSoundSystem::dblBuffCallback(MacLowLevelPCMDriver::DoubleBuffer *dblBuff
for (int i = 0; i < _numChannels; ++i) {
DJMSoundChannel &c = *_channels[i];
- if (c._mute) {
- ++sil;
- continue;
+ if (c._mute ||
+ // This is our "trick" for telling apart music and sfx: We look at the sound bank that
+ // is currently in use, the musical one (1000 - 1127) or the sfx one (2000 - 2255).
+ (dblBuffer->chanHandle == _musicChan && c._instr.get()->id() >= 2000) ||
+ (dblBuffer->chanHandle == _sfxChan && c._instr.get()->id() < 2000)) {
+ ++sil;
+ continue;
}
const byte *a = &_ampTable[(c._volume & ~3) << 6];
@@ -477,6 +490,11 @@ void DJMSoundSystem::dblBuffCallback(MacLowLevelPCMDriver::DoubleBuffer *dblBuff
dblBuffer->flags |= MacLowLevelPCMDriver::DoubleBuffer::kBufferReady;
}
+void DJMSoundSystem::setMasterVolume(Audio::Mixer::SoundType type, uint16 volume) {
+ if (_macstr)
+ _macstr->setMasterVolume(type, volume);
+}
+
void DJMSoundSystem::fillPitchTable() {
static const float fltbl[12] = {
1664510.62f, 1763487.62f, 1868350.00f, 1979447.88f, 2097152.00f, 2221855.25f,
@@ -538,7 +556,7 @@ void IMuseChannel_Mac_DJM::noteOn(byte note, byte velocity) {
node->_sustain = false;
_device->setInstrument(node->_number, _prog);
- debug(4, "NOTE ON: chan '%d', note '%d', instr '%s'", node->_number, note, _prog.get()->name());
+ debug(4, "NOTE ON: chan '%d', note '%d', instr id '%d' ('%s')", node->_number, note, _prog.get()->id(), _prog.get()->name());
_device->noteOn(node->_number, ((note + _transpose) << 7) + _pitchBendEff, _volume); // ignoring velocity
}
@@ -641,7 +659,7 @@ namespace Scumm {
using namespace IMSMacintosh;
IMuseDriver_Mac_DJM::IMuseDriver_Mac_DJM(Audio::Mixer *mixer) : MidiDriver(), _isOpen(false), _device(nullptr), _imsParts(nullptr), _channels(nullptr),
- _numParts(24), _numChannels(8), _baseTempo(16667), _quality(1) {
+ _numParts(24), _numChannels(8), _baseTempo(16667), _quality(1), _musicVolume(0), _sfxVolume(0) {
_device = new DJMSoundSystem(mixer);
}
@@ -661,7 +679,7 @@ int IMuseDriver_Mac_DJM::open() {
nullptr
};
- if (!loadDefaultInstruments(fileNames, ARRAYSIZE(fileNames)) || !_device->init(true) || !_device->start())
+ if (!loadDefaultInstruments(fileNames, ARRAYSIZE(fileNames)) || !_device->init(false) || !_device->start())
return MERR_DEVICE_NOT_AVAILABLE;
_isOpen = true;
@@ -690,6 +708,23 @@ uint32 IMuseDriver_Mac_DJM::property(int prop, uint32 param) {
_device->setQuality(param == MacSound::kQualityAuto || param == MacSound::kQualityHighest ? 2: 0);
}
break;
+
+ case IMuse::PROP_MUSICVOLUME:
+ res = _musicVolume;
+ if (param != (uint32)-1 && param != _musicVolume) {
+ _musicVolume = param;
+ if (_device)
+ _device->setMasterVolume(Audio::Mixer::kMusicSoundType, param);
+ }
+ break;
+ case IMuse::PROP_SFXVOLUME:
+ res = _sfxVolume;
+ if (param != (uint32)-1 && param != _sfxVolume) {
+ _sfxVolume = param;
+ if (_device)
+ _device->setMasterVolume(Audio::Mixer::kSFXSoundType, param);
+ }
+ break;
default:
break;
}
diff --git a/engines/scumm/imuse/drivers/macintosh.h b/engines/scumm/imuse/drivers/macintosh.h
index 68d53346b7b..f1f4255f3fd 100644
--- a/engines/scumm/imuse/drivers/macintosh.h
+++ b/engines/scumm/imuse/drivers/macintosh.h
@@ -63,6 +63,8 @@ private:
bool _isOpen;
uint32 _quality;
+ uint32 _musicVolume;
+ uint32 _sfxVolume;
IMSMacintosh::DJMSoundSystem *_device;
IMSMacintosh::IMuseChannel_Mac_DJM **_imsParts;
diff --git a/engines/scumm/imuse/imuse.cpp b/engines/scumm/imuse/imuse.cpp
index 356ef705644..36019b24768 100644
--- a/engines/scumm/imuse/imuse.cpp
+++ b/engines/scumm/imuse/imuse.cpp
@@ -69,6 +69,7 @@ IMuseInternal::IMuseInternal(ScummEngine *vm, MidiDriverFlags sndType, bool nati
_trigger_count(0),
_snm_trigger_index(0),
_soundType(sndType),
+ _lowLevelVolumeControl(sndType == MDT_MACINTOSH),
_game_id(vm ? vm->_game.id : 0),
_mutex(vm ? vm->_mixer->mutex() : _dummyMutex) {
memset(_channel_volume, 0, sizeof(_channel_volume));
@@ -489,6 +490,12 @@ uint32 IMuseInternal::property(int prop, uint32 value) {
_midi_native->property(IMuse::PROP_QUALITY, value);
break;
+ case IMuse::PROP_MUSICVOLUME:
+ case IMuse::PROP_SFXVOLUME:
+ if (_midi_native && _lowLevelVolumeControl)
+ _midi_native->property(prop, value);
+ break;
+
default:
break;
}
@@ -515,6 +522,11 @@ void IMuseInternal::startSoundWithNoteOffset(int sound, int offset) {
////////////////////////////////////////
void IMuseInternal::setMusicVolume(int vol) {
+ if (_lowLevelVolumeControl) {
+ property(IMuse::PROP_MUSICVOLUME, vol);
+ return;
+ }
+
Common::StackLock lock(_mutex);
if (vol > 255)
vol = 255;
@@ -532,6 +544,14 @@ void IMuseInternal::setMusicVolume(int vol) {
update_volumes();
}
+void IMuseInternal::setSfxVolume(int vol) {
+ // This is supported only for drivers that can distinguish music from sound effects at the driver or emulator level.
+ // The imuse engine in its early version does not have volume groups. It simply (and successfully for the more relevant
+ // targets) relies on sound effects not being played through the imuse engine.
+ if (_lowLevelVolumeControl)
+ property(IMuse::PROP_SFXVOLUME, vol);
+}
+
void IMuseInternal::startSound(int sound) {
Common::StackLock lock(_mutex);
startSound_internal(sound);
@@ -1497,6 +1517,9 @@ int IMuseInternal::initialize(OSystem *syst, MidiDriver *native_midi, MidiDriver
_tempoFactor = 100;
_master_volume = 255;
+ if (_lowLevelVolumeControl)
+ _music_volume = _music_volume_eff = 255;
+
for (i = 0; i != 8; i++)
_channel_volume[i] = _channel_volume_eff[i] = _volchan_table[i] = 127;
diff --git a/engines/scumm/imuse/imuse.h b/engines/scumm/imuse/imuse.h
index e4396798919..32ec9dfd35e 100644
--- a/engines/scumm/imuse/imuse.h
+++ b/engines/scumm/imuse/imuse.h
@@ -53,7 +53,9 @@ public:
PROP_TEMPO_BASE,
PROP_LIMIT_PLAYERS,
PROP_RECYCLE_PLAYERS,
- PROP_QUALITY
+ PROP_QUALITY,
+ PROP_MUSICVOLUME,
+ PROP_SFXVOLUME
};
public:
diff --git a/engines/scumm/imuse/imuse_internal.h b/engines/scumm/imuse/imuse_internal.h
index 47e2a776f25..a4290fc9205 100644
--- a/engines/scumm/imuse/imuse_internal.h
+++ b/engines/scumm/imuse/imuse_internal.h
@@ -57,8 +57,6 @@ class IMuseSysex_Scumm;
#define TRIGGER_ID 0
#define COMMAND_ID 1
-#define MDPG_TAG "MDpg"
-
#define MUS_REDUCTION_TIMER_TICKS 16667 // 60 Hz
@@ -577,12 +575,22 @@ public:
// MusicEngine interface
void setMusicVolume(int vol) override;
+ void setSfxVolume(int vol) override;
void startSound(int sound) override;
void stopSound(int sound) override;
void stopAllSounds() override;
int getSoundStatus(int sound) const override;
int getMusicTimer() override;
+protected:
+ // Our normal volume control is high-level, i. e. it uses the imuse engine to generate the proper volume values and send these to the midi driver.
+ // For older titles (like MI2 and INDY4) who never had music and sfx volume controls in the original interpreters, this works well only if the
+ // engine can somehow distinguish between music and sound effects. It works for targets/platforms where this can be done by resource type, where
+ // the sfx resources aren't even played through the imuse engine. The imuse engine can then just assume that everything it plays is music. For
+ // MI2/INDY4 Macintosh it won't work like this, because both music and sound effects have the same resource type and are played through the imuse
+ // engine. For these targets it works better to pass the volume values on to the driver where other methods of distinction may be available.
+ const bool _lowLevelVolumeControl;
+
public:
// Factory function
static IMuseInternal *create(ScummEngine *vm, MidiDriver *nativeMidiDriver, MidiDriver *adlibMidiDriver, MidiDriverFlags sndType, bool nativeMT32);
diff --git a/engines/scumm/players/mac_sound_lowlevel.h b/engines/scumm/players/mac_sound_lowlevel.h
index f1bc45a7b8b..140b472dbb5 100644
--- a/engines/scumm/players/mac_sound_lowlevel.h
+++ b/engines/scumm/players/mac_sound_lowlevel.h
@@ -119,9 +119,10 @@ public:
kBufferReady = 1,
kLastBufferLast = 4
};
- DoubleBuffer() : numFrames(0), flags(0), data(0) {}
+ DoubleBuffer(ChanHandle hdl, uint32 numframes) : numFrames(numframes), flags(0), data(0), chanHandle(hdl) {}
~DoubleBuffer() { delete[] data; }
uint32 numFrames;
+ const ChanHandle chanHandle;
uint32 flags;
byte *data;
};
@@ -154,7 +155,7 @@ public:
void setTimbre(ChanHandle handle, ExecMode mode, uint16 timbre);
void callback(ChanHandle handle, ExecMode mode, uint16 arg1, const void *arg2);
- bool playDoubleBuffer(ChanHandle handle, byte numChan, byte bitsPerSample, uint32 rate, DBCallback *callback, byte externalMixNumChan = 1);
+ bool playDoubleBuffer(ChanHandle handle, byte numChan, byte bitsPerSample, uint32 rate, DBCallback *callback, byte numMixChan = 1);
uint8 getChannelStatus(ChanHandle handle) const;
void clearChannelFlags(ChanHandle handle, uint8 flags);
@@ -164,7 +165,7 @@ private:
MacSndChannel *findAndCheckChannel(ChanHandle h, const char *caller, byte reqSynthType) const;
MacSndChannel *findChannel(ChanHandle h) const;
Common::Array<MacSndChannel*> _channels;
- int _numInternalMixChannels;
+ int _numInternalMixChannels[4];
int32 *_mixBuffer = 0;
uint32 _mixBufferSize;
};
@@ -185,6 +186,7 @@ public:
void initBuffers(uint32 feedBufferSize);
void initDrivers();
void addVolumeGroup(Audio::Mixer::SoundType type);
+ void scaleVolume(uint scale) { _scale = scale; }
typedef Common::Functor0Mem<void, VblTaskClientDriver> CallbackProc;
void setVblCallback(const CallbackProc *proc);
void clearBuffer();
@@ -203,6 +205,7 @@ private:
VblTaskClientDriver *_drv;
int _numGroups;
+ uint16 _scale;
uint32 _vblSmpQty;
uint32 _vblSmpQtyRem;
diff --git a/engines/scumm/players/player_mac_new.cpp b/engines/scumm/players/player_mac_new.cpp
index 9f76d17eca0..b410e2c222e 100644
--- a/engines/scumm/players/player_mac_new.cpp
+++ b/engines/scumm/players/player_mac_new.cpp
@@ -37,7 +37,30 @@ namespace Scumm {
#define RATECNV_BIT_PRECSN 24
-class DoubleBufferIntern;
+class DoubleBufferIntern {
+public:
+ DoubleBufferIntern(MacLowLevelPCMDriver::ChanHandle hdl, uint32 numFrames, byte bitsPerSample, byte numChannels, MacLowLevelPCMDriver::DBCallback *cb, byte numMixChannels);
+ ~DoubleBufferIntern();
+ void callback();
+ void update();
+
+ const int8 *data() const { return _data; }
+ uint32 bufferSize() const { return _processSize; }
+ uint32 flags() const { return _buff.flags; }
+ byte bitsPerSample() const { return _bitsPerSample; }
+ byte numMixChannels() const { return _numMixChan; }
+
+private:
+ MacLowLevelPCMDriver::DoubleBuffer _buff;
+ MacLowLevelPCMDriver::DBCallback *_callback;
+ int8 *_data;
+ byte _bitsPerSample;
+ byte _numChan;
+ byte _numMixChan;
+ uint32 _bufferSize;
+ uint32 _processSize;
+};
+
class MacSndChannel {
public:
MacSndChannel(MacLowLevelPCMDriver *drv, Audio::Mixer::SoundType sndtp, int synth, bool interp, bool enableL, bool enableR, MacLowLevelPCMDriver::ChanCallback *callback);
@@ -55,7 +78,7 @@ public:
void setTimbre(uint16 timbre);
void callback(uint16 p1, const void *p2);
- bool playDoubleBuffer(byte numChan, byte bitsPerSample, uint32 rate, MacLowLevelPCMDriver::DBCallback *callback);
+ bool playDoubleBuffer(byte numChan, byte bitsPerSample, uint32 rate, MacLowLevelPCMDriver::DBCallback *callback, byte numMixChan = 1);
struct SoundCommand {
SoundCommand() : cmd(0), arg1(0), arg2(0), ptr(nullptr) {}
@@ -70,6 +93,8 @@ public:
void enqueueSndCmd(uint8 c, uint16 p1, uint32 p2, byte mode);
void enqueueSndCmd(uint8 c, uint16 p1, const void *p2, byte ptrType, byte mode);
bool idle() const;
+ bool dblBufferModeEnabled() const { return _dblBuff != nullptr; }
+ byte dblBuffNumMixChannels() const { return _dblBuff ? _dblBuff->numMixChannels() : 0; }
void setFlags(uint8 flags) { _flags |= flags; }
void clearFlags(uint8 flags) { _flags &= ~flags; }
@@ -148,31 +173,9 @@ uint32 fixMul(uint32 fx1, uint32 fx2) {
return (a << 16) + b + c + (d >> 16);
}
-class DoubleBufferIntern {
-public:
- DoubleBufferIntern(uint32 numFrames, byte bitsPerSample, byte numChannels, MacLowLevelPCMDriver::DBCallback *cb);
- ~DoubleBufferIntern();
- void callback();
- void update();
-
- const int8 *data() const { return _data; }
- uint32 bufferSize() const { return _processSize; }
- uint32 flags() const { return _buff.flags; }
- byte bitsPerSample() const { return _bitsPerSample; }
-
-private:
- MacLowLevelPCMDriver::DoubleBuffer _buff;
- MacLowLevelPCMDriver::DBCallback *_callback;
- int8 *_data;
- byte _bitsPerSample;
- byte _numChan;
- uint32 _bufferSize;
- uint32 _processSize;
-};
-
MacPlayerAudioStream::MacPlayerAudioStream(VblTaskClientDriver *drv, uint32 scummVMOutputrate, bool stereo, bool interpolate, bool internal16Bit) : Audio::AudioStream(), _drv(drv),
_vblSmpQty(0), _vblSmpQtyRem(0), _frameSize((stereo ? 2 : 1) * (internal16Bit ? 2 : 1)), _vblCountDown(0), _vblCountDownRem(0), _outputRate(scummVMOutputrate),
- _vblCbProc(nullptr), _numGroups(1), _isStereo(stereo), _interp(interpolate), _smpInternalSize(internal16Bit ? 2 : 1) {
+ _vblCbProc(nullptr), _numGroups(1), _isStereo(stereo), _interp(interpolate), _smpInternalSize(internal16Bit ? 2 : 1), _scale(1) {
assert(_drv);
_buffers = new SmpBuffer[2];
_vblSmpQty = (_outputRate << 16) / VBL_UPDATE_RATE;
@@ -283,14 +286,14 @@ int MacPlayerAudioStream::readBuffer(int16 *buffer, const int numSamples) {
int diff = smpN - _buffers[ii].lastL;
if (diff && _buffers[ii].rateConvAcc && interp)
diff = (diff * _buffers[ii].rateConvAcc) >> RATECNV_BIT_PRECSN;
- smpL += (int32)((_buffers[ii].lastL + diff) * (_buffers[ii].volume / numch));
+ smpL += (int32)((_buffers[ii].lastL + diff) * (_buffers[ii].volume * _scale / numch));
if (_isStereo) {
smpN = _smpInternalSize == 2 ? *reinterpret_cast<int16*>(&_buffers[ii].pos[2]) : _buffers[ii].pos[1];
diff = smpN - _buffers[ii].lastR;
if (diff && _buffers[ii].rateConvAcc && interp)
diff = (diff * _buffers[ii].rateConvAcc) >> RATECNV_BIT_PRECSN;
- smpR += (int32)((_buffers[ii].lastR + diff) * (_buffers[ii].volume / numch));
+ smpR += (int32)((_buffers[ii].lastR + diff) * (_buffers[ii].volume * _scale / numch));
}
}
@@ -345,7 +348,8 @@ void MacPlayerAudioStream::runVblTask() {
}
MacLowLevelPCMDriver::MacLowLevelPCMDriver(Common::Mutex &mutex, uint32 deviceRate, bool internal16Bit) :
- MacSoundDriver(mutex, deviceRate, 0, true, internal16Bit), _numInternalMixChannels(1), _mixBufferSize(0), _mixBuffer(nullptr) {
+ MacSoundDriver(mutex, deviceRate, 0, true, internal16Bit), _mixBufferSize(0), _mixBuffer(nullptr) {
+ _numInternalMixChannels[0] = _numInternalMixChannels[1] = _numInternalMixChannels[2] = _numInternalMixChannels[3] = 1;
}
MacLowLevelPCMDriver::~MacLowLevelPCMDriver() {
@@ -386,7 +390,7 @@ void MacLowLevelPCMDriver::feed(int8 *dst, uint32 byteSize, Audio::Mixer::SoundT
if (_smpSize == 2)
*reinterpret_cast<int16*>(dst) += CLIP<int32>(*src, _smpMin, _smpMax);
else
- *dst += CLIP<int32>(*src / _numInternalMixChannels, _smpMin, _smpMax);
+ *dst += CLIP<int32>(*src / _numInternalMixChannels[type], _smpMin, _smpMax);
dst += _smpSize;
}
}
@@ -506,7 +510,7 @@ void MacLowLevelPCMDriver::callback(ChanHandle handle, ExecMode mode, uint16 arg
ch->enqueueSndCmd(13, arg1, arg2, 0, mode);
}
-bool MacLowLevelPCMDriver::playDoubleBuffer(ChanHandle handle, byte numChan, byte bitsPerSample, uint32 rate, DBCallback *callback, byte externalMixNumChan) {
+bool MacLowLevelPCMDriver::playDoubleBuffer(ChanHandle handle, byte numChan, byte bitsPerSample, uint32 rate, DBCallback *callback, byte numMixChan) {
Common::StackLock lock(_mutex);
MacSndChannel *ch = findAndCheckChannel(handle, __FUNCTION__, kSampledSynth);
if (!ch)
@@ -517,9 +521,10 @@ bool MacLowLevelPCMDriver::playDoubleBuffer(ChanHandle handle, byte numChan, byt
return false;
}
- _status[ch->_sndType].numExternalMixChannels = bitsPerSample > 8 ? externalMixNumChan : 1;
+ bool res = ch->playDoubleBuffer(numChan, bitsPerSample, rate, callback, numMixChan);
+ updateStatus(ch->_sndType);
- return ch->playDoubleBuffer(numChan, bitsPerSample, rate, callback);
+ return res;
}
uint8 MacLowLevelPCMDriver::getChannelStatus(ChanHandle handle) const {
@@ -531,16 +536,23 @@ void MacLowLevelPCMDriver::clearChannelFlags(ChanHandle handle, uint8 flags) {
}
void MacLowLevelPCMDriver::updateStatus(Audio::Mixer::SoundType sndType) {
- _numInternalMixChannels = _smpSize > 1 ? 1 : _channels.size();
- _status[sndType].allowInterPolation = true;
int count = 0;
+ int count2 = 0;
+ _status[sndType].allowInterPolation = true;
for (Common::Array<MacSndChannel*>::const_iterator ch = _channels.begin(); ch != _channels.end(); ++ch) {
- if ((*ch)->_sndType == sndType)
- ++count;
+ if ((*ch)->_sndType == sndType) {
+ if ((*ch)->dblBufferModeEnabled()) {
+ count += (*ch)->dblBuffNumMixChannels();
+ ++count2;
+ } else {
+ ++count;
+ }
+ }
if (!(*ch)->_interpolate)
_status[sndType].allowInterPolation = false;
}
_status[sndType].numExternalMixChannels = _smpSize > 1 ? count : 1;
+ _numInternalMixChannels[sndType] = _smpSize > 1 ? 1 : MAX<int>(_channels.size() - count2, 1);
}
MacSndChannel *MacLowLevelPCMDriver::findAndCheckChannel(ChanHandle h, const char *callingProcName, byte reqSynthType) const {
@@ -693,7 +705,7 @@ void MacSndChannel::callback(uint16 p1, const void *p2) {
(*_callback)(p1, p2);
}
-bool MacSndChannel::playDoubleBuffer(byte numChan, byte bitsPerSample, uint32 rate, MacLowLevelPCMDriver::DBCallback *callback) {
+bool MacSndChannel::playDoubleBuffer(byte numChan, byte bitsPerSample, uint32 rate, MacLowLevelPCMDriver::DBCallback *callback, byte numMixChan) {
if (!numChan || !bitsPerSample || !rate || !callback) {
warning("%s(): Invalid argument", __FUNCTION__);
return false;
@@ -704,7 +716,7 @@ bool MacSndChannel::playDoubleBuffer(byte numChan, byte bitsPerSample, uint32 ra
return false;
}
- _dblBuff = new DoubleBufferIntern(1024, bitsPerSample, numChan, callback);
+ _dblBuff = new DoubleBufferIntern(getHandle(), 1024, bitsPerSample, numChan, callback, numMixChan);
setupRateConv(_drv->getStatus().deviceRate, 0x10000, rate, false);
_duration = _tmrInc = _tmrPos = _loopSt = _loopEnd = _rcPos = _smpWtAcc = _phase = 0;
@@ -1193,8 +1205,7 @@ void MacSndChannel::makeSquareWave(int8 *dstBuff, uint16 dstSize, byte timbre) {
*d2++ = *d1++ ^ 0xff;
}
-DoubleBufferIntern::DoubleBufferIntern(uint32 numFrames, byte bitsPerSample, byte numChannels, MacLowLevelPCMDriver::DBCallback *cb) : _bitsPerSample(bitsPerSample), _numChan(numChannels), _callback(cb), _data(nullptr), _bufferSize(0), _processSize(0) {
- _buff.numFrames = numFrames;
+DoubleBufferIntern::DoubleBufferIntern(MacLowLevelPCMDriver::ChanHandle hdl, uint32 numFrames, byte bitsPerSample, byte numChannels, MacLowLevelPCMDriver::DBCallback *cb, byte numMixChan) : _buff(hdl, numFrames), _bitsPerSample(bitsPerSample), _numChan(numChannels), _callback(cb), _numMixChan(numMixChan), _data(nullptr), _bufferSize(0), _processSize(0) {
update();
}
Commit: 673e33813595bad218734f4840e38e1842d89353
https://github.com/scummvm/scummvm/commit/673e33813595bad218734f4840e38e1842d89353
Author: athrxx (athrxx at scummvm.org)
Date: 2024-11-23T23:07:33+01:00
Commit Message:
SCUMM: (IMS/Mac) - prevent playback of ADL resources
This sometimes happens in MI2 and causes weird sounds. The original
would be unable to play that, since the sound tags are strictly tied to
the drivers and no driver will ever eat anything but its own data type.
But our resource maangement works differently, so it could happen...
Changed paths:
engines/scumm/sound.cpp
diff --git a/engines/scumm/sound.cpp b/engines/scumm/sound.cpp
index fff13bd64e4..c6a5622bd95 100644
--- a/engines/scumm/sound.cpp
+++ b/engines/scumm/sound.cpp
@@ -1602,9 +1602,10 @@ int ScummEngine::readSoundResource(ResId idx) {
pri = 15;
break;
case MKTAG('A','D','L',' '):
- pri = 1;
if (_sound->_musicType == MDT_ADLIB || _sound->_musicType == MDT_TOWNS)
pri = 10;
+ else if (_sound->_musicType != MDT_MACINTOSH && _sound->_musicType != MDT_AMIGA)
+ pri = 1;
break;
case MKTAG('A','M','I',' '):
pri = 3;
@@ -1613,10 +1614,8 @@ int ScummEngine::readSoundResource(ResId idx) {
// Some of the Mac MI2 music only exists as Roland tracks. The
// original interpreter doesn't play them. I don't think there
// is any similarly missing FoA music.
- if (_game.id == GID_MONKEY2 && _game.platform == Common::kPlatformMacintosh && !enhancementEnabled(kEnhAudioChanges)) {
- pri = -1;
+ if (_game.id == GID_MONKEY2 && _game.platform == Common::kPlatformMacintosh && !enhancementEnabled(kEnhAudioChanges))
break;
- }
pri = 3;
if (_native_mt32)
@@ -1629,7 +1628,6 @@ int ScummEngine::readSoundResource(ResId idx) {
pri = 2;
break;
case MKTAG('S','P','K',' '):
- pri = -1;
if (_sound->_musicType == MDT_PCSPK || _sound->_musicType == MDT_PCJR)
pri = 11;
break;
@@ -1676,6 +1674,11 @@ int ScummEngine::readSoundResource(ResId idx) {
_fileHandle->read(ptr, best_size);
//dumpResource("sound-", idx, ptr);
return 1;
+ } else if (_game.platform == Common::kPlatformAmiga || _game.platform == Common::kPlatformMacintosh) {
+ // This isn't a bug. These versions simply don't have all the sounds. Very noticeable for Amiga (which
+ // doesn't even have the Woodtick song), but also for Macintosh in a few cases.
+ debugC(DEBUG_RESOURCE, "No appropriate sound data for target platform found in resource %d", idx);
+ return 0;
}
break;
case MKTAG('M','a','c','0'):
@@ -1781,7 +1784,7 @@ int ScummEngine::readSoundResource(ResId idx) {
}
if (total_size)
- warning("Unrecognized base tag 0x%08x in sound %d", basetag, idx);
+ warning("Unrecognized base tag '%c%c%c%c' in sound %d", (basetag >> 24) & 0xFF, (basetag >> 16) & 0xFF, (basetag >> 8) & 0xFF, basetag & 0xFF, idx);
_res->_types[rtSound][idx]._roomoffs = RES_INVALID_OFFSET;
return 0;
}
Commit: 7e875c273e695d75e456a29ea3223743aae90a3f
https://github.com/scummvm/scummvm/commit/7e875c273e695d75e456a29ea3223743aae90a3f
Author: athrxx (athrxx at scummvm.org)
Date: 2024-11-23T23:07:39+01:00
Commit Message:
SCUMM: (IMS/Mac) - add DOTT sound support
SAM mostly works, too, but there hasn't been done any
adjustment work yet for the newer imuse system yet.
DOTT (and probably SAM) actually support stereo. The
hardware caps get detected and it is activated based on
that. The samples are still 8-bit mono, but it allows some
pan pos effects. Nonetheless, I have currently set it to
mono, since that sounds better (less noise, better balance
between channels. Not sure if is a bug of mine or an
original issue, since I haven't been able to get stereo
sound on an emulator).
Changed paths:
engines/scumm/imuse/drivers/macintosh.cpp
engines/scumm/imuse/drivers/macintosh.h
engines/scumm/imuse/imuse.cpp
engines/scumm/imuse/imuse_internal.h
engines/scumm/imuse/instrument.cpp
engines/scumm/metaengine.cpp
engines/scumm/players/mac_sound_lowlevel.h
engines/scumm/players/player_mac_loom_monkey.cpp
engines/scumm/players/player_mac_new.cpp
engines/scumm/scumm.cpp
diff --git a/engines/scumm/imuse/drivers/macintosh.cpp b/engines/scumm/imuse/drivers/macintosh.cpp
index adb8f7f664a..7a116df4a39 100644
--- a/engines/scumm/imuse/drivers/macintosh.cpp
+++ b/engines/scumm/imuse/drivers/macintosh.cpp
@@ -22,6 +22,9 @@
#include "common/macresman.h"
#include "common/punycode.h"
+#include "scumm/detection.h"
+#include "scumm/file.h"
+#include "scumm/scumm.h"
#include "scumm/players/mac_sound_lowlevel.h"
#include "scumm/players/player_mac_new.h"
#include "scumm/imuse/drivers/macintosh.h"
@@ -31,46 +34,57 @@
namespace IMSMacintosh {
using namespace Scumm;
-class DJMSoundChannel {
-public:
- DJMSoundChannel(const uint32 *pitchTable) : _pitchTable(pitchTable), _frequency(0), _phase(0), _end(nullptr), _pos(nullptr), _smpBuffStart(nullptr), _smpBuffEnd(nullptr), _loopStart(nullptr), _loopEnd(nullptr), _pitch(0), _volume(0), _mute(1), _instr(nullptr), _baseFreq(0) {}
- ~DJMSoundChannel() {}
+struct ChanControlNode;
+struct DeviceChannel {
+ DeviceChannel(const uint32 *pitchTable) : pitchTable(pitchTable), frequency(0), phase(0), end(nullptr), pos(nullptr), smpBuffStart(nullptr),
+ smpBuffEnd(nullptr), loopStart(nullptr), loopEnd(nullptr), pitch(0), mute(true), release(false), instr(nullptr),
+ prog(0), baseFreq(0), note(0), volumeL(0), volumeR(0), rate(0), totalLevelL(0), totalLevelR(0), node(nullptr), prev(nullptr), next(nullptr) {}
+ ~DeviceChannel() {}
void recalcFrequency();
- uint32 _frequency;
- uint32 _phase;
- const byte *_end;
- const byte *_pos;
- const byte *_smpBuffStart;
- const byte *_smpBuffEnd;
- const byte *_loopStart;
- const byte *_loopEnd;
- uint16 _pitch;
- byte _volume;
- byte _mute;
- byte _baseFreq;
- Common::SharedPtr<MacSndInstrument> _instr;
- const uint32 *_pitchTable;
+ uint32 frequency;
+ uint32 phase;
+ const byte *end;
+ const byte *pos;
+ const byte *smpBuffStart;
+ const byte *smpBuffEnd;
+ const byte *loopStart;
+ const byte *loopEnd;
+ uint16 pitch;
+ byte volumeL;
+ byte volumeR;
+ byte totalLevelL;
+ byte totalLevelR;
+ byte baseFreq;
+ uint32 rate;
+ uint16 prog;
+ byte note;
+ bool mute;
+ bool release;
+ const ChanControlNode *node;
+ Common::SharedPtr<MacSndResource> instr;
+ const uint32 *pitchTable;
+ DeviceChannel *prev;
+ DeviceChannel *next;
};
-class DJMSoundSystem : public VblTaskClientDriver, public MacLowLevelPCMDriver::CallbackClient {
+class IMSMacSoundSystem : public VblTaskClientDriver, public MacLowLevelPCMDriver::CallbackClient {
public:
- DJMSoundSystem(Audio::Mixer *mixer);
- ~DJMSoundSystem();
+ IMSMacSoundSystem(Audio::Mixer *mixer, byte version);
+ virtual ~IMSMacSoundSystem();
- bool init(bool internal16Bit);
+ bool init(const char *const *instrFileNames, int numInstrFileNames, bool stereo, bool internal16Bit);
void deinit();
- bool start();
+ virtual bool start() = 0;
void stop();
- void setQuality(int qual);
-
- void setInstrument(byte chan, Common::SharedPtr<MacSndInstrument> &instr);
- void noteOn(byte chan, uint16 pitch, byte volume);
- void noteOff(byte chan);
- void setVolume(byte chan, byte volume);
- void setPitch(byte chan, uint16 pitch);
+ virtual void setQuality(int qual) = 0;
+ void noteOn(const ChanControlNode *node);
+ void noteOff(const ChanControlNode *node);
+ void soundOff(const ChanControlNode *node);
+ void setVolume(const ChanControlNode *node);
+ void setPitchBend(const ChanControlNode *node);
void setTimerCallback(void *timerParam, Common::TimerManager::TimerProc timerProc);
@@ -82,37 +96,111 @@ public:
void setMasterVolume(Audio::Mixer::SoundType type, uint16 volume);
-private:
+protected:
+ bool setupResourceFork(Common::MacResManager &rm, const char *const *fileNames, int numFileNames);
+ Common::SharedPtr<MacSndResource> getSndResource(uint16 id);
void fillPitchTable();
- byte _quality;
+ Common::Array<Common::SharedPtr<MacSndResource> > _smpResources;
+ MacLowLevelPCMDriver::ChanHandle _musicChan;
+ MacLowLevelPCMDriver::ChanHandle _sfxChan;
+ MacLowLevelPCMDriver *_sdrv;
+ bool _internal16Bit;
+ bool _stereo;
uint16 _feedBufferSize;
+ uint16 _defaultInstrID;
+ byte _quality;
+ const uint16 _numChannels;
+ Audio::Mixer *_mixer;
+ DeviceChannel **_channels;
+
+private:
+ virtual bool loadInstruments(const char *const *fileNames, int numFileNames) = 0;
+ virtual void setInstrument(DeviceChannel *chan) = 0;
+ virtual void recalcFrequency(DeviceChannel *chan) = 0;
+ virtual void recalcVolume(DeviceChannel *chan) = 0;
+ virtual void noteOffIntern(DeviceChannel *chan) = 0;
+
+ virtual DeviceChannel *allocateChannel(const ChanControlNode *node) = 0;
+
uint32 *_pitchTable;
byte *_ampTable;
+ int16 *_mixTable;
int16 *_mixBuffer16Bit;
- DJMSoundChannel **_channels;
-
MacPlayerAudioStream *_macstr;
- MacLowLevelPCMDriver *_sdrv;
Audio::SoundHandle _soundHandle;
MacPlayerAudioStream::CallbackProc _vblTskProc;
- MacLowLevelPCMDriver::ChanHandle _musicChan;
- MacLowLevelPCMDriver::ChanHandle _sfxChan;
- MacLowLevelPCMDriver::DBCallback _dbCbProc;
- Audio::Mixer *_mixer;
void *_timerParam;
Common::TimerManager::TimerProc _timerProc;
- bool _internal16Bit;
- const byte _numChannels;
+
+ const byte _loopstLim;
+ const byte _version;
+};
+
+class DJMSoundSystem final : public IMSMacSoundSystem {
+public:
+ DJMSoundSystem(Audio::Mixer *mixer);
+ ~DJMSoundSystem() override {}
+
+ bool start() override;
+ void setQuality(int qual) override;
+
+private:
+ bool loadInstruments(const char *const *fileNames, int numFileNames) override;
+ void setInstrument(DeviceChannel *c) override;
+ void recalcFrequency(DeviceChannel *c) override;
+ void recalcVolume(DeviceChannel *c) override;
+ void noteOffIntern(DeviceChannel *chan) override;
+
+ DeviceChannel *allocateChannel(const ChanControlNode *node) override;
+
+ MacLowLevelPCMDriver::DBCallback _dbCbProc;
+};
+
+class NewMacSoundSystem final : public IMSMacSoundSystem {
+public:
+ NewMacSoundSystem(ScummEngine *vm, Audio::Mixer *mixer);
+ ~NewMacSoundSystem() override;
+
+ bool start() override;
+ void setQuality(int qual) override {}
+
+private:
+ bool loadInstruments(const char *const *fileNames, int numFileNames) override;
+ Common::SharedPtr<MacSndResource> getNoteRangeSndResource(uint16 id, byte note);
+ void setInstrument(DeviceChannel *c) override;
+ void recalcFrequency(DeviceChannel *c) override;
+ void recalcVolume(DeviceChannel *c) override;
+ void noteOffIntern(DeviceChannel *chan) override;
+
+ DeviceChannel *allocateChannel(const ChanControlNode *node) override;
+
+ MacLowLevelPCMDriver::DBCallback _dbCbProc;
+ ScummFile *_fileMan;
+ Common::Path &_container;
+ Common::Path _dummy;
+
+ struct Instrument {
+ Instrument(uint16 resId) : id(resId), noteSmplsMapping(nullptr) {
+ noteSmplsMapping = new byte[128]();
+ memset(noteSmplsMapping, 0xFF, 128);
+ }
+ ~Instrument() {
+ delete[] noteSmplsMapping;
+ }
+ uint16 id;
+ byte *noteSmplsMapping;
+ Common::Array<Common::SharedPtr<MacSndResource> > sndRes;
+ };
+ Common::Array<Common::SharedPtr<Instrument> > _instruments;
};
-struct ChannelNode;
-class IMuseChannel_Mac_DJM final : public MidiChannel {
+class IMuseChannel_Macintosh final : public MidiChannel {
public:
- IMuseChannel_Mac_DJM(IMuseDriver_Mac_DJM *drv, int number);
- ~IMuseChannel_Mac_DJM() override;
+ IMuseChannel_Macintosh(IMuseDriver_Macintosh *drv, int number);
+ ~IMuseChannel_Macintosh() override {}
MidiDriver *device() override { return _drv; }
byte getNumber() override { return _number; }
@@ -133,105 +221,120 @@ public:
void pitchBendFactor(byte value) override { pitchBend(0); _pitchBendSensitivity = value; }
void transpose(int8 value) override { _transpose = value; pitchBend(_pitchBendSet); }
void detune(int16 value) override { _detune = value; pitchBend(_pitchBendSet); }
- void priority(byte value) override { _prio = value; }
+ void priority(byte value) override { _prio = value; }
void sustain(bool value) override;
void allNotesOff() override;
void sysEx_customInstrument(uint32 type, const byte *instr, uint32 dataSize) override {}
private:
+ void dataEntry(byte value);
+ void updateVolume();
bool _allocated;
byte _prio;
byte _bank;
byte _volume;
+ byte _panPos;
int16 _detune;
int8 _transpose;
int16 _pitchBendSet;
byte _pitchBendSensitivity;
int16 _pitchBendEff;
+ uint16 _pitchBendRange;
+ byte _rpn;
bool _sustain;
- Common::SharedPtr<MacSndInstrument> _prog;
+ uint16 _prog;
const byte _number;
- ChannelNode *allocateDeviceChannel(int prio);
+ ChanControlNode *allocateNode(int prio);
- ChannelNode **_channels;
- ChannelNode *_out;
+ ChanControlNode **_channels;
+ ChanControlNode *_rtmChannel;
+ ChanControlNode *_out;
static byte _allocCur;
- IMuseDriver_Mac_DJM *_drv;
- DJMSoundSystem *_device;
+ IMuseDriver_Macintosh *_drv;
+ IMSMacSoundSystem *_device;
- Common::Array<Common::SharedPtr<MacSndInstrument> > *_instruments;
const byte _numChannels;
+ const int8 _version;
};
-struct ChannelNode {
- ChannelNode(int num) : _prev(nullptr), _next(nullptr), _in(nullptr), _number(num), _note(0), _sustain(false) {}
- ChannelNode *_prev;
- ChannelNode *_next;
- IMuseChannel_Mac_DJM *_in;
- const byte _number;
- byte _note;
- bool _sustain;
+struct ChanControlNode {
+ ChanControlNode(int num) : in(nullptr), number(num), note(0), sustain(false), prio(0x7F), volume(0x7F), panPos(0x40), velocity(0), pitchBend(0), prog(0), prev(nullptr), next(nullptr) {}
+ IMuseChannel_Macintosh *in;
+ const byte number;
+ bool sustain;
+ byte prio;
+ byte note;
+ byte volume;
+ byte panPos;
+ byte velocity;
+ int16 pitchBend;
+ uint16 prog;
+ ChanControlNode *prev;
+ ChanControlNode *next;
};
-void connect(ChannelNode *&chain, ChannelNode *node) {
- if (!node || node->_prev || node->_next)
+void connect(ChanControlNode *&chain, ChanControlNode *node) {
+ if (!node || node->prev || node->next)
return;
- if ((node->_next = chain))
- chain->_prev = node;
+ if ((node->next = chain))
+ chain->prev = node;
chain = node;
}
-void disconnect(ChannelNode *&chain, ChannelNode *node) {
+void disconnect(ChanControlNode *&chain, ChanControlNode *node) {
if (!node || !chain)
return;
- const ChannelNode *ch = chain;
+ const ChanControlNode *ch = chain;
while (ch && ch != node)
- ch = ch->_next;
+ ch = ch->next;
if (!ch)
return;
- if (node->_next)
- node->_next->_prev = node->_prev;
+ if (node->next)
+ node->next->prev = node->prev;
- if (node->_prev)
- node->_prev->_next = node->_next;
+ if (node->prev)
+ node->prev->next = node->next;
else
- chain = node->_next;
+ chain = node->next;
- node->_in = nullptr;
- node->_next = node->_prev = nullptr;
+ node->in = nullptr;
+ node->next = node->prev = nullptr;
}
-void DJMSoundChannel::recalcFrequency() {
- int pos = (_pitch >> 7) + 60 - _baseFreq;
- assert(pos>=0);
- if (_pitch & 0x7F)
- _frequency = _pitchTable[pos] + (((_pitchTable[pos + 1] - _pitchTable[pos]) * (_pitch & 0x7F)) >> 7);
+void DeviceChannel::recalcFrequency() {
+ int cpos = (pitch >> 7) + 60 - baseFreq;
+ if (cpos < 0)
+ frequency = (uint32)-1;
+ else if (pitch & 0x7F)
+ frequency = pitchTable[cpos] + (((pitchTable[cpos + 1] - pitchTable[cpos]) * (pitch & 0x7F)) >> 7);
else
- _frequency = _pitchTable[pos];
+ frequency = pitchTable[cpos];
}
-DJMSoundSystem::DJMSoundSystem(Audio::Mixer *mixer) : VblTaskClientDriver(), _mixer(mixer), _macstr(nullptr), _sdrv(nullptr), _vblTskProc(this, &VblTaskClientDriver::vblCallback),
- _dbCbProc(this, &MacLowLevelPCMDriver::CallbackClient::dblBuffCallback), _musicChan(0), _sfxChan(0), _quality(22), _feedBufferSize(1024), _channels(nullptr),
- _timerParam(nullptr), _timerProc(nullptr), _pitchTable(nullptr), _ampTable(nullptr), _numChannels(8) {
+IMSMacSoundSystem::IMSMacSoundSystem(Audio::Mixer *mixer, byte version) : VblTaskClientDriver(), _mixer(mixer), _macstr(nullptr), _sdrv(nullptr), _vblTskProc(this, &VblTaskClientDriver::vblCallback),
+ _musicChan(0), _sfxChan(0), _quality(22), _feedBufferSize(1024), _channels(nullptr), _timerParam(nullptr), _timerProc(nullptr), _pitchTable(nullptr),
+ _ampTable(nullptr), _mixTable(nullptr), _numChannels(version ? 12 : 8), _defaultInstrID(0), _version(version), _loopstLim(version ? 10 : 12), _stereo(false) {
_pitchTable = new uint32[128]();
assert(_pitchTable);
- _channels = new DJMSoundChannel*[_numChannels];
+ _channels = new DeviceChannel*[_numChannels];
assert(_channels);
for (int i = 0; i < _numChannels; ++i)
- _channels[i] = new DJMSoundChannel(_pitchTable);
+ _channels[i] = new DeviceChannel(_pitchTable);
_mixBuffer16Bit = new int16[_feedBufferSize]();
assert(_mixBuffer16Bit);
fillPitchTable();
_ampTable = new byte[8192]();
assert(_ampTable);
+ _mixTable = new int16[_numChannels << 8]();
+ assert(_mixTable);
}
-DJMSoundSystem::~DJMSoundSystem() {
+IMSMacSoundSystem::~IMSMacSoundSystem() {
deinit();
if (_channels) {
for (int i = 0; i < _numChannels; ++i)
@@ -239,13 +342,26 @@ DJMSoundSystem::~DJMSoundSystem() {
delete[] _channels;
}
delete[] _pitchTable;
+ delete[] _mixTable;
delete[] _mixBuffer16Bit;
}
-bool DJMSoundSystem::init(bool internal16Bit) {
+bool IMSMacSoundSystem::init(const char *const *instrFileNames, int numInstrFileNames, bool stereo, bool internal16Bit) {
+ if (!loadInstruments(instrFileNames, numInstrFileNames))
+ return false;
+
_internal16Bit = internal16Bit;
+ _stereo = _version > 0 && stereo;
+
+ uint16 feedBufferSize = _stereo ? 2048 : 1024;
+ if (_feedBufferSize != feedBufferSize) {
+ delete[] _mixBuffer16Bit;
+ _feedBufferSize = feedBufferSize;
+ _mixBuffer16Bit = new int16[_feedBufferSize]();
+ assert(_mixBuffer16Bit);
+ }
- static const byte ampTable[32] = {
+ static const byte ampTableV0[32] = {
0x00, 0x00, 0x01, 0x01, 0x02, 0x03, 0x05, 0x06,
0x08, 0x0b, 0x0d, 0x10, 0x13, 0x16, 0x1a, 0x1e,
0x22, 0x26, 0x2b, 0x30, 0x35, 0x3a, 0x40, 0x46,
@@ -253,13 +369,26 @@ bool DJMSoundSystem::init(bool internal16Bit) {
};
byte *dst = _ampTable;
- byte offs = _internal16Bit ? 0 : 128;
for (int i = 0; i < 32; ++i) {
+ byte mul = (_version == 0) ? ampTableV0[i] : i << 2;
for (int ii = 0; ii < 256; ++ii)
- *dst++ = (((ii - 128) * ampTable[i]) / 127) + offs;
+ *dst++ = (((ii - 128) * mul) / 127) + 128;
+ }
+
+ uint32 m = _numChannels << 7;
+ int16 *d1 = &_mixTable[m];
+ int16 *d2 = &_mixTable[m - 1];
+
+ byte base = _internal16Bit? 0 : 128;
+ uint16 ml = _internal16Bit ? _numChannels : 1;
+ uint16 sv = _stereo ? 2 : 1;
+ for (uint32 i = 0; i < m; ++i) {
+ int val = (((((i * (m - _numChannels)) << 7) >> 1) / (((_numChannels >> 1) - 1) * i + m - _numChannels) + base) * ml * sv) >> 7;
+ *d1++ = base + val;
+ *d2-- = base -val - 1;
}
- _macstr = new MacPlayerAudioStream(this, _mixer->getOutputRate(), false, false, internal16Bit);
+ _macstr = new MacPlayerAudioStream(this, _mixer->getOutputRate(), _stereo, false, internal16Bit);
if (!_macstr)
return false;
@@ -271,8 +400,9 @@ bool DJMSoundSystem::init(bool internal16Bit) {
_macstr->initBuffers(1024);
_macstr->addVolumeGroup(Audio::Mixer::kMusicSoundType);
- _macstr->addVolumeGroup(Audio::Mixer::kSFXSoundType);
- _macstr->scaleVolume(2);
+ // Only MI2 and FOA have MIDI sound effects
+ if (_version == 0)
+ _macstr->addVolumeGroup(Audio::Mixer::kSFXSoundType);
_macstr->setVblCallback(&_vblTskProc);
_mixer->playStream(Audio::Mixer::kPlainSoundType, &_soundHandle, _macstr, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
@@ -280,7 +410,7 @@ bool DJMSoundSystem::init(bool internal16Bit) {
return true;
}
-void DJMSoundSystem::deinit() {
+void IMSMacSoundSystem::deinit() {
Common::StackLock lock(_mixer->mutex());
stop();
_mixer->stopHandle(_soundHandle);
@@ -292,363 +422,773 @@ void DJMSoundSystem::deinit() {
_ampTable = nullptr;
}
-bool DJMSoundSystem::start() {
- _musicChan = _sdrv->createChannel(Audio::Mixer::kMusicSoundType, MacLowLevelPCMDriver::kSampledSynth, 0x8C, nullptr);
- _sfxChan = _sdrv->createChannel(Audio::Mixer::kSFXSoundType, MacLowLevelPCMDriver::kSampledSynth, 0x8C, nullptr);
- if (!_musicChan || !_sfxChan)
- return false;
-
- uint32 rate = 0;
- switch (_quality) {
- case 5:
- rate = 0x15BBA2E8;
- break;
- case 7:
- rate = 0x1CFA2E8B;
- break;
- case 11:
- rate = 0x2B7745D1;
- break;
- case 22:
- rate = 0x56EE8BA3;
- break;
- default:
- warning("%s(): Invalid quality setting %d", __FUNCTION__, _quality);
- return false;
- }
-
- return _sdrv->playDoubleBuffer(_musicChan, 1, _internal16Bit ? 16 : 8, rate, &_dbCbProc, _internal16Bit ? 8 : 1) &&
- _sdrv->playDoubleBuffer(_sfxChan, 1, _internal16Bit ? 16 : 8, rate, &_dbCbProc, _internal16Bit ? 8 : 1);
-}
-
-void DJMSoundSystem::stop() {
+void IMSMacSoundSystem::stop() {
Common::StackLock lock(_mixer->mutex());
if (_sdrv) {
_sdrv->disposeChannel(_musicChan);
- _sdrv->disposeChannel(_sfxChan);
+ if (_sfxChan)
+ _sdrv->disposeChannel(_sfxChan);
}
}
-void DJMSoundSystem::setQuality(int qual) {
+void IMSMacSoundSystem::noteOn(const ChanControlNode *node) {
+ assert(node);
Common::StackLock lock(_mixer->mutex());
- qual = qual > 1 ? 22 : 11;
- if (qual == _quality)
+ DeviceChannel *c = allocateChannel(node);
+ if (!c)
return;
- _quality = qual;
- _feedBufferSize = (qual == 22) ? 1024 : 512;
- fillPitchTable();
-
- for (int i = 0; i < _numChannels; ++i)
- _channels[i]->recalcFrequency();
-
- stop();
- if (!start())
- error("%s(): Unknown error", __FUNCTION__);
-}
+ setInstrument(c);
-void DJMSoundSystem::setInstrument(byte chan, Common::SharedPtr<MacSndInstrument> &instr) {
- Common::StackLock lock(_mixer->mutex());
- if (chan > 7)
+ if (c->instr == nullptr)
return;
- DJMSoundChannel &c = *_channels[chan];
- if (c._instr == instr)
- return;
+ debug(6, "%s(): chan %d, note %d, instr id %d (%s)", __FUNCTION__, node->number, node->note, node->prog, c->instr.get()->name());
+
+ recalcVolume(c);
- const MacLowLevelPCMDriver::PCMSound *s = instr.get()->data();
- c._instr = instr;
- c._baseFreq = s->baseFreq;
- c._smpBuffStart = s->data.get();
- c._smpBuffEnd = c._smpBuffStart + s->len;
- if (s->loopst >= s->loopend - 12) {
- c._loopStart = nullptr;
- c._loopEnd = c._smpBuffEnd;
+ const MacLowLevelPCMDriver::PCMSound *s = c->instr.get()->data();
+ c->baseFreq = (node->number != 9) ? s->baseFreq : 60;
+ c->rate = s->rate;
+ c->smpBuffStart = s->data.get();
+ c->smpBuffEnd = c->smpBuffStart + s->len;
+ if ((int32)s->loopst >= (int32)s->loopend - _loopstLim) {
+ c->loopStart = nullptr;
+ c->loopEnd = c->smpBuffEnd;
} else {
- c._loopStart = c._smpBuffStart + s->loopst;
- c._loopEnd = c._smpBuffStart + s->loopend;
- }
+ c->loopStart = c->smpBuffStart + s->loopst;
+ c->loopEnd = c->smpBuffStart + s->loopend;
+ }
+
+ c->pitch = (node->note << 7) + node->pitchBend;
+ c->mute = c->release = false;
+ c->end = c->loopEnd;
+ c->pos = c->smpBuffStart;
+ c->phase = 0;
+
+ recalcFrequency(c);
}
-void DJMSoundSystem::noteOn(byte chan, uint16 pitch, byte volume) {
+void IMSMacSoundSystem::noteOff(const ChanControlNode *node) {
Common::StackLock lock(_mixer->mutex());
- if (chan > 7)
- return;
- DJMSoundChannel &c = *_channels[chan];
- c._pitch = pitch;
- c._volume = volume;
- c._mute = 0;
- c._end = c._loopEnd;
- c._pos = c._smpBuffStart;
- c._phase = 0;
- c.recalcFrequency();
+ for (int i = 0; i < _numChannels; ++i) {
+ DeviceChannel *c = _channels[i];
+ if (c->node == node)
+ noteOffIntern(c);
+ }
}
-void DJMSoundSystem::noteOff(byte chan) {
+void IMSMacSoundSystem::soundOff(const ChanControlNode *node) {
Common::StackLock lock(_mixer->mutex());
- if (chan > 7)
- return;
- DJMSoundChannel &c = *_channels[chan];
- if (c._loopStart)
- c._mute = 1;
+ for (int i = 0; i < _numChannels; ++i) {
+ DeviceChannel *c = _channels[i];
+ if (c->node == node) {
+ c->mute = true;
+ c->release = false;
+ }
+ }
}
-void DJMSoundSystem::setVolume(byte chan, byte volume) {
+void IMSMacSoundSystem::setVolume(const ChanControlNode *node) {
Common::StackLock lock(_mixer->mutex());
- if (chan > 7)
- return;
- DJMSoundChannel &c = *_channels[chan];
- c._volume = volume;
- c._mute = 0;
+ for (int i = 0; i < _numChannels; ++i) {
+ DeviceChannel *c = _channels[i];
+ if (c->node == node)
+ recalcVolume(c);
+ }
}
-void DJMSoundSystem::setPitch(byte chan, uint16 pitch) {
+void IMSMacSoundSystem::setPitchBend(const ChanControlNode *node) {
Common::StackLock lock(_mixer->mutex());
- if (chan > 7)
- return;
- DJMSoundChannel &c = *_channels[chan];
- c._pitch = pitch;
- c._mute = 0;
- c.recalcFrequency();
+ for (int i = 0; i < _numChannels; ++i) {
+ DeviceChannel *c = _channels[i];
+ if (c->node == node) {
+ c->pitch = (node->note << 7) + node->pitchBend;
+ recalcFrequency(c);
+ }
+ }
}
-void DJMSoundSystem::setTimerCallback(void *timerParam, Common::TimerManager::TimerProc timerProc) {
+void IMSMacSoundSystem::setTimerCallback(void *timerParam, Common::TimerManager::TimerProc timerProc) {
_timerParam = timerParam;
_timerProc = timerProc;
}
-void DJMSoundSystem::vblCallback() {
+void IMSMacSoundSystem::vblCallback() {
if (_timerProc)
_timerProc(_timerParam);
}
-void DJMSoundSystem::generateData(int8 *dst, uint32 len, Audio::Mixer::SoundType type, bool expectStereo) const {
+void IMSMacSoundSystem::generateData(int8 *dst, uint32 len, Audio::Mixer::SoundType type, bool expectStereo) const {
assert(dst);
memset(dst, 0, len);
_sdrv->feed(dst, len, type, expectStereo);
}
-const MacSoundDriver::Status &DJMSoundSystem::getDriverStatus(Audio::Mixer::SoundType type) const {
+const MacSoundDriver::Status &IMSMacSoundSystem::getDriverStatus(Audio::Mixer::SoundType type) const {
return _sdrv->getStatus(type);
}
-void DJMSoundSystem::dblBuffCallback(MacLowLevelPCMDriver::DoubleBuffer *dblBuffer) {
+void IMSMacSoundSystem::dblBuffCallback(MacLowLevelPCMDriver::DoubleBuffer *dblBuffer) {
uint16 sil = 0;
memset(_mixBuffer16Bit, 0, _feedBufferSize * sizeof(uint16));
- byte b = _internal16Bit ? 0 : 0x80;
+ uint16 frameSize = _stereo ? 2 : 1;
for (int i = 0; i < _numChannels; ++i) {
- DJMSoundChannel &c = *_channels[i];
- if (c._mute ||
- // This is our "trick" for telling apart music and sfx: We look at the sound bank that
- // is currently in use, the musical one (1000 - 1127) or the sfx one (2000 - 2255).
- (dblBuffer->chanHandle == _musicChan && c._instr.get()->id() >= 2000) ||
- (dblBuffer->chanHandle == _sfxChan && c._instr.get()->id() < 2000)) {
+ DeviceChannel &c = *_channels[i];
+
+ if (c.release) {
+ c.totalLevelL = (c.totalLevelL > 12) ? c.totalLevelL - 12 : 0;
+ c.totalLevelR = (c.totalLevelR > 12) ? c.totalLevelR - 12 : 0;
+
+ if (c.totalLevelL == 0 && c.totalLevelR == 0) {
+ c.release = false;
+ c.mute = true;
+ }
+ }
+
+ if (c.mute || c.frequency == (uint32)-1 ||
+ // This is our "trick" for telling apart music and sfx in MI2/INDY4: We look at the sound bank that
+ // is currently in use, the musical one (1000 - 1127) or the sfx one (2000 - 2255). Unfortunately,
+ // most sound effects seem to use the musical bank, though. So, in the end we can't distinguish all that much...
+ (_version == 0 && ((dblBuffer->chanHandle == _musicChan && c.instr.get()->id() >= 2000) ||
+ (dblBuffer->chanHandle == _sfxChan && c.instr.get()->id() < 2000)))) {
++sil;
continue;
}
- const byte *a = &_ampTable[(c._volume & ~3) << 6];
+ const byte *a1 = &_ampTable[(c.totalLevelL & ~3) << 6];
+ const byte *a2 = &_ampTable[(c.totalLevelR & ~3) << 6];
int16 *t = _mixBuffer16Bit;
- for (int ii = 0; ii < _feedBufferSize; ++ii) {
- if (!c._pos) {
- *t++ += b;
+ for (int ii = 0; ii < _feedBufferSize; ii += frameSize) {
+ if (!c.pos) {
+ *t++ += 0x80;
+ if (_stereo)
+ *t++ += 0x80;
continue;
}
- c._phase += c._frequency;
- if (c._phase >> 16) {
- c._pos += (c._phase >> 16);
- c._phase &= 0xFFFF;
- if (c._loopEnd <= c._pos) {
- c._pos = c._loopStart;
- c._phase = 0;
- if (!c._pos) {
- c._mute = 1;
- --ii;
+ c.phase += c.frequency;
+ if (c.phase >> 16) {
+ c.pos += (c.phase >> 16);
+ c.phase &= 0xFFFF;
+ if (c.loopEnd <= c.pos) {
+ if (c.loopStart) {
+ c.pos -= (c.loopEnd - c.loopStart);
+ } else {
+ c.pos = nullptr;
+ c.mute = true;
+ c.release = false;
+ ii -= frameSize;
continue;
}
}
}
- *t++ += _internal16Bit ? static_cast<int8>(a[*c._pos]) : a[*c._pos];
+ *t++ += a1[*c.pos];
+ if (_stereo)
+ *t++ += a2[*c.pos];
}
}
const int16 *s = _mixBuffer16Bit;
- byte *d = dblBuffer->data;
+ sil <<= 7;
if (_internal16Bit) {
- memcpy(d, s, _feedBufferSize * sizeof(int16));
+ int16 *d = reinterpret_cast<int16*>(dblBuffer->data);
+ for (int i = 0; i < _feedBufferSize; ++i)
+ *d++ = _mixTable[sil + *s++];
} else {
- sil <<= 7;
+ byte *d = dblBuffer->data;
for (int i = 0; i < _feedBufferSize; ++i)
- *d++ = (sil + *s++) >> 3;
+ *d++ = _mixTable[sil + *s++];
}
- dblBuffer->numFrames = _feedBufferSize;
+ dblBuffer->numFrames = _feedBufferSize / frameSize;
dblBuffer->flags |= MacLowLevelPCMDriver::DoubleBuffer::kBufferReady;
}
-void DJMSoundSystem::setMasterVolume(Audio::Mixer::SoundType type, uint16 volume) {
+void IMSMacSoundSystem::setMasterVolume(Audio::Mixer::SoundType type, uint16 volume) {
if (_macstr)
_macstr->setMasterVolume(type, volume);
}
-void DJMSoundSystem::fillPitchTable() {
- static const float fltbl[12] = {
- 1664510.62f, 1763487.62f, 1868350.00f, 1979447.88f, 2097152.00f, 2221855.25f,
- 2353973.50f, 2493948.00f, 2642246.00f, 2799362.00f, 2965820.75f, 3142177.75f
+bool IMSMacSoundSystem::setupResourceFork(Common::MacResManager &rm, const char *const *fileNames, int numFileNames) {
+ const Common::CodePage tryCodePages[] = {
+ Common::kMacRoman,
+ Common::kISO8859_1
};
+
+ Common::Path resFile;
+
+ for (int i = 0; resFile.empty() && i < numFileNames && fileNames[i] != nullptr; ++i) {
+ for (int ii = 0; resFile.empty() && ii < ARRAYSIZE(tryCodePages); ++ii) {
+ Common::U32String fn(fileNames[i], tryCodePages[ii]);
+ resFile = Common::Path(fn.encode(Common::kUtf8));
+ if (!rm.exists(resFile) || !rm.open(resFile) || !rm.hasResFork()) {
+ rm.close();
+ resFile = Common::Path(Common::punycode_encodefilename(fn));
+ if (!rm.exists(resFile) || !rm.open(resFile) || !rm.hasResFork()) {
+ rm.close();
+ resFile.clear();
+ }
+ }
+ }
+ }
+
+ if (resFile.empty()) {
+ warning("%s(): Resource fork not found", __FUNCTION__);
+ return false;
+ }
+
+ return true;
+}
+
+Common::SharedPtr<MacSndResource> IMSMacSoundSystem::getSndResource(uint16 id) {
+ Common::SharedPtr<MacSndResource> res;
+ Common::SharedPtr<MacSndResource> *def = nullptr;
+ for (Common::Array<Common::SharedPtr<MacSndResource> >::iterator it = _smpResources.begin(); res == nullptr && it != _smpResources.end(); ++it) {
+ uint16 cid = (*it)->id();
+ if (cid == id)
+ res = *it;
+ else if (cid == _defaultInstrID)
+ def = it;
+ }
+ if (res == nullptr) {
+ if (def != nullptr)
+ res = *def;
+ else
+ error("%s(): Failure (instrument id %d)", __FUNCTION__, id);
+ }
+ return res;
+}
+
+void IMSMacSoundSystem::fillPitchTable() {
+ static const double ptbl[12] = {
+ 1664510.645469, 1763487.599042, 1868350.028545, 1979447.902589,
+ 2097152.000000, 2221855.147262, 2353973.529536, 2493948.079642,
+ 2642245.949629, 2799362.069852, 2965820.800758, 3142177.682886
+ };
+
for (int i = 0; i < 12; ++i)
- _pitchTable[116 + i] = (uint32)fltbl[i];
+ _pitchTable[116 + i] = (uint32)trunc(ptbl[i]);
for (int i = 115; i >= 0; --i)
_pitchTable[i] = _pitchTable[i + 12] >> 1;
if (_quality != 11)
return;
for (int i = 0; i < 128; ++i)
- _pitchTable[i] <<= 1;
+ _pitchTable[i] <<= 1;
+}
+
+DJMSoundSystem::DJMSoundSystem(Audio::Mixer *mixer) : IMSMacSoundSystem(mixer, 0), _dbCbProc(this, &MacLowLevelPCMDriver::CallbackClient::dblBuffCallback) {
+ _defaultInstrID = 999;
+}
+
+bool DJMSoundSystem::start() {
+ _musicChan = _sdrv->createChannel(Audio::Mixer::kMusicSoundType, MacLowLevelPCMDriver::kSampledSynth, 0x8C, nullptr);
+ _sfxChan = _sdrv->createChannel(Audio::Mixer::kSFXSoundType, MacLowLevelPCMDriver::kSampledSynth, 0x8C, nullptr);
+ if (!_musicChan || !_sfxChan)
+ return false;
+
+ uint32 rate = 0;
+ switch (_quality) {
+ case 5:
+ rate = 0x15BBA2E8;
+ break;
+ case 7:
+ rate = 0x1CFA2E8B;
+ break;
+ case 11:
+ rate = 0x2B7745D1;
+ break;
+ case 22:
+ rate = 0x56EE8BA3;
+ break;
+ default:
+ warning("%s(): Invalid quality setting %d", __FUNCTION__, _quality);
+ return false;
+ }
+
+ return _sdrv->playDoubleBuffer(_musicChan, 1, _internal16Bit ? 16 : 8, rate, &_dbCbProc, _internal16Bit ? _numChannels : 1) &&
+ _sdrv->playDoubleBuffer(_sfxChan, 1, _internal16Bit ? 16 : 8, rate, &_dbCbProc, _internal16Bit ? _numChannels : 1);
+}
+
+void DJMSoundSystem::setQuality(int qual) {
+ Common::StackLock lock(_mixer->mutex());
+
+ qual = qual > 1 ? 22 : 11;
+ if (qual == _quality)
+ return;
+
+ _quality = qual;
+ _feedBufferSize = (qual == 22) ? 1024 : 512;
+ fillPitchTable();
+
+ for (int i = 0; i < _numChannels; ++i)
+ _channels[i]->recalcFrequency();
+
+ stop();
+ if (!start())
+ error("%s(): Unknown error", __FUNCTION__);
+}
+
+bool DJMSoundSystem::loadInstruments(const char *const *fileNames, int numFileNames) {
+ Common::MacResManager resMan;
+ if (!setupResourceFork(resMan, fileNames, numFileNames))
+ return false;
+
+ _smpResources.clear();
+
+ Common::MacResIDArray ids = resMan.getResIDArray(MKTAG('s', 'n', 'd', ' '));
+ for (Common::MacResIDArray::const_iterator i = ids.begin(); i != ids.end(); ++i) {
+ Common::SeekableReadStream *str = resMan.getResource(MKTAG('s', 'n', 'd', ' '), *i);
+ uint16 type = str ? str->readUint16BE() : 0;
+ if (type == 1 || type == 2)
+ _smpResources.push_back(Common::SharedPtr<MacSndResource>(new MacSndResource(*i, str, resMan.getResName(MKTAG('s', 'n', 'd', ' '), *i))));
+ delete str;
+ }
+
+ return !_smpResources.empty();
+}
+
+void DJMSoundSystem::setInstrument(DeviceChannel *c) {
+ assert(c && c->node);
+ if (c->instr == nullptr || c->prog != c->node->prog) {
+ c->instr = getSndResource(c->node->prog);
+ c->prog = c->node->prog;
+ }
+}
+
+void DJMSoundSystem::recalcFrequency(DeviceChannel *c) {
+ assert(c);
+ c->recalcFrequency();
+ if (c->instr != nullptr)
+ c->mute = false;
+}
+
+void DJMSoundSystem::recalcVolume(DeviceChannel *c) {
+ assert(c && c->node);
+ c->totalLevelL = c->node->volume;
+ if (c->instr != nullptr)
+ c->mute = false;
+ }
+
+void DJMSoundSystem::noteOffIntern(DeviceChannel *chan) {
+ if (chan->loopStart)
+ chan->mute = true;
+ chan->note = 0;
+}
+
+DeviceChannel *DJMSoundSystem::allocateChannel(const ChanControlNode *node) {
+ assert(node && node->number < _numChannels);
+ DeviceChannel *c = _channels[node->number];
+ c->node = node;
+ return c;
+}
+
+NewMacSoundSystem::NewMacSoundSystem(ScummEngine *vm, Audio::Mixer *mixer) : IMSMacSoundSystem(mixer, 1),
+ _fileMan(nullptr), _container(vm ? vm->_containerFile : _dummy), _dbCbProc(this, &MacLowLevelPCMDriver::CallbackClient::dblBuffCallback) {
+ assert(vm);
+ _defaultInstrID = 0xFFFF;
+ _fileMan = new ScummFile(vm);
+}
+
+NewMacSoundSystem::~NewMacSoundSystem() {
+ delete _fileMan;
+}
+
+bool NewMacSoundSystem::start() {
+ _musicChan = _sdrv->createChannel(Audio::Mixer::kMusicSoundType, MacLowLevelPCMDriver::kSampledSynth, _stereo ? 0xCC : 0x8C, nullptr);
+ return _musicChan ? _sdrv->playDoubleBuffer(_musicChan, _stereo ? 2 : 1, _internal16Bit ? 16 : 8, 0x56220000, &_dbCbProc, _internal16Bit ? _numChannels : 1) : false;
+}
+
+bool NewMacSoundSystem::loadInstruments(const char *const *fileNames, int numFileNames) {
+ if ((_container.empty() && !_fileMan->open(fileNames[0])) || (!_container.empty() && (!_fileMan->open(_container) || !_fileMan->openSubFile(fileNames[0]))))
+ return false;
+
+ uint32 num = _fileMan->readUint32BE();
+ uint32 size = _fileMan->readUint32BE();
+
+ byte *buff = new byte[size]();
+ _fileMan->read(buff, size);
+ _fileMan->close();
+
+ _smpResources.clear();
+ const byte *s = buff;
+ for (uint i = 0; i < num; ++i) {
+ _smpResources.push_back(Common::SharedPtr<MacSndResource>(new MacSndResource(READ_BE_UINT16(s + 2), buff + READ_BE_UINT32(s + 4))));
+ s += 8;
+ }
+ delete[] buff;
+
+ if (_smpResources.empty())
+ return false;
+
+ Common::MacResManager resMan;
+ if (!setupResourceFork(resMan, &fileNames[1], numFileNames - 1))
+ return false;
+
+ Common::MacResIDArray instIDs = resMan.getResIDArray(MKTAG('I', 'N', 'S', 'T'));
+ for (Common::MacResIDArray::const_iterator i = instIDs.begin(); i != instIDs.end(); ++i) {
+ Common::SeekableReadStream *str = resMan.getResource(MKTAG('I', 'N', 'S', 'T'), *i);
+ if (!str)
+ error("%s(): Failed to load instrument resource (type 'INST', id '%d')", __FUNCTION__, *i);
+ uint sz = str->size();
+ byte *b = new byte[sz]();
+ str->read(b, sz);
+ delete str;
+
+ Instrument *ins = new Instrument(*i);
+
+ ins->sndRes.push_back(getSndResource(READ_BE_UINT16(b)));
+ if (ins->sndRes[0] != nullptr)
+ memset(ins->noteSmplsMapping, 0, 128);
+ int8 numRanges = CLIP<int8>(b[13], 0, 7);
+ for (int ii = 0; ii < numRanges; ++ii) {
+ ins->sndRes.push_back(getSndResource(READ_BE_INT16(b + 16 + ii * 8)));
+ if (ins->sndRes.back() != nullptr) {
+ byte rl = b[14 + ii * 8];
+ byte rh = b[15 + ii * 8];
+ assert(rl < 128 && rh < 128);
+ for (int iii = rl; iii <= rh; ++iii)
+ ins->noteSmplsMapping[iii] = ii + 1;
+ }
+ }
+ delete[] b;
+ _instruments.push_back(Common::SharedPtr<Instrument>(ins));
+ }
+
+ return !_instruments.empty();
+}
+
+Common::SharedPtr<MacSndResource> NewMacSoundSystem::getNoteRangeSndResource(uint16 id, byte note) {
+ assert(note < 128);
+ Common::SharedPtr<MacSndResource> res;
+ for (Common::Array<Common::SharedPtr<Instrument> >::iterator it = _instruments.begin(); res == nullptr && it != _instruments.end(); ++it) {
+ uint16 cid = (*it)->id;
+ if (cid == id)
+ res = (*it)->sndRes[(*it)->noteSmplsMapping[note]];
+ }
+ return res;
+}
+
+void NewMacSoundSystem::setInstrument(DeviceChannel *c) {
+ assert(c && c->node);
+ if (c->instr == nullptr || (c->node->number != 9 && c->prog != c->node->prog) || c->note != c->node->note) {
+ c->note = c->node->note;
+ c->prog = c->node->prog;
+ c->instr = (c->node->number == 9) ? getSndResource(6000 + c->note) : getNoteRangeSndResource(c->prog, c->note);
+ }
+}
+
+void NewMacSoundSystem::recalcFrequency(DeviceChannel *c) {
+ assert(c && c->node);
+ if (c->node->number == 9)
+ c->frequency = 0x8000;
+ else
+ c->recalcFrequency();
+ c->frequency = MacLowLevelPCMDriver::calcRate(0x56220000, c->rate , c->frequency);
+ //if (c->frequency == (uint32)-1)
+ // error("%s(): Frequency calculation failed", __FUNCTION__);
+}
+
+void NewMacSoundSystem::recalcVolume(DeviceChannel *c) {
+ assert(c && c->node);
+
+ static const byte volumeTable[] = {
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x05, 0x05,
+ 0x05, 0x05, 0x06, 0x06, 0x06, 0x06, 0x07, 0x07,
+ 0x07, 0x08, 0x08, 0x08, 0x09, 0x09, 0x09, 0x0a,
+ 0x0a, 0x0a, 0x0b, 0x0b, 0x0c, 0x0c, 0x0c, 0x0d,
+ 0x0d, 0x0e, 0x0e, 0x0f, 0x0f, 0x10, 0x10, 0x11,
+ 0x11, 0x12, 0x12, 0x13, 0x14, 0x14, 0x15, 0x16,
+ 0x16, 0x17, 0x18, 0x18, 0x19, 0x1a, 0x1b, 0x1b,
+ 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23,
+ 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b,
+ 0x2d, 0x2e, 0x2f, 0x31, 0x32, 0x33, 0x35, 0x36,
+ 0x38, 0x39, 0x3b, 0x3c, 0x3e, 0x40, 0x42, 0x43,
+ 0x45, 0x47, 0x49, 0x4b, 0x4d, 0x4f, 0x51, 0x53,
+ 0x56, 0x58, 0x5a, 0x5d, 0x5f, 0x62, 0x64, 0x67,
+ 0x6a, 0x6c, 0x6f, 0x72, 0x75, 0x78, 0x7c, 0x7f
+ };
+
+ if (_stereo) {
+ c->volumeL = (volumeTable[127 - (c->node->panPos >> 2)] * c->node->volume) >> 7;
+ c->volumeR = (volumeTable[96 + (c->node->panPos >> 2)] * c->node->volume) >> 7;
+ } else {
+ c->volumeL = c->node->volume;
+ }
+
+ static const byte veloTable[] = {
+ 0x41, 0x43, 0x45, 0x46, 0x47, 0x49, 0x4a, 0x4b,
+ 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53,
+ 0x53, 0x54, 0x55, 0x55, 0x56, 0x57, 0x57, 0x58,
+ 0x58, 0x59, 0x59, 0x59, 0x5a, 0x5a, 0x5b, 0x5b,
+ 0x5b, 0x5c, 0x5c, 0x5c, 0x5d, 0x5d, 0x5d, 0x5d,
+ 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5f, 0x5f,
+ 0x5f, 0x5f, 0x5f, 0x5f, 0x5f, 0x60, 0x60, 0x60,
+ 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60,
+ 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60,
+ 0x60, 0x60, 0x60, 0x61, 0x61, 0x61, 0x61, 0x61,
+ 0x61, 0x61, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62,
+ 0x63, 0x63, 0x63, 0x63, 0x64, 0x64, 0x64, 0x65,
+ 0x65, 0x65, 0x66, 0x66, 0x67, 0x67, 0x67, 0x68,
+ 0x68, 0x69, 0x69, 0x6a, 0x6b, 0x6b, 0x6c, 0x6d,
+ 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74,
+ 0x75, 0x76, 0x77, 0x79, 0x7a, 0x7b, 0x7d, 0x7f
+ };
+
+ if (c->release)
+ return;
+
+ c->totalLevelL = volumeTable[(veloTable[c->node->velocity] * c->volumeL) >> 7];
+ c->totalLevelR = volumeTable[(veloTable[c->node->velocity] * c->volumeR) >> 7];
+}
+
+void NewMacSoundSystem::noteOffIntern(DeviceChannel *chan) {
+ if (chan->node->number != 9)
+ chan->release = true;
+ chan->loopStart = nullptr;
+ chan->loopEnd = chan->smpBuffEnd;
+}
+
+DeviceChannel *NewMacSoundSystem::allocateChannel(const ChanControlNode *node) {
+ assert(node);
+ DeviceChannel *res = nullptr;
+ DeviceChannel *res2 = nullptr;
+ DeviceChannel *res3 = nullptr;
+ byte lovol = 0x7F;
+ byte lopri = node->prio;
+
+ for (byte i = 0; i < _numChannels; ++i) {
+ DeviceChannel *c = _channels[i];
+
+ if (c->mute) {
+ res = c;
+ break;
+ }
+
+ if (c->release) {
+ if (c->node && c->node->volume < lovol) {
+ res2 = c;
+ lovol = c->node->volume;
+ }
+ }
+
+ if (c->node && c->node->prio < lopri) {
+ res3 = c;
+ lopri = c->node->prio;
+ }
+ }
+
+ if (!res)
+ res = res2;
+ if (!res)
+ res = res3;
+
+ if (res) {
+ res->release = false;
+ res->mute = true;
+ res->node = node;
+ }
+
+ return res;
}
-byte IMuseChannel_Mac_DJM::_allocCur = 0;
+byte IMuseChannel_Macintosh::_allocCur = 0;
-IMuseChannel_Mac_DJM::IMuseChannel_Mac_DJM(IMuseDriver_Mac_DJM *drv, int number) : MidiChannel(), _drv(drv), _number(number), _allocated(false), _sustain(false), _bank(0),
- _pitchBendEff(0), _prio(0x80), _detune(0), _transpose(0), _pitchBendSet(0), _pitchBendSensitivity(2), _volume(0), _channels(drv ? drv->_channels : nullptr), _prog(nullptr),
- _out(nullptr), _device(drv ? drv->_device : nullptr), _instruments(drv ? &drv->_instruments : nullptr), _numChannels(8) {
+IMuseChannel_Macintosh::IMuseChannel_Macintosh(IMuseDriver_Macintosh *drv, int number) : MidiChannel(), _drv(drv), _number(number), _allocated(false),
+ _sustain(false), _bank(0), _panPos(0x40), _pitchBendEff(0), _prio(0x80), _detune(0), _transpose(0), _pitchBendSet(0), _pitchBendSensitivity(2), _volume(0x7F),
+ _rpn(0), _pitchBendRange(0), _channels(drv ? drv->_channels : nullptr), _prog(0), _out(nullptr), _device(drv ? drv->_device : nullptr), _version(drv->_version),
+ _rtmChannel((drv && drv->_version > 0) ? drv->_channels[drv->_numChannels - 1] : nullptr), _numChannels(drv ? (drv->_version < 1 ? drv->_numChannels : drv->_numChannels - 1) : 0) {
assert(_drv);
assert(_channels);
assert(_device);
- assert(_instruments);
_allocCur = 0;
}
-IMuseChannel_Mac_DJM::~IMuseChannel_Mac_DJM() {
-}
-
-bool IMuseChannel_Mac_DJM::allocate() {
+bool IMuseChannel_Macintosh::allocate() {
if (_allocated)
return false;
return (_allocated = true);
}
-void IMuseChannel_Mac_DJM::noteOff(byte note) {
- for (ChannelNode *node = _out; node; node = node->_next) {
- if (node->_note == note) {
- if (_sustain) {
- node->_sustain = true;
+void IMuseChannel_Macintosh::noteOff(byte note) {
+ for (ChanControlNode *node = (_version > 0 && _number == 9) ? _rtmChannel : _out; node; ) {
+ ChanControlNode *n = node->next;
+ if (node->note == note) {
+ if (_sustain && node != _rtmChannel) {
+ node->sustain = true;
} else {
- debug(5, "NOTE OFF: chan '%d', note '%d'", node->_number, note);
- _device->noteOff(node->_number);
+ _device->noteOff(node);
disconnect(_out, node);
}
}
+ node = n;
}
}
-void IMuseChannel_Mac_DJM::noteOn(byte note, byte velocity) {
- ChannelNode *node = allocateDeviceChannel(_prio);
- if (node == nullptr || _prog == nullptr)
- return;
- connect(_out, node);
- node->_in = this;
- node->_note = note;
- node->_sustain = false;
+void IMuseChannel_Macintosh::noteOn(byte note, byte velocity) {
+ ChanControlNode *node = (_version > 0 && _number == 9) ? _rtmChannel : allocateNode(_prio);
+ if (node == nullptr)
+ return;
+
+ if (node != _rtmChannel) {
+ if (_prog == 0)
+ return;
+ connect(_out, node);
+ node->in = this;
+ node->prio = _prio;
+ node->prog = _prog;
+ node->pitchBend = _pitchBendEff;
+ node->volume = _volume;
+ } else {
+ node->volume = _volume * 6 / 7;
+ }
+
+ node->note = note;
+ node->sustain = false;
+ node->panPos = _panPos;
+ node->velocity = velocity;
- _device->setInstrument(node->_number, _prog);
- debug(4, "NOTE ON: chan '%d', note '%d', instr id '%d' ('%s')", node->_number, note, _prog.get()->id(), _prog.get()->name());
- _device->noteOn(node->_number, ((note + _transpose) << 7) + _pitchBendEff, _volume); // ignoring velocity
+ _device->noteOn(node);
}
-void IMuseChannel_Mac_DJM::controlChange(byte control, byte value) {
+void IMuseChannel_Macintosh::controlChange(byte control, byte value) {
switch (control) {
- case 7:
- _volume = value;
- for (ChannelNode *node = _out; node; node = node->_next)
- _device->setVolume(node->_number, value);
+ case 0:
+ // The original MI2/INDY4 code doesn't have that. It will just call a different
+ // programChange() method from the sysex handler. Only DOTT and SAMNMAX have the
+ // bank select like this in the original code.
+ _bank = value;
+ break;
+ case 6:
+ dataEntry(value);
break;
- case 32:
- _bank = value; // The original code doesn't have that. It will just call a different programChange() method from the sysex handler.
+ case 7:
+ case 10:
+ if (control == 7)
+ _volume = value;
+ else
+ _panPos = value;
+ updateVolume();
break;
case 64:
- _sustain = (value != 0);
- if (value == 0) {
- for (ChannelNode *node = _out; node; node = node->_next) {
- if (node->_sustain) {
- _device->noteOff(node->_number);
- disconnect(_out, node);
- }
- }
- }
+ sustain(value);
+ break;
+ case 100:
+ _rpn = value;
+ break;
+ case 101:
+ _rpn = value | 0x80;
break;
default:
break;
}
}
-void IMuseChannel_Mac_DJM::programChange(byte program) {
- _prog.reset();
- uint16 base = _bank ? 2000 : 1000;
- Common::SharedPtr<MacSndInstrument> *def = nullptr;
- for (Common::Array<Common::SharedPtr<MacSndInstrument> >::iterator it = _instruments->begin(); _prog == nullptr && it != _instruments->end(); ++it) {
- uint16 id = (*it)->id();
- if (id == program + base)
- _prog = *it;
- else if (id == 999u)
- def = it;
- }
- if (_prog == nullptr) {
- if (def != nullptr)
- _prog = *def;
- else
- error("%s(): Failure (program %d)", __FUNCTION__, program);
- }
+void IMuseChannel_Macintosh::programChange(byte program) {
+ if (_version == 0)
+ _prog = (_bank ? 2000 : 1000) + program;
+ else
+ _prog = program;
}
-void IMuseChannel_Mac_DJM::pitchBend(int16 bend) {
+void IMuseChannel_Macintosh::pitchBend(int16 bend) {
_pitchBendSet = bend;
- //if (_newSystem) {
- // SAMNMAX formula (same for Roland MT-32 and GM)
- //bend = (((bend * _pitchBendSensitivity) >> 5) + _detune + (_transpose << 8)) << 1;
- //} else {
- // DOTT, INDY4 and MI2 formula
- bend = CLIP<int>(((bend * _pitchBendSensitivity) >> 6) + _detune, -2048, 2047);
- //}
-
+
+ if (_version == 2)
+ bend = (((bend * _pitchBendSensitivity) >> 5) + _detune + (_transpose << 8)) << 1; // SAMNMAX formula
+ else
+ bend = ((bend * _pitchBendSensitivity) >> 6) + _detune + (_transpose << 7); // DOTT, INDY4 and MI2 formula
+
+ if (_version == 1)
+ bend = CLIP<int16>(bend, -2048, 2047) << 2;
+
+ if (_version > 0) {
+ if (bend > 7936)
+ bend = 8192;
+ else if (bend < -7936)
+ bend = -8192;
+ bend = (bend * _pitchBendRange) >> 13;
+ }
+
_pitchBendEff = bend;
- debug(4, "PITCH BEND: chan %d, eff pitch '%d'", _number, _pitchBendEff);
- for (ChannelNode *node = _out; node; node = node->_next)
- _device->setPitch(node->_number, ((node->_note + _transpose) << 7) + _pitchBendEff);
+ for (ChanControlNode *node = _out; node; node = node->next) {
+ node->pitchBend = _pitchBendEff;
+ _device->setPitchBend(node);
+ }
}
-void IMuseChannel_Mac_DJM::sustain(bool value) {
+void IMuseChannel_Macintosh::sustain(bool value) {
_sustain = value;
+ if (value)
+ return;
+
+ for (ChanControlNode *node = _out; node; ) {
+ ChanControlNode *n = node->next;
+ if (node->sustain) {
+ _device->noteOff(node);
+ disconnect(_out, node);
+ }
+ node = n;
+ }
}
-void IMuseChannel_Mac_DJM::allNotesOff() {
- for (ChannelNode *node = _out; node; node = node->_next) {
- _device->noteOff(node->_number);
+void IMuseChannel_Macintosh::allNotesOff() {
+ for (ChanControlNode *node = (_version > 0 && _number == 9) ? _rtmChannel : _out; node; ) {
+ _device->soundOff(node);
+ ChanControlNode *n = node->next;
disconnect(_out, node);
+ node = n;
+ }
+}
+
+void IMuseChannel_Macintosh::dataEntry(byte value) {
+ uint16 m = 0x7F;
+ uint16 v = value;
+ if (_rpn & 0x80) {
+ m <<= 7;
+ v <<= 7;
+ }
+ if (!(_rpn & 0x7F)) {
+ _pitchBendRange = (_pitchBendRange & ~m) + v;
+ pitchBend(_pitchBendSet);
+ }
+ _rpn = 0;
+}
+
+void IMuseChannel_Macintosh::updateVolume() {
+ for (ChanControlNode *node = _out; node; node = node->next) {
+ node->panPos = _panPos;
+ node->volume = (node == _rtmChannel) ? _volume * 6 / 7 : _volume;
+ _device->setVolume(node);
}
}
-ChannelNode *IMuseChannel_Mac_DJM::allocateDeviceChannel(int prio) {
- ChannelNode *res = nullptr;
+ChanControlNode *IMuseChannel_Macintosh::allocateNode(int prio) {
+ ChanControlNode *res = nullptr;
+ if (_version > 0)
+ _allocCur = 0;
+
for (byte i = 0; i < _numChannels; ++i) {
_allocCur = (_allocCur + 1) % _numChannels;
- ChannelNode *node = _channels[_allocCur];
- if (node->_in == nullptr)
+ ChanControlNode *node = _channels[_allocCur];
+ if (node->in == nullptr)
return node;
- if (!node->_next && node->_in && node->_in->_prio <= prio) {
+ if (!node->next && node->in && node->in->_prio <= prio) {
res = node;
- prio = node->_in->_prio;
+ prio = node->in->_prio;
}
}
if (res) {
- _device->noteOff(res->_number);
- disconnect(res->_in->_out, res);
+ _device->soundOff(res);
+ disconnect(res->in->_out, res);
}
return res;
}
@@ -658,36 +1198,92 @@ ChannelNode *IMuseChannel_Mac_DJM::allocateDeviceChannel(int prio) {
namespace Scumm {
using namespace IMSMacintosh;
-IMuseDriver_Mac_DJM::IMuseDriver_Mac_DJM(Audio::Mixer *mixer) : MidiDriver(), _isOpen(false), _device(nullptr), _imsParts(nullptr), _channels(nullptr),
- _numParts(24), _numChannels(8), _baseTempo(16667), _quality(1), _musicVolume(0), _sfxVolume(0) {
- _device = new DJMSoundSystem(mixer);
+IMuseDriver_Macintosh::IMuseDriver_Macintosh(ScummEngine *vm, Audio::Mixer *mixer, byte gameID) : MidiDriver(), _isOpen(false), _device(nullptr), _imsParts(nullptr), _channels(nullptr),
+ _numParts(24), _numChannels(8), _baseTempo(16666), _quality(1), _musicVolume(0), _sfxVolume(0), _version(-1) {
+
+ switch (gameID) {
+ case GID_SAMNMAX:
+ _version = 2;
+ _numChannels = 16;
+ _numParts = 32;
+ //_baseTempo = 46439;
+ _device = new NewMacSoundSystem(vm, mixer);
+ break;
+ case GID_TENTACLE:
+ _version = 1;
+ _numChannels = 16;
+ _numParts = 32;
+ //_baseTempo = 46439;
+ _device = new NewMacSoundSystem(vm, mixer);
+ break;
+ case GID_INDY4:
+ case GID_MONKEY2:
+ _version = 0;
+ _device = new DJMSoundSystem(mixer);
+ break;
+ default:
+ error("%s(): Unsupported game ID %d", __FUNCTION__, gameID);
+ break;
+ }
}
-IMuseDriver_Mac_DJM::~IMuseDriver_Mac_DJM() {
+IMuseDriver_Macintosh::~IMuseDriver_Macintosh() {
close();
delete _device;
}
-int IMuseDriver_Mac_DJM::open() {
+int IMuseDriver_Macintosh::open() {
if (_isOpen)
return MERR_ALREADY_OPEN;
+ if (_version < 0 || _version > 2)
+ return MERR_DEVICE_NOT_AVAILABLE;
+
createChannels();
- static const char *fileNames[] = {
- "iMUSE Setups",
- nullptr
+ static const char *fileNames[3][3] = {
+ {
+ "iMUSE Setups",
+ nullptr,
+ nullptr
+ },
+ {
+ "Instruments",
+ "Day of the Tentacle Demo",
+ "Day of the Tentacle"
+ },
+ {
+ "Instruments",
+ "Sam & Max Demo",
+ "Sam & Max"
+ }
};
- if (!loadDefaultInstruments(fileNames, ARRAYSIZE(fileNames)) || !_device->init(false) || !_device->start())
+ if (!_device->init(fileNames[_version], ARRAYSIZE(fileNames[_version]), false, true) || !_device->start())
return MERR_DEVICE_NOT_AVAILABLE;
_isOpen = true;
+ for (int i = 0; i < _numParts; ++i) {
+ _imsParts[i]->controlChange(0x64, 0x00);
+ _imsParts[i]->controlChange(0x65, 0x00);
+ _imsParts[i]->controlChange(0x06, 0x10);
+ _imsParts[i]->controlChange(0x07, 0x7F);
+ _imsParts[i]->controlChange(0x0A, 0x40);
+ _imsParts[i]->controlChange(0x01, 0x00);
+ _imsParts[i]->controlChange(0x40, 0x00);
+ _imsParts[i]->controlChange(0x5B, 0x40);
+ _imsParts[i]->controlChange(0x5D, 0x00);
+ _imsParts[i]->controlChange(0x00, 0x00);
+ _imsParts[i]->controlChange(0x7B, 0x00);
+ _imsParts[i]->programChange(0);
+ _imsParts[i]->pitchBend(0x2000);
+ }
+
return 0;
}
-void IMuseDriver_Mac_DJM::close() {
+void IMuseDriver_Macintosh::close() {
if (!_isOpen)
return;
@@ -697,7 +1293,7 @@ void IMuseDriver_Mac_DJM::close() {
releaseChannels();
}
-uint32 IMuseDriver_Mac_DJM::property(int prop, uint32 param) {
+uint32 IMuseDriver_Macintosh::property(int prop, uint32 param) {
uint32 res = 0;
switch (prop) {
case IMuse::PROP_QUALITY:
@@ -731,45 +1327,45 @@ uint32 IMuseDriver_Mac_DJM::property(int prop, uint32 param) {
return res;
}
-void IMuseDriver_Mac_DJM::setTimerCallback(void *timerParam, Common::TimerManager::TimerProc timerProc) {
+void IMuseDriver_Macintosh::setTimerCallback(void *timerParam, Common::TimerManager::TimerProc timerProc) {
if (_device)
_device->setTimerCallback(timerParam, timerProc);
}
-MidiChannel *IMuseDriver_Mac_DJM::allocateChannel() {
+MidiChannel *IMuseDriver_Macintosh::allocateChannel() {
if (!_isOpen)
return nullptr;
for (int i = 0; i < _numParts; ++i) {
- IMuseChannel_Mac_DJM *ch = _imsParts[i];
- if (ch && ch->allocate())
+ IMuseChannel_Macintosh *ch = _imsParts[i];
+ if (ch && !(_version > 0 && i == 9) && ch->allocate())
return ch;
}
return nullptr;
}
-MidiChannel *IMuseDriver_Mac_DJM::getPercussionChannel() {
- return nullptr;
+MidiChannel *IMuseDriver_Macintosh::getPercussionChannel() {
+ return (_isOpen && _version > 0) ? _imsParts[9] : nullptr;
}
-void IMuseDriver_Mac_DJM::createChannels() {
+void IMuseDriver_Macintosh::createChannels() {
releaseChannels();
- _channels = new ChannelNode*[_numChannels];
+ _channels = new ChanControlNode*[_numChannels];
assert(_channels);
for (int i = 0; i < _numChannels; ++i) {
- _channels[i] = new ChannelNode(i);
+ _channels[i] = new ChanControlNode(i < 9 ? i : (i == 15 ? 9 : i + 1));
assert(_channels[i]);
}
- _imsParts = new IMuseChannel_Mac_DJM*[_numParts];
+ _imsParts = new IMuseChannel_Macintosh*[_numParts];
assert(_imsParts);
for (int i = 0; i < _numParts; ++i)
- _imsParts[i] = new IMuseChannel_Mac_DJM(this, i);
+ _imsParts[i] = new IMuseChannel_Macintosh(this, i);
}
-void IMuseDriver_Mac_DJM::releaseChannels() {
+void IMuseDriver_Macintosh::releaseChannels() {
if (_imsParts) {
for (int i = 0; i < _numParts; ++i)
delete _imsParts[i];
@@ -785,47 +1381,4 @@ void IMuseDriver_Mac_DJM::releaseChannels() {
}
}
-bool IMuseDriver_Mac_DJM::loadDefaultInstruments(const char *const *tryFileNames, uint16 numTryFileNames) {
- const Common::CodePage tryCodePages[] = {
- Common::kMacRoman,
- Common::kISO8859_1
- };
-
- Common::MacResManager resMan;
- Common::Path resFile;
-
- for (int i = 0; resFile.empty() && i < numTryFileNames && tryFileNames[i] != nullptr; ++i) {
- for (int ii = 0; resFile.empty() && ii < ARRAYSIZE(tryCodePages); ++ii) {
- Common::U32String fn(tryFileNames[i], tryCodePages[ii]);
- resFile = Common::Path(fn.encode(Common::kUtf8));
- if (!resMan.exists(resFile) || !resMan.open(resFile) || !resMan.hasResFork()) {
- resMan.close();
- resFile = Common::Path(Common::punycode_encodefilename(fn));
- if (!resMan.exists(resFile) || !resMan.open(resFile) || !resMan.hasResFork()) {
- resMan.close();
- resFile.clear();
- }
- }
- }
- }
-
- if (resFile.empty()) {
- warning("%s(): Resource fork not found", __FUNCTION__);
- return false;
- }
-
- _instruments.clear();
-
- Common::MacResIDArray ids = resMan.getResIDArray(MKTAG('s', 'n', 'd', ' '));
- for (Common::MacResIDArray::const_iterator i = ids.begin(); i != ids.end(); ++i) {
- Common::SeekableReadStream *str = resMan.getResource(MKTAG('s', 'n', 'd', ' '), *i);
- uint16 type = str ? str->readUint16BE() : 0;
- if (type == 1 || type == 2)
- _instruments.push_back(Common::SharedPtr<MacSndInstrument>(new MacSndInstrument(*i, str, resMan.getResName(MKTAG('s', 'n', 'd', ' '), *i))));
- delete str;
- }
-
- return !_instruments.empty();
-}
-
} // End of namespace Scumm
diff --git a/engines/scumm/imuse/drivers/macintosh.h b/engines/scumm/imuse/drivers/macintosh.h
index f1f4255f3fd..035f9b3651b 100644
--- a/engines/scumm/imuse/drivers/macintosh.h
+++ b/engines/scumm/imuse/drivers/macintosh.h
@@ -30,18 +30,18 @@ class Mixer;
}
namespace IMSMacintosh {
-class IMuseChannel_Mac_DJM;
-class DJMSoundSystem;
-struct ChannelNode;
+class IMuseChannel_Macintosh;
+class IMSMacSoundSystem;
+struct ChanControlNode;
} // End of namespace IMSMacintosh
namespace Scumm {
-class IMuseDriver_Mac_DJM final : public MidiDriver {
- friend class IMSMacintosh::IMuseChannel_Mac_DJM;
+class IMuseDriver_Macintosh final : public MidiDriver {
+ friend class IMSMacintosh::IMuseChannel_Macintosh;
public:
- IMuseDriver_Mac_DJM(Audio::Mixer *mixer);
- virtual ~IMuseDriver_Mac_DJM() override;
+ IMuseDriver_Macintosh(ScummEngine *vm, Audio::Mixer *mixer, byte gameID);
+ virtual ~IMuseDriver_Macintosh() override;
int open() override;
void close() override;
@@ -59,21 +59,20 @@ public:
private:
void createChannels();
void releaseChannels();
- bool loadDefaultInstruments(const char *const *tryFileNames, uint16 numTryFileNames);
bool _isOpen;
uint32 _quality;
uint32 _musicVolume;
uint32 _sfxVolume;
- IMSMacintosh::DJMSoundSystem *_device;
- IMSMacintosh::IMuseChannel_Mac_DJM **_imsParts;
- IMSMacintosh::ChannelNode **_channels;
- Common::Array<Common::SharedPtr<MacSndInstrument> > _instruments;
+ IMSMacintosh::IMSMacSoundSystem *_device;
+ IMSMacintosh::IMuseChannel_Macintosh **_imsParts;
+ IMSMacintosh::ChanControlNode **_channels;
- const byte _numChannels;
- const byte _numParts;
- const uint32 _baseTempo;
+ byte _numChannels;
+ byte _numParts;
+ uint32 _baseTempo;
+ int8 _version;
};
} // End of namespace Scumm
diff --git a/engines/scumm/imuse/imuse.cpp b/engines/scumm/imuse/imuse.cpp
index 36019b24768..b2dcd14ef32 100644
--- a/engines/scumm/imuse/imuse.cpp
+++ b/engines/scumm/imuse/imuse.cpp
@@ -162,7 +162,7 @@ bool IMuseInternal::isMT32(int sound) {
case MKTAG('A', 'M', 'I', ' '): // MI2 Amiga
return false;
- case MKTAG('R', 'O', 'L', ' '): // Unfortunately FOA Amiga also uses this resource type
+ case MKTAG('R', 'O', 'L', ' '): // Roland LAPC/MT-32/CM32L track, but FOA Amiga and and DOTT Demo Mac also use this resource type
return _soundType != MDT_AMIGA && _soundType != MDT_MACINTOSH;
case MKTAG('M', 'A', 'C', ' '): // Occurs in the Mac version of FOA and MI2
@@ -209,7 +209,7 @@ bool IMuseInternal::isMIDI(int sound) {
case MKTAG('A', 'M', 'I', ' '): // Amiga (return true, since the driver is initalized as native midi)
return true;
- case MKTAG('R', 'O', 'L', ' '):
+ case MKTAG('R', 'O', 'L', ' '): // Roland LAPC/MT-32/CM32L track
return true;
case MKTAG('M', 'A', 'C', ' '): // Occurs in the Mac version of FOA and MI2
@@ -251,15 +251,15 @@ bool IMuseInternal::supportsPercussion(int sound) {
case MKTAG('A', 'M', 'I', ' '): // MI2 Amiga
return false;
- case MKTAG('R', 'O', 'L', ' '): // Roland LAPC/MT-32/CM32L track, but also used by INDY4 Amiga
- return _soundType != MDT_AMIGA && _soundType != MDT_MACINTOSH;
+ case MKTAG('R', 'O', 'L', ' '): // Roland LAPC/MT-32/CM32L track, but also used by INDY4 Amiga and DOTT Demo Mac (but the latter does support percussion).
+ return _soundType != MDT_AMIGA;
- case MKTAG('M', 'A', 'C', ' '): // Occurs in the Mac version of FOA and MI2
+ case MKTAG('M', 'A', 'C', ' '): // Occurs in the Mac version of FOA and MI2. The early Mac imuse system doesn't support percussion.
return false;
- case MKTAG('G', 'M', 'D', ' '):
- case MKTAG('M', 'I', 'D', 'I'): // Occurs in Sam & Max
- return true;
+ case MKTAG('G', 'M', 'D', ' '): // DOTT
+ case MKTAG('M', 'I', 'D', 'I'): // Sam & Max
+ return true; // This is correct for Mac, too. The later Mac imuse system does have a percussion channel.
default:
break;
diff --git a/engines/scumm/imuse/imuse_internal.h b/engines/scumm/imuse/imuse_internal.h
index a4290fc9205..95a5f174baf 100644
--- a/engines/scumm/imuse/imuse_internal.h
+++ b/engines/scumm/imuse/imuse_internal.h
@@ -589,6 +589,7 @@ protected:
// the sfx resources aren't even played through the imuse engine. The imuse engine can then just assume that everything it plays is music. For
// MI2/INDY4 Macintosh it won't work like this, because both music and sound effects have the same resource type and are played through the imuse
// engine. For these targets it works better to pass the volume values on to the driver where other methods of distinction may be available.
+ // This isn't needed for SCUMM6, since these games don't have MIDI sound effects.
const bool _lowLevelVolumeControl;
public:
diff --git a/engines/scumm/imuse/instrument.cpp b/engines/scumm/imuse/instrument.cpp
index 38dcd80bce4..317f3fef502 100644
--- a/engines/scumm/imuse/instrument.cpp
+++ b/engines/scumm/imuse/instrument.cpp
@@ -551,8 +551,8 @@ void Instrument_MacSfx::saveLoadWithSerializer(Common::Serializer &s) {
void Instrument_MacSfx::send(MidiChannel *mc) {
if (!mc)
return;
- mc->controlChange(0x20, 1);
+ mc->controlChange(0, 1);
mc->programChange(_program);
- mc->controlChange(0x20, 0);
+ mc->controlChange(0, 0);
}
} // End of namespace Scumm
diff --git a/engines/scumm/metaengine.cpp b/engines/scumm/metaengine.cpp
index a33595a500c..4bf12c7e3e3 100644
--- a/engines/scumm/metaengine.cpp
+++ b/engines/scumm/metaengine.cpp
@@ -411,7 +411,7 @@ Common::Error ScummMetaEngine::createInstance(OSystem *syst, Engine **engine,
// TODO: Maybe allow the null driver, too?
if (res.game.platform == Common::kPlatformFMTowns && res.game.version == 3)
res.game.midi = MDT_TOWNS;
- else if (res.game.platform == Common::kPlatformMacintosh && res.game.version < 6)
+ else if (res.game.platform == Common::kPlatformMacintosh && res.game.version < 7)
res.game.midi = MDT_MACINTOSH;
// Finally, we have massaged the GameDescriptor to our satisfaction, and can
diff --git a/engines/scumm/players/mac_sound_lowlevel.h b/engines/scumm/players/mac_sound_lowlevel.h
index 140b472dbb5..1b1059749ce 100644
--- a/engines/scumm/players/mac_sound_lowlevel.h
+++ b/engines/scumm/players/mac_sound_lowlevel.h
@@ -160,6 +160,8 @@ public:
uint8 getChannelStatus(ChanHandle handle) const;
void clearChannelFlags(ChanHandle handle, uint8 flags);
+ static uint32 calcRate(uint32 outRate, uint32 factor, uint32 dataRate);
+
private:
void updateStatus(Audio::Mixer::SoundType sndType);
MacSndChannel *findAndCheckChannel(ChanHandle h, const char *caller, byte reqSynthType) const;
@@ -236,10 +238,13 @@ private:
const bool _isStereo;
};
-struct MacSndInstrument {
+struct MacSndResource {
public:
- MacSndInstrument(uint32 id, Common::SeekableReadStream *&in, Common::String &&name);
- ~MacSndInstrument() {}
+ // Construct from Mac resource stream
+ MacSndResource(uint32 id, Common::SeekableReadStream *&in, Common::String &&name);
+ // Construct from Mac sound data buffer
+ MacSndResource(uint32 id, const byte *in);
+ ~MacSndResource() {}
const MacLowLevelPCMDriver::PCMSound *data() const { return &_snd; }
uint32 id() const { return _id; }
const char* name() { return _name.c_str(); }
diff --git a/engines/scumm/players/player_mac_loom_monkey.cpp b/engines/scumm/players/player_mac_loom_monkey.cpp
index a5990f8746c..e83d61e48b3 100644
--- a/engines/scumm/players/player_mac_loom_monkey.cpp
+++ b/engines/scumm/players/player_mac_loom_monkey.cpp
@@ -62,7 +62,7 @@ public:
protected:
bool loadInstruments(const char *const *tryFileNames, uint16 numTryFileNames, uint16 numInstruments);
- Common::Array<Common::SharedPtr<MacSndInstrument> > _instruments;
+ Common::Array<Common::SharedPtr<MacSndResource> > _instruments;
byte _sndRes6;
byte _isMusic;
@@ -75,8 +75,8 @@ protected:
const byte *_chanSndData[5];
uint32 _chanNumEvents[5];
uint32 _chanCurEvent[5];
- const Common::SharedPtr<MacSndInstrument> *_chanInstr[5];
- const Common::SharedPtr<MacSndInstrument> *_chanInstr2[5];
+ const Common::SharedPtr<MacSndResource> *_chanInstr[5];
+ const Common::SharedPtr<MacSndResource> *_chanInstr2[5];
const bool _useInstrTag;
};
@@ -99,7 +99,7 @@ public:
bool restartSoundAfterLoad() const override { return isMusic(); }
bool ignoreMachineRating() const override { return false; }
private:
- const Common::SharedPtr<MacSndInstrument> *fetchInstrument(uint16 id) const;
+ const Common::SharedPtr<MacSndResource> *fetchInstrument(uint16 id) const;
};
class MonkeyMacSndLoader final : public MacSndLoader {
@@ -120,7 +120,7 @@ public:
bool restartSoundAfterLoad() const override { return _isMusic && _loop; }
bool ignoreMachineRating() const override { return true; }
private:
- const Common::SharedPtr<MacSndInstrument> *fetchInstrument(const byte *data, uint32 dataSize, uint32 tagOrOffset);
+ const Common::SharedPtr<MacSndResource> *fetchInstrument(const byte *data, uint32 dataSize, uint32 tagOrOffset);
bool _blockSfx;
byte _transpose;
const byte _numInstrumentsMax;
@@ -181,7 +181,7 @@ bool MacSndLoader::loadInstruments(const char *const *tryFileNames, uint16 numTr
Common::String nm(resMan.getResName(MKTAG('s', 'n', 'd', ' '), ids[i]));
memcpy(&tag, nm.c_str(), MIN<uint>(nm.size(), sizeof(tag)));
uint32 id = _useInstrTag ? FROM_BE_32(tag) : ids[i];
- _instruments.push_back(Common::SharedPtr<MacSndInstrument>(new MacSndInstrument(id, str, Common::move(nm))));
+ _instruments.push_back(Common::SharedPtr<MacSndResource>(new MacSndResource(id, str, Common::move(nm))));
delete str;
}
@@ -245,9 +245,9 @@ bool LoomMacSndLoader::parseNextEvent(uint16 chan, uint16 &duration, uint8 ¬e
return true;
}
-const Common::SharedPtr<MacSndInstrument> *LoomMacSndLoader::fetchInstrument(uint16 id) const {
- Common::Array<Common::SharedPtr<MacSndInstrument> >::const_iterator instr = _instruments.end();
- for (Common::Array<Common::SharedPtr<MacSndInstrument> >::const_iterator i = _instruments.begin(); i != _instruments.end(); ++i) {
+const Common::SharedPtr<MacSndResource> *LoomMacSndLoader::fetchInstrument(uint16 id) const {
+ Common::Array<Common::SharedPtr<MacSndResource> >::const_iterator instr = _instruments.end();
+ for (Common::Array<Common::SharedPtr<MacSndResource> >::const_iterator i = _instruments.begin(); i != _instruments.end(); ++i) {
if ((*i)->id() == id)
return i;
else if ((*i)->id() == 0x2D1C)
@@ -356,10 +356,10 @@ bool MonkeyMacSndLoader::parseNextEvent(uint16 chan, uint16 &duration, uint8 &no
return true;
}
-const Common::SharedPtr<MacSndInstrument> *MonkeyMacSndLoader::fetchInstrument(const byte *data, uint32 dataSize, uint32 tagOrOffset) {
- Common::Array<Common::SharedPtr<MacSndInstrument> >::const_iterator instr = _instruments.end();
+const Common::SharedPtr<MacSndResource> *MonkeyMacSndLoader::fetchInstrument(const byte *data, uint32 dataSize, uint32 tagOrOffset) {
+ Common::Array<Common::SharedPtr<MacSndResource> >::const_iterator instr = _instruments.end();
if (tagOrOffset & ~0x7fffff) {
- for (Common::Array<Common::SharedPtr<MacSndInstrument> >::const_iterator i = _instruments.begin(); i != _instruments.end(); ++i) {
+ for (Common::Array<Common::SharedPtr<MacSndResource> >::const_iterator i = _instruments.begin(); i != _instruments.end(); ++i) {
if ((*i)->id() == tagOrOffset)
return i;
else if ((*i)->id() == MKTAG('s', 'i', 'l', 'e'))
@@ -369,7 +369,7 @@ const Common::SharedPtr<MacSndInstrument> *MonkeyMacSndLoader::fetchInstrument(c
Common::SeekableReadStream *str = new Common::MemoryReadStream(&data[tagOrOffset + 8], READ_BE_UINT32(data + tagOrOffset + 4), DisposeAfterUse::NO);
if (_instruments.size() == _numInstrumentsMax)
_instruments.pop_back();
- _instruments.push_back(Common::SharedPtr<MacSndInstrument>(new MacSndInstrument(READ_BE_UINT32(&data[tagOrOffset]), str, Common::String())));
+ _instruments.push_back(Common::SharedPtr<MacSndResource>(new MacSndResource(READ_BE_UINT32(&data[tagOrOffset]), str, Common::String())));
delete str;
instr = _instruments.end() - 1;
}
diff --git a/engines/scumm/players/player_mac_new.cpp b/engines/scumm/players/player_mac_new.cpp
index b410e2c222e..8f6ee54154d 100644
--- a/engines/scumm/players/player_mac_new.cpp
+++ b/engines/scumm/players/player_mac_new.cpp
@@ -101,6 +101,8 @@ public:
void feed(int32 *dst, uint32 dstSize, byte dstFrameSize);
+ static uint32 calcRate(uint32 outRate, uint32 factor, uint32 dataRate);
+
const Audio::Mixer::SoundType _sndType;
const int _synth;
const bool _interpolate;
@@ -111,7 +113,6 @@ private:
void setupRateConv(uint32 drate, uint32 mod, uint32 srate, bool ppr);
void startSound(uint32 tmr);
void processSndCmdQueue();
- uint32 calcRate(uint32 outRate, uint32 factor, uint32 dataRate);
uint32 calcNoteRateAdj(int diff);
void makeSquareWave(int8 *dstBuff, uint16 dstSize, byte timbre);
@@ -332,7 +333,7 @@ int MacPlayerAudioStream::readBuffer(int16 *buffer, const int numSamples) {
*buffer++ = CLIP<int32>((smpL / _numGroups) >> 2, -32768, 32767);
if (_isStereo)
- *buffer++ = CLIP<int32>((smpL / _numGroups) >> 2, -32768, 32767);
+ *buffer++ = CLIP<int32>((smpR / _numGroups) >> 2, -32768, 32767);
}
return numSamples;
}
@@ -397,7 +398,7 @@ void MacLowLevelPCMDriver::feed(int8 *dst, uint32 byteSize, Audio::Mixer::SoundT
MacLowLevelPCMDriver::ChanHandle MacLowLevelPCMDriver::createChannel(Audio::Mixer::SoundType sndType, SynthType synthType, byte attributes, ChanCallback *callback) {
Common::StackLock lock(_mutex);
- MacSndChannel *ch = new MacSndChannel(this, sndType, synthType, synthType == kSampledSynth && !(attributes & kNoInterp), !(synthType == kSampledSynth && ((attributes & 3) == kInitChanRight)), !(synthType == kSampledSynth && (((attributes & 3) == kInitChanLeft) || ((attributes & 0x80) == kInitMono))), callback);
+ MacSndChannel *ch = new MacSndChannel(this, sndType, synthType, synthType == kSampledSynth && !(attributes & kNoInterp), !(synthType == kSampledSynth && ((attributes & 3) == kInitChanRight)), !(synthType == kSampledSynth && (((attributes & 3) == kInitChanLeft) || ((attributes & 0xC0) == kInitMono))), callback);
assert(ch);
_channels.push_back(ch);
@@ -535,6 +536,10 @@ void MacLowLevelPCMDriver::clearChannelFlags(ChanHandle handle, uint8 flags) {
MacSndChannel *ch = findChannel(handle); if (ch) ch->clearFlags(flags);
}
+uint32 MacLowLevelPCMDriver::calcRate(uint32 outRate, uint32 factor, uint32 dataRate) {
+ return MacSndChannel::calcRate(outRate, factor, dataRate);
+}
+
void MacLowLevelPCMDriver::updateStatus(Audio::Mixer::SoundType sndType) {
int count = 0;
int count2 = 0;
@@ -891,128 +896,6 @@ void MacSndChannel::feed(int32 *dst, uint32 dstSize, byte dstFrameSize) {
}
}
-void MacSndChannel::setupSound(const MacLowLevelPCMDriver::PCMSound *snd) {
- assert(_synth == MacLowLevelPCMDriver::kSampledSynth);
-
- _len = snd->len;
- const byte *in = snd->data.get();
- assert(in);
- int8 *buff = new int8[_len];
- for (uint32 i = 0; i < _len; ++i)
- buff[i] = *in++ ^ 0x80;
- _res = Common::SharedPtr<const int8>(buff, Common::ArrayDeleter<const int8>());
- _frameSize = snd->stereo ? 2 : 1;
- _loopSt = 0;
- _data = nullptr;
-
- if (snd->loopend - snd->loopst < 2 || snd->loopend < snd->loopst) {
- _loopSt2 = 0;
- _loopEnd = 0;
- } else {
- _loopSt2 = snd->loopst - (snd->loopst % _frameSize);
- _loopEnd = snd->loopend - (snd->loopend % _frameSize);
- assert(_loopEnd <= _len);
- }
-
- _baseFreq = snd->baseFreq;
- _srate = snd->rate;
-}
-
-void MacSndChannel::setupRateConv(uint32 drate, uint32 mod, uint32 srate, bool ppr) {
- uint32 rmul = calcRate(drate, mod, srate);
-
- if (ppr) {
- if (rmul >= 0x7FFD && rmul <= 0x8003)
- rmul = 0x8000;
- else if (ABS((int16)(rmul & 0xffff)) <= 7)
- rmul = (rmul + 7) & ~0xffff;
-
- if (rmul > (uint32)-64)
- rmul = (uint32)-64;
- }
-
- assert(rmul);
- _rmL = rmul & 0xffff;
- _rmH = rmul >> 16;
-}
-
-void MacSndChannel::startSound(uint32 duration) {
- _duration = duration;
- _tmrInc = duration ? _tmrRate : 0;
- _tmrPos = 0;
-
- _data = _res.get();
- _lastSmp[0] = _data[0];
- if (_len >= _frameSize)
- _lastSmp[1] = _data[1];
- _rcPos = 0;
- _loopSt = 0;
- _loopLen = _loopEnd ? _loopEnd : _len;
- _smpWtAcc = 0;
- _phase = 0;
-}
-
-void MacSndChannel::processSndCmdQueue() {
- if ((_data && _tmrInc == 0) || _duration)
- return;
-
- if (_sndCmdQueue.empty()) {
- setFlags(MacLowLevelPCMDriver::kStatusDone);
- clearFlags(MacLowLevelPCMDriver::kStatusPlaying);
- return;
- }
-
- _drv->clearFlags(MacLowLevelPCMDriver::kStatusDone);
- clearFlags(MacLowLevelPCMDriver::kStatusDone);
- setFlags(MacLowLevelPCMDriver::kStatusPlaying);
-
- SoundCommand &c = _sndCmdQueue.front();
- MacLowLevelPCMDriver::PCMSound *p = (c.ptr && c.arg2 == 1) ? reinterpret_cast<MacLowLevelPCMDriver::PCMSound*>(c.ptr) : nullptr;
- const byte *b = (c.ptr && c.arg2 == 2) ? reinterpret_cast<byte*>(c.ptr) : nullptr;
-
- switch (c.cmd) {
- case 3:
- quiet();
- break;
- case 4:
- flush();
- break;
- case 10:
- wait(c.arg1);
- break;
- case 13:
- callback(c.arg1, c.ptr);
- break;
- case 40:
- playNote(c.arg1, c.arg2);
- break;
- case 44:
- setTimbre(c.arg1);
- break;
- case 60:
- loadWaveTable(b, c.arg1);
- break;
- case 80:
- loadInstrument(p);
- break;
- case 81:
- playSamples(p);
- break;
- default:
- break;
- }
-
- if (p) {
- p->data.reset();
- delete p;
- } else if (b) {
- delete[] b;
- }
-
- if (!_sndCmdQueue.empty())
- _sndCmdQueue.erase(_sndCmdQueue.begin());
-}
-
uint32 MacSndChannel::calcRate(uint32 outRate, uint32 factor, uint32 dataRate) {
uint32 result = outRate;
uint64 t = 0;
@@ -1166,6 +1049,128 @@ uint32 MacSndChannel::calcRate(uint32 outRate, uint32 factor, uint32 dataRate) {
return result;
}
+void MacSndChannel::setupSound(const MacLowLevelPCMDriver::PCMSound *snd) {
+ assert(_synth == MacLowLevelPCMDriver::kSampledSynth);
+
+ _len = snd->len;
+ const byte *in = snd->data.get();
+ assert(in);
+ int8 *buff = new int8[_len];
+ for (uint32 i = 0; i < _len; ++i)
+ buff[i] = *in++ ^ 0x80;
+ _res = Common::SharedPtr<const int8>(buff, Common::ArrayDeleter<const int8>());
+ _frameSize = snd->stereo ? 2 : 1;
+ _loopSt = 0;
+ _data = nullptr;
+
+ if (snd->loopend - snd->loopst < 2 || snd->loopend < snd->loopst) {
+ _loopSt2 = 0;
+ _loopEnd = 0;
+ } else {
+ _loopSt2 = snd->loopst - (snd->loopst % _frameSize);
+ _loopEnd = snd->loopend - (snd->loopend % _frameSize);
+ assert(_loopEnd <= _len);
+ }
+
+ _baseFreq = snd->baseFreq;
+ _srate = snd->rate;
+}
+
+void MacSndChannel::setupRateConv(uint32 drate, uint32 mod, uint32 srate, bool ppr) {
+ uint32 rmul = calcRate(drate, mod, srate);
+
+ if (ppr) {
+ if (rmul >= 0x7FFD && rmul <= 0x8003)
+ rmul = 0x8000;
+ else if (ABS((int16)(rmul & 0xffff)) <= 7)
+ rmul = (rmul + 7) & ~0xffff;
+
+ if (rmul > (uint32)-64)
+ rmul = (uint32)-64;
+ }
+
+ assert(rmul);
+ _rmL = rmul & 0xffff;
+ _rmH = rmul >> 16;
+}
+
+void MacSndChannel::startSound(uint32 duration) {
+ _duration = duration;
+ _tmrInc = duration ? _tmrRate : 0;
+ _tmrPos = 0;
+
+ _data = _res.get();
+ _lastSmp[0] = _data[0];
+ if (_len >= _frameSize)
+ _lastSmp[1] = _data[1];
+ _rcPos = 0;
+ _loopSt = 0;
+ _loopLen = _loopEnd ? _loopEnd : _len;
+ _smpWtAcc = 0;
+ _phase = 0;
+}
+
+void MacSndChannel::processSndCmdQueue() {
+ if ((_data && _tmrInc == 0) || _duration)
+ return;
+
+ if (_sndCmdQueue.empty()) {
+ setFlags(MacLowLevelPCMDriver::kStatusDone);
+ clearFlags(MacLowLevelPCMDriver::kStatusPlaying);
+ return;
+ }
+
+ _drv->clearFlags(MacLowLevelPCMDriver::kStatusDone);
+ clearFlags(MacLowLevelPCMDriver::kStatusDone);
+ setFlags(MacLowLevelPCMDriver::kStatusPlaying);
+
+ SoundCommand &c = _sndCmdQueue.front();
+ MacLowLevelPCMDriver::PCMSound *p = (c.ptr && c.arg2 == 1) ? reinterpret_cast<MacLowLevelPCMDriver::PCMSound*>(c.ptr) : nullptr;
+ const byte *b = (c.ptr && c.arg2 == 2) ? reinterpret_cast<byte*>(c.ptr) : nullptr;
+
+ switch (c.cmd) {
+ case 3:
+ quiet();
+ break;
+ case 4:
+ flush();
+ break;
+ case 10:
+ wait(c.arg1);
+ break;
+ case 13:
+ callback(c.arg1, c.ptr);
+ break;
+ case 40:
+ playNote(c.arg1, c.arg2);
+ break;
+ case 44:
+ setTimbre(c.arg1);
+ break;
+ case 60:
+ loadWaveTable(b, c.arg1);
+ break;
+ case 80:
+ loadInstrument(p);
+ break;
+ case 81:
+ playSamples(p);
+ break;
+ default:
+ break;
+ }
+
+ if (p) {
+ p->data.reset();
+ delete p;
+ } else if (b) {
+ delete[] b;
+ }
+
+ if (!_sndCmdQueue.empty())
+ _sndCmdQueue.erase(_sndCmdQueue.begin());
+}
+
uint32 MacSndChannel::calcNoteRateAdj(int diff) {
static const uint32 adjFrac[23] = {
0x21e71f26, 0x23eb3588, 0x260dfc14, 0x285145f3, 0x2ab70212, 0x2d413ccd,
@@ -1247,7 +1252,7 @@ void DoubleBufferIntern::update() {
}
}
-MacSndInstrument::MacSndInstrument(uint32 id, Common::SeekableReadStream *&in, Common::String &&name) : _id(id), _name(Common::move(name)) {
+MacSndResource::MacSndResource(uint32 id, Common::SeekableReadStream *&in, Common::String &&name) : _id(id), _name(Common::move(name)) {
in->seek(2);
uint16 numTypes = in->readUint16BE();
in->seek(numTypes * 6 + 4);
@@ -1266,6 +1271,24 @@ MacSndInstrument::MacSndInstrument(uint32 id, Common::SeekableReadStream *&in, C
_snd.data = Common::SharedPtr<const byte>(buff, Common::ArrayDeleter<const byte>());
}
+MacSndResource::MacSndResource(uint32 id, const byte *in) : _id(id) {
+ in += 4;
+ _snd.len = READ_BE_UINT32(in);
+ in += 4;
+ _snd.rate = READ_BE_UINT32(in);
+ in += 4;
+ _snd.loopst = READ_BE_UINT32(in);
+ in += 4;
+ _snd.loopend = READ_BE_UINT32(in);
+ in += 4;
+ _snd.enc = *in++;
+ _snd.baseFreq = *in++;
+
+ byte *buff = new byte[_snd.len];
+ memcpy(buff, in, _snd.len);
+ _snd.data = Common::SharedPtr<const byte>(buff, Common::ArrayDeleter<const byte>());
+}
+
const uint8 _fourToneSynthWaveForm[256] = {
0x80, 0x7a, 0x74, 0x6e, 0x69, 0x63, 0x5d, 0x57, 0x52, 0x4c, 0x47, 0x42, 0x3e, 0x3b, 0x38, 0x35,
0x34, 0x33, 0x34, 0x35, 0x37, 0x3a, 0x3e, 0x43, 0x49, 0x4e, 0x54, 0x5b, 0x61, 0x67, 0x6c, 0x71,
diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp
index dc819c34394..088146aca51 100644
--- a/engines/scumm/scumm.cpp
+++ b/engines/scumm/scumm.cpp
@@ -2339,8 +2339,7 @@ void ScummEngine::setupMusic(int midi) {
_native_mt32 = enable_gs = false;
useOnlyNative = true;
} else if (_sound->_musicType == MDT_MACINTOSH) {
- //nativeMidiDriver = new IMuseDriver_MacM68k(_mixer);
- nativeMidiDriver = new IMuseDriver_Mac_DJM(_mixer);
+ nativeMidiDriver = new IMuseDriver_Macintosh(this, _mixer, _game.id);
_native_mt32 = enable_gs = false;
useOnlyNative = true;
} else if (_sound->_musicType != MDT_ADLIB && _sound->_musicType != MDT_TOWNS && _sound->_musicType != MDT_PCSPK) {
Commit: f06279c8b274242b86a39e616b9050c408517397
https://github.com/scummvm/scummvm/commit/f06279c8b274242b86a39e616b9050c408517397
Author: athrxx (athrxx at scummvm.org)
Date: 2024-11-23T23:07:44+01:00
Commit Message:
SCUMM: (IMS) - get rid of Mac sfx instrument type
(since it was an unnecessary hack)
Changed paths:
audio/mididrv.h
engines/scumm/imuse/drivers/macintosh.cpp
engines/scumm/imuse/imuse_part.cpp
engines/scumm/imuse/instrument.cpp
engines/scumm/imuse/instrument.h
engines/scumm/imuse/sysex_scumm.cpp
engines/scumm/saveload.cpp
diff --git a/audio/mididrv.h b/audio/mididrv.h
index 57b89da37e2..7a215907a5d 100644
--- a/audio/mididrv.h
+++ b/audio/mididrv.h
@@ -562,6 +562,7 @@ public:
virtual void sustain(bool value) { controlChange(MidiDriver::MIDI_CONTROLLER_SUSTAIN, value ? 1 : 0); }
virtual void effectLevel(byte value) { controlChange(MidiDriver::MIDI_CONTROLLER_REVERB, value); }
virtual void chorusLevel(byte value) { controlChange(MidiDriver::MIDI_CONTROLLER_CHORUS, value); }
+ virtual void bankSelect(byte bank) {}
virtual void allNotesOff() { controlChange(MidiDriver::MIDI_CONTROLLER_ALL_NOTES_OFF, 0); }
// SysEx messages
diff --git a/engines/scumm/imuse/drivers/macintosh.cpp b/engines/scumm/imuse/drivers/macintosh.cpp
index 7a116df4a39..938cd726dc1 100644
--- a/engines/scumm/imuse/drivers/macintosh.cpp
+++ b/engines/scumm/imuse/drivers/macintosh.cpp
@@ -223,6 +223,7 @@ public:
void detune(int16 value) override { _detune = value; pitchBend(_pitchBendSet); }
void priority(byte value) override { _prio = value; }
void sustain(bool value) override;
+ void bankSelect(byte bank) override { _bank = bank; }
void allNotesOff() override;
void sysEx_customInstrument(uint32 type, const byte *instr, uint32 dataSize) override {}
diff --git a/engines/scumm/imuse/imuse_part.cpp b/engines/scumm/imuse/imuse_part.cpp
index cb912318e2a..d6caf785fa9 100644
--- a/engines/scumm/imuse/imuse_part.cpp
+++ b/engines/scumm/imuse/imuse_part.cpp
@@ -425,20 +425,19 @@ void Part::sendDetune() {
void Part::programChange(byte value) {
_bank = 0;
- _instrument.program(value, _player->isMT32());
+ _instrument.program(value, 0, _player->isMT32());
if (clearToTransmit())
_instrument.send(_mc);
}
void Part::set_instrument(uint b) {
_bank = (byte)(b >> 8);
- if (_bank)
- error("Non-zero instrument bank selection. Please report this");
- if (_se->_soundType == MDT_MACINTOSH)
- _instrument.macSfx(b);
- else
- _instrument.program((byte)b, _player->isMT32());
+ // Indy4 and Monkey2 Macintosh versions always use the second bank for sound effects here.
+ if (_se->_soundType == MDT_MACINTOSH && (_se->_game_id == GID_MONKEY2 || _se->_game_id == GID_INDY4))
+ _bank = 1;
+
+ _instrument.program((byte)b, _bank, _player->isMT32());
if (clearToTransmit())
_instrument.send(_mc);
diff --git a/engines/scumm/imuse/instrument.cpp b/engines/scumm/imuse/instrument.cpp
index 317f3fef502..0bcf97399db 100644
--- a/engines/scumm/imuse/instrument.cpp
+++ b/engines/scumm/imuse/instrument.cpp
@@ -123,15 +123,16 @@ const byte Instrument::_gmRhythmMap[35] = {
class Instrument_Program : public InstrumentInternal {
private:
byte _program;
+ byte _bank;
bool _soundTypeMT32;
bool _nativeMT32Device;
public:
- Instrument_Program(byte program, bool soundTypeMT32, bool nativeMT32Device);
+ Instrument_Program(byte program, byte bank, bool soundTypeMT32, bool nativeMT32Device);
Instrument_Program(Common::Serializer &s, bool nativeMT32Device);
void saveLoadWithSerializer(Common::Serializer &s) override;
void send(MidiChannel *mc) override;
- void copy_to(Instrument *dest) override { dest->program(_program, _soundTypeMT32); }
+ void copy_to(Instrument *dest) override { dest->program(_program, _bank, _soundTypeMT32); }
bool is_valid() override {
return (_program < 128) &&
((_nativeMT32Device == _soundTypeMT32) || (_nativeMT32Device
@@ -276,21 +277,6 @@ private:
byte _instrument[23];
};
-class Instrument_MacSfx : public InstrumentInternal {
-private:
- byte _program;
-
-public:
- Instrument_MacSfx(byte program);
- Instrument_MacSfx(Common::Serializer &s);
- void saveLoadWithSerializer(Common::Serializer &s) override;
- void send(MidiChannel *mc) override;
- void copy_to(Instrument *dest) override { dest->macSfx(_program); }
- bool is_valid() override {
- return (true);
- }
-};
-
////////////////////////////////////////
//
// Instrument class members
@@ -303,12 +289,12 @@ void Instrument::clear() {
_type = itNone;
}
-void Instrument::program(byte prog, bool mt32SoundType) {
+void Instrument::program(byte prog, byte bank, bool mt32SoundType) {
clear();
if (prog > 127)
return;
_type = itProgram;
- _instrument = new Instrument_Program(prog, mt32SoundType, _nativeMT32Device);
+ _instrument = new Instrument_Program(prog, bank, mt32SoundType, _nativeMT32Device);
}
void Instrument::adlib(const byte *instrument) {
@@ -335,12 +321,6 @@ void Instrument::pcspk(const byte *instrument) {
_instrument = new Instrument_PcSpk(instrument);
}
-void Instrument::macSfx(byte prog) {
- clear();
- _type = itMacSfx;
- _instrument = new Instrument_MacSfx(prog);
-}
-
void Instrument::saveLoadWithSerializer(Common::Serializer &s) {
if (s.isSaving()) {
s.syncAsByte(_type);
@@ -364,9 +344,11 @@ void Instrument::saveLoadWithSerializer(Common::Serializer &s) {
case itPcSpk:
_instrument = new Instrument_PcSpk(s);
break;
- case itMacSfx:
- _instrument = new Instrument_MacSfx(s);
- break;
+ case itMacDeprecated: {
+ byte prog = 255;
+ s.syncAsByte(prog);
+ _instrument = new Instrument_Program(prog, 1, false, false);
+ } break;
default:
warning("No known instrument classification #%d", (int)_type);
_type = itNone;
@@ -380,8 +362,9 @@ void Instrument::saveLoadWithSerializer(Common::Serializer &s) {
//
////////////////////////////////////////
-Instrument_Program::Instrument_Program(byte program, bool soundTypeMT32, bool nativeMT32Device) :
+Instrument_Program::Instrument_Program(byte program, byte bank, bool soundTypeMT32, bool nativeMT32Device) :
_program(program),
+ _bank(bank),
_soundTypeMT32(soundTypeMT32),
_nativeMT32Device(nativeMT32Device) {
if (program > 127)
@@ -391,6 +374,7 @@ Instrument_Program::Instrument_Program(byte program, bool soundTypeMT32, bool na
Instrument_Program::Instrument_Program(Common::Serializer &s, bool nativeMT32Device) :
_nativeMT32Device(nativeMT32Device) {
_program = 255;
+ _bank = 0;
_soundTypeMT32 = false;
if (!s.isSaving())
saveLoadWithSerializer(s);
@@ -398,6 +382,7 @@ Instrument_Program::Instrument_Program(Common::Serializer &s, bool nativeMT32Dev
void Instrument_Program::saveLoadWithSerializer(Common::Serializer &s) {
s.syncAsByte(_program);
+ s.syncAsByte(_bank, VER(123));
if (s.isSaving()) {
s.syncAsByte(_soundTypeMT32);
s.syncAsByte(_nativeMT32Device);
@@ -417,8 +402,13 @@ void Instrument_Program::send(MidiChannel *mc) {
byte program = _program;
if (!_nativeMT32Device && _soundTypeMT32)
program = MidiDriver::_mt32ToGm[program];
+
+ if (_bank)
+ mc->bankSelect(_bank);
if (program < 128)
mc->programChange(program);
+ if (_bank)
+ mc->bankSelect(0);
}
////////////////////////////////////////
@@ -527,32 +517,4 @@ void Instrument_PcSpk::send(MidiChannel *mc) {
mc->sysEx_customInstrument('SPK ', (byte *)&_instrument, sizeof(_instrument));
}
-////////////////////////////////////////
-//
-// Instrument_MacSfx class members
-//
-////////////////////////////////////////
-
-Instrument_MacSfx::Instrument_MacSfx(byte program) :
- _program(program) {
-}
-
-Instrument_MacSfx::Instrument_MacSfx(Common::Serializer &s) {
- _program = 255;
- if (!s.isSaving()) {
- saveLoadWithSerializer(s);
- }
-}
-
-void Instrument_MacSfx::saveLoadWithSerializer(Common::Serializer &s) {
- s.syncAsByte(_program);
-}
-
-void Instrument_MacSfx::send(MidiChannel *mc) {
- if (!mc)
- return;
- mc->controlChange(0, 1);
- mc->programChange(_program);
- mc->controlChange(0, 0);
-}
} // End of namespace Scumm
diff --git a/engines/scumm/imuse/instrument.h b/engines/scumm/imuse/instrument.h
index 43dd2fd290f..1b6b73fee7e 100644
--- a/engines/scumm/imuse/instrument.h
+++ b/engines/scumm/imuse/instrument.h
@@ -52,7 +52,7 @@ public:
itAdLib = 2,
itRoland = 3,
itPcSpk = 4,
- itMacSfx = 5
+ itMacDeprecated = 5
};
Instrument() : _type(0), _instrument(0), _nativeMT32Device(false) { }
@@ -68,11 +68,10 @@ public:
dest->clear();
}
- void program(byte program, bool mt32SoundType);
+ void program(byte program, byte bank, bool mt32SoundType);
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); }
diff --git a/engines/scumm/imuse/sysex_scumm.cpp b/engines/scumm/imuse/sysex_scumm.cpp
index 3b9292e79df..09b6ea161f1 100644
--- a/engines/scumm/imuse/sysex_scumm.cpp
+++ b/engines/scumm/imuse/sysex_scumm.cpp
@@ -88,7 +88,7 @@ void sysexHandler_Scumm(Player *player, const byte *msg, uint16 len) {
// 0 is a valid program number. MI2 tests show that in such
// cases, a regular program change message always seems to follow
// anyway.
- part->_instrument.program(buf[8], player->_isMT32);
+ part->_instrument.program(buf[8], 0, player->_isMT32);
} else {
// Like the original we set up the instrument data of the
// specified program here too. In case the global
diff --git a/engines/scumm/saveload.cpp b/engines/scumm/saveload.cpp
index 408f3948192..ad9bb9d1634 100644
--- a/engines/scumm/saveload.cpp
+++ b/engines/scumm/saveload.cpp
@@ -70,7 +70,7 @@ struct SaveInfoSection {
#define SaveInfoSectionSize (4+4+4 + 4+4 + 4+2)
-#define CURRENT_VER 122
+#define CURRENT_VER 123
#define INFOSECTION_VERSION 2
#pragma mark -
Commit: b3baa636ee7ad549961a9341e2f840d6ce759c85
https://github.com/scummvm/scummvm/commit/b3baa636ee7ad549961a9341e2f840d6ce759c85
Author: athrxx (athrxx at scummvm.org)
Date: 2024-11-23T23:11:51+01:00
Commit Message:
SCUMM: whitespace
Changed paths:
engines/scumm/actor.cpp
engines/scumm/actor_he.h
engines/scumm/debugger.cpp
engines/scumm/detection_internal.h
engines/scumm/dialog-sessionselector.cpp
engines/scumm/gfx.cpp
engines/scumm/gfx_mac.cpp
engines/scumm/gfx_towns.cpp
engines/scumm/imuse/drivers/macintosh.cpp
engines/scumm/imuse/drivers/midi.cpp
engines/scumm/imuse/drivers/midi.h
engines/scumm/imuse/imuse.cpp
engines/scumm/imuse/imuse_player.cpp
engines/scumm/imuse/sysex_scumm.cpp
engines/scumm/input.cpp
engines/scumm/palette.cpp
engines/scumm/room.cpp
engines/scumm/saveload.cpp
engines/scumm/script.cpp
engines/scumm/script_v6.cpp
engines/scumm/string.cpp
diff --git a/engines/scumm/actor.cpp b/engines/scumm/actor.cpp
index ae990aaa732..0df30c89ded 100644
--- a/engines/scumm/actor.cpp
+++ b/engines/scumm/actor.cpp
@@ -1535,7 +1535,7 @@ int Actor_v2::remapDirection(int dir, bool is_walking) {
if (_moving & ~MF_TURN)
_targetFacing = oldDirToNewDir(remapTable2[newDirToOldDir(dir) * 8 + remapTable1[_walkdata.facing * 8 + (_vm->getBoxFlags(_walkbox) & 7)]]);
- else
+ else
_targetFacing = oldDirToNewDir(remapTable3[newDirToOldDir(dir) * 8 + (_vm->getBoxFlags(_walkbox) & 7)]);
return _targetFacing | 0x400;
diff --git a/engines/scumm/actor_he.h b/engines/scumm/actor_he.h
index fc30416aaea..c9cd241608a 100644
--- a/engines/scumm/actor_he.h
+++ b/engines/scumm/actor_he.h
@@ -46,7 +46,7 @@ struct HEAnimAuxEntry {
};
struct HEAnimAuxData {
- HEAnimAuxData() : auxDefaultSearchBlock(nullptr), externalDataPtr(nullptr), auxDataBlock(nullptr), actor(0) {}
+ HEAnimAuxData() : auxDefaultSearchBlock(nullptr), externalDataPtr(nullptr), auxDataBlock(nullptr), actor(0) {}
byte *auxDefaultSearchBlock;
byte *externalDataPtr;
const byte *auxDataBlock;
diff --git a/engines/scumm/debugger.cpp b/engines/scumm/debugger.cpp
index c9dcb68a396..eaf8182f1c4 100644
--- a/engines/scumm/debugger.cpp
+++ b/engines/scumm/debugger.cpp
@@ -791,33 +791,33 @@ bool ScummDebugger::Cmd_Cosdump(int argc, const char **argv) {
curState += 3;
break;
// case AKC_SoundStuff:
- // break;
+ // break;
// case AKC_Flip:
- // break;
+ // break;
// case AKC_StartActionOn:
- // break;
+ // break;
// case AKC_StartScriptVar:
- // break;
+ // break;
case AKC_StartSoundVar:
debugPrintf("START SOUND VAR[%d]\n", GB(2));
curState += 3;
break;
// case AKC_DisplayAuxFrame:
- // break;
+ // break;
// case AKC_IfVarEQDo:
- // break;
+ // break;
// case AKC_SkipNE:
- // break;
+ // break;
// case AKC_IfVarLTDo:
- // break;
+ // break;
// case AKC_IfVarLEDo:
- // break;
+ // break;
// case AKC_IfVarGTDo:
- // break;
+ // break;
// case AKC_IfVarGEDo:
- // break;
+ // break;
// case AKC_EndOfIfDo:
- // break;
+ // break;
case AKC_StartActorTalkie:
debugPrintf("START TALK %d {%d}\n", GB(2), GB(3));
curState += 4;
@@ -835,13 +835,13 @@ bool ScummDebugger::Cmd_Cosdump(int argc, const char **argv) {
curState += 3;
break;
// case AKC_IfAnyTalkingGoTo:
- // break;
+ // break;
// case AKC_IfNotAnyTalkingGoTo:
- // break;
+ // break;
// case AKC_IfTalkingPickGoTo:
- // break;
+ // break;
// case AKC_IfNotTalkingPickGoTo:
- // break;
+ // break;
case AKC_EndSeq:
debugPrintf("STOP\n");
curState += 2;
diff --git a/engines/scumm/detection_internal.h b/engines/scumm/detection_internal.h
index 7b70d0f6a2e..6b4baf9c751 100644
--- a/engines/scumm/detection_internal.h
+++ b/engines/scumm/detection_internal.h
@@ -899,7 +899,7 @@ static Common::String customizeGuiOptions(const DetectorResult &res) {
guiOptions.erase(pos, 1);
}
}
-
+
Common::String defaultRenderOption = "";
Common::String defaultSoundOption = "";
diff --git a/engines/scumm/dialog-sessionselector.cpp b/engines/scumm/dialog-sessionselector.cpp
index ad5f0176161..454e0c36150 100644
--- a/engines/scumm/dialog-sessionselector.cpp
+++ b/engines/scumm/dialog-sessionselector.cpp
@@ -47,8 +47,8 @@ SessionSelectorDialog::SessionSelectorDialog(Scumm::ScummEngine_v90he *vm)
_timestamp = 0;
_queryProgressText = new GUI::StaticTextWidget(this, "SessionSelector.QueryProgressText",
- // I18N: Retrieving list of online multiplayer games
- _("Querying games..."));
+ // I18N: Retrieving list of online multiplayer games
+ _("Querying games..."));
_queryProgressText->setAlign(Graphics::kTextAlignCenter);
diff --git a/engines/scumm/gfx.cpp b/engines/scumm/gfx.cpp
index ec1c5cc6191..59431fe3d0a 100644
--- a/engines/scumm/gfx.cpp
+++ b/engines/scumm/gfx.cpp
@@ -611,7 +611,7 @@ void ScummEngine::updateDirtyScreen(VirtScreenNumber slot) {
if (_game.platform == Common::kPlatformFMTowns && vs->number == kBannerVirtScreen) {
int scl = _textSurfaceMultiplier;
towns_drawStripToScreen(vs, start * 8 * scl, (vs->topline + top) * scl, start * 8 * scl, top * scl, w * scl, bottom - top);
- } else
+ } else
#endif
drawStripToScreen(vs, start * 8, w, top, bottom);
w = 8;
@@ -1949,7 +1949,7 @@ void ScummEngine_v5::drawFlashlight() {
}
} else {
const byte *fwdCurvePtr, *bkwdCurvePtr;
-
+
switch (_game.version) {
case 1:
fwdCurvePtr = v1FwdCurveData;
@@ -2850,7 +2850,7 @@ void Gdi::drawBMAPObject(const byte *ptr, VirtScreen *vs, int obj, int x, int y,
Common::Rect renderArea, clipArea, backgroundCoords;
if (_vm->_game.heversion > 98) {
- ((ScummEngine_v71he *)_vm)->_wiz->makeSizedRectAt(&renderArea, x + scrX, y, w, h);
+ ((ScummEngine_v71he *)_vm)->_wiz->makeSizedRectAt(&renderArea, x + scrX, y, w, h);
((ScummEngine_v71he *)_vm)->_wiz->makeSizedRect(&clipArea, scrWidth, scrHeight);
((ScummEngine_v71he *)_vm)->_wiz->findRectOverlap(&renderArea, &clipArea);
diff --git a/engines/scumm/gfx_mac.cpp b/engines/scumm/gfx_mac.cpp
index e27d78da3c7..757fa1aa496 100644
--- a/engines/scumm/gfx_mac.cpp
+++ b/engines/scumm/gfx_mac.cpp
@@ -279,17 +279,17 @@ void ScummEngine::mac_applyEPXToBuffer(const byte *inputBuffer, byte *outputBuff
// C P B --> +---+---+
// D | 3 | 4 |
// +---+---+
- //
+ //
// Let P be the pixel, and A,B,C,D its neighbors,
// then the new 1,2,3,4 pixels are defined as follows:
- //
+ //
// 1=P; 2=P; 3=P; 4=P;
- //
+ //
// IF C==A => 1=A
// IF A==B => 2=B
// IF D==C => 3=C
// IF B==D => 4=D
- //
+ //
// IF of A, B, C, D, three or more are identical: 1=2=3=4=P
for (int h = 0; h < height; h++) {
diff --git a/engines/scumm/gfx_towns.cpp b/engines/scumm/gfx_towns.cpp
index 14bac620b03..3b474570b9b 100644
--- a/engines/scumm/gfx_towns.cpp
+++ b/engines/scumm/gfx_towns.cpp
@@ -607,7 +607,7 @@ void TownsScreen::scrollLayer(int layer, int offset, int top, int bottom, bool f
if (top == 0 && bottom == _height - 1)
_numDirtyRects = kDirtyRectsMax;
- addDirtyRect(0, top, _width, bottom - top);
+ addDirtyRect(0, top, _width, bottom - top);
}
void TownsScreen::update() {
diff --git a/engines/scumm/imuse/drivers/macintosh.cpp b/engines/scumm/imuse/drivers/macintosh.cpp
index 938cd726dc1..7163a1b7320 100644
--- a/engines/scumm/imuse/drivers/macintosh.cpp
+++ b/engines/scumm/imuse/drivers/macintosh.cpp
@@ -838,7 +838,7 @@ bool NewMacSoundSystem::loadInstruments(const char *const *fileNames, int numFil
byte *b = new byte[sz]();
str->read(b, sz);
delete str;
-
+
Instrument *ins = new Instrument(*i);
ins->sndRes.push_back(getSndResource(READ_BE_UINT16(b)));
@@ -858,7 +858,7 @@ bool NewMacSoundSystem::loadInstruments(const char *const *fileNames, int numFil
delete[] b;
_instruments.push_back(Common::SharedPtr<Instrument>(ins));
}
-
+
return !_instruments.empty();
}
@@ -1035,7 +1035,7 @@ void IMuseChannel_Macintosh::noteOff(byte note) {
void IMuseChannel_Macintosh::noteOn(byte note, byte velocity) {
ChanControlNode *node = (_version > 0 && _number == 9) ? _rtmChannel : allocateNode(_prio);
if (node == nullptr)
- return;
+ return;
if (node != _rtmChannel) {
if (_prog == 0)
@@ -1064,7 +1064,7 @@ void IMuseChannel_Macintosh::controlChange(byte control, byte value) {
// The original MI2/INDY4 code doesn't have that. It will just call a different
// programChange() method from the sysex handler. Only DOTT and SAMNMAX have the
// bank select like this in the original code.
- _bank = value;
+ _bank = value;
break;
case 6:
dataEntry(value);
diff --git a/engines/scumm/imuse/drivers/midi.cpp b/engines/scumm/imuse/drivers/midi.cpp
index 257fbb5666f..184137070ac 100644
--- a/engines/scumm/imuse/drivers/midi.cpp
+++ b/engines/scumm/imuse/drivers/midi.cpp
@@ -882,7 +882,7 @@ void IMuseDriver_MT32::initDevice() {
for (int i = (20 - (int)infoStr.size()) >> 1; i > 0; --i)
infoStr = ' ' + infoStr + ' ';
sendMT32Sysex(0x80000, (const byte*)infoStr.c_str(), MIN<uint32>(infoStr.size(), 20));
-
+
// Reset the MT-32
sendMT32Sysex(0x1FC000, 0, 0);
diff --git a/engines/scumm/imuse/drivers/midi.h b/engines/scumm/imuse/drivers/midi.h
index 9c91622fa53..d4cb7f5fcc4 100644
--- a/engines/scumm/imuse/drivers/midi.h
+++ b/engines/scumm/imuse/drivers/midi.h
@@ -47,7 +47,7 @@ public:
void setTimerCallback(void *timerParam, Common::TimerManager::TimerProc timerProc) override { if (_drv) _drv->setTimerCallback(timerParam, timerProc); }
uint32 getBaseTempo() override { return _drv ? _drv->getBaseTempo() : 0; }
void send(uint32 b) override { if (_drv) _drv->send(b); };
- void sysEx(const byte *msg, uint16 length) override { if (_drv) _drv->sysEx(msg, length); }
+ void sysEx(const byte *msg, uint16 length) override { if (_drv) _drv->sysEx(msg, length); }
virtual void setPitchBendRange(byte channel, uint range) override { if (_drv) _drv->setPitchBendRange(channel, range); }
// Channel allocation functions
diff --git a/engines/scumm/imuse/imuse.cpp b/engines/scumm/imuse/imuse.cpp
index b2dcd14ef32..7f2d936d88c 100644
--- a/engines/scumm/imuse/imuse.cpp
+++ b/engines/scumm/imuse/imuse.cpp
@@ -370,7 +370,7 @@ void IMuseInternal::pause(bool paused) {
// The result is hanging notes on pause. Reportedly
// happens in the original distro, too. To fix that,
// just send AllNotesOff to the channels.
- if (_midi_native && _native_mt32) {
+ if (_midi_native && _native_mt32) {
for (int i = 0; i < 16; ++i)
_midi_native->send(123 << 8 | 0xB0 | i);
}
diff --git a/engines/scumm/imuse/imuse_player.cpp b/engines/scumm/imuse/imuse_player.cpp
index e04bd4acac8..b18b84ef78c 100644
--- a/engines/scumm/imuse/imuse_player.cpp
+++ b/engines/scumm/imuse/imuse_player.cpp
@@ -469,7 +469,7 @@ uint16 Player::sysExNoDelay(const byte *msg, uint16 length) {
// but most of the time not. So it seems to be rather a delicate and race-condition prone matter. The original
// parser handles the timing differently than our general purpose parser and the code execution is also expected
// to be much slower, so that might make all the difference here. It is really a flaw of the track. The time stamps
- // after the sysex messages should have been made a bit more generous.
+ // after the sysex messages should have been made a bit more generous.
// Now, I have added some delays here that I have taken from the original DOTT MT-32 driver's sysex function which
// are supposed to handle the situation when _scanning is enabled. For non-_scanning situations there is no delay in
// the original driver, since apparently is wasn't necessary.
@@ -617,7 +617,7 @@ int Player::setTranspose(byte relative, int b) {
if (b > 24 || b < -24 || relative > 1)
return -1;
if (relative)
- b = transpose_clamp(_transpose + b, -7, 7);
+ b = transpose_clamp(_transpose + b, -7, 7);
_transpose = b;
@@ -1085,7 +1085,7 @@ void Player::fixAfterLoad() {
_midi = _se->getBestMidiDriver(_id);
if (!_midi) {
clear();
- } else {
+ } else {
start_seq_sound(_id, false);
setSpeed(_speed);
if (_parser)
@@ -1128,7 +1128,7 @@ static void syncWithSerializer(Common::Serializer &s, ParameterFader &pf) {
}
pf.irem = 0;
pf.cntdwn = 0;
-
+
} else {
s.syncAsSByte(pf.dir, VER(116));
s.syncAsSint16LE(pf.incr, VER(116));
diff --git a/engines/scumm/imuse/sysex_scumm.cpp b/engines/scumm/imuse/sysex_scumm.cpp
index 09b6ea161f1..2dd43337d26 100644
--- a/engines/scumm/imuse/sysex_scumm.cpp
+++ b/engines/scumm/imuse/sysex_scumm.cpp
@@ -99,7 +99,7 @@ void sysexHandler_Scumm(Player *player, const byte *msg, uint16 len) {
// The newer reallocateMidiChannels() method can fail to assign a
// hardware channel here (bug #14618: Inaccurate fades in INDY4,
// "Test 1"). So instead, we implement the less dynamic alloacation
- // method of the early version drivers here.
+ // method of the early version drivers here.
if (!part->_mc) {
part->_mc = se->allocateChannel(player->_midi, part->_pri_eff);
if (!part->_mc)
diff --git a/engines/scumm/input.cpp b/engines/scumm/input.cpp
index 51eadfecefe..7c01cf2dfb6 100644
--- a/engines/scumm/input.cpp
+++ b/engines/scumm/input.cpp
@@ -682,7 +682,7 @@ void ScummEngine::waitForBannerInput(int32 waitTime, Common::KeyState &ks, bool
return;
}
- validKey = ks.keycode != Common::KEYCODE_INVALID &&
+ validKey = ks.keycode != Common::KEYCODE_INVALID &&
ks.keycode != Common::KEYCODE_LALT &&
ks.keycode != Common::KEYCODE_RALT &&
ks.keycode != Common::KEYCODE_LCTRL &&
diff --git a/engines/scumm/palette.cpp b/engines/scumm/palette.cpp
index 586cf9757f5..a1cb32557dd 100644
--- a/engines/scumm/palette.cpp
+++ b/engines/scumm/palette.cpp
@@ -62,10 +62,10 @@ void ScummEngine::resetPalette() {
0x85, 0x53, 0x1C, 0x50, 0x3C, 0x00, 0xB4, 0x6B, 0x61, 0x4A, 0x4A, 0x4A,
0x75, 0x75, 0x75, 0xA3, 0xE7, 0x7C, 0x70, 0x64, 0xD6, 0xA3, 0xA3, 0xA3,
#else
- 0x00, 0x00, 0x00, 0xFD, 0xFE, 0xFC, 0xBE, 0x1A, 0x24, 0x30, 0xE6, 0xC6,
- 0xB4, 0x1A, 0xE2, 0x1F, 0xD2, 0x1E, 0x21, 0x1B, 0xAE, 0xDF, 0xF6, 0x0A,
- 0xB8, 0x41, 0x04, 0x6A, 0x33, 0x04, 0xFE, 0x4A, 0x57, 0x42, 0x45, 0x40,
- 0x70, 0x74, 0x6F, 0x59, 0xFE, 0x59, 0x5F, 0x53, 0xFE, 0xA4, 0xA7, 0xA2,
+ 0x00, 0x00, 0x00, 0xFD, 0xFE, 0xFC, 0xBE, 0x1A, 0x24, 0x30, 0xE6, 0xC6,
+ 0xB4, 0x1A, 0xE2, 0x1F, 0xD2, 0x1E, 0x21, 0x1B, 0xAE, 0xDF, 0xF6, 0x0A,
+ 0xB8, 0x41, 0x04, 0x6A, 0x33, 0x04, 0xFE, 0x4A, 0x57, 0x42, 0x45, 0x40,
+ 0x70, 0x74, 0x6F, 0x59, 0xFE, 0x59, 0x5F, 0x53, 0xFE, 0xA4, 0xA7, 0xA2,
#endif
// Use 17 color table for v1 games to allow correct color for inventory and
// sentence line. Original games used some kind of dynamic color table
@@ -125,24 +125,24 @@ void ScummEngine::resetPalette() {
};
static const byte tableAmigaPalette[] = {
- 0x00, 0x00, 0x00, 0x00, 0x00, 0xBB, 0x00, 0xBB, 0x00, 0x00, 0xBB, 0xBB,
- 0xBB, 0x00, 0x00, 0xBB, 0x00, 0xBB, 0xBB, 0x77, 0x00, 0xBB, 0xBB, 0xBB,
- 0x77, 0x77, 0x77, 0x77, 0x77, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0xFF, 0xFF,
- 0xFF, 0x88, 0x88, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xBB, 0x00, 0xBB, 0x00, 0x00, 0xBB, 0xBB,
+ 0xBB, 0x00, 0x00, 0xBB, 0x00, 0xBB, 0xBB, 0x77, 0x00, 0xBB, 0xBB, 0xBB,
+ 0x77, 0x77, 0x77, 0x77, 0x77, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0xFF, 0xFF,
+ 0xFF, 0x88, 0x88, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF
};
static const byte tableAmigaMIPalette[] = {
- 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0x00, 0x88, 0x22, 0x00, 0x66, 0x77,
- 0xBB, 0x66, 0x66, 0xAA, 0x22, 0xAA, 0x88, 0x55, 0x22, 0x77, 0x77, 0x77,
- 0x33, 0x33, 0x33, 0x22, 0x55, 0xDD, 0x22, 0xDD, 0x44, 0x00, 0xCC, 0xFF,
- 0xFF, 0x99, 0x99, 0xFF, 0x55, 0xFF, 0xFF, 0xFF, 0x77, 0xFF, 0xFF, 0xFF
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0x00, 0x88, 0x22, 0x00, 0x66, 0x77,
+ 0xBB, 0x66, 0x66, 0xAA, 0x22, 0xAA, 0x88, 0x55, 0x22, 0x77, 0x77, 0x77,
+ 0x33, 0x33, 0x33, 0x22, 0x55, 0xDD, 0x22, 0xDD, 0x44, 0x00, 0xCC, 0xFF,
+ 0xFF, 0x99, 0x99, 0xFF, 0x55, 0xFF, 0xFF, 0xFF, 0x77, 0xFF, 0xFF, 0xFF
};
static const byte tableEGAPalette[] = {
- 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0x00, 0xAA, 0x00, 0x00, 0xAA, 0xAA,
- 0xAA, 0x00, 0x00, 0xAA, 0x00, 0xAA, 0xAA, 0x55, 0x00, 0xAA, 0xAA, 0xAA,
- 0x55, 0x55, 0x55, 0x55, 0x55, 0xFF, 0x55, 0xFF, 0x55, 0x55, 0xFF, 0xFF,
- 0xFF, 0x55, 0x55, 0xFF, 0x55, 0xFF, 0xFF, 0xFF, 0x55, 0xFF, 0xFF, 0xFF
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0x00, 0xAA, 0x00, 0x00, 0xAA, 0xAA,
+ 0xAA, 0x00, 0x00, 0xAA, 0x00, 0xAA, 0xAA, 0x55, 0x00, 0xAA, 0xAA, 0xAA,
+ 0x55, 0x55, 0x55, 0x55, 0x55, 0xFF, 0x55, 0xFF, 0x55, 0x55, 0xFF, 0xFF,
+ 0xFF, 0x55, 0x55, 0xFF, 0x55, 0xFF, 0xFF, 0xFF, 0x55, 0xFF, 0xFF, 0xFF
};
static const byte _cgaColors[4][12] = {
@@ -153,11 +153,11 @@ void ScummEngine::resetPalette() {
};
static const byte tableHercAPalette[] = {
- 0x00, 0x00, 0x00, 0xAE, 0x69, 0x38
+ 0x00, 0x00, 0x00, 0xAE, 0x69, 0x38
};
static const byte tableHercGPalette[] = {
- 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00
+ 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00
};
// Palette based on Apple IIgs Technical Notes: IIgs 2523063 Master Color Values
@@ -1009,10 +1009,10 @@ void ScummEngine::setShadowPalette(int redScale, int greenScale, int blueScale,
byte egaFindBestMatch(int r, int g, int b) {
// This is almost like the normal EGA palette, but a bit different
static const byte matchPalette[] = {
- 0x00, 0x00, 0x00, 0x00, 0x00, 0xAB, 0x00, 0xAB, 0x00, 0x00, 0xAB, 0xAB,
- 0xAB, 0x00, 0x00, 0xAB, 0x00, 0xAB, 0xAB, 0x57, 0x00, 0xAB, 0xAB, 0xAB,
- 0x57, 0x57, 0x57, 0x57, 0x57, 0xFF, 0x57, 0xFF, 0x57, 0x57, 0xFF, 0xFF,
- 0xFF, 0x57, 0x57, 0xFF, 0x57, 0xFF, 0xFF, 0xFF, 0x57, 0xFF, 0xFF, 0xFF
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0xAB, 0x00, 0xAB, 0x00, 0x00, 0xAB, 0xAB,
+ 0xAB, 0x00, 0x00, 0xAB, 0x00, 0xAB, 0xAB, 0x57, 0x00, 0xAB, 0xAB, 0xAB,
+ 0x57, 0x57, 0x57, 0x57, 0x57, 0xFF, 0x57, 0xFF, 0x57, 0x57, 0xFF, 0xFF,
+ 0xFF, 0x57, 0x57, 0xFF, 0x57, 0xFF, 0xFF, 0xFF, 0x57, 0xFF, 0xFF, 0xFF
};
uint32 best = (uint32)-1;
@@ -1606,7 +1606,7 @@ void ScummEngine::applyGrayscaleToPaletteRange(int min, int max) {
// The original checks for colors to be < 126,
// but we handle things a little bit differently,
// so this is not needed...
- //
+ //
// if (r < 126 || g < 126 || b < 126) {
paletteEntry[0] = average;
paletteEntry[1] = average;
diff --git a/engines/scumm/room.cpp b/engines/scumm/room.cpp
index 5633ec0d295..5dc4dd770e3 100644
--- a/engines/scumm/room.cpp
+++ b/engines/scumm/room.cpp
@@ -126,11 +126,11 @@ void ScummEngine::startScene(int room, Actor *a, int objectNr) {
if (_game.features & GF_SMALL_HEADER)
setDirtyColors(0, 255);
}
-
+
// WORKAROUND: In the CD version of MI1 a certain palette slot (47)
// points to a dark blue color in room 36 (the Marley Mansion outside view).
// The same palette slot points to white in the Floppy VGA version.
- //
+ //
// This is believed to be an oversight in the scripts/datafiles, as it affects:
// - The "Important Notice" sign about how the dogs are only sleeping.
// - The color of some of the stars in the sky.
@@ -138,7 +138,7 @@ void ScummEngine::startScene(int room, Actor *a, int objectNr) {
// It has been noted that the Mac version apparently fixes that on the fly
// within the interpreter, so we do that as well even if kEnhVisualChanges
// is not active.
- //
+ //
// The SEGA CD version points to the correct color, and the FM Towns
// version makes the text more readable by giving it a black outline.
// The Ultimate Talkie version already takes care of that within the data files.
@@ -196,7 +196,7 @@ void ScummEngine::startScene(int room, Actor *a, int objectNr) {
VAR(VAR_CAMERA_MAX_X) = _roomWidth - (_screenWidth / 2);
if (_game.version >= 7) {
- VAR(VAR_CAMERA_MIN_Y) = _screenHeight / 2;
+ VAR(VAR_CAMERA_MIN_Y) = _screenHeight / 2;
VAR(VAR_CAMERA_MAX_Y) = _roomHeight - (_screenHeight / 2);
setCameraAt(_screenWidth / 2, _screenHeight / 2);
} else {
diff --git a/engines/scumm/saveload.cpp b/engines/scumm/saveload.cpp
index ad9bb9d1634..c436d171ba5 100644
--- a/engines/scumm/saveload.cpp
+++ b/engines/scumm/saveload.cpp
@@ -1953,7 +1953,7 @@ void ScummEngine::saveLoadWithSerializer(Common::Serializer &s) {
const char *soundCards[] = {
"PC Speaker", "IBM PCjr/Tandy", "Creative Music System", "AdLib", "Roland MT-32/CM-32L"
};
-
+
GUI::MessageDialog dialog(
Common::U32String::format(_("Warning: incompatible sound settings detected between the current configuration and this saved game.\n\n"
"Current music device: %s (id %d)\nSave file music device: %s (id %d)\n\n"
@@ -1967,7 +1967,7 @@ void ScummEngine::saveLoadWithSerializer(Common::Serializer &s) {
}
// This is again from disasm...
- if (_game.id == GID_TENTACLE) {
+ if (_game.id == GID_TENTACLE) {
for (int j = 0; j < 5; j++)
_scummVars[120 + j] = dottVarsBackup[j];
diff --git a/engines/scumm/script.cpp b/engines/scumm/script.cpp
index c41a0a713ed..252070b228b 100644
--- a/engines/scumm/script.cpp
+++ b/engines/scumm/script.cpp
@@ -59,7 +59,7 @@ void ScummEngine::runScript(int script, bool freezeResistant, bool recursive, in
putOwner(56, VAR(VAR_EGO));
}
- if (!script)
+ if (!script)
return;
if (!recursive)
diff --git a/engines/scumm/script_v6.cpp b/engines/scumm/script_v6.cpp
index 6b31b539b24..bdfe1538a11 100644
--- a/engines/scumm/script_v6.cpp
+++ b/engines/scumm/script_v6.cpp
@@ -3532,7 +3532,7 @@ void ScummEngine_v6::o6_findAllObjects() {
push(readVar(0));
}
-void ScummEngine_v6::shuffleArray(int num, int minIdx, int maxIdx) {
+void ScummEngine_v6::shuffleArray(int num, int minIdx, int maxIdx) {
int rand1, rand2;
int range = maxIdx - minIdx;
int count = range * 2;
diff --git a/engines/scumm/string.cpp b/engines/scumm/string.cpp
index b7edea54a92..7c1272d84df 100644
--- a/engines/scumm/string.cpp
+++ b/engines/scumm/string.cpp
@@ -79,7 +79,7 @@ void ScummEngine::printString(int m, const byte *msg) {
// reactions to Max beating up the scientist run much too quick
// for the animation to match. We get around this by slowing
// down that animation.
- //
+ //
// In the italian CD version, the whole scene is sped up to
// keep up with Sam's speech. We compensate for this by slowing
// down the other animations.
More information about the Scummvm-git-logs
mailing list