[Scummvm-git-logs] scummvm master -> 851430aecfe2c9f37217649cc0479861bf908359
NMIError
noreply at scummvm.org
Sat Feb 22 22:14:32 UTC 2025
This automated email contains information about 4 new commits which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .
Summary:
9789f6914b DARKSEED: Implement floppy version music
ed650e1cf0 AUDIO: Fix OPL channel allocation for rhythm mode
c1e19467a6 DARKSEED: Add music fade-outs
851430aecf DARKSEED: Add floppy music option
Commit: 9789f6914b7af3e384d2fd281e0818f12e5467b2
https://github.com/scummvm/scummvm/commit/9789f6914b7af3e384d2fd281e0818f12e5467b2
Author: Coen Rampen (crampen at gmail.com)
Date: 2025-02-22T23:13:54+01:00
Commit Message:
DARKSEED: Implement floppy version music
Changed paths:
A engines/darkseed/adlib_dsf.cpp
A engines/darkseed/adlib_dsf.h
A engines/darkseed/midiparser_sbr.cpp
A engines/darkseed/midiparser_sbr.h
audio/adlib_ms.cpp
audio/adlib_ms.h
engines/darkseed/adlib_worx.cpp
engines/darkseed/module.mk
engines/darkseed/music.cpp
engines/darkseed/music.h
engines/darkseed/sound.cpp
engines/darkseed/sound.h
diff --git a/audio/adlib_ms.cpp b/audio/adlib_ms.cpp
index d9d60ac6197..07af79619af 100644
--- a/audio/adlib_ms.cpp
+++ b/audio/adlib_ms.cpp
@@ -447,12 +447,13 @@ MidiDriver_ADLIB_Multisource::MidiDriver_ADLIB_Multisource(OPL::Config::OplType
_allocationMode(ALLOCATION_MODE_DYNAMIC),
_instrumentWriteMode(INSTRUMENT_WRITE_MODE_NOTE_ON),
_rhythmModeIgnoreNoteOffs(false),
- _channel10Melodic(false),
+ _rhythmInstrumentMode(RHYTHM_INSTRUMENT_MODE_CHANNEL_10),
_defaultChannelVolume(0),
_noteSelect(NOTE_SELECT_MODE_0),
_modulationDepth(MODULATION_DEPTH_HIGH),
_vibratoDepth(VIBRATO_DEPTH_HIGH),
_rhythmMode(false),
+ _rhythmModeRewriteSharedRegister(false),
_instrumentBank(OPL_INSTRUMENT_BANK),
_rhythmBank(OPL_RHYTHM_BANK),
_rhythmBankFirstNote(GS_RHYTHM_FIRST_NOTE),
@@ -653,13 +654,21 @@ void MidiDriver_ADLIB_Multisource::send(int8 source, uint32 b) {
void MidiDriver_ADLIB_Multisource::noteOff(uint8 channel, uint8 note, uint8 velocity, uint8 source) {
_activeNotesMutex.lock();
- if (_rhythmMode && channel == MIDI_RHYTHM_CHANNEL) {
+ InstrumentInfo instrument = determineInstrument(channel, source, note);
+ // If rhythm mode is on and the note is on the rhythm channel or the
+ // instrument is a rhythm instrument, this note was played using the OPL
+ // rhythm register.
+ bool rhythmNote = _rhythmMode && ((_rhythmInstrumentMode == RHYTHM_INSTRUMENT_MODE_CHANNEL_10 && channel == MIDI_RHYTHM_CHANNEL) ||
+ (_rhythmInstrumentMode == RHYTHM_INSTRUMENT_MODE_RHYTHM_TYPE &&
+ instrument.instrumentDef != nullptr && instrument.instrumentDef->rhythmType != RHYTHM_TYPE_UNDEFINED));
+
+ if (rhythmNote) {
if (!_rhythmModeIgnoreNoteOffs) {
// Find the OPL rhythm instrument playing this note.
for (int i = 0; i < OPL_NUM_RHYTHM_INSTRUMENTS; i++) {
if (_activeRhythmNotes[i].noteActive && _activeRhythmNotes[i].source == source &&
- _activeRhythmNotes[i].note == note) {
- writeKeyOff(0, static_cast<OplInstrumentRhythmType>(i + 1));
+ _activeRhythmNotes[i].channel == channel && _activeRhythmNotes[i].note == note) {
+ writeKeyOff(OPL_RHYTHM_INSTRUMENT_CHANNELS[i], static_cast<OplInstrumentRhythmType>(i + 1));
break;
}
}
@@ -692,14 +701,17 @@ void MidiDriver_ADLIB_Multisource::noteOn(uint8 channel, uint8 note, uint8 veloc
}
InstrumentInfo instrument = determineInstrument(channel, source, note);
- // If rhythm mode is on and the note is on the rhythm channel, this note
- // will be played using the OPL rhythm register.
- bool rhythmNote = _rhythmMode && channel == MIDI_RHYTHM_CHANNEL;
-
- if (!instrument.instrumentDef || instrument.instrumentDef->isEmpty() ||
- (rhythmNote && instrument.instrumentDef->rhythmType == RHYTHM_TYPE_UNDEFINED)) {
+ // If rhythm mode is on and the note is on the rhythm channel or the
+ // instrument is a rhythm instrument, this note will be played using the
+ // OPL rhythm register.
+ bool rhythmNote = _rhythmMode && ((_rhythmInstrumentMode == RHYTHM_INSTRUMENT_MODE_CHANNEL_10 && channel == MIDI_RHYTHM_CHANNEL) ||
+ (_rhythmInstrumentMode == RHYTHM_INSTRUMENT_MODE_RHYTHM_TYPE &&
+ instrument.instrumentDef != nullptr && instrument.instrumentDef->rhythmType != RHYTHM_TYPE_UNDEFINED));
+
+ if (instrument.instrumentDef == nullptr || instrument.instrumentDef->isEmpty() ||
+ (rhythmNote && instrument.instrumentDef->rhythmType == RHYTHM_TYPE_UNDEFINED)) {
// Instrument definition contains no data or it is not suitable for
- // rhythm mode, so the note cannot be played.
+ // a rhythm note, so the note cannot be played.
return;
}
@@ -708,13 +720,14 @@ void MidiDriver_ADLIB_Multisource::noteOn(uint8 channel, uint8 note, uint8 veloc
// Determine the OPL channel to use and the active note data to update.
uint8 oplChannel = 0xFF;
ActiveNote *activeNote = nullptr;
- if (rhythmNote) {
- activeNote = &_activeRhythmNotes[instrument.instrumentDef->rhythmType - 1];
- } else {
- // Allocate a melodic OPL channel.
- oplChannel = allocateOplChannel(channel, source, instrument.instrumentId);
- if (oplChannel != 0xFF)
+ oplChannel = allocateOplChannel(channel, source, instrument.instrumentId);
+ if (oplChannel != 0xFF) {
+ if (rhythmNote) {
+ activeNote = &_activeRhythmNotes[instrument.instrumentDef->rhythmType - 1];
+ }
+ else {
activeNote = &_activeNotes[oplChannel];
+ }
}
if (activeNote != nullptr) {
if (activeNote->noteActive) {
@@ -737,21 +750,22 @@ void MidiDriver_ADLIB_Multisource::noteOn(uint8 channel, uint8 note, uint8 veloc
activeNote->instrumentId = instrument.instrumentId;
activeNote->instrumentDef = instrument.instrumentDef;
- if (_instrumentWriteMode == INSTRUMENT_WRITE_MODE_NOTE_ON) {
+ if (_instrumentWriteMode == INSTRUMENT_WRITE_MODE_NOTE_ON ||
+ (_instrumentWriteMode == INSTRUMENT_WRITE_MODE_FIRST_NOTE_ON &&
+ activeNote->lastWrittenInstrumentId != activeNote->instrumentId)) {
// Write out the instrument definition, volume and panning.
writeInstrument(oplChannel, instrument);
}
- else if (_instrumentWriteMode == INSTRUMENT_WRITE_MODE_FIRST_NOTE_ON) {
- if (activeNote->lastWrittenInstrumentId != activeNote->instrumentId) {
- // Write out the instrument definition, volume and panning.
- writeInstrument(oplChannel, instrument);
+ else {
+ if (_rhythmModeRewriteSharedRegister && rhythmNote && instrument.instrumentDef->rhythmType != RHYTHM_TYPE_BASS_DRUM) {
+ // Rewrite the shared panning / feedback / connection register.
+ writePanning(oplChannel, instrument.instrumentDef->rhythmType);
}
- else {
- // Write out volume, if applicable.
- for (int i = 0; i < activeNote->instrumentDef->getNumberOfOperators(); i++) {
- if (isVolumeApplicableToOperator(*activeNote->instrumentDef, i))
- writeVolume(oplChannel, i);
- }
+
+ // Write out volume, if applicable.
+ for (int i = 0; i < activeNote->instrumentDef->getNumberOfOperators(); i++) {
+ if (isVolumeApplicableToOperator(*activeNote->instrumentDef, i))
+ writeVolume(oplChannel, i, instrument.instrumentDef->rhythmType);
}
}
@@ -822,14 +836,25 @@ void MidiDriver_ADLIB_Multisource::controlChange(uint8 channel, uint8 controller
}
void MidiDriver_ADLIB_Multisource::programChange(uint8 channel, uint8 program, uint8 source) {
- // Just set the MIDI program value; this event does not affect active notes.
+ // Set the MIDI program value. This event does not affect active notes...
_controlData[source][channel].program = program;
- if (_instrumentWriteMode == INSTRUMENT_WRITE_MODE_PROGRAM_CHANGE && !(_rhythmMode && channel == MIDI_RHYTHM_CHANNEL)) {
+ // ...unless instrument write mode on program change is set.
+ if (_instrumentWriteMode == INSTRUMENT_WRITE_MODE_PROGRAM_CHANGE &&
+ !(_rhythmMode && _rhythmInstrumentMode == RHYTHM_INSTRUMENT_MODE_CHANNEL_10 && channel == MIDI_RHYTHM_CHANNEL)) {
InstrumentInfo instrument = determineInstrument(channel, source, 0);
- if (!instrument.instrumentDef || instrument.instrumentDef->isEmpty()) {
- // Instrument definition contains no data.
+ // If rhythm mode is on and the note is on the rhythm channel or the
+ // instrument is a rhythm instrument, notes on this channel will be
+ // played using the OPL rhythm register.
+ bool rhythmNote = _rhythmMode && ((_rhythmInstrumentMode == RHYTHM_INSTRUMENT_MODE_CHANNEL_10 && channel == MIDI_RHYTHM_CHANNEL) ||
+ (_rhythmInstrumentMode == RHYTHM_INSTRUMENT_MODE_RHYTHM_TYPE &&
+ instrument.instrumentDef != nullptr && instrument.instrumentDef->rhythmType != RHYTHM_TYPE_UNDEFINED));
+
+ if (instrument.instrumentDef == nullptr || instrument.instrumentDef->isEmpty() ||
+ (rhythmNote && instrument.instrumentDef->rhythmType == RHYTHM_TYPE_UNDEFINED)) {
+ // Instrument definition contains no data or it is not suitable for
+ // a rhythm note.
return;
}
@@ -838,10 +863,17 @@ void MidiDriver_ADLIB_Multisource::programChange(uint8 channel, uint8 program, u
// Determine the OPL channel to use and the active note data to update.
uint8 oplChannel = 0xFF;
ActiveNote *activeNote = nullptr;
- // Allocate a melodic OPL channel.
oplChannel = allocateOplChannel(channel, source, instrument.instrumentId);
if (oplChannel != 0xFF) {
- activeNote = &_activeNotes[oplChannel];
+ if (rhythmNote) {
+ activeNote = &_activeRhythmNotes[instrument.instrumentDef->rhythmType - 1];
+ }
+ else {
+ activeNote = &_activeNotes[oplChannel];
+ }
+ }
+
+ if (activeNote != nullptr) {
if (activeNote->noteActive) {
// Turn off the note currently playing on this OPL channel or
// rhythm instrument.
@@ -930,6 +962,13 @@ void MidiDriver_ADLIB_Multisource::deinitSource(uint8 source) {
uint8 oplChannel = _melodicChannels[i];
if (_activeNotes[oplChannel].channelAllocated && _activeNotes[oplChannel].source == source) {
_activeNotes[oplChannel].channelAllocated = false;
+ _activeNotes[oplChannel].lastWrittenInstrumentId = -1;
+ }
+ }
+ for (int i = 0; i < 5; i++) {
+ if (_activeRhythmNotes[i].channelAllocated && _activeRhythmNotes[i].source == source) {
+ _activeRhythmNotes[i].channelAllocated = false;
+ _activeRhythmNotes[i].lastWrittenInstrumentId = -1;
}
}
@@ -1376,7 +1415,7 @@ void MidiDriver_ADLIB_Multisource::recalculateVolumes(uint8 channel, uint8 sourc
MidiDriver_ADLIB_Multisource::InstrumentInfo MidiDriver_ADLIB_Multisource::determineInstrument(uint8 channel, uint8 source, uint8 note) {
InstrumentInfo instrument = { 0, nullptr, 0 };
- if (!_channel10Melodic && channel == MIDI_RHYTHM_CHANNEL) {
+ if (_rhythmInstrumentMode == RHYTHM_INSTRUMENT_MODE_CHANNEL_10 && channel == MIDI_RHYTHM_CHANNEL) {
// On the rhythm channel, the note played indicates which instrument
// should be used.
if (note < _rhythmBankFirstNote || note > _rhythmBankLastNote)
diff --git a/audio/adlib_ms.h b/audio/adlib_ms.h
index 01796d0fb50..2a0dcc988f2 100644
--- a/audio/adlib_ms.h
+++ b/audio/adlib_ms.h
@@ -343,6 +343,22 @@ public:
INSTRUMENT_WRITE_MODE_FIRST_NOTE_ON
};
+ enum RhythmInstrumentMode {
+ /**
+ * If rhythm mode is active, any note played on the MIDI rhythm channel
+ * 10 will be played as an OPL rhythm note. The instrument returned by
+ * determineInstrument is expected to be a rhythm instrument.
+ */
+ RHYTHM_INSTRUMENT_MODE_CHANNEL_10,
+ /**
+ * If rhythm mode is active, any note for which determineInstrument
+ * returns a rhythm instrument will be played as an OPL rhythm note.
+ * This will disable the behavior of MIDI channel 10 as the rhythm
+ * channel entirely and treat it as a melodic channel.
+ */
+ RHYTHM_INSTRUMENT_MODE_RHYTHM_TYPE
+ };
+
/**
* The available modes for the OPL note select setting.
*/
@@ -1207,11 +1223,15 @@ protected:
ChannelAllocationMode _allocationMode;
// Controls when the instrument definitions are written.
InstrumentWriteMode _instrumentWriteMode;
+ // In instrument write mode First Note On or Program Change, this flag controls if the Cx register,
+ // which is shared between rhythm mode instrument definitions (except bass drum), is rewritten
+ // before each note on.
+ bool _rhythmModeRewriteSharedRegister;
// Controls response to rhythm note off events when rhythm mode is active.
bool _rhythmModeIgnoreNoteOffs;
- // Controls whether MIDI channel 10 is treated as the rhythm channel or as
- // a melodic channel.
- bool _channel10Melodic;
+ // Controls how rhythm notes are played in OPL rhythm mode and whether MIDI
+ // channel 10 is treated as the rhythm channel or as a melodic channel.
+ RhythmInstrumentMode _rhythmInstrumentMode;
// The default MIDI channel volume (set when opening the driver).
uint8 _defaultChannelVolume;
diff --git a/engines/darkseed/adlib_dsf.cpp b/engines/darkseed/adlib_dsf.cpp
new file mode 100644
index 00000000000..1091aa49b09
--- /dev/null
+++ b/engines/darkseed/adlib_dsf.cpp
@@ -0,0 +1,232 @@
+/* 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 "darkseed/adlib_dsf.h"
+
+namespace Darkseed {
+
+// F-num values used for the 12 octave notes.
+const uint16 MidiDriver_DarkSeedFloppy_AdLib::OPL_NOTE_FREQUENCIES[12] = {
+ 0x157, 0x16B, 0x181, 0x198, 0x1B0, 0x1CA, 0x1E5, 0x202, 0x220, 0x241, 0x263, 0x287
+};
+
+MidiDriver_DarkSeedFloppy_AdLib::MidiDriver_DarkSeedFloppy_AdLib(OPL::Config::OplType oplType, int timerFrequency) :
+ MidiDriver_ADLIB_Multisource::MidiDriver_ADLIB_Multisource(oplType, timerFrequency) {
+
+ _dsfInstrumentBank = new OplInstrumentDefinition[128];
+ _instrumentBank = _dsfInstrumentBank;
+ Common::fill(_sourcePriority, _sourcePriority + sizeof(_sourcePriority), 0);
+
+ _defaultChannelVolume = 0x7F;
+ // Dark Seed uses rhythm instrument definitions with instrument numbers 0x0 - 0xE
+ _rhythmInstrumentMode = RHYTHM_INSTRUMENT_MODE_RHYTHM_TYPE;
+ _instrumentWriteMode = INSTRUMENT_WRITE_MODE_FIRST_NOTE_ON;
+}
+
+MidiDriver_DarkSeedFloppy_AdLib::~MidiDriver_DarkSeedFloppy_AdLib() {
+ delete[] _dsfInstrumentBank;
+}
+
+int MidiDriver_DarkSeedFloppy_AdLib::open() {
+ int result = MidiDriver_ADLIB_Multisource::open();
+ if (result == 0)
+ // Dark Seed has the OPL rhythm mode always on
+ setRhythmMode(true);
+
+ return result;
+}
+
+void MidiDriver_DarkSeedFloppy_AdLib::deinitSource(uint8 source) {
+ MidiDriver_ADLIB_Multisource::deinitSource(source);
+
+ _sourcePriority[source] = 0;
+}
+
+void MidiDriver_DarkSeedFloppy_AdLib::setSourcePriority(uint8 source, uint8 priority) {
+ assert(source < MAXIMUM_SOURCES);
+ _sourcePriority[source] = priority;
+}
+
+void MidiDriver_DarkSeedFloppy_AdLib::loadInstrumentBank(uint8 *instrumentBankData) {
+ // Dark Seed stores instruments in SIT files. Most music tracks have their
+ // own instrument bank, but there are only 2 significantly different banks.
+ // START loads the instrument bank for each of the 8 tracks it plays, but
+ // each bank is effectively the same. TOS only loads the TOS1.SIT
+ // instrument bank; the SIT files from the music tracks it plays are
+ // ignored.
+ //
+ // All SIT files contain 256 instruments in the 16 byte AdLib BNK format.
+ // Only the first 128 instruments are actually loaded; the rest is usually
+ // empty.
+ for (int i = 0; i < 128; i++) {
+ AdLibIbkInstrumentDefinition _ibkInstrument;
+ _ibkInstrument.o0FreqMultMisc = *instrumentBankData++;
+ _ibkInstrument.o1FreqMultMisc = *instrumentBankData++;
+ _ibkInstrument.o0Level = *instrumentBankData++;
+ _ibkInstrument.o1Level = *instrumentBankData++;
+ _ibkInstrument.o0DecayAttack = *instrumentBankData++;
+ _ibkInstrument.o1DecayAttack = *instrumentBankData++;
+ _ibkInstrument.o0ReleaseSustain = *instrumentBankData++;
+ _ibkInstrument.o1ReleaseSustain = *instrumentBankData++;
+ _ibkInstrument.o0WaveformSelect = *instrumentBankData++;
+ _ibkInstrument.o1WaveformSelect = *instrumentBankData++;
+ _ibkInstrument.connectionFeedback = *instrumentBankData++;
+
+ // The first 15 instruments are rhythm instrument definitions, meant
+ // for the 5 OPL rhythm mode instruments. 0-2 are bass drum instruments,
+ // 3-5 are snare drum instruments, etc.
+ uint8 rhythmType = 0;
+ if (i < 15) {
+ rhythmType = 6 + (i / 3);
+ }
+ _ibkInstrument.rhythmType = rhythmType;
+ _ibkInstrument.rhythmNote = 0;
+ _ibkInstrument.transpose = 0;
+
+ _ibkInstrument.toOplInstrumentDefinition(_dsfInstrumentBank[i]);
+
+ // Skip padding bytes
+ instrumentBankData += 5;
+ }
+}
+
+uint8 MidiDriver_DarkSeedFloppy_AdLib::allocateOplChannel(uint8 channel, uint8 source, uint8 instrumentId) {
+ uint8 allocatedChannel = 0xFF;
+
+ _allocationMutex.lock();
+ _activeNotesMutex.lock();
+
+ if (instrumentId <= 0xE) {
+ // The first 15 instruments are rhythm instruments. These get assigned
+ // to the corresponding OPL rhythm instruments.
+ // Note: original code also processes instrument 0xF, leading to
+ // undefined behavior.
+
+ // The order of the rhythm instruments is flipped compared to the order
+ // in the _activeRhythmNotes array.
+ uint8 rhythmInstType = 4 - (instrumentId / 3);
+ allocatedChannel = OPL_RHYTHM_INSTRUMENT_CHANNELS[rhythmInstType];
+ if (_activeRhythmNotes[rhythmInstType].channelAllocated && _activeRhythmNotes[rhythmInstType].source != source) {
+ // OPL rhythm instrument is already allocated
+ if (_sourcePriority[_activeRhythmNotes[rhythmInstType].source] >= _sourcePriority[source]) {
+ // Current source priority is equal to or higher than the new
+ // source priority. Do not re-allocate this rhythm instrument.
+ allocatedChannel = 0xFF;
+ } else {
+ // Current source priority is lower than the new source
+ // priority. Deallocate the channel from the current source.
+ if (_activeRhythmNotes[rhythmInstType].noteActive)
+ writeKeyOff(allocatedChannel, static_cast<OplInstrumentRhythmType>(rhythmInstType + 1));
+ _channelAllocations[_activeRhythmNotes[rhythmInstType].source][_activeRhythmNotes[rhythmInstType].channel] = 0xFF;
+ }
+ }
+
+ if (allocatedChannel != 0xFF) {
+ // Allocate the OPL channel to the source and rhythm instrument.
+ _activeRhythmNotes[rhythmInstType].channelAllocated = true;
+ _activeRhythmNotes[rhythmInstType].source = source;
+ _activeRhythmNotes[rhythmInstType].channel = channel;
+ }
+ }
+ else {
+ // For melodic instruments, the following OPL channel is allocated:
+ // - The OPL channel already allocated to this data channel.
+ // - The OPL channel with the lowest priority, if it is lower than the
+ // priority of the new source.
+ uint8 lowestPriority = 0x64;
+ for (int i = 0; i < _numMelodicChannels; i++) {
+ uint8 oplChannel = _melodicChannels[i];
+ if (_activeNotes[oplChannel].channelAllocated && _activeNotes[oplChannel].source == source && _activeNotes[oplChannel].channel == channel) {
+ // This OPL channel is already allocated to this source and
+ // data channel. Use this OPL channel.
+ allocatedChannel = oplChannel;
+ lowestPriority = 0;
+ break;
+ }
+ // Unallocated channels are treated as having priority 0, the
+ // lowest priority.
+ uint8 currentChannelPriority = !_activeNotes[oplChannel].channelAllocated ? 0 : _sourcePriority[_activeNotes[oplChannel].source];
+ if (currentChannelPriority < lowestPriority) {
+ // Found an OPL channel with a lower priority than the
+ // previously found OPL channel.
+ allocatedChannel = i;
+ lowestPriority = currentChannelPriority;
+ }
+ }
+
+ if (_sourcePriority[source] <= lowestPriority) {
+ // New source priority is lower than the lowest found OPL channel
+ // priority. Do not re-allocate this OPL channel.
+ allocatedChannel = 0xFF;
+ }
+
+ if (allocatedChannel != 0xFF) {
+ if (_activeNotes[allocatedChannel].channelAllocated && _activeNotes[allocatedChannel].source != source) {
+ // OPL channel is already allocated. De-allocate it.
+ if (_activeNotes[allocatedChannel].noteActive)
+ writeKeyOff(allocatedChannel);
+ _channelAllocations[_activeRhythmNotes[allocatedChannel].source][_activeRhythmNotes[allocatedChannel].channel] = 0xFF;
+ }
+
+ // Allocate the OPL channel to the source and data channel.
+ _activeNotes[allocatedChannel].channelAllocated = true;
+ _activeNotes[allocatedChannel].source = source;
+ _activeNotes[allocatedChannel].channel = channel;
+ }
+ }
+
+ if (allocatedChannel != 0xFF) {
+ _channelAllocations[source][channel] = allocatedChannel;
+ }
+
+ _allocationMutex.unlock();
+ _activeNotesMutex.unlock();
+
+ return allocatedChannel;
+}
+
+uint16 MidiDriver_DarkSeedFloppy_AdLib::calculateFrequency(uint8 channel, uint8 source, uint8 note) {
+ uint8 octaveNote = ((note >= 120) ? 0x7B : (note % 12));
+ uint8 block;
+ if (note < 12) {
+ block = 0;
+ } else if (note >= 108) {
+ block = 7;
+ } else {
+ block = (note / 12) - 1;
+ }
+ uint16 fnum = OPL_NOTE_FREQUENCIES[octaveNote];
+
+ return fnum | (block << 10);
+}
+
+uint8 MidiDriver_DarkSeedFloppy_AdLib::calculateUnscaledVolume(uint8 channel, uint8 source, uint8 velocity, const OplInstrumentDefinition &instrumentDef, uint8 operatorNum) {
+ uint8 instrumentLevel = instrumentDef.getOperatorDefinition(operatorNum).level & 0x3F;
+ if (instrumentDef.getNumberOfOperators() >= 2 && operatorNum == 0) {
+ // For operator 0 of a 2 operator instrument, the level from the
+ // instrument definition is used without scaling, even if the
+ // connection type is additive.
+ return instrumentLevel;
+ }
+ return 0x3F - (((velocity + 0x80) * (0x3F - instrumentLevel)) >> 8);
+}
+
+} // namespace Darkseed
diff --git a/engines/darkseed/adlib_dsf.h b/engines/darkseed/adlib_dsf.h
new file mode 100644
index 00000000000..488aeff36ec
--- /dev/null
+++ b/engines/darkseed/adlib_dsf.h
@@ -0,0 +1,61 @@
+/* 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 DARKSEED_ADLIB_DSF_H
+#define DARKSEED_ADLIB_DSF_H
+
+#include "audio/adlib_ms.h"
+
+namespace Darkseed {
+
+/**
+ * Implementation of the AdLib code used by Dark Seed (floppy version).
+ * The game uses a combination of the Creative FM-Music Functions library and
+ * its own sound code.
+ */
+class MidiDriver_DarkSeedFloppy_AdLib : public MidiDriver_ADLIB_Multisource {
+protected:
+ static const uint16 OPL_NOTE_FREQUENCIES[12];
+
+public:
+ MidiDriver_DarkSeedFloppy_AdLib(OPL::Config::OplType oplType, int timerFrequency = OPL::OPL::kDefaultCallbackFrequency);
+ ~MidiDriver_DarkSeedFloppy_AdLib();
+
+ int open() override;
+
+ void deinitSource(uint8 source) override;
+
+ void setSourcePriority(uint8 source, uint8 priority);
+
+ void loadInstrumentBank(uint8 *instrumentBankData);
+
+protected:
+ uint8 allocateOplChannel(uint8 channel, uint8 source, uint8 instrumentId) override;
+ uint16 calculateFrequency(uint8 channel, uint8 source, uint8 note) override;
+ uint8 calculateUnscaledVolume(uint8 channel, uint8 source, uint8 velocity, const OplInstrumentDefinition &instrumentDef, uint8 operatorNum) override;
+
+ uint8 _sourcePriority[MAXIMUM_SOURCES];
+ OplInstrumentDefinition *_dsfInstrumentBank;
+};
+
+} // namespace Darkseed
+
+#endif // DARKSEED_ADLIB_DSF_H
diff --git a/engines/darkseed/adlib_worx.cpp b/engines/darkseed/adlib_worx.cpp
index 976abeca229..376051e3778 100644
--- a/engines/darkseed/adlib_worx.cpp
+++ b/engines/darkseed/adlib_worx.cpp
@@ -194,7 +194,7 @@ MidiDriver_Worx_AdLib::MidiDriver_Worx_AdLib(OPL::Config::OplType oplType, int t
_instrumentBank = instrumentBank;
_defaultChannelVolume = 0x7F;
- _channel10Melodic = true;
+ _rhythmInstrumentMode = RHYTHM_INSTRUMENT_MODE_RHYTHM_TYPE;
_instrumentWriteMode = INSTRUMENT_WRITE_MODE_FIRST_NOTE_ON;
}
diff --git a/engines/darkseed/midiparser_sbr.cpp b/engines/darkseed/midiparser_sbr.cpp
new file mode 100644
index 00000000000..18c83af61b5
--- /dev/null
+++ b/engines/darkseed/midiparser_sbr.cpp
@@ -0,0 +1,297 @@
+/* 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 "darkseed/midiparser_sbr.h"
+
+#include "audio/mididrv.h"
+
+namespace Darkseed {
+
+MidiParser_SBR::MidiParser_SBR(int8 source, bool sfx) : MidiParser_SMF(source), _sfx(sfx) {
+ Common::fill(_trackDeltas, _trackDeltas + ARRAYSIZE(_trackDeltas), 0);
+ Common::fill(_trackInstruments, _trackInstruments + ARRAYSIZE(_trackInstruments), 0xFF);
+ Common::fill(_trackNoteActive, _trackNoteActive + ARRAYSIZE(_trackNoteActive), 0xFF);
+ Common::fill(_trackLoopCounter, _trackLoopCounter + ARRAYSIZE(_trackLoopCounter), 0);
+
+ // SBR uses a fixed tempo.
+ _ppqn = 96;
+ setTempo(1318200);
+}
+
+void MidiParser_SBR::parseNextEvent(EventInfo &info) {
+ uint8 subtrack = info.subtrack;
+ byte *parsePos = _position._subtracks[subtrack]._playPos;
+ uint8 *start = parsePos;
+
+ /**
+ * SBR uses 6 byte structures to represent events:
+ * - Event type / note
+ * - Note velocity
+ * - (unused)
+ * - Delta (word, little-endian)
+ * - Instrument
+ * Delta is ticks to the next event, not preceding this event like SMF.
+ *
+ * The following event types are used:
+ * - 0x00: End of track. This is the only byte in this event.
+ * - 0x01: Subtrack list. See loadMusic.
+ * - 0x02: Rest. Effectively a note off and a delta to the next event.
+ * - 0x03: Sample. Indicates the sample filename shoud be read from the
+ * corresponding DIG file entry. See Sound class.
+ * - 0x05: Restart playback from the beginning of the subtrack.
+ * - 0x06: Loop. Velocity specifies the number of events to jump back.
+ * Delta specifies the number of times to repeat this section.
+ * - 0x24+: Note on.
+ */
+ info.start = start;
+ info.length = 0;
+ info.noop = false;
+ info.loop = false;
+ info.delta = _trackDeltas[subtrack];
+ _trackDeltas[subtrack] = 0;
+
+ // Any event will turn off the active note on this subtrack.
+ if (_trackNoteActive[subtrack] != 0xFF) {
+ info.event = 0x80 | subtrack;
+ info.basic.param1 = _trackNoteActive[subtrack];
+ info.basic.param2 = 0x00;
+ _trackNoteActive[subtrack] = 0xFF;
+ return;
+ }
+
+ if (parsePos == nullptr || parsePos[0] == 0) {
+ // Reached the end of the track. Generate an end-of-track event.
+ info.event = 0xFF;
+ info.ext.type = MidiDriver::MIDI_META_END_OF_TRACK;
+ info.ext.data = parsePos;
+ return;
+ }
+
+ // There are more MIDI events in this track.
+ uint8 noteType = parsePos[0];
+ if (noteType == 5) {
+ // Subtrack needs to be restarted. Generate a custom meta event.
+ info.event = 0xFF;
+ info.ext.type = 5;
+ info.loop = true;
+ return;
+ }
+
+ if (noteType == 6) {
+ // Subtrack needs to be looped. Generate a custom meta event.
+ info.event = 0xFF;
+ info.ext.type = 6;
+ info.ext.data = parsePos;
+ info.loop = true;
+ // Delta needs to be one more than specified due to differences in the
+ // way this event is processed compared to the original code.
+ info.delta++;
+ return;
+ }
+
+ // Check if the instrument specified in this event is different from the
+ // current instrument for this track. Typically, all events in a subtrack
+ // have the same instrument.
+ uint8 instrument = parsePos[5];
+ if (_trackInstruments[subtrack] != instrument) {
+ if (_trackInstruments[subtrack] <= 0xF && (_trackInstruments[subtrack] / 3) != (instrument / 3)) {
+ // WORKAROUND The current instrument for this subtrack is a rhythm
+ // instrument, and the instrument specified in this event is for a
+ // different rhythm instrument type. This occurs a few times in
+ // the Dark Seed SBR files. The instrument for the offending events
+ // is 0, probably by mistake. The original code will allocate a
+ // subtrack to an OPL rhythm instrument based on the first event
+ // and then never change it, even when an event with an instrument
+ // with a different rhythm type is encountered. Instead, it will
+ // write the new instrument definition to the originally allocated
+ // OPL rhythm instrument, even if it has the wrong rhythm type.
+ // The ScummVM code will change the allocation to the rhythm
+ // instrument indicated by the instrument on the new event, which
+ // causes incorrect playback, because typically there already is
+ // a subtrack allocated to this instrument.
+ // To fix this, the incorrect instrument on this event is simply
+ // ignored.
+ }
+ else {
+ // The instrument on this event is different from the current
+ // instrument. Generate a program change event.
+ _trackInstruments[subtrack] = instrument;
+ info.event = 0xC0 | subtrack;
+ info.basic.param1 = instrument;
+ info.basic.param2 = 0;
+ return;
+ }
+ }
+
+ if (noteType >= 24) {
+ // Note on.
+ info.event = 0x90 | subtrack;
+ info.basic.param1 = noteType;
+ info.basic.param2 = parsePos[1];
+ _trackNoteActive[subtrack] = noteType;
+ }
+ else {
+ // For rest events, nothing needs to be done other than turning off
+ // the current note (already done above) and process the delta.
+ // Subtrack list and sample events are not processed here.
+ info.noop = true;
+ }
+ if (noteType == 2 || noteType >= 24) {
+ // Delta to the next event is only processed for
+ // note on and rest events.
+ _trackDeltas[subtrack] = READ_LE_UINT16(parsePos + 3);
+ }
+
+ // Set play position to the start of the next event.
+ _position._subtracks[subtrack]._playPos += 6;
+}
+
+bool MidiParser_SBR::processEvent(const EventInfo &info, bool fireEvents) {
+ // Handle custom meta events here.
+ if (info.event == 0xFF) {
+ uint8 subtrack = info.subtrack;
+ if (info.ext.type == 5) {
+ // Restart. Set play position to the beginning of the subtrack.
+ _position._subtracks[subtrack]._playPos = _tracks[_activeTrack][subtrack];
+ return true;
+ }
+ else if (info.ext.type == 6) {
+ // Loop.
+ bool loop = false;
+ if (_trackLoopCounter[subtrack] > 0) {
+ // A loop iteration has completed.
+ _trackLoopCounter[subtrack]--;
+ if (_trackLoopCounter[subtrack] > 0) {
+ // There are more iterations remaining.
+ loop = true;
+ }
+ else {
+ // Loop has finished. Playback will resume at the event
+ // after the loop event.
+ _position._subtracks[subtrack]._playPos += 6;
+ }
+ }
+ else {
+ // Initialize the loop. Read number of iterations from the
+ // event delta.
+ _trackLoopCounter[subtrack] = READ_LE_UINT16(info.ext.data + 3);
+ loop = true;
+ }
+ if (loop) {
+ // Set the play position back by the number of events indicated
+ // by the event velocity.
+ _position._subtracks[subtrack]._playPos -= ((info.ext.data[1]) * 6);
+ }
+ return true;
+ }
+ }
+
+ // All other events are handled like SMF events.
+ return MidiParser::processEvent(info, fireEvents);
+}
+
+void MidiParser_SBR::onTrackStart(uint8 track) {
+ Common::fill(_trackDeltas, _trackDeltas + ARRAYSIZE(_trackDeltas), 0);
+ Common::fill(_trackInstruments, _trackInstruments + ARRAYSIZE(_trackInstruments), 0xFF);
+ Common::fill(_trackNoteActive, _trackNoteActive + ARRAYSIZE(_trackNoteActive), 0xFF);
+ Common::fill(_trackLoopCounter, _trackLoopCounter + ARRAYSIZE(_trackLoopCounter), 0);
+}
+
+bool MidiParser_SBR::loadMusic(byte *data, uint32 size) {
+ assert(size > 0);
+
+ unloadMusic();
+
+ // SBR files typically contain 120 tracks; some have 100.
+ // Music files only use the first 10. SFX files use the remaining 110.
+ uint8 startTrack = _sfx ? 10 : 0;
+ uint8 endTrack = _sfx ? 120 : 10;
+
+ // Read all the tracks. These consist of 0 or more 6 byte events,
+ // terminated by a 00 byte.
+ uint16 bytesRead = 0;
+ for (int i = 0; i < endTrack; i++) {
+ byte *startOfTrack = data;
+ uint16 trackSize = 0;
+
+ bool foundEndOfTrack = false;
+ while (bytesRead < size) {
+ uint8 eventType = data[0];
+ if (eventType == 0) {
+ foundEndOfTrack = true;
+ data++;
+ trackSize++;
+ bytesRead++;
+ break;
+ }
+ else {
+ data += 6;
+ trackSize += 6;
+ bytesRead += 6;
+ }
+ }
+
+ if (!foundEndOfTrack) {
+ // Some files have less than 120 tracks
+ endTrack = i;
+ break;
+ }
+ else if (i < startTrack) {
+ _tracks[i][0] = nullptr;
+ }
+ else {
+ _tracks[i][0] = startOfTrack;
+ }
+ }
+ _numTracks = endTrack;
+
+ // Look for tracks starting with a subtrack list event (type 01).
+ // These are tracks consisting of multiple parallel subtracks.
+ // Music files typically have a subtrack list in track 0. Some SFX use
+ // multiple subtracks as well.
+ for (int i = 0; i < _numTracks; i++) {
+ if (_tracks[i][0] != nullptr && _tracks[i][0][0] == 0x01) {
+ // Read the subtrack list. This is a variable-length list of
+ // subtrack indices, terminated by a 00 byte.
+ // (The track is padded with garbage and terminated by another
+ // 00 byte to match the x * 6 byte event format used by all tracks.)
+ uint8 *tracklist = _tracks[i][0] + 1;
+ uint8 subtrackIndex = 0;
+ while (*tracklist != 0) {
+ _tracks[i][subtrackIndex++] = _tracks[*tracklist][0];
+ tracklist++;
+ }
+ _numSubtracks[i] = subtrackIndex;
+ }
+ else {
+ // This is a track containing just a single subtrack.
+ _numSubtracks[i] = 1;
+ }
+ }
+
+ _disableAutoStartPlayback = true;
+ resetTracking();
+
+ setTrack(0);
+ return true;
+}
+
+} // End of namespace Darkseed
diff --git a/engines/darkseed/midiparser_sbr.h b/engines/darkseed/midiparser_sbr.h
new file mode 100644
index 00000000000..6738680a142
--- /dev/null
+++ b/engines/darkseed/midiparser_sbr.h
@@ -0,0 +1,52 @@
+/* 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 DARKSEED_MIDIPARSER_SBR_H
+#define DARKSEED_MIDIPARSER_SBR_H
+
+#include "audio/midiparser_smf.h"
+
+namespace Darkseed {
+
+/**
+ * MIDI parser for the SBR format used by Dark Seed floppy version.
+ */
+class MidiParser_SBR : public MidiParser_SMF {
+public:
+ MidiParser_SBR(int8 source = -1, bool sfx = false);
+
+ bool loadMusic(byte *data, uint32 size) override;
+
+protected:
+ void parseNextEvent(EventInfo &info) override;
+ bool processEvent(const EventInfo &info, bool fireEvents = true) override;
+ void onTrackStart(uint8 track) override;
+
+ bool _sfx = false;
+ uint8 _trackInstruments[10];
+ uint16 _trackDeltas[10];
+ uint8 _trackNoteActive[10];
+ uint16 _trackLoopCounter[10];
+};
+
+} // End of namespace Darkseed
+
+#endif
diff --git a/engines/darkseed/module.mk b/engines/darkseed/module.mk
index e594df1b521..ea88ab8d13c 100644
--- a/engines/darkseed/module.mk
+++ b/engines/darkseed/module.mk
@@ -1,6 +1,7 @@
MODULE := engines/darkseed
MODULE_OBJS = \
+ adlib_dsf.o \
adlib_worx.o \
animation.o \
anm.o \
@@ -18,6 +19,7 @@ MODULE_OBJS = \
langtext.o \
menu.o \
metaengine.o \
+ midiparser_sbr.o \
morph.o \
music.o \
nsp.o \
diff --git a/engines/darkseed/music.cpp b/engines/darkseed/music.cpp
index 693896fdf69..8034ab967c1 100644
--- a/engines/darkseed/music.cpp
+++ b/engines/darkseed/music.cpp
@@ -21,32 +21,39 @@
#include "darkseed/music.h"
#include "darkseed/darkseed.h"
+#include "darkseed/midiparser_sbr.h"
namespace Darkseed {
-MusicPlayer::MusicPlayer(DarkseedEngine* vm) :
+MusicPlayer::MusicPlayer(DarkseedEngine* vm, bool useFloppyMusic) :
_vm(vm),
_driver(nullptr),
+ _floppyAdLibDriver(nullptr),
_paused(false),
_deviceType(MT_NULL),
_parser(nullptr),
- _musicData(nullptr) {
+ _musicData(nullptr),
+ _tosInstrumentBankData(nullptr),
+ _tosInstrumentBankLoaded(false),
+ _useFloppyMusic(useFloppyMusic) {
}
MusicPlayer::~MusicPlayer() {
stop();
- if (_driver) {
+ if (_driver != nullptr) {
_driver->setTimerCallback(nullptr, nullptr);
_driver->close();
}
Common::StackLock lock(_mutex);
- if (_parser)
+ if (_parser != nullptr)
delete _parser;
- if (_musicData)
+ if (_musicData != nullptr)
delete[] _musicData;
- if (_driver) {
+ if (_tosInstrumentBankData != nullptr)
+ delete[] _tosInstrumentBankData;
+ if (_driver != nullptr) {
delete _driver;
_driver = nullptr;
}
@@ -59,9 +66,20 @@ int MusicPlayer::open() {
MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(devFlags);
_deviceType = MidiDriver::getMusicType(dev);
- if (!_vm->isCdVersion()) {
- // TODO Initialize driver and parser for floppy version
- _driver = new MidiDriver_NULL_Multisource();
+ if (_useFloppyMusic) {
+ switch (_deviceType) {
+ case MT_ADLIB:
+ _floppyAdLibDriver = new MidiDriver_DarkSeedFloppy_AdLib(OPL::Config::kOpl2);
+ _driver = _floppyAdLibDriver;
+ break;
+ case MT_PCSPK:
+ // TODO Implement PC speaker driver
+ default:
+ _driver = new MidiDriver_NULL_Multisource();
+ break;
+ }
+
+ _parser = new MidiParser_SBR(0);
} else {
switch (_deviceType) {
case MT_ADLIB:
@@ -157,7 +175,7 @@ void MusicPlayer::setLoop(bool loop) {
_parser->property(MidiParser::mpAutoLoop, loop);
}
-void MusicPlayer::load(Common::SeekableReadStream *in, int32 size) {
+void MusicPlayer::load(Common::SeekableReadStream *in, int32 size, bool sfx) {
Common::StackLock lock(_mutex);
if (!_parser)
@@ -189,13 +207,67 @@ void MusicPlayer::load(Common::SeekableReadStream *in, int32 size) {
_parser->loadMusic(_musicData, size);
}
-void MusicPlayer::play(bool loop) {
+void MusicPlayer::loadTosInstrumentBankData(Common::SeekableReadStream* in, int32 size) {
+ if (size != 4096) {
+ warning("MusicPlayer::loadTosInstrumentBankData - Specified instrument bank has unexpected size %d", size);
+ return;
+ }
+
+ if (_tosInstrumentBankData != nullptr)
+ delete[] _tosInstrumentBankData;
+
+ _tosInstrumentBankData = new byte[size];
+ in->read(_tosInstrumentBankData, size);
+}
+
+void MusicPlayer::loadTosInstrumentBank() {
+ if (_floppyAdLibDriver == nullptr) {
+ warning("MusicPlayer::loadTosInstrumentBank - Driver does not support instrument banks");
+ return;
+ }
+ if (_tosInstrumentBankData == nullptr) {
+ warning("MusicPlayer::loadTosInstrumentBank - TOS instrument bank data has not been loaded");
+ return;
+ }
+
+ if (!_tosInstrumentBankLoaded) {
+ if (isPlaying())
+ stop();
+ _floppyAdLibDriver->loadInstrumentBank(_tosInstrumentBankData);
+ _tosInstrumentBankLoaded = true;
+ }
+}
+
+void MusicPlayer::loadInstrumentBank(Common::SeekableReadStream *in, int32 size) {
+ if (_floppyAdLibDriver == nullptr) {
+ warning("MusicPlayer::loadInstrumentBank - Driver does not support instrument banks");
+ return;
+ }
+ if (size != 4096) {
+ warning("MusicPlayer::loadInstrumentBank - Specified instrument bank has unexpected size %d", size);
+ return;
+ }
+
+ byte *instrumentBankData = new byte[size];
+ in->read(instrumentBankData, size);
+
+ if (isPlaying())
+ stop();
+ _floppyAdLibDriver->loadInstrumentBank(instrumentBankData);
+ _tosInstrumentBankLoaded = false;
+}
+
+void MusicPlayer::play(uint8 priority, bool loop) {
Common::StackLock lock(_mutex);
if (!_parser)
return;
- _parser->property(MidiParser::mpAutoLoop, loop);
+ if (!_useFloppyMusic)
+ _parser->property(MidiParser::mpAutoLoop, loop);
+ if (_floppyAdLibDriver != nullptr)
+ _floppyAdLibDriver->setSourcePriority(0, priority);
+
_parser->startPlaying();
}
diff --git a/engines/darkseed/music.h b/engines/darkseed/music.h
index b04184e10f0..ab7f1545c30 100644
--- a/engines/darkseed/music.h
+++ b/engines/darkseed/music.h
@@ -22,6 +22,7 @@
#ifndef DARKSEED_MUSIC_H
#define DARKSEED_MUSIC_H
+#include "darkseed/adlib_dsf.h"
#include "darkseed/adlib_worx.h"
#include "audio/mididrv_ms.h"
@@ -38,23 +39,30 @@ protected:
Common::Mutex _mutex;
MidiDriver_Multisource *_driver;
+ MidiDriver_DarkSeedFloppy_AdLib *_floppyAdLibDriver;
MidiParser *_parser;
byte *_musicData;
+ byte *_tosInstrumentBankData;
+ bool _tosInstrumentBankLoaded;
+ bool _useFloppyMusic;
bool _paused;
static void onTimer(void *data);
public:
- MusicPlayer(DarkseedEngine *vm);
+ MusicPlayer(DarkseedEngine *vm, bool useFloppyMusic);
~MusicPlayer();
int open();
- void load(Common::SeekableReadStream *in, int32 size = -1);
+ void load(Common::SeekableReadStream *in, int32 size = -1, bool sfx = false);
+ void loadTosInstrumentBankData(Common::SeekableReadStream *in, int32 size = -1);
+ void loadTosInstrumentBank();
+ void loadInstrumentBank(Common::SeekableReadStream *in, int32 size = -1);
- void play(bool loop = false);
+ void play(uint8 priority = 0xFF, bool loop = false);
void setLoop(bool loop);
bool isPlaying();
void stop();
diff --git a/engines/darkseed/sound.cpp b/engines/darkseed/sound.cpp
index b0657a80829..dbeb561f1dd 100644
--- a/engines/darkseed/sound.cpp
+++ b/engines/darkseed/sound.cpp
@@ -133,7 +133,9 @@ static constexpr char sfxCDFilenameTbl[][14] = {
};
Sound::Sound(Audio::Mixer *mixer) : _mixer(mixer) {
- _musicPlayer = new MusicPlayer(g_engine);
+ // TODO Add config setting for CD version with floppy music
+ _useFloppyMusic = !g_engine->isCdVersion();
+ _musicPlayer = new MusicPlayer(g_engine, _useFloppyMusic);
_didSpeech.resize(978);
resetSpeech();
}
@@ -143,6 +145,16 @@ Sound::~Sound() {
}
int Sound::init() {
+ Common::File file;
+ Common::Path path = Common::Path("tos1.sit");
+ if (file.open(path)) {
+ _musicPlayer->loadTosInstrumentBankData(&file, (int32)file.size());
+ }
+ else {
+ debug("Failed to load TOS instrument bank data %s", path.toString().c_str());
+ }
+ file.close();
+
return _musicPlayer->open();
}
@@ -195,27 +207,49 @@ void Sound::playMusic(MusicId musicId, bool loop) {
return;
}
int filenameIdx = static_cast<uint8>(musicId) - 1;
- playMusic(g_engine->isCdVersion()
- ? musicDosCDFilenameTbl[filenameIdx]
- : musicDosFloppyFilenameTbl[filenameIdx],
- loop);
+ playMusic(_useFloppyMusic ?
+ Common::String(musicDosFloppyFilenameTbl[filenameIdx]) + ".sbr" : musicDosCDFilenameTbl[filenameIdx],
+ nullptr, 6, loop);
}
void Sound::playMusic(StartMusicId musicId) {
int filenameIdx = static_cast<uint8>(musicId);
- playMusic(g_engine->isCdVersion()
- ? startMusicDosCDFilenameTbl[filenameIdx]
- : startMusicDosFloppyFilenameTbl[filenameIdx]);
+ if (_useFloppyMusic) {
+ Common::String const &filenameBase = startMusicDosFloppyFilenameTbl[filenameIdx];
+ Common::String const &filenameSbr = filenameBase + ".sbr";
+ Common::String const &filenameSit = filenameBase + ".sit";
+
+ playMusic(filenameSbr, &filenameSit, 5);
+ }
+ else {
+ playMusic(startMusicDosCDFilenameTbl[filenameIdx]);
+ }
}
-void Sound::playMusic(Common::String const &filename, bool loop) {
- debug("Loading music: %s", filename.c_str());
+void Sound::playMusic(Common::String const &musicFilename, Common::String const *instrBankFilename, uint8 priority, bool loop) {
Common::File file;
Common::Path path;
- if (!g_engine->isCdVersion()) {
- path = Common::Path(filename);
+ if (_useFloppyMusic) {
+ if (instrBankFilename != nullptr) {
+ debug("Loading instrument bank: %s", instrBankFilename->c_str());
+ path = Common::Path(instrBankFilename->c_str());
+ if (!file.open(path)) {
+ debug("Failed to load %s", path.toString().c_str());
+ return;
+ }
+ _musicPlayer->loadInstrumentBank(&file, (int32)file.size());
+ file.close();
+ }
+ else {
+ debug("Loading TOS instrument bank");
+ _musicPlayer->loadTosInstrumentBank();
+ }
+ }
+ debug("Loading music: %s", musicFilename.c_str());
+ if (_useFloppyMusic) {
+ path = Common::Path(musicFilename);
} else {
- path = Common::Path("sound").join(filename);
+ path = Common::Path("sound").join(musicFilename);
}
if (!file.open(path)) {
debug("Failed to load %s", path.toString().c_str());
@@ -224,7 +258,7 @@ void Sound::playMusic(Common::String const &filename, bool loop) {
_musicPlayer->load(&file, (int32)file.size());
file.close();
- _musicPlayer->play(loop);
+ _musicPlayer->play(priority, loop);
}
void Sound::stopMusic() {
diff --git a/engines/darkseed/sound.h b/engines/darkseed/sound.h
index cec4bdd3eab..8237ef34d61 100644
--- a/engines/darkseed/sound.h
+++ b/engines/darkseed/sound.h
@@ -63,6 +63,7 @@ class Sound {
Audio::SoundHandle _sfxHandle;
MusicPlayer *_musicPlayer;
Common::Array<uint8> _didSpeech;
+ bool _useFloppyMusic;
public:
explicit Sound(Audio::Mixer *mixer);
@@ -81,7 +82,7 @@ public:
void resetSpeech();
void playMusic(MusicId musicId, bool loop = true);
void playMusic(StartMusicId musicId);
- void playMusic(Common::String const &filename, bool loop = false);
+ void playMusic(Common::String const &filename, Common::String const *instrBankFilename = nullptr, uint8 priority = 0xFF, bool loop = false);
void stopMusic();
void playSfx(uint8 sfxId, int unk1, int unk2);
void stopSfx();
Commit: ed650e1cf031c954a3cefd4e7c07355543419f29
https://github.com/scummvm/scummvm/commit/ed650e1cf031c954a3cefd4e7c07355543419f29
Author: Coen Rampen (crampen at gmail.com)
Date: 2025-02-22T23:13:54+01:00
Commit Message:
AUDIO: Fix OPL channel allocation for rhythm mode
Changed paths:
audio/adlib_ms.cpp
audio/adlib_ms.h
engines/agos/drivers/accolade/adlib.cpp
engines/agos/drivers/accolade/adlib.h
engines/agos/drivers/simon1/adlib.cpp
engines/agos/drivers/simon1/adlib.h
engines/darkseed/adlib_dsf.cpp
engines/darkseed/adlib_dsf.h
engines/darkseed/adlib_worx.cpp
engines/darkseed/adlib_worx.h
engines/darkseed/midiparser_sbr.cpp
engines/ultima/nuvie/sound/mididrv_m_adlib.cpp
engines/ultima/nuvie/sound/mididrv_m_adlib.h
diff --git a/audio/adlib_ms.cpp b/audio/adlib_ms.cpp
index 07af79619af..689707dcc86 100644
--- a/audio/adlib_ms.cpp
+++ b/audio/adlib_ms.cpp
@@ -720,7 +720,7 @@ void MidiDriver_ADLIB_Multisource::noteOn(uint8 channel, uint8 note, uint8 veloc
// Determine the OPL channel to use and the active note data to update.
uint8 oplChannel = 0xFF;
ActiveNote *activeNote = nullptr;
- oplChannel = allocateOplChannel(channel, source, instrument.instrumentId);
+ oplChannel = allocateOplChannel(channel, source, instrument);
if (oplChannel != 0xFF) {
if (rhythmNote) {
activeNote = &_activeRhythmNotes[instrument.instrumentDef->rhythmType - 1];
@@ -843,27 +843,23 @@ void MidiDriver_ADLIB_Multisource::programChange(uint8 channel, uint8 program, u
if (_instrumentWriteMode == INSTRUMENT_WRITE_MODE_PROGRAM_CHANGE &&
!(_rhythmMode && _rhythmInstrumentMode == RHYTHM_INSTRUMENT_MODE_CHANNEL_10 && channel == MIDI_RHYTHM_CHANNEL)) {
InstrumentInfo instrument = determineInstrument(channel, source, 0);
-
- // If rhythm mode is on and the note is on the rhythm channel or the
- // instrument is a rhythm instrument, notes on this channel will be
- // played using the OPL rhythm register.
- bool rhythmNote = _rhythmMode && ((_rhythmInstrumentMode == RHYTHM_INSTRUMENT_MODE_CHANNEL_10 && channel == MIDI_RHYTHM_CHANNEL) ||
- (_rhythmInstrumentMode == RHYTHM_INSTRUMENT_MODE_RHYTHM_TYPE &&
- instrument.instrumentDef != nullptr && instrument.instrumentDef->rhythmType != RHYTHM_TYPE_UNDEFINED));
-
- if (instrument.instrumentDef == nullptr || instrument.instrumentDef->isEmpty() ||
- (rhythmNote && instrument.instrumentDef->rhythmType == RHYTHM_TYPE_UNDEFINED)) {
- // Instrument definition contains no data or it is not suitable for
- // a rhythm note.
+ if (instrument.instrumentDef == nullptr || instrument.instrumentDef->isEmpty()) {
+ // Instrument definition contains no data.
return;
}
+
+ // If rhythm mode is on and the instrument is a rhythm instrument,
+ // notes on this channel will be played using the OPL rhythm register.
+ bool rhythmNote = _rhythmMode && _rhythmInstrumentMode == RHYTHM_INSTRUMENT_MODE_RHYTHM_TYPE &&
+ instrument.instrumentDef->rhythmType != RHYTHM_TYPE_UNDEFINED;
+
_activeNotesMutex.lock();
// Determine the OPL channel to use and the active note data to update.
uint8 oplChannel = 0xFF;
ActiveNote *activeNote = nullptr;
- oplChannel = allocateOplChannel(channel, source, instrument.instrumentId);
+ oplChannel = allocateOplChannel(channel, source, instrument);
if (oplChannel != 0xFF) {
if (rhythmNote) {
activeNote = &_activeRhythmNotes[instrument.instrumentDef->rhythmType - 1];
@@ -1084,20 +1080,23 @@ void MidiDriver_ADLIB_Multisource::panning(uint8 channel, uint8 panning, uint8 s
_activeNotesMutex.lock();
+ bool rhythmChannel = (_rhythmInstrumentMode == RHYTHM_INSTRUMENT_MODE_CHANNEL_10 && channel == MIDI_RHYTHM_CHANNEL);
+
// Apply the new channel panning to any active notes.
- if (_rhythmMode && channel == MIDI_RHYTHM_CHANNEL) {
+ if (_rhythmMode) {
for (int i = 0; i < OPL_NUM_RHYTHM_INSTRUMENTS; i++) {
- if (_activeRhythmNotes[i].noteActive && _activeRhythmNotes[i].source == source) {
+ if (_activeRhythmNotes[i].noteActive && (rhythmChannel || _activeRhythmNotes[i].channel == channel) &&
+ _activeRhythmNotes[i].source == source) {
writePanning(0xFF, static_cast<OplInstrumentRhythmType>(i + 1));
}
}
- } else {
- for (int i = 0; i < _numMelodicChannels; i++) {
- uint8 oplChannel = _melodicChannels[i];
- if (_activeNotes[oplChannel].noteActive && _activeNotes[oplChannel].channel == channel &&
- _activeNotes[oplChannel].source == source) {
- writePanning(oplChannel);
- }
+ }
+
+ for (int i = 0; i < _numMelodicChannels; i++) {
+ uint8 oplChannel = _melodicChannels[i];
+ if (_activeNotes[oplChannel].noteActive && _activeNotes[oplChannel].channel == channel &&
+ _activeNotes[oplChannel].source == source) {
+ writePanning(oplChannel);
}
}
@@ -1168,21 +1167,24 @@ void MidiDriver_ADLIB_Multisource::resetAllControllers(uint8 channel, uint8 sour
void MidiDriver_ADLIB_Multisource::allNotesOff(uint8 channel, uint8 source) {
_activeNotesMutex.lock();
+ bool rhythmChannel = (_rhythmInstrumentMode == RHYTHM_INSTRUMENT_MODE_CHANNEL_10 && channel == MIDI_RHYTHM_CHANNEL);
+
// Execute a note off for all active notes on this MIDI channel. This will
// turn the notes off if sustain is off and sustain the notes if it is on.
- if (_rhythmMode && channel == MIDI_RHYTHM_CHANNEL) {
+ if (_rhythmMode) {
for (int i = 0; i < OPL_NUM_RHYTHM_INSTRUMENTS; i++) {
- if (_activeRhythmNotes[i].noteActive && _activeRhythmNotes[i].source == source) {
+ if (_activeRhythmNotes[i].noteActive && (rhythmChannel || _activeRhythmNotes[i].channel == channel) &&
+ _activeRhythmNotes[i].source == source) {
noteOff(channel, _activeRhythmNotes[i].note, 0, source);
}
}
- } else {
- for (int i = 0; i < _numMelodicChannels; i++) {
- uint8 oplChannel = _melodicChannels[i];
- if (_activeNotes[oplChannel].noteActive && !_activeNotes[oplChannel].noteSustained &&
- _activeNotes[oplChannel].source == source && _activeNotes[oplChannel].channel == channel) {
- noteOff(channel, _activeNotes[oplChannel].note, 0, source);
- }
+ }
+
+ for (int i = 0; i < _numMelodicChannels; i++) {
+ uint8 oplChannel = _melodicChannels[i];
+ if (_activeNotes[oplChannel].noteActive && !_activeNotes[oplChannel].noteSustained &&
+ _activeNotes[oplChannel].source == source && _activeNotes[oplChannel].channel == channel) {
+ noteOff(channel, _activeNotes[oplChannel].note, 0, source);
}
}
@@ -1207,6 +1209,8 @@ void MidiDriver_ADLIB_Multisource::stopAllNotes(bool stopSustainedNotes) {
void MidiDriver_ADLIB_Multisource::stopAllNotes(uint8 source, uint8 channel) {
_activeNotesMutex.lock();
+ bool rhythmChannel = (_rhythmInstrumentMode == RHYTHM_INSTRUMENT_MODE_CHANNEL_10 && channel == MIDI_RHYTHM_CHANNEL);
+
// Write the key off bit for all active notes on this MIDI channel and
// source.
for (int i = 0; i < _numMelodicChannels; i++) {
@@ -1216,10 +1220,11 @@ void MidiDriver_ADLIB_Multisource::stopAllNotes(uint8 source, uint8 channel) {
writeKeyOff(oplChannel);
}
}
- if (_rhythmMode && !_rhythmModeIgnoreNoteOffs && (channel == 0xFF || channel == MIDI_RHYTHM_CHANNEL)) {
+ if (_rhythmMode && !_rhythmModeIgnoreNoteOffs) {
bool rhythmChanged = false;
for (int i = 0; i < 5; i++) {
- if (_activeRhythmNotes[i].noteActive && (source == 0xFF || _activeRhythmNotes[i].source == source)) {
+ if (_activeRhythmNotes[i].noteActive && (channel == 0xFF || rhythmChannel || _activeRhythmNotes[i].channel == channel) &&
+ (source == 0xFF || _activeRhythmNotes[i].source == source)) {
_activeRhythmNotes[i].noteActive = false;
rhythmChanged = true;
}
@@ -1332,19 +1337,27 @@ void MidiDriver_ADLIB_Multisource::initOpl() {
void MidiDriver_ADLIB_Multisource::recalculateFrequencies(uint8 channel, uint8 source) {
_activeNotesMutex.lock();
+ bool rhythmChannel = (_rhythmInstrumentMode == RHYTHM_INSTRUMENT_MODE_CHANNEL_10 && channel == MIDI_RHYTHM_CHANNEL);
+
// Calculate and write the frequency of all active notes on this MIDI
// channel and source.
- if (_rhythmMode && channel == MIDI_RHYTHM_CHANNEL) {
+ if (_rhythmMode) {
// Always rewrite bass drum frequency if it is active.
- if (_activeRhythmNotes[RHYTHM_TYPE_BASS_DRUM - 1].noteActive && _activeRhythmNotes[RHYTHM_TYPE_BASS_DRUM - 1].source == source) {
+ if (_activeRhythmNotes[RHYTHM_TYPE_BASS_DRUM - 1].noteActive &&
+ (rhythmChannel || _activeRhythmNotes[RHYTHM_TYPE_BASS_DRUM - 1].channel == channel) &&
+ _activeRhythmNotes[RHYTHM_TYPE_BASS_DRUM - 1].source == source) {
writeFrequency(0xFF, RHYTHM_TYPE_BASS_DRUM);
}
// Snare drum and hi-hat share the same frequency setting. If both are
// active, use the most recently played instrument.
OplInstrumentRhythmType rhythmType = RHYTHM_TYPE_UNDEFINED;
- bool snareActive = _activeRhythmNotes[RHYTHM_TYPE_SNARE_DRUM - 1].noteActive && _activeRhythmNotes[RHYTHM_TYPE_SNARE_DRUM - 1].source == source;
- bool hiHatActive = _activeRhythmNotes[RHYTHM_TYPE_HI_HAT - 1].noteActive && _activeRhythmNotes[RHYTHM_TYPE_HI_HAT - 1].source == source;
+ bool snareActive = _activeRhythmNotes[RHYTHM_TYPE_SNARE_DRUM - 1].noteActive &&
+ (rhythmChannel || _activeRhythmNotes[RHYTHM_TYPE_SNARE_DRUM - 1].channel == channel) &&
+ _activeRhythmNotes[RHYTHM_TYPE_SNARE_DRUM - 1].source == source;
+ bool hiHatActive = _activeRhythmNotes[RHYTHM_TYPE_HI_HAT - 1].noteActive &&
+ (rhythmChannel || _activeRhythmNotes[RHYTHM_TYPE_HI_HAT - 1].channel == channel) &&
+ _activeRhythmNotes[RHYTHM_TYPE_HI_HAT - 1].source == source;
if (snareActive && hiHatActive) {
rhythmType = (_activeRhythmNotes[RHYTHM_TYPE_SNARE_DRUM - 1].noteCounterValue >=
_activeRhythmNotes[RHYTHM_TYPE_HI_HAT - 1].noteCounterValue ? RHYTHM_TYPE_SNARE_DRUM : RHYTHM_TYPE_HI_HAT);
@@ -1359,8 +1372,12 @@ void MidiDriver_ADLIB_Multisource::recalculateFrequencies(uint8 channel, uint8 s
// Tom tom and cymbal share the same frequency setting. If both are
// active, use the most recently played instrument.
rhythmType = RHYTHM_TYPE_UNDEFINED;
- bool tomTomActive = _activeRhythmNotes[RHYTHM_TYPE_TOM_TOM - 1].noteActive && _activeRhythmNotes[RHYTHM_TYPE_TOM_TOM - 1].source == source;
- bool cymbalActive = _activeRhythmNotes[RHYTHM_TYPE_CYMBAL - 1].noteActive && _activeRhythmNotes[RHYTHM_TYPE_CYMBAL - 1].source == source;
+ bool tomTomActive = _activeRhythmNotes[RHYTHM_TYPE_TOM_TOM - 1].noteActive &&
+ (rhythmChannel || _activeRhythmNotes[RHYTHM_TYPE_TOM_TOM - 1].channel == channel) &&
+ _activeRhythmNotes[RHYTHM_TYPE_TOM_TOM - 1].source == source;
+ bool cymbalActive = _activeRhythmNotes[RHYTHM_TYPE_CYMBAL - 1].noteActive &&
+ (rhythmChannel || _activeRhythmNotes[RHYTHM_TYPE_CYMBAL - 1].channel == channel) &&
+ _activeRhythmNotes[RHYTHM_TYPE_CYMBAL - 1].source == source;
if (tomTomActive && cymbalActive) {
rhythmType = (_activeRhythmNotes[RHYTHM_TYPE_TOM_TOM - 1].noteCounterValue >=
_activeRhythmNotes[RHYTHM_TYPE_CYMBAL - 1].noteCounterValue ? RHYTHM_TYPE_TOM_TOM : RHYTHM_TYPE_CYMBAL);
@@ -1371,13 +1388,13 @@ void MidiDriver_ADLIB_Multisource::recalculateFrequencies(uint8 channel, uint8 s
}
if (rhythmType != RHYTHM_TYPE_UNDEFINED)
writeFrequency(0xFF, rhythmType);
- } else {
- for (int i = 0; i < _numMelodicChannels; i++) {
- uint8 oplChannel = _melodicChannels[i];
- if (_activeNotes[oplChannel].noteActive && _activeNotes[oplChannel].channel == channel &&
- _activeNotes[oplChannel].source == source) {
- writeFrequency(oplChannel);
- }
+ }
+
+ for (int i = 0; i < _numMelodicChannels; i++) {
+ uint8 oplChannel = _melodicChannels[i];
+ if (_activeNotes[oplChannel].noteActive && _activeNotes[oplChannel].channel == channel &&
+ _activeNotes[oplChannel].source == source) {
+ writeFrequency(oplChannel);
}
}
@@ -1387,6 +1404,8 @@ void MidiDriver_ADLIB_Multisource::recalculateFrequencies(uint8 channel, uint8 s
void MidiDriver_ADLIB_Multisource::recalculateVolumes(uint8 channel, uint8 source) {
_activeNotesMutex.lock();
+ bool rhythmChannel = (_rhythmInstrumentMode == RHYTHM_INSTRUMENT_MODE_CHANNEL_10 && channel == MIDI_RHYTHM_CHANNEL);
+
// Calculate and write the volume of all operators of all active notes on
// this MIDI channel and source.
for (int i = 0; i < _numMelodicChannels; i++) {
@@ -1399,9 +1418,11 @@ void MidiDriver_ADLIB_Multisource::recalculateVolumes(uint8 channel, uint8 sourc
}
}
}
- if (_rhythmMode && (channel == 0xFF || channel == MIDI_RHYTHM_CHANNEL)) {
+ if (_rhythmMode) {
for (int i = 0; i < OPL_NUM_RHYTHM_INSTRUMENTS; i++) {
- if (_activeRhythmNotes[i].noteActive && (source == 0xFF || _activeRhythmNotes[i].source == source)) {
+ if (_activeRhythmNotes[i].noteActive &&
+ (channel == 0xFF || rhythmChannel || _activeRhythmNotes[i].channel == channel) &&
+ (source == 0xFF || _activeRhythmNotes[i].source == source)) {
for (int j = 0; j < _activeRhythmNotes[i].instrumentDef->getNumberOfOperators(); j++) {
writeVolume(0xFF, j, static_cast<OplInstrumentRhythmType>(i + 1));
}
@@ -1442,7 +1463,28 @@ MidiDriver_ADLIB_Multisource::InstrumentInfo MidiDriver_ADLIB_Multisource::deter
return instrument;
}
-uint8 MidiDriver_ADLIB_Multisource::allocateOplChannel(uint8 channel, uint8 source, uint8 instrumentId) {
+uint8 MidiDriver_ADLIB_Multisource::allocateOplChannel(uint8 channel, uint8 source, InstrumentInfo &instrumentInfo) {
+
+ // Allocate channel for OPL rhythm mode notes. These are just mapped to the
+ // OPL channel used to play the OPL rhythm mode instrument.
+ if (_rhythmMode) {
+ if (_rhythmInstrumentMode == RHYTHM_INSTRUMENT_MODE_CHANNEL_10 && channel == MIDI_RHYTHM_CHANNEL) {
+ // OPL rhythm notes are played using the MIDI rhythm channel,
+ // and this channel is being used to play a note.
+ // If the instrument is an OPL rhythm instrument, return the
+ // corresponding OPL channel. Otherwise the note should not be
+ // played, so do not return a channel.
+ return instrumentInfo.instrumentDef->rhythmType != RHYTHM_TYPE_UNDEFINED ?
+ OPL_RHYTHM_INSTRUMENT_CHANNELS[instrumentInfo.instrumentDef->rhythmType - 1] :
+ 0xFF;
+ }
+ else if (instrumentInfo.instrumentDef->rhythmType != RHYTHM_TYPE_UNDEFINED) {
+ // OPL rhythm notes are played based on the instrument rhythm type.
+ // If it is a rhythm instrument, return the corresponding OPL
+ // channel. Otherwise, allocate a melodic channel.
+ return OPL_RHYTHM_INSTRUMENT_CHANNELS[instrumentInfo.instrumentDef->rhythmType - 1];
+ }
+ }
uint8 allocatedChannel = 0xFF;
if (_allocationMode == ALLOCATION_MODE_DYNAMIC) {
@@ -1479,7 +1521,7 @@ uint8 MidiDriver_ADLIB_Multisource::allocateOplChannel(uint8 channel, uint8 sour
inactiveChannel = oplChannel;
continue;
}
- if (_activeNotes[oplChannel].noteActive && _activeNotes[oplChannel].instrumentId == instrumentId &&
+ if (_activeNotes[oplChannel].noteActive && _activeNotes[oplChannel].instrumentId == instrumentInfo.instrumentId &&
_activeNotes[oplChannel].noteCounterValue < instrumentNoteCounter) {
// A channel playing a note using the same instrument with a
// lower note counter value has been found.
diff --git a/audio/adlib_ms.h b/audio/adlib_ms.h
index 2a0dcc988f2..6b777e27d00 100644
--- a/audio/adlib_ms.h
+++ b/audio/adlib_ms.h
@@ -965,12 +965,12 @@ protected:
*
* @param channel The MIDI channel on which the note is played.
* @param source The source playing the note.
- * @param instrumentId The ID of the instrument playing the note. Not used
+ * @param instrumentInfo Data of the instrument playing the note. Not used
* by the static channel allocation mode.
* @return The number of the allocated OPL channel; 0xFF if allocation
* failed (not possible using the dynamic channel allocation mode).
*/
- virtual uint8 allocateOplChannel(uint8 channel, uint8 source, uint8 instrumentId);
+ virtual uint8 allocateOplChannel(uint8 channel, uint8 source, InstrumentInfo &instrumentInfo);
/**
* Determines which melodic channels are available based on the OPL chip
* type and rhythm mode setting and sets _melodicChannels and
diff --git a/engines/agos/drivers/accolade/adlib.cpp b/engines/agos/drivers/accolade/adlib.cpp
index 3b7033237b8..c410e609ead 100644
--- a/engines/agos/drivers/accolade/adlib.cpp
+++ b/engines/agos/drivers/accolade/adlib.cpp
@@ -165,7 +165,7 @@ void MidiDriver_Accolade_AdLib::deinitSource(uint8 source) {
MidiDriver_ADLIB_Multisource::deinitSource(source);
}
-uint8 MidiDriver_Accolade_AdLib::allocateOplChannel(uint8 channel, uint8 source, uint8 instrumentId) {
+uint8 MidiDriver_Accolade_AdLib::allocateOplChannel(uint8 channel, uint8 source, InstrumentInfo &instrumentInfo) {
Common::StackLock lock(_allocationMutex);
if (_sources[source].type == SOURCE_TYPE_SFX) {
@@ -177,7 +177,7 @@ uint8 MidiDriver_Accolade_AdLib::allocateOplChannel(uint8 channel, uint8 source,
allocatedChannel = 6 - source;
} else {
// For OPL3, use the dynamic allocation algorithm.
- allocatedChannel = MidiDriver_ADLIB_Multisource::allocateOplChannel(channel, source, instrumentId);
+ allocatedChannel = MidiDriver_ADLIB_Multisource::allocateOplChannel(channel, source, instrumentInfo);
}
_activeNotesMutex.lock();
@@ -202,6 +202,10 @@ uint8 MidiDriver_Accolade_AdLib::allocateOplChannel(uint8 channel, uint8 source,
// Channel allocation for music sources.
if (_oplType != OPL::Config::kOpl3) {
+ // Use the regular allocation algorithm for rhythm instruments.
+ if (channel == MIDI_RHYTHM_CHANNEL)
+ return MidiDriver_ADLIB_Multisource::allocateOplChannel(channel, source, instrumentInfo);
+
// For OPL2, discard events for channels 6 and 7 and channels allocated
// for SFX.
if (channel >= 6 || _activeNotes[channel].channelAllocated)
@@ -211,7 +215,7 @@ uint8 MidiDriver_Accolade_AdLib::allocateOplChannel(uint8 channel, uint8 source,
return channel;
} else {
// For OPL3, use the dynamic allocation algorithm.
- return MidiDriver_ADLIB_Multisource::allocateOplChannel(channel, source, instrumentId);
+ return MidiDriver_ADLIB_Multisource::allocateOplChannel(channel, source, instrumentInfo);
}
}
@@ -232,7 +236,7 @@ void MidiDriver_Accolade_AdLib::loadSfxInstrument(uint8 source, byte *instrument
// Allocate a channel
programChange(0, 0, source);
InstrumentInfo instrument = determineInstrument(0, source, 0);
- uint8 oplChannel = allocateOplChannel(0, source, instrument.instrumentId);
+ uint8 oplChannel = allocateOplChannel(0, source, instrument);
// Update the active note data.
ActiveNote *activeNote = &_activeNotes[oplChannel];
diff --git a/engines/agos/drivers/accolade/adlib.h b/engines/agos/drivers/accolade/adlib.h
index abbdd03d104..35ef06815f9 100644
--- a/engines/agos/drivers/accolade/adlib.h
+++ b/engines/agos/drivers/accolade/adlib.h
@@ -62,7 +62,7 @@ public:
protected:
InstrumentInfo determineInstrument(uint8 channel, uint8 source, uint8 note) override;
- uint8 allocateOplChannel(uint8 channel, uint8 source, uint8 instrumentId) override;
+ uint8 allocateOplChannel(uint8 channel, uint8 source, InstrumentInfo &instrumentInfo) override;
uint16 calculateFrequency(uint8 channel, uint8 source, uint8 note) override;
uint8 calculateUnscaledVolume(uint8 channel, uint8 source, uint8 velocity, const OplInstrumentDefinition &instrumentDef, uint8 operatorNum) override;
diff --git a/engines/agos/drivers/simon1/adlib.cpp b/engines/agos/drivers/simon1/adlib.cpp
index 84a99b218bf..c5c63dc6864 100644
--- a/engines/agos/drivers/simon1/adlib.cpp
+++ b/engines/agos/drivers/simon1/adlib.cpp
@@ -208,7 +208,11 @@ void MidiDriver_Simon1_AdLib::disableMusicRhythmNotes() {
_musicRhythmNotesDisabled = true;
}
-uint8 MidiDriver_Simon1_AdLib::allocateOplChannel(uint8 channel, uint8 source, uint8 instrumentId) {
+uint8 MidiDriver_Simon1_AdLib::allocateOplChannel(uint8 channel, uint8 source, InstrumentInfo &instrumentInfo) {
+ // Use the regular allocation algorithm for rhythm instruments.
+ if (channel == MIDI_RHYTHM_CHANNEL)
+ return MidiDriver_ADLIB_Multisource::allocateOplChannel(channel, source, instrumentInfo);
+
// When allocating an OPL channel for playback of a note, the algorithm
// looks for the following types of channels:
// - An OPL channel already allocated to this source and MIDI channel that
diff --git a/engines/agos/drivers/simon1/adlib.h b/engines/agos/drivers/simon1/adlib.h
index a878e9c407c..e4f9caa8a9d 100644
--- a/engines/agos/drivers/simon1/adlib.h
+++ b/engines/agos/drivers/simon1/adlib.h
@@ -61,7 +61,7 @@ private:
static const RhythmMapEntry RHYTHM_MAP[];
static const uint16 FREQUENCY_TABLE[];
- uint8 allocateOplChannel(uint8 channel, uint8 source, uint8 instrumentId) override;
+ uint8 allocateOplChannel(uint8 channel, uint8 source, InstrumentInfo &instrumentInfo) override;
uint16 calculateFrequency(uint8 channel, uint8 source, uint8 note) override;
uint8 calculateUnscaledVolume(uint8 channel, uint8 source, uint8 velocity,
const OplInstrumentDefinition &instrumentDef, uint8 operatorNum) override;
diff --git a/engines/darkseed/adlib_dsf.cpp b/engines/darkseed/adlib_dsf.cpp
index 1091aa49b09..9856656ccc4 100644
--- a/engines/darkseed/adlib_dsf.cpp
+++ b/engines/darkseed/adlib_dsf.cpp
@@ -108,13 +108,13 @@ void MidiDriver_DarkSeedFloppy_AdLib::loadInstrumentBank(uint8 *instrumentBankDa
}
}
-uint8 MidiDriver_DarkSeedFloppy_AdLib::allocateOplChannel(uint8 channel, uint8 source, uint8 instrumentId) {
+uint8 MidiDriver_DarkSeedFloppy_AdLib::allocateOplChannel(uint8 channel, uint8 source, InstrumentInfo &instrumentInfo) {
uint8 allocatedChannel = 0xFF;
_allocationMutex.lock();
_activeNotesMutex.lock();
- if (instrumentId <= 0xE) {
+ if (instrumentInfo.instrumentId <= 0xE) {
// The first 15 instruments are rhythm instruments. These get assigned
// to the corresponding OPL rhythm instruments.
// Note: original code also processes instrument 0xF, leading to
@@ -122,7 +122,7 @@ uint8 MidiDriver_DarkSeedFloppy_AdLib::allocateOplChannel(uint8 channel, uint8 s
// The order of the rhythm instruments is flipped compared to the order
// in the _activeRhythmNotes array.
- uint8 rhythmInstType = 4 - (instrumentId / 3);
+ uint8 rhythmInstType = 4 - (instrumentInfo.instrumentId / 3);
allocatedChannel = OPL_RHYTHM_INSTRUMENT_CHANNELS[rhythmInstType];
if (_activeRhythmNotes[rhythmInstType].channelAllocated && _activeRhythmNotes[rhythmInstType].source != source) {
// OPL rhythm instrument is already allocated
diff --git a/engines/darkseed/adlib_dsf.h b/engines/darkseed/adlib_dsf.h
index 488aeff36ec..53358db63e3 100644
--- a/engines/darkseed/adlib_dsf.h
+++ b/engines/darkseed/adlib_dsf.h
@@ -48,7 +48,7 @@ public:
void loadInstrumentBank(uint8 *instrumentBankData);
protected:
- uint8 allocateOplChannel(uint8 channel, uint8 source, uint8 instrumentId) override;
+ uint8 allocateOplChannel(uint8 channel, uint8 source, InstrumentInfo &instrumentInfo) override;
uint16 calculateFrequency(uint8 channel, uint8 source, uint8 note) override;
uint8 calculateUnscaledVolume(uint8 channel, uint8 source, uint8 velocity, const OplInstrumentDefinition &instrumentDef, uint8 operatorNum) override;
diff --git a/engines/darkseed/adlib_worx.cpp b/engines/darkseed/adlib_worx.cpp
index 376051e3778..5a6804cf01d 100644
--- a/engines/darkseed/adlib_worx.cpp
+++ b/engines/darkseed/adlib_worx.cpp
@@ -202,7 +202,7 @@ MidiDriver_Worx_AdLib::~MidiDriver_Worx_AdLib() {
delete[] _instrumentBank;
}
-uint8 MidiDriver_Worx_AdLib::allocateOplChannel(uint8 channel, uint8 source, uint8 instrumentId) {
+uint8 MidiDriver_Worx_AdLib::allocateOplChannel(uint8 channel, uint8 source, InstrumentInfo &instrumentInfo) {
uint8 allocatedChannel = 0xFF;
_allocationMutex.lock();
diff --git a/engines/darkseed/adlib_worx.h b/engines/darkseed/adlib_worx.h
index b6eda97195a..481bf7f5e83 100644
--- a/engines/darkseed/adlib_worx.h
+++ b/engines/darkseed/adlib_worx.h
@@ -55,7 +55,7 @@ public:
~MidiDriver_Worx_AdLib();
protected:
- uint8 allocateOplChannel(uint8 channel, uint8 source, uint8 instrumentId) override;
+ uint8 allocateOplChannel(uint8 channel, uint8 source, InstrumentInfo &instrumentInfo) override;
uint16 calculateFrequency(uint8 channel, uint8 source, uint8 note) override;
uint8 calculateUnscaledVolume(uint8 channel, uint8 source, uint8 velocity, const OplInstrumentDefinition &instrumentDef, uint8 operatorNum) override;
};
diff --git a/engines/darkseed/midiparser_sbr.cpp b/engines/darkseed/midiparser_sbr.cpp
index 18c83af61b5..18481ab3c2c 100644
--- a/engines/darkseed/midiparser_sbr.cpp
+++ b/engines/darkseed/midiparser_sbr.cpp
@@ -33,7 +33,7 @@ MidiParser_SBR::MidiParser_SBR(int8 source, bool sfx) : MidiParser_SMF(source),
// SBR uses a fixed tempo.
_ppqn = 96;
- setTempo(1318200);
+ setTempo(1318214);
}
void MidiParser_SBR::parseNextEvent(EventInfo &info) {
diff --git a/engines/ultima/nuvie/sound/mididrv_m_adlib.cpp b/engines/ultima/nuvie/sound/mididrv_m_adlib.cpp
index e52dd1f9d57..123b53dbf9e 100644
--- a/engines/ultima/nuvie/sound/mididrv_m_adlib.cpp
+++ b/engines/ultima/nuvie/sound/mididrv_m_adlib.cpp
@@ -130,7 +130,9 @@ void MidiDriver_M_AdLib::send(int8 source, uint32 b) {
oplChannel = 0xFF;
activeNote = nullptr;
// Allocate a melodic OPL channel.
- oplChannel = allocateOplChannel(channel, source, 0);
+ InstrumentInfo instrumentInfo;
+ instrumentInfo = { };
+ oplChannel = allocateOplChannel(channel, source, instrumentInfo);
if (oplChannel != 0xFF)
activeNote = &_activeNotes[oplChannel];
@@ -293,7 +295,7 @@ void MidiDriver_M_AdLib::modulation(uint8 channel, uint8 modulation, uint8 sourc
writeRegister(OPL_REGISTER_BASE_LEVEL + registerOffset, modulation);
}
-uint8 MidiDriver_M_AdLib::allocateOplChannel(uint8 channel, uint8 source, uint8 instrumentId) {
+uint8 MidiDriver_M_AdLib::allocateOplChannel(uint8 channel, uint8 source, InstrumentInfo &instrumentInfo) {
// Allocation of M data channels to OPL output channels is simply 1 on 1.
return channel;
}
diff --git a/engines/ultima/nuvie/sound/mididrv_m_adlib.h b/engines/ultima/nuvie/sound/mididrv_m_adlib.h
index 35e1d69fa6f..0b914f6bd55 100644
--- a/engines/ultima/nuvie/sound/mididrv_m_adlib.h
+++ b/engines/ultima/nuvie/sound/mididrv_m_adlib.h
@@ -63,7 +63,7 @@ protected:
void programChange(uint8 channel, uint8 program, uint8 source) override;
void modulation(uint8 channel, uint8 modulation, uint8 source) override;
- uint8 allocateOplChannel(uint8 channel, uint8 source, uint8 instrumentId) override;
+ uint8 allocateOplChannel(uint8 channel, uint8 source, InstrumentInfo &instrumentInfo) override;
uint16 calculateFrequency(uint8 channel, uint8 source, uint8 note) override;
uint8 calculateUnscaledVolume(uint8 channel, uint8 source, uint8 velocity, const OplInstrumentDefinition &instrumentDef, uint8 operatorNum) override;
void writeVolume(uint8 oplChannel, uint8 operatorNum, OplInstrumentRhythmType rhythmType = RHYTHM_TYPE_UNDEFINED) override;
Commit: c1e19467a6139c0e00d3f0e6dbfa1e6c0a42f2f7
https://github.com/scummvm/scummvm/commit/c1e19467a6139c0e00d3f0e6dbfa1e6c0a42f2f7
Author: Coen Rampen (crampen at gmail.com)
Date: 2025-02-22T23:13:54+01:00
Commit Message:
DARKSEED: Add music fade-outs
Changed paths:
engines/darkseed/cutscene.cpp
engines/darkseed/darkseed.cpp
engines/darkseed/menu.cpp
engines/darkseed/music.cpp
engines/darkseed/music.h
engines/darkseed/sound.cpp
engines/darkseed/sound.h
diff --git a/engines/darkseed/cutscene.cpp b/engines/darkseed/cutscene.cpp
index d4714c22965..1f07e0c2644 100644
--- a/engines/darkseed/cutscene.cpp
+++ b/engines/darkseed/cutscene.cpp
@@ -592,11 +592,12 @@ bool Cutscene::introScene() {
}
break;
case 51:
- g_engine->_sound->stopMusic();
+ if (g_engine->_sound->isPlayingMusic())
+ g_engine->_sound->startFadeOut();
g_engine->fadeOut();
break;
case 52:
- if (g_engine->fadeStep()) {
+ if (g_engine->fadeStep() || g_engine->_sound->isFading()) {
return true;
}
break;
@@ -734,11 +735,12 @@ bool Cutscene::embryoInsertedScene() {
}
break;
case 16:
- g_engine->_sound->stopMusic();
+ if (g_engine->_sound->isPlayingMusic())
+ g_engine->_sound->startFadeOut();
g_engine->fadeOut();
break;
case 17:
- if (g_engine->fadeStep()) {
+ if (g_engine->fadeStep() || g_engine->_sound->isFading()) {
return true;
}
break;
@@ -828,10 +830,12 @@ bool Cutscene::shipLaunchScene() {
}
break;
case 13:
+ if (g_engine->_sound->isPlayingMusic())
+ g_engine->_sound->startFadeOut();
g_engine->fadeOut();
break;
case 14:
- if (g_engine->fadeStep()) {
+ if (g_engine->fadeStep() || g_engine->_sound->isFading()) {
return true;
}
break;
@@ -887,10 +891,12 @@ bool Cutscene::alienBornScene() {
}
break;
case 7:
+ if (g_engine->_sound->isPlayingMusic())
+ g_engine->_sound->startFadeOut();
g_engine->fadeOut();
break;
case 8:
- if (g_engine->fadeStep()) {
+ if (g_engine->fadeStep() || g_engine->_sound->isFading()) {
return true;
}
break;
@@ -975,10 +981,12 @@ bool Cutscene::babyDollScene() {
}
break;
case 11:
+ if (g_engine->_sound->isPlayingMusic())
+ g_engine->_sound->startFadeOut();
g_engine->fadeOut();
break;
case 12:
- if (g_engine->fadeStep()) {
+ if (g_engine->fadeStep() || g_engine->_sound->isFading()) {
return true;
}
freeMorph();
@@ -1076,10 +1084,12 @@ bool Cutscene::bookScene() {
break;
case 14:
freeMorph();
+ if (g_engine->_sound->isPlayingMusic())
+ g_engine->_sound->startFadeOut();
g_engine->fadeOut();
break;
case 15:
- if (g_engine->fadeStep()) {
+ if (g_engine->fadeStep() || g_engine->_sound->isFading()) {
return true;
}
break;
@@ -1190,10 +1200,12 @@ bool Cutscene::nightmare2Scene() {
break;
case 16:
freeMorph();
+ if (g_engine->_sound->isPlayingMusic())
+ g_engine->_sound->startFadeOut();
g_engine->fadeOut();
break;
case 17:
- if (g_engine->fadeStep()) {
+ if (g_engine->fadeStep() || g_engine->_sound->isFading()) {
return true;
}
break;
@@ -1269,15 +1281,16 @@ bool Cutscene::nightmare3Scene() {
}
break;
case 11:
+ if (g_engine->_sound->isPlayingMusic())
+ g_engine->_sound->startFadeOut();
g_engine->fadeOut();
break;
case 12:
- if (g_engine->fadeStep()) {
+ if (g_engine->fadeStep() || g_engine->_sound->isFading()) {
return true;
}
break;
default:
- g_engine->_sound->stopMusic();
_movieStep = 9999;
return false;
}
diff --git a/engines/darkseed/darkseed.cpp b/engines/darkseed/darkseed.cpp
index 56501368f77..6d707e6b5eb 100644
--- a/engines/darkseed/darkseed.cpp
+++ b/engines/darkseed/darkseed.cpp
@@ -1076,6 +1076,15 @@ void DarkseedEngine::loadRoom(int roomNumber) {
}
void DarkseedEngine::changeToRoom(int newRoomNumber, bool placeDirectly) { // AKA LoadNewRoom
+ MusicId newMusicId = Room::getMusicIdForRoom(newRoomNumber);
+ if (g_engine->_sound->isPlayingMusic() && Room::getMusicIdForRoom(_room->_roomNumber) != newMusicId) {
+ g_engine->_sound->startFadeOut();
+ while (g_engine->_sound->isFading()) {
+ waitxticks(1);
+ }
+ g_engine->_sound->stopMusic();
+ }
+
_objectVar[99] = 0;
_objectVar[66] = 0;
_objectVar[67] = 0;
diff --git a/engines/darkseed/menu.cpp b/engines/darkseed/menu.cpp
index 79990953e69..d95a2eca5d2 100644
--- a/engines/darkseed/menu.cpp
+++ b/engines/darkseed/menu.cpp
@@ -99,6 +99,14 @@ Common::KeyCode Menu::getLocalisedConfirmToQuitKeycode() {
}
void Menu::loadMenu() {
+ if (g_engine->_sound->isPlayingMusic()) {
+ g_engine->_sound->startFadeOut();
+ while (g_engine->_sound->isFading()) {
+ g_engine->wait();
+ }
+ g_engine->_sound->stopMusic();
+ }
+
_open = true;
Graphics::Surface screenCopy;
screenCopy.copyFrom(*g_engine->_screen);
@@ -199,6 +207,8 @@ void Menu::loadMenu() {
g_engine->wait();
}
+ g_engine->_room->loadRoomMusic();
+
g_engine->removeFullscreenPic();
_open = false;
}
diff --git a/engines/darkseed/music.cpp b/engines/darkseed/music.cpp
index 8034ab967c1..f3e11d3ae88 100644
--- a/engines/darkseed/music.cpp
+++ b/engines/darkseed/music.cpp
@@ -268,7 +268,19 @@ void MusicPlayer::play(uint8 priority, bool loop) {
if (_floppyAdLibDriver != nullptr)
_floppyAdLibDriver->setSourcePriority(0, priority);
+ if (_driver->isFading())
+ _driver->abortFade();
+ _driver->resetSourceVolume(0);
+
_parser->startPlaying();
}
+void MusicPlayer::startFadeOut() {
+ _driver->startFade(0, 1100, 0);
+}
+
+bool MusicPlayer::isFading() {
+ return _driver->isFading();
+}
+
} // namespace Darkseed
diff --git a/engines/darkseed/music.h b/engines/darkseed/music.h
index ab7f1545c30..8df15026388 100644
--- a/engines/darkseed/music.h
+++ b/engines/darkseed/music.h
@@ -68,6 +68,9 @@ public:
void stop();
void pause(bool pause);
+ void startFadeOut();
+ bool isFading();
+
void syncSoundSettings();
private:
diff --git a/engines/darkseed/sound.cpp b/engines/darkseed/sound.cpp
index dbeb561f1dd..da0529239dc 100644
--- a/engines/darkseed/sound.cpp
+++ b/engines/darkseed/sound.cpp
@@ -364,4 +364,12 @@ void Sound::playFloppySpeech(int tosIdx) {
_mixer->playStream(Audio::Mixer::kSpeechSoundType, &_speechHandle, stream);
}
+void Sound::startFadeOut() {
+ _musicPlayer->startFadeOut();
+}
+
+bool Sound::isFading() {
+ return _musicPlayer->isFading();
+}
+
} // End of namespace Darkseed
diff --git a/engines/darkseed/sound.h b/engines/darkseed/sound.h
index 8237ef34d61..c3169a16bb2 100644
--- a/engines/darkseed/sound.h
+++ b/engines/darkseed/sound.h
@@ -88,6 +88,9 @@ public:
void stopSfx();
void syncSoundSettings();
void killAllSound();
+ void startFadeOut();
+ bool isFading();
+
private:
void playDosCDSfx(int sfxId);
void playFloppySpeech(int tosIdx);
Commit: 851430aecfe2c9f37217649cc0479861bf908359
https://github.com/scummvm/scummvm/commit/851430aecfe2c9f37217649cc0479861bf908359
Author: Coen Rampen (crampen at gmail.com)
Date: 2025-02-22T23:13:54+01:00
Commit Message:
DARKSEED: Add floppy music option
Changed paths:
engines/darkseed/darkseed.cpp
engines/darkseed/darkseed.h
engines/darkseed/detection.h
engines/darkseed/detection_tables.h
engines/darkseed/metaengine.cpp
engines/darkseed/sound.cpp
engines/darkseed/sound.h
diff --git a/engines/darkseed/darkseed.cpp b/engines/darkseed/darkseed.cpp
index 6d707e6b5eb..fe0fbdb53fc 100644
--- a/engines/darkseed/darkseed.cpp
+++ b/engines/darkseed/darkseed.cpp
@@ -403,6 +403,12 @@ void DarkseedEngine::syncSoundSettings() {
_sound->syncSoundSettings();
}
+void DarkseedEngine::pauseEngineIntern(bool pause) {
+ _sound->pauseMusic(pause);
+
+ Engine::pauseEngineIntern(pause);
+}
+
static constexpr uint8 walkToDirTbl[] = {
0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
diff --git a/engines/darkseed/darkseed.h b/engines/darkseed/darkseed.h
index b99f9db836f..b958276673a 100644
--- a/engines/darkseed/darkseed.h
+++ b/engines/darkseed/darkseed.h
@@ -167,6 +167,7 @@ public:
void waitForSpeechOrSfx();
void syncSoundSettings() override;
+ void pauseEngineIntern(bool pause) override;
DarkseedEngine(OSystem *syst, const ADGameDescription *gameDesc);
~DarkseedEngine() override;
diff --git a/engines/darkseed/detection.h b/engines/darkseed/detection.h
index 324960379d5..778213a9936 100644
--- a/engines/darkseed/detection.h
+++ b/engines/darkseed/detection.h
@@ -39,6 +39,7 @@ extern const PlainGameDescriptor darkseedGames[];
extern const ADGameDescription gameDescriptions[];
#define GAMEOPTION_ORIGINAL_SAVELOAD GUIO_GAMEOPTIONS1
+#define GAMEOPTION_FLOPPY_MUSIC GUIO_GAMEOPTIONS2
} // End of namespace Darkseed
diff --git a/engines/darkseed/detection_tables.h b/engines/darkseed/detection_tables.h
index e52aab7c64c..451ae0ee6cf 100644
--- a/engines/darkseed/detection_tables.h
+++ b/engines/darkseed/detection_tables.h
@@ -79,7 +79,7 @@ const ADGameDescription gameDescriptions[] = {
Common::EN_ANY,
Common::kPlatformDOS,
ADGF_TESTING | ADGF_CD,
- GUIO1(GUIO_NONE)
+ GUIO1(GAMEOPTION_FLOPPY_MUSIC)
},
{ // 1.51 according to DS.BAT, 1.5P according to intro
"darkseed",
@@ -88,7 +88,7 @@ const ADGameDescription gameDescriptions[] = {
Common::EN_ANY,
Common::kPlatformDOS,
ADGF_TESTING | ADGF_CD,
- GUIO1(GUIO_NONE)
+ GUIO1(GAMEOPTION_FLOPPY_MUSIC)
},
{
"darkseed",
@@ -97,7 +97,7 @@ const ADGameDescription gameDescriptions[] = {
Common::DE_DEU,
Common::kPlatformDOS,
ADGF_TESTING | ADGF_CD,
- GUIO1(GUIO_NONE)
+ GUIO1(GAMEOPTION_FLOPPY_MUSIC)
},
{
"darkseed",
@@ -106,7 +106,7 @@ const ADGameDescription gameDescriptions[] = {
Common::FR_FRA,
Common::kPlatformDOS,
ADGF_TESTING | ADGF_CD,
- GUIO1(GUIO_NONE)
+ GUIO1(GAMEOPTION_FLOPPY_MUSIC)
},
{
"darkseed",
@@ -115,7 +115,7 @@ const ADGameDescription gameDescriptions[] = {
Common::ES_ESP,
Common::kPlatformDOS,
ADGF_TESTING | ADGF_CD,
- GUIO1(GUIO_NONE)
+ GUIO1(GAMEOPTION_FLOPPY_MUSIC)
},
{
"darkseed",
diff --git a/engines/darkseed/metaengine.cpp b/engines/darkseed/metaengine.cpp
index 0a1b2cf0566..0d7b6bf2170 100644
--- a/engines/darkseed/metaengine.cpp
+++ b/engines/darkseed/metaengine.cpp
@@ -41,6 +41,17 @@ static const ADExtraGuiOptionsMap optionsList[] = {
0
}
},
+ {
+ GAMEOPTION_FLOPPY_MUSIC,
+ {
+ _s("Use floppy version music"),
+ _s("Use the music from the floppy version. The floppy version's music files must be copied to the SOUND directory."),
+ "use_floppy_music",
+ false,
+ 0,
+ 0
+ }
+ },
AD_EXTRA_GUI_OPTIONS_TERMINATOR
};
diff --git a/engines/darkseed/sound.cpp b/engines/darkseed/sound.cpp
index da0529239dc..7feab1a0419 100644
--- a/engines/darkseed/sound.cpp
+++ b/engines/darkseed/sound.cpp
@@ -133,8 +133,8 @@ static constexpr char sfxCDFilenameTbl[][14] = {
};
Sound::Sound(Audio::Mixer *mixer) : _mixer(mixer) {
- // TODO Add config setting for CD version with floppy music
- _useFloppyMusic = !g_engine->isCdVersion();
+ bool floppyMusicSetting = ConfMan.hasKey("use_floppy_music") ? ConfMan.getBool("use_floppy_music") : false;
+ _useFloppyMusic = !g_engine->isCdVersion() || floppyMusicSetting;
_musicPlayer = new MusicPlayer(g_engine, _useFloppyMusic);
_didSpeech.resize(978);
resetSpeech();
@@ -145,15 +145,16 @@ Sound::~Sound() {
}
int Sound::init() {
- Common::File file;
- Common::Path path = Common::Path("tos1.sit");
- if (file.open(path)) {
- _musicPlayer->loadTosInstrumentBankData(&file, (int32)file.size());
- }
- else {
- debug("Failed to load TOS instrument bank data %s", path.toString().c_str());
+ if (_useFloppyMusic) {
+ Common::File file;
+ Common::Path path = g_engine->isCdVersion() ? Common::Path("sound").join("tos1.sit") : Common::Path("tos1.sit");
+ if (file.open(path)) {
+ _musicPlayer->loadTosInstrumentBankData(&file, (int32)file.size());
+ } else {
+ debug("Failed to load TOS instrument bank data %s", path.toString().c_str());
+ }
+ file.close();
}
- file.close();
return _musicPlayer->open();
}
@@ -231,8 +232,8 @@ void Sound::playMusic(Common::String const &musicFilename, Common::String const
Common::Path path;
if (_useFloppyMusic) {
if (instrBankFilename != nullptr) {
- debug("Loading instrument bank: %s", instrBankFilename->c_str());
- path = Common::Path(instrBankFilename->c_str());
+ path = g_engine->isCdVersion() ? Common::Path("sound").join(instrBankFilename->c_str()) : Common::Path(instrBankFilename->c_str());
+ debug("Loading instrument bank: %s", path.toString().c_str());
if (!file.open(path)) {
debug("Failed to load %s", path.toString().c_str());
return;
@@ -245,12 +246,8 @@ void Sound::playMusic(Common::String const &musicFilename, Common::String const
_musicPlayer->loadTosInstrumentBank();
}
}
- debug("Loading music: %s", musicFilename.c_str());
- if (_useFloppyMusic) {
- path = Common::Path(musicFilename);
- } else {
- path = Common::Path("sound").join(musicFilename);
- }
+ path = g_engine->isCdVersion() ? Common::Path("sound").join(musicFilename) : Common::Path(musicFilename);
+ debug("Loading music: %s", path.toString().c_str());
if (!file.open(path)) {
debug("Failed to load %s", path.toString().c_str());
return;
@@ -265,6 +262,10 @@ void Sound::stopMusic() {
_musicPlayer->stop();
}
+void Sound::pauseMusic(bool pause) {
+ _musicPlayer->pause(pause);
+}
+
void Sound::killAllSound() {
stopMusic();
stopSfx();
diff --git a/engines/darkseed/sound.h b/engines/darkseed/sound.h
index c3169a16bb2..ca6ff3d6dcb 100644
--- a/engines/darkseed/sound.h
+++ b/engines/darkseed/sound.h
@@ -84,6 +84,7 @@ public:
void playMusic(StartMusicId musicId);
void playMusic(Common::String const &filename, Common::String const *instrBankFilename = nullptr, uint8 priority = 0xFF, bool loop = false);
void stopMusic();
+ void pauseMusic(bool pause);
void playSfx(uint8 sfxId, int unk1, int unk2);
void stopSfx();
void syncSoundSettings();
More information about the Scummvm-git-logs
mailing list