[Scummvm-git-logs] scummvm master -> f731cfd648ad6958c8307f2482d6ca36bd593fb4
aquadran
noreply at scummvm.org
Mon Nov 15 23:05:21 UTC 2021
This automated email contains information about 1 new commit which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .
Summary:
f731cfd648 SCUMM: DiMUSE: New Digital iMUSE Engine (#3368)
Commit: f731cfd648ad6958c8307f2482d6ca36bd593fb4
https://github.com/scummvm/scummvm/commit/f731cfd648ad6958c8307f2482d6ca36bd593fb4
Author: Andrea Boscarino (andywinxp at gmail.com)
Date: 2021-11-16T00:05:18+01:00
Commit Message:
SCUMM: DiMUSE: New Digital iMUSE Engine (#3368)
SCUMM: DiMUSE: Add new implementation of the engine
Changed paths:
A engines/scumm/imuse_digi/dimuse_cmds.cpp
A engines/scumm/imuse_digi/dimuse_defs.h
A engines/scumm/imuse_digi/dimuse_dispatch.cpp
A engines/scumm/imuse_digi/dimuse_engine.cpp
A engines/scumm/imuse_digi/dimuse_engine.h
A engines/scumm/imuse_digi/dimuse_fades.cpp
A engines/scumm/imuse_digi/dimuse_fades.h
A engines/scumm/imuse_digi/dimuse_files.cpp
A engines/scumm/imuse_digi/dimuse_files.h
A engines/scumm/imuse_digi/dimuse_groups.cpp
A engines/scumm/imuse_digi/dimuse_groups.h
A engines/scumm/imuse_digi/dimuse_internalmixer.cpp
A engines/scumm/imuse_digi/dimuse_internalmixer.h
A engines/scumm/imuse_digi/dimuse_scripts.cpp
A engines/scumm/imuse_digi/dimuse_streamer.cpp
A engines/scumm/imuse_digi/dimuse_tracks.cpp
A engines/scumm/imuse_digi/dimuse_triggers.cpp
A engines/scumm/imuse_digi/dimuse_triggers.h
A engines/scumm/imuse_digi/dimuse_utils.cpp
A engines/scumm/imuse_digi/dimuse_wave.cpp
A engines/scumm/imuse_digi/dimuse_waveout.cpp
R engines/scumm/imuse_digi/dimuse.cpp
R engines/scumm/imuse_digi/dimuse.h
R engines/scumm/imuse_digi/dimuse_music.cpp
R engines/scumm/imuse_digi/dimuse_script.cpp
R engines/scumm/imuse_digi/dimuse_track.cpp
R engines/scumm/imuse_digi/dimuse_track.h
R engines/scumm/smush/imuse_channel.cpp
base/commandLine.cpp
engines/scumm/akos.cpp
engines/scumm/dialogs.cpp
engines/scumm/imuse_digi/dimuse_bndmgr.cpp
engines/scumm/imuse_digi/dimuse_bndmgr.h
engines/scumm/imuse_digi/dimuse_codecs.cpp
engines/scumm/imuse_digi/dimuse_codecs.h
engines/scumm/imuse_digi/dimuse_sndmgr.cpp
engines/scumm/imuse_digi/dimuse_sndmgr.h
engines/scumm/imuse_digi/dimuse_tables.cpp
engines/scumm/insane/insane.cpp
engines/scumm/insane/insane_scenes.cpp
engines/scumm/module.mk
engines/scumm/resource.cpp
engines/scumm/saveload.cpp
engines/scumm/script_v6.cpp
engines/scumm/script_v8.cpp
engines/scumm/scumm.cpp
engines/scumm/scumm.h
engines/scumm/smush/channel.h
engines/scumm/smush/smush_player.cpp
engines/scumm/smush/smush_player.h
engines/scumm/sound.cpp
engines/scumm/sound.h
engines/scumm/string.cpp
diff --git a/base/commandLine.cpp b/base/commandLine.cpp
index b304181fd1..aec62e7287 100644
--- a/base/commandLine.cpp
+++ b/base/commandLine.cpp
@@ -199,10 +199,6 @@ static const char HELP_STRING[] =
#ifdef ENABLE_SCUMM
" --tempo=NUM Set music tempo (in percent, 50-200) for SCUMM games\n"
" (default: 100)\n"
-#endif
-#if (defined(ENABLE_SCUMM) && defined(ENABLE_SCUMM_7_8)) || defined(ENABLE_GRIM)
- " --dimuse-tempo=NUM Set internal Digital iMuse tempo (10 - 100) per second\n"
- " (default: 10)\n"
#endif
" --engine-speed=NUM Set frame per second limit (0 - 100), 0 = no limit\n"
" (default: 60)\n"
@@ -308,10 +304,6 @@ void registerDefaults() {
#ifdef ENABLE_SCUMM
ConfMan.registerDefault("tempo", 0);
#endif
-#if (defined(ENABLE_SCUMM) && defined(ENABLE_SCUMM_7_8)) || defined(ENABLE_GRIM)
- ConfMan.registerDefault("dimuse_tempo", 10);
-#endif
-
#if defined(ENABLE_SKY) || defined(ENABLE_QUEEN)
ConfMan.registerDefault("alt_intro", false);
#endif
@@ -841,10 +833,7 @@ Common::String parseCommandLine(Common::StringMap &settings, int argc, const cha
DO_LONG_OPTION_INT("tempo")
END_OPTION
#endif
-#if (defined(ENABLE_SCUMM) && defined(ENABLE_SCUMM_7_8)) || defined(ENABLE_GRIM)
- DO_LONG_OPTION_INT("dimuse-tempo")
- END_OPTION
-#endif
+
#if defined(ENABLE_SCUMM) || defined(ENABLE_GROOVIE)
DO_LONG_OPTION_BOOL("demo-mode")
END_OPTION
diff --git a/engines/scumm/akos.cpp b/engines/scumm/akos.cpp
index 9648dd11ac..39cbf52407 100644
--- a/engines/scumm/akos.cpp
+++ b/engines/scumm/akos.cpp
@@ -24,8 +24,7 @@
#include "scumm/actor.h"
#include "scumm/akos.h"
#include "scumm/bomp.h"
-#include "scumm/imuse/imuse.h"
-#include "scumm/imuse_digi/dimuse.h"
+#include "scumm/imuse_digi/dimuse_engine.h"
#include "scumm/he/intern_he.h"
#include "scumm/resource.h"
#include "scumm/scumm_v7.h"
diff --git a/engines/scumm/dialogs.cpp b/engines/scumm/dialogs.cpp
index 127bad18c2..23722af29e 100644
--- a/engines/scumm/dialogs.cpp
+++ b/engines/scumm/dialogs.cpp
@@ -38,7 +38,6 @@
#include "scumm/sound.h"
#include "scumm/scumm.h"
#include "scumm/imuse/imuse.h"
-#include "scumm/imuse_digi/dimuse.h"
#include "scumm/verbs.h"
#ifndef DISABLE_HELP
diff --git a/engines/scumm/imuse_digi/dimuse.cpp b/engines/scumm/imuse_digi/dimuse.cpp
deleted file mode 100644
index 8c8213254c..0000000000
--- a/engines/scumm/imuse_digi/dimuse.cpp
+++ /dev/null
@@ -1,588 +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 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#include "common/system.h"
-#include "common/timer.h"
-
-#include "scumm/actor.h"
-#include "scumm/scumm_v7.h"
-#include "scumm/sound.h"
-#include "scumm/imuse_digi/dimuse.h"
-#include "scumm/imuse_digi/dimuse_bndmgr.h"
-#include "scumm/imuse_digi/dimuse_codecs.h"
-#include "scumm/imuse_digi/dimuse_track.h"
-#include "scumm/imuse_digi/dimuse_tables.h"
-
-#include "audio/audiostream.h"
-#include "audio/mixer.h"
-#include "audio/decoders/raw.h"
-
-namespace Scumm {
-
-void IMuseDigital::timer_handler(void *refCon) {
- IMuseDigital *imuseDigital = (IMuseDigital *)refCon;
- imuseDigital->callback();
-}
-
-IMuseDigital::IMuseDigital(ScummEngine_v7 *scumm, Audio::Mixer *mixer, int fps)
- : _vm(scumm), _mixer(mixer) {
- assert(_vm);
- assert(mixer);
-
- _pause = false;
- _sound = new ImuseDigiSndMgr(_vm);
- assert(_sound);
- _callbackFps = fps;
- if (_vm->_game.id == GID_FT)
- _callbackFps *= 2;
- resetState();
- for (int l = 0; l < MAX_DIGITAL_TRACKS + MAX_DIGITAL_FADETRACKS; l++) {
- _track[l] = new Track;
- assert(_track[l]);
- _track[l]->reset();
- _track[l]->trackId = l;
- }
- _vm->getTimerManager()->installTimerProc(timer_handler, 1000000 / _callbackFps, this, "IMuseDigital");
-
- _audioNames = NULL;
- _numAudioNames = 0;
-}
-
-IMuseDigital::~IMuseDigital() {
- _vm->getTimerManager()->removeTimerProc(timer_handler);
- stopAllSounds();
- for (int l = 0; l < MAX_DIGITAL_TRACKS + MAX_DIGITAL_FADETRACKS; l++) {
- delete _track[l];
- }
- delete _sound;
- free(_audioNames);
-}
-
-static int32 makeMixerFlags(Track *track) {
- const int32 flags = track->mixerFlags;
- int32 mixerFlags = 0;
- if (flags & kFlagUnsigned)
- mixerFlags |= Audio::FLAG_UNSIGNED;
- if (flags & kFlag16Bits)
- mixerFlags |= Audio::FLAG_16BITS;
-
-#ifdef SCUMM_LITTLE_ENDIAN
- if (track->sndDataExtComp)
- mixerFlags |= Audio::FLAG_LITTLE_ENDIAN;
-#endif
- if (track->littleEndian)
- mixerFlags |= Audio::FLAG_LITTLE_ENDIAN;
- if (flags & kFlagStereo)
- mixerFlags |= Audio::FLAG_STEREO;
- return mixerFlags;
-}
-
-void IMuseDigital::resetState() {
- _curMusicState = 0;
- _curMusicSeq = 0;
- _curMusicCue = 0;
- memset(_attributes, 0, sizeof(_attributes));
- _nextSeqToPlay = 0;
- _stopingSequence = 0;
- _radioChatterSFX = 0;
- _triggerUsed = false;
- _speechIsPlaying = false;
-}
-
-static void syncWithSerializer(Common::Serializer &s, Track &t) {
- s.syncAsSByte(t.pan, VER(31));
- s.syncAsSint32LE(t.vol, VER(31));
- s.syncAsSint32LE(t.volFadeDest, VER(31));
- s.syncAsSint32LE(t.volFadeStep, VER(31));
- s.syncAsSint32LE(t.volFadeDelay, VER(31));
- s.syncAsByte(t.volFadeUsed, VER(31));
- s.syncAsSint32LE(t.soundId, VER(31));
- s.syncArray(t.soundName, 15, Common::Serializer::SByte, VER(31));
- s.syncAsByte(t.used, VER(31));
- s.syncAsByte(t.toBeRemoved, VER(31));
- s.syncAsByte(t.souStreamUsed, VER(31));
- s.skip(1, VER(31), VER(76)); // mixerStreamRunning
- s.syncAsSint32LE(t.soundPriority, VER(31));
- s.syncAsSint32LE(t.regionOffset, VER(31));
- s.skip(4, VER(31), VER(31)); // trackOffset
- s.syncAsSint32LE(t.dataOffset, VER(31));
- s.syncAsSint32LE(t.curRegion, VER(31));
- s.syncAsSint32LE(t.curHookId, VER(31));
- s.syncAsSint32LE(t.volGroupId, VER(31));
- s.syncAsSint32LE(t.soundType, VER(31));
- s.syncAsSint32LE(t.feedSize, VER(31));
- s.syncAsSint32LE(t.dataMod12Bit, VER(31));
- s.syncAsSint32LE(t.mixerFlags, VER(31));
- s.skip(4, VER(31), VER(42)); // mixerVol
- s.skip(4, VER(31), VER(42)); // mixerPan
- s.syncAsByte(t.sndDataExtComp, VER(45));
-}
-
-void IMuseDigital::saveLoadEarly(Common::Serializer &s) {
- Common::StackLock lock(_mutex, "IMuseDigital::saveLoadEarly()");
-
- s.skip(4, VER(31), VER(42)); // _volVoice
- s.skip(4, VER(31), VER(42)); // _volSfx
- s.skip(4, VER(31), VER(42)); // _volMusic
- s.syncAsSint32LE(_curMusicState, VER(31));
- s.syncAsSint32LE(_curMusicSeq, VER(31));
- s.syncAsSint32LE(_curMusicCue, VER(31));
- s.syncAsSint32LE(_nextSeqToPlay, VER(31));
- s.syncAsByte(_radioChatterSFX, VER(76));
- s.syncArray(_attributes, 188, Common::Serializer::Sint32LE, VER(31));
-
- for (int l = 0; l < MAX_DIGITAL_TRACKS + MAX_DIGITAL_FADETRACKS; l++) {
- Track *track = _track[l];
- if (s.isLoading()) {
- track->reset();
- }
- syncWithSerializer(s, *track);
- if (s.isLoading()) {
- _track[l]->trackId = l;
- if (!track->used)
- continue;
- if ((track->toBeRemoved) || (track->souStreamUsed) || (track->curRegion == -1)) {
- track->used = false;
- continue;
- }
-
- // TODO: The code below has a lot in common with that in IMuseDigital::startSound.
- // Try to refactor them to reduce the code duplication.
-
- track->soundDesc = _sound->openSound(track->soundId, track->soundName, track->soundType, track->volGroupId, -1);
- if (!track->soundDesc)
- track->soundDesc = _sound->openSound(track->soundId, track->soundName, track->soundType, track->volGroupId, 1);
- if (!track->soundDesc)
- track->soundDesc = _sound->openSound(track->soundId, track->soundName, track->soundType, track->volGroupId, 2);
-
- if (!track->soundDesc) {
- warning("IMuseDigital::saveOrLoad: Can't open sound so will not be resumed");
- track->used = false;
- continue;
- }
-
- if (_vm->_game.id == GID_CMI) {
- track->gainRedFadeDest = 127 * 290;
- }
-
- if (_vm->_game.id == GID_FT) {
- track->gainRedFadeDest = 127 * 180;
- }
-
- track->sndDataExtComp = _sound->isSndDataExtComp(track->soundDesc);
- track->dataOffset = _sound->getRegionOffset(track->soundDesc, track->curRegion);
- int bits = _sound->getBits(track->soundDesc);
- int channels = _sound->getChannels(track->soundDesc);
- int freq = _sound->getFreq(track->soundDesc);
- track->feedSize = freq * channels;
- track->mixerFlags = 0;
- track->littleEndian = track->soundDesc->littleEndian;
- if (channels == 2)
- track->mixerFlags = kFlagStereo;
-
- if ((bits == 12) || (bits == 16)) {
- track->mixerFlags |= kFlag16Bits;
- track->feedSize *= 2;
- } else if (bits == 8) {
- track->mixerFlags |= kFlagUnsigned;
- } else
- error("IMuseDigital::saveOrLoad(): Can't handle %d bit samples", bits);
-
- track->stream = Audio::makeQueuingAudioStream(freq, (track->mixerFlags & kFlagStereo) != 0);
-
- _mixer->playStream(track->getType(), &track->mixChanHandle, track->stream, -1, track->getVol(), track->getPan());
- _mixer->pauseHandle(track->mixChanHandle, true);
- }
- }
-}
-
-void IMuseDigital::callback() {
- Common::StackLock lock(_mutex, "IMuseDigital::callback()");
- _speechIsPlaying = false;
- // Check for any track playing a speech line
- if (_vm->_game.id != GID_DIG) {
- for (int l = 0; l < MAX_DIGITAL_TRACKS; l++) {
- if (_track[l]->used && _track[l]->soundId == kTalkSoundID) {
- // Set flag and break
- _speechIsPlaying = true;
- break;
- }
- }
- }
-
- for (int l = 0; l < MAX_DIGITAL_TRACKS + MAX_DIGITAL_FADETRACKS; l++) {
- Track *track = _track[l];
- if (track->used) {
- // Ignore tracks which are about to finish. Also, if it did finish in the meantime,
- // mark it as unused.
- if (!track->stream) {
- if (!_mixer->isSoundHandleActive(track->mixChanHandle))
- track->reset();
- continue;
- }
-
- if (_pause)
- return;
-
- // Fades and crossfades handling
- if (track->volFadeUsed) {
- if (track->vol == track->volFadeDest) // Sanity check (needed for some edge cases in COMI, FT and DIG)
- track->volFadeUsed = false;
- if (track->volFadeStep < 0) { // Fade out
- if (track->vol > track->volFadeDest) {
- // COMI uses non-linear fade curves
- if (_vm->_game.id == GID_CMI) {
- int tempVolume = transformVolumeEqualPowToLinear(track->vol, 1); // Equal power to linear...
- tempVolume += track->volFadeStep; // Remove step...
- track->vol = transformVolumeLinearToEqualPow(tempVolume, 1); // Linear to equal power...
- } else {
- track->vol += track->volFadeStep; // Remove step...
- }
- if (track->vol <= track->volFadeDest) {
- track->vol = track->volFadeDest;
- track->volFadeUsed = false;
- // In COMI we flush the track if we've faded out to the destination volume;
- // this is because there are no situations in which there is a fade out to a
- // non-zero volume, so this will always mean that we can free the track.
- // This is not true in Full Throttle, for example, so we have to make this distinction.
- if (_vm->_game.id == GID_CMI) {
- flushTrack(track);
- continue;
- }
- }
- if (track->vol == 0) {
- // Fade out complete -> remove this track
- flushTrack(track);
- continue;
- }
- }
- } else if (track->volFadeStep > 0) { // Fade in
- if (track->vol < track->volFadeDest) {
- // Again, COMI uses non-linear fade curves
- // Curiously, FT uses a different curve for fade-ins
- if (_vm->_game.id == GID_CMI) {
- int tempVolume = transformVolumeEqualPowToLinear(track->vol, 1); // Equal power to linear...
- tempVolume += track->volFadeStep; // Add step...
- track->vol = transformVolumeLinearToEqualPow(tempVolume, 1); // Linear to equal power...
- } else if (_vm->_game.id == GID_FT) {
- int tempVolume = transformVolumeEqualPowToLinear(track->vol, 6); // Equal power to linear...
- tempVolume += (track->volFadeStep); // Add step...
- track->vol = transformVolumeLinearToEqualPow(tempVolume, 6); // Linear to equal power...
- } else {
- track->vol += track->volFadeStep; // Add step...
- }
- if (track->vol >= track->volFadeDest) {
- track->vol = track->volFadeDest;
- track->volFadeUsed = false;
- }
- }
- }
- debug(5, "Fade: sound(%d), Vol(%d) in track(%d)", track->soundId, track->vol / 1000, track->trackId);
- }
-
- // Music gain reduction during speech (used, at the moment, on FT and COMI)
- if (_vm->_game.id != GID_DIG && track->volGroupId == IMUSE_VOLGRP_MUSIC) {
- if (_speechIsPlaying) {
- // Check if we have to fade down or the reduction volume is already at the right value
- if (track->gainReduction >= track->gainRedFadeDest) {
- track->gainRedFadeUsed = false;
- track->gainReduction = track->gainRedFadeDest; // Clip to destination volume
- } else {
- track->gainRedFadeUsed = true;
- }
- // Gradually bring up the gain reduction (20 ms)
- if (track->gainRedFadeUsed && track->gainReduction < track->gainRedFadeDest) {
- int tempReduction = transformVolumeEqualPowToLinear(track->gainReduction, 2); // Equal power to linear...
- tempReduction += (track->gainRedFadeDest - track->gainReduction) * 60 * (1000 / _callbackFps) / (1000 * 20); // Add step...
- track->gainReduction = transformVolumeLinearToEqualPow(tempReduction, 2); // Linear to equal power...
- debug(5, "Gain reduction: sound(%d), reduction amount(%d) in track(%d)", track->soundId, track->gainReduction / 1000, track->trackId);
- }
- } else if (!_speechIsPlaying && track->gainReduction > 0) {
- // Just like the original interpreter, disable gain reduction immediately without a fade
- track->gainReduction = 0;
- debug(5, "Gain reduction: no speech playing reduction stopped for sound(%d) in track(%d)", track->soundId, track->trackId);
- }
- }
-
- if (!track->souStreamUsed) {
- assert(track->stream);
- byte *tmpSndBufferPtr = NULL;
- int32 curFeedSize = 0;
-
- if (track->curRegion == -1) {
- switchToNextRegion(track);
- if (!track->stream) // Seems we reached the end of the stream
- continue;
- }
-
- int bits = _sound->getBits(track->soundDesc);
- int channels = _sound->getChannels(track->soundDesc);
-
- int32 feedSize = track->feedSize / _callbackFps;
-
- if (track->stream->endOfData()) {
- feedSize *= 2;
- }
-
- if ((bits == 12) || (bits == 16)) {
- if (channels == 1)
- feedSize &= ~1;
- if (channels == 2)
- feedSize &= ~3;
- } else if (bits == 8) {
- if (channels == 2)
- feedSize &= ~1;
- } else {
- warning("IMuseDigita::callback: Unexpected sample width, %d bits", bits);
- continue;
- }
-
- if (feedSize == 0)
- continue;
-
- do {
- if (bits == 12) {
- byte *tmpPtr = NULL;
-
- feedSize += track->dataMod12Bit;
- int tmpFeedSize12Bits = (feedSize * 3) / 4;
- int tmpLength12Bits = (tmpFeedSize12Bits / 3) * 4;
- track->dataMod12Bit = feedSize - tmpLength12Bits;
-
- int32 tmpOffset = (track->regionOffset * 3) / 4;
- int tmpFeedSize = _sound->getDataFromRegion(track->soundDesc, track->curRegion, &tmpPtr, tmpOffset, tmpFeedSize12Bits);
- curFeedSize = BundleCodecs::decode12BitsSample(tmpPtr, &tmpSndBufferPtr, tmpFeedSize);
-
- free(tmpPtr);
- } else if (bits == 16) {
- curFeedSize = _sound->getDataFromRegion(track->soundDesc, track->curRegion, &tmpSndBufferPtr, track->regionOffset, feedSize);
- if (channels == 1) {
- curFeedSize &= ~1;
- }
- if (channels == 2) {
- curFeedSize &= ~3;
- }
- } else if (bits == 8) {
- curFeedSize = _sound->getDataFromRegion(track->soundDesc, track->curRegion, &tmpSndBufferPtr, track->regionOffset, feedSize);
- if (_radioChatterSFX && track->soundId == 10000) {
- if (curFeedSize > feedSize)
- curFeedSize = feedSize;
- byte *buf = (byte *)malloc(curFeedSize);
- int index = 0;
- int count = curFeedSize - 4;
- byte *ptr_1 = tmpSndBufferPtr;
- byte *ptr_2 = tmpSndBufferPtr + 4;
- int value = ptr_1[0] - 0x80;
- value += ptr_1[1] - 0x80;
- value += ptr_1[2] - 0x80;
- value += ptr_1[3] - 0x80;
- do {
- int t = *ptr_1++;
- int v = t - (value / 4);
- value = *ptr_2++ - 0x80 + (value - t + 0x80);
- buf[index++] = v * 2 + 0x80;
- } while (--count);
- buf[curFeedSize - 1] = 0x80;
- buf[curFeedSize - 2] = 0x80;
- buf[curFeedSize - 3] = 0x80;
- buf[curFeedSize - 4] = 0x80;
- free(tmpSndBufferPtr);
- tmpSndBufferPtr = buf;
- }
- if (channels == 2) {
- curFeedSize &= ~1;
- }
- }
-
- if (curFeedSize > feedSize)
- curFeedSize = feedSize;
-
- if (_mixer->isReady()) {
- track->stream->queueBuffer(tmpSndBufferPtr, curFeedSize, DisposeAfterUse::YES, makeMixerFlags(track));
- track->regionOffset += curFeedSize;
- } else
- free(tmpSndBufferPtr);
-
- if (_sound->isEndOfRegion(track->soundDesc, track->curRegion)) {
- switchToNextRegion(track);
- if (!track->stream) // Seems we reached the end of the stream
- break;
- }
- feedSize -= curFeedSize;
- assert(feedSize >= 0);
- } while (feedSize != 0);
- }
- if (_mixer->isReady()) {
- int effVol = track->getVol();
- int effPan = track->getPan();
- if (_vm->_game.id == GID_CMI) {
- // Adjust audio mix for The Curse of Monkey Island
- if (track->volGroupId == IMUSE_VOLGRP_MUSIC) {
- effVol -= track->gainReduction / 1000;
- if (effVol < 0) // In case a music crossfading happens during gain reduction...
- effVol = 0;
- effVol = int(round(effVol * 1.9)); // Adjust default music mix for COMI
- } else if (track->volGroupId == IMUSE_VOLGRP_VOICE) {
- // Just in case the speakingActor is not being set...
- // This allows for a fallback to pan = 64 (center) and volume = 127 (full)
- if (track->speakingActor != nullptr) {
- if (track->speakingActor->_talkVolume <= 127)
- effVol = track->speakingActor->_talkVolume;
-
- effVol = int(round(effVol * 1.04));
- effPan = (track->speakingActor->_talkPan != 64) ? 2 * track->speakingActor->_talkPan - 127 : 0;
- }
- }
- } else if (_vm->_game.id == GID_FT) {
- // Adjust audio mix for Full Throttle
- // (this affects music and sfx only, speech volume mix
- // is changed accordingly in IMuseDigital::startSound()
- // since that's the only handle we have for speech)
- if (track->volGroupId == IMUSE_VOLGRP_MUSIC) {
- // Gain reduction
- effVol -= track->gainReduction / 1000;
- if (effVol < 0) // In case a music crossfading happens during gain reduction...
- effVol = 0;
- effVol = int(round(effVol * 1.5));
- } else {
- effVol = int(round(effVol * 1.1));
- }
- }
- _mixer->setChannelVolume(track->mixChanHandle, effVol);
- _mixer->setChannelBalance(track->mixChanHandle, effPan);
- }
- }
- }
-}
-
-void IMuseDigital::switchToNextRegion(Track *track) {
- assert(track);
-
- if (track->trackId >= MAX_DIGITAL_TRACKS) {
- flushTrack(track);
- debug(5, "SwToNeReg(trackId:%d) - fadetrack can't go next region, exiting SwToNeReg", track->trackId);
- return;
- }
-
- int num_regions = _sound->getNumRegions(track->soundDesc);
-
- if (++track->curRegion == num_regions) {
- flushTrack(track);
- debug(5, "SwToNeReg(trackId:%d) - end of region, exiting SwToNeReg", track->trackId);
- return;
- }
-
- ImuseDigiSndMgr::SoundDesc *soundDesc = track->soundDesc;
- if (_triggerUsed && track->soundDesc->numMarkers) {
- if (_sound->checkForTriggerByRegionAndMarker(soundDesc, track->curRegion, _triggerParams.marker)) {
- if (_vm->_game.id != GID_CMI) {
- debug(5, "SwToNeReg(trackId:%d) - trigger %s reached", track->trackId, _triggerParams.marker);
- debug(5, "SwToNeReg(trackId:%d) - exit current region %d", track->trackId, track->curRegion);
- debug(5, "SwToNeReg(trackId:%d) - call cloneToFadeOutTrack(delay:%d)", track->trackId, _triggerParams.fadeOutDelay);
- Track *fadeTrack = cloneToFadeOutTrack(track, _triggerParams.fadeOutDelay);
- if (fadeTrack) {
- fadeTrack->dataOffset = _sound->getRegionOffset(fadeTrack->soundDesc, fadeTrack->curRegion);
- fadeTrack->regionOffset = 0;
- debug(5, "SwToNeReg(trackId:%d)-sound(%d) select region %d, curHookId: %d", fadeTrack->trackId, fadeTrack->soundId, fadeTrack->curRegion, fadeTrack->curHookId);
- fadeTrack->curHookId = 0;
- }
- flushTrack(track);
- startMusic(_triggerParams.filename, _triggerParams.soundId, _triggerParams.hookId, _triggerParams.volume);
- _triggerUsed = false;
- return;
- } else {
- // Behavior for "_end" (and "exit") marker
- debug(5, "SwToNeReg(trackId:%d) - trigger %s reached", track->trackId, _triggerParams.marker);
- debug(5, "SwToNeReg(trackId:%d) - exit current region %d", track->trackId, track->curRegion);
- debug(5, "SwToNeReg(trackId:%d) - call handleComiFadeOut(delay:%d)", track->trackId, _triggerParams.fadeOutDelay);
- handleFadeOut(track, _triggerParams.fadeOutDelay);
- track->dataOffset = _sound->getRegionOffset(track->soundDesc, track->curRegion);
- track->regionOffset = 0;
- debug(5, "SwToNeReg(trackId:%d)-sound(%d) select region %d, curHookId: %d", track->trackId, track->soundId, track->curRegion, track->curHookId);
- track->curHookId = 0;
- if (!scumm_stricmp(_triggerParams.marker, "exit"))
- startMusic(_triggerParams.filename, _triggerParams.soundId, _triggerParams.hookId, _triggerParams.volume);
- _triggerUsed = false;
- return;
- }
- }
- }
-
- int jumpId = _sound->getJumpIdByRegionAndHookId(soundDesc, track->curRegion, track->curHookId);
- if ((_vm->_game.id != GID_CMI && jumpId != -1) || (_vm->_game.id == GID_CMI && jumpId != -1 && !track->toBeRemoved && !track->alreadyCrossfading)) {
- int region = _sound->getRegionIdByJumpId(soundDesc, jumpId);
- assert(region != -1);
- int sampleHookId = _sound->getJumpHookId(soundDesc, jumpId);
- assert(sampleHookId != -1);
-
- bool isJumpToStart = false;
- if (_vm->_game.id == GID_CMI) {
- isJumpToStart = (soundDesc->jump[jumpId].dest == soundDesc->marker[2].pos && !scumm_stricmp(soundDesc->marker[2].ptr, "start"));
- }
-
- debug(5, "SwToNeReg(trackId:%d) - JUMP found - sound:%d, track hookId:%d, data hookId:%d", track->trackId, track->soundId, track->curHookId, sampleHookId);
- if (track->curHookId == sampleHookId) {
- int fadeDelay = (60 * _sound->getJumpFade(soundDesc, jumpId)) / 1000;
- debug(5, "SwToNeReg(trackId:%d) - sound(%d) match hookId", track->trackId, track->soundId);
-
- if (_vm->_game.id == GID_CMI) {
- // The original exe crossfades the jump to the destination region.
- // For the time being this is implemented as a simple region switch.
- track->curRegion = region;
- } else {
- if (fadeDelay) {
- debug(5, "SwToNeReg(trackId:%d) - call cloneToFadeOutTrack(delay:%d)", track->trackId, fadeDelay);
- Track *fadeTrack = cloneToFadeOutTrack(track, fadeDelay);
- if (fadeTrack) {
- fadeTrack->dataOffset = _sound->getRegionOffset(fadeTrack->soundDesc, fadeTrack->curRegion);
- fadeTrack->regionOffset = 0;
- debug(5, "SwToNeReg(trackId:%d) - sound(%d) faded track, select region %d, curHookId: %d", fadeTrack->trackId, fadeTrack->soundId, fadeTrack->curRegion, fadeTrack->curHookId);
- fadeTrack->curHookId = 0;
- }
- } else {
- track->curRegion = region;
- }
- }
-
- debug(5, "SwToNeReg(trackId:%d) - sound(%d) jump to region %d, curHookId: %d", track->trackId, track->soundId, track->curRegion, track->curHookId);
- track->curHookId = 0;
- } else {
- // Check if the jump led to a "start" marker; if so, we have to enforce it anyway.
- // Fixes bug/edge-case #11956;
- // Go see ImuseDigiSndMgr::getJumpIdByRegionAndHookId(...) for further information.
- if (_vm->_game.id == GID_CMI && isJumpToStart) {
- track->curRegion = region;
- debug(5, "SwToNeReg(trackId:%d) - Enforced sound(%d) jump to region %d marked with a \"start\" marker, hookId(%d)", track->trackId, track->soundId, track->curRegion, track->curHookId);
- } else {
- debug(5, "SwToNeReg(trackId:%d) - Normal switch region, sound(%d), hookId(%d)", track->trackId, track->soundId, track->curHookId);
- }
- }
- } else {
- debug(5, "SwToNeReg(trackId:%d) - Normal switch region, sound(%d), hookId(%d)", track->trackId, track->soundId, track->curHookId);
- }
- debug(5, "SwToNeReg(trackId:%d) - sound(%d), select region %d", track->trackId, track->soundId, track->curRegion);
- track->dataOffset = _sound->getRegionOffset(soundDesc, track->curRegion);
- track->regionOffset = 0;
- debug(5, "SwToNeReg(trackId:%d) - end of func", track->trackId);
-}
-
-} // End of namespace Scumm
diff --git a/engines/scumm/imuse_digi/dimuse.h b/engines/scumm/imuse_digi/dimuse.h
deleted file mode 100644
index d6d05d0a0f..0000000000
--- a/engines/scumm/imuse_digi/dimuse.h
+++ /dev/null
@@ -1,175 +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 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#if !defined(SCUMM_IMUSE_DIGI_H) && defined(ENABLE_SCUMM_7_8)
-#define SCUMM_IMUSE_DIGI_H
-
-#include "common/scummsys.h"
-#include "common/mutex.h"
-#include "common/serializer.h"
-#include "common/textconsole.h"
-#include "common/util.h"
-
-#include "scumm/imuse_digi/dimuse.h"
-#include "scumm/imuse_digi/dimuse_bndmgr.h"
-#include "scumm/imuse_digi/dimuse_sndmgr.h"
-#include "scumm/music.h"
-#include "scumm/sound.h"
-
-namespace Audio {
-class AudioStream;
-class Mixer;
-}
-
-namespace Scumm {
-
-enum {
- MAX_DIGITAL_TRACKS = 8,
- MAX_DIGITAL_FADETRACKS = 8
-};
-
-struct imuseDigTable;
-struct imuseComiTable;
-class ScummEngine_v7;
-struct Track;
-
-class IMuseDigital : public MusicEngine {
-private:
-
- int _callbackFps; // value how many times callback needs to be called per second
-
- struct TriggerParams {
- char marker[10];
- int fadeOutDelay;
- char filename[13];
- int soundId;
- int hookId;
- int volume;
- };
-
- TriggerParams _triggerParams;
- bool _triggerUsed;
-
- Track *_track[MAX_DIGITAL_TRACKS + MAX_DIGITAL_FADETRACKS];
-
- Common::Mutex _mutex;
- ScummEngine_v7 *_vm;
- Audio::Mixer *_mixer;
- ImuseDigiSndMgr *_sound;
-
- char *_audioNames; // filenames of sound SFX used in FT
- int32 _numAudioNames; // number of above filenames
-
- bool _pause; // flag mean that iMuse callback should be idle
-
- int32 _attributes[188]; // internal attributes for each music file to store and check later
- int32 _nextSeqToPlay; // id of sequence type of music needed played
- int32 _curMusicState; // current or previous id of music
- int32 _curMusicSeq; // current or previous id of sequence music
- int32 _curMusicCue; // current cue for current music. used in FT
- int _stopingSequence;
- bool _radioChatterSFX;
- bool _speechIsPlaying;
-
- static void timer_handler(void *refConf);
- void callback();
- void switchToNextRegion(Track *track);
- int allocSlot(int priority);
- int startSound(int soundId, const char *soundName, int soundType, int volGroupId, Audio::AudioStream *input, int hookId, int volume, int priority, Track *otherTrack);
- void selectVolumeGroup(int soundId, int volGroupId);
-
- int32 getPosInMs(int soundId);
- void getLipSync(int soundId, int syncId, int32 msPos, int32 &width, int32 &height);
-
- int getSoundIdByName(const char *soundName);
- void fadeOutMusic(int fadeDelay);
- void fadeOutMusicAndStartNew(int fadeDelay, const char *filename, int soundId);
- void setTrigger(TriggerParams *trigger);
- void setHookIdForMusic(int hookId);
- Track *cloneToFadeOutTrack(Track *track, int fadeDelay);
- Track *handleFadeOut(Track *track, int fadeDelay);
- int transformVolumeLinearToEqualPow(int volume, int mode);
- int transformVolumeEqualPowToLinear(int volume, int mode);
-
- void setFtMusicState(int stateId);
- void setFtMusicSequence(int seqId);
- void setFtMusicCuePoint(int cueId);
- void playFtMusic(const char *songName, int opcode, int volume);
-
- void setComiMusicState(int stateId);
- void setComiMusicSequence(int seqId);
- void setComiDemoMusicState(int stateId);
- void playComiMusic(const char *songName, const imuseComiTable *table, int attribPos, bool sequence);
- void playComiDemoMusic(const char *songName, const imuseComiTable *table, int attribPos);
-
- void setDigMusicState(int stateId);
- void setDigMusicSequence(int seqId);
- void playDigMusic(const char *songName, const imuseDigTable *table, int attribPos, bool sequence);
-
- void flushTrack(Track *track);
-
-public:
- IMuseDigital(ScummEngine_v7 *scumm, Audio::Mixer *mixer, int fps);
- ~IMuseDigital() override;
-
- void setAudioNames(int32 num, char *names);
-
- int startVoice(int soundId, Audio::AudioStream *input);
- int startVoice(int soundId, const char *soundName);
- int startMusic(int soundId, int volume);
- int startMusic(const char *soundName, int soundId, int hookId, int volume);
- int startMusicWithOtherPos(const char *soundName, int soundId, int hookId, int volume, Track *otherTrack);
- int startSfx(int soundId, int priority);
- void startSound(int sound) override
- { error("IMuseDigital::startSound(int) should be never called"); }
-
- void saveLoadEarly(Common::Serializer &ser);
- void resetState();
- void setRadioChatterSFX(bool state) {
- _radioChatterSFX = state;
- }
-
- void setPriority(int soundId, int priority);
- void setVolume(int soundId, int volume);
- void setPan(int soundId, int pan);
- void setFade(int soundId, int destVolume, int delay60HzTicks);
- int getCurMusicSoundId();
- void setHookId(int soundId, int hookId);
- void setMusicVolume(int vol) override {}
- void stopSound(int sound) override;
- void stopAllSounds() override;
- void pause(bool pause);
- void parseScriptCmds(int cmd, int soundId, int sub_cmd, int d, int e, int f, int g, int h);
- void refreshScripts();
- void flushTracks();
- int getSoundStatus(int sound) const override;
- int32 getCurMusicPosInMs();
- int32 getCurVoiceLipSyncWidth();
- int32 getCurVoiceLipSyncHeight();
- int32 getCurMusicLipSyncWidth(int syncId);
- int32 getCurMusicLipSyncHeight(int syncId);
- int32 getSoundElapsedTimeInMs(int soundId);
-};
-
-} // End of namespace Scumm
-
-#endif
diff --git a/engines/scumm/imuse_digi/dimuse_bndmgr.cpp b/engines/scumm/imuse_digi/dimuse_bndmgr.cpp
index e7602528ad..8b115cc7cc 100644
--- a/engines/scumm/imuse_digi/dimuse_bndmgr.cpp
+++ b/engines/scumm/imuse_digi/dimuse_bndmgr.cpp
@@ -132,7 +132,7 @@ int BundleDirCache::matchFile(const char *filename) {
_budleDirCache[freeSlot].indexTable[i].index = i;
}
qsort(_budleDirCache[freeSlot].indexTable, _budleDirCache[freeSlot].numFiles,
- sizeof(IndexNode), (int (*)(const void*, const void*))scumm_stricmp);
+ sizeof(IndexNode), (int (*)(const void *, const void *))scumm_stricmp);
return freeSlot;
} else {
return fileId;
@@ -145,6 +145,7 @@ BundleMgr::BundleMgr(BundleDirCache *cache) {
_compTable = nullptr;
_numFiles = 0;
_numCompItems = 0;
+ _lastBlockDecompressedSize = 0;
_curSampleId = -1;
_fileBundleId = -1;
_file = new ScummFile();
@@ -160,7 +161,7 @@ Common::SeekableReadStream *BundleMgr::getFile(const char *filename, int32 &offs
BundleDirCache::IndexNode target;
strcpy(target.filename, filename);
BundleDirCache::IndexNode *found = (BundleDirCache::IndexNode *)bsearch(&target, _indexTable, _numFiles,
- sizeof(BundleDirCache::IndexNode), (int (*)(const void*, const void*))scumm_stricmp);
+ sizeof(BundleDirCache::IndexNode), (int (*)(const void *, const void *))scumm_stricmp);
if (found) {
_file->seek(_bundleTable[found->index].offset, SEEK_SET);
offset = _bundleTable[found->index].offset;
@@ -171,7 +172,7 @@ Common::SeekableReadStream *BundleMgr::getFile(const char *filename, int32 &offs
return nullptr;
}
-bool BundleMgr::open(const char *filename, bool &compressed, bool errorFlag) {
+bool BundleMgr::open(const char *filename, bool &isCompressed, bool errorFlag) {
if (_file->isOpen())
return true;
@@ -186,7 +187,7 @@ bool BundleMgr::open(const char *filename, bool &compressed, bool errorFlag) {
int slot = _cache->matchFile(filename);
assert(slot != -1);
- compressed = _cache->isSndDataExtComp(slot);
+ isCompressed = _cache->isSndDataExtComp(slot);
_numFiles = _cache->getNumFiles(slot);
assert(_numFiles);
_bundleTable = _cache->getTable(slot);
@@ -195,6 +196,8 @@ bool BundleMgr::open(const char *filename, bool &compressed, bool errorFlag) {
_compTableLoaded = false;
_isUncompressed = false;
_outputSize = 0;
+ _lastBlockDecompressedSize = 0;
+ _curDecompressedFilePos = 0;
_lastBlock = -1;
return true;
@@ -206,6 +209,8 @@ void BundleMgr::close() {
_bundleTable = nullptr;
_numFiles = 0;
_numCompItems = 0;
+ _lastBlockDecompressedSize = 0;
+ _curDecompressedFilePos = 0;
_compTableLoaded = false;
_isUncompressed = false;
_lastBlock = -1;
@@ -229,8 +234,8 @@ bool BundleMgr::loadCompTable(int32 index) {
_numCompItems = _file->readUint32BE();
assert(_numCompItems > 0);
- _file->seek(8, SEEK_CUR);
-
+ _file->seek(4, SEEK_CUR);
+ _lastBlockDecompressedSize = _file->readUint32BE();
if (tag != MKTAG('C','O','M','P')) {
debug("BundleMgr::loadCompTable() Compressed sound %d (%s:%d) invalid (%s)", index, _file->getName(), _bundleTable[index].offset, tag2str(tag));
return false;
@@ -254,122 +259,165 @@ bool BundleMgr::loadCompTable(int32 index) {
return true;
}
-int32 BundleMgr::decompressSampleByCurIndex(int32 offset, int32 size, byte **compFinal, int headerSize, bool headerOutside) {
- bool ignored = false;
- return decompressSampleByIndex(_curSampleId, offset, size, compFinal, headerSize, headerOutside, ignored);
+int32 BundleMgr::seekFile(int32 offset, int mode) {
+ // We don't actually seek the file, but instead try to find that the specified offset exists
+ // within the decompressed blocks, and save that offset in _curDecompressedFilePos
+ int result = 0;
+ switch (mode) {
+ case SEEK_END:
+ if (_isUncompressed) {
+ result = offset + _bundleTable[_curSampleId].size;
+ } else {
+ result = offset + ((_numCompItems - 1) * DIMUSE_BUN_CHUNK_SIZE) + _lastBlockDecompressedSize;
+ }
+ _curDecompressedFilePos = result;
+ break;
+ case SEEK_SET:
+ default:
+ if (_isUncompressed) {
+ result = offset;
+ _curDecompressedFilePos = result;
+ } else {
+ int destBlock = offset / DIMUSE_BUN_CHUNK_SIZE + (offset % DIMUSE_BUN_CHUNK_SIZE != 0);
+ if (destBlock <= _numCompItems) {
+ result = offset;
+ _curDecompressedFilePos = result;
+ }
+ }
+ break;
+ }
+ return result;
}
-int32 BundleMgr::decompressSampleByIndex(int32 index, int32 offset, int32 size, byte **compFinal, int headerSize, bool headerOutside,
- bool &uncompressedBundle) {
- int32 i, finalSize, outputSize;
- int skip, firstBlock, lastBlock;
-
- assert(0 <= index && index < _numFiles);
+int32 BundleMgr::readFile(const char *name, int32 size, byte **comp_final, bool header_outside) {
+ int32 final_size = 0;
- if (_file->isOpen() == false) {
- error("BundleMgr::decompressSampleByIndex() File is not open");
+ if (!_file->isOpen()) {
+ error("BundleMgr::readFile() File is not open");
return 0;
}
- if (_curSampleId == -1)
- _curSampleId = index;
+ // Find the sound in the bundle
+ BundleDirCache::IndexNode target;
+ strncpy(target.filename, name, sizeof(target.filename));
+ target.filename[sizeof(target.filename) - 1] = '\0';
+ BundleDirCache::IndexNode *found = (BundleDirCache::IndexNode *)bsearch(&target, _indexTable, _numFiles,
+ sizeof(BundleDirCache::IndexNode), (int(*)(const void *, const void *))scumm_stricmp);
- assert(_curSampleId == index);
+ if (found) {
+ int32 i, finalSize, outputSize;
+ int skip, firstBlock, lastBlock;
+ int headerSize = 0;
+
+ assert(0 <= found->index && found->index < _numFiles);
- if (!_compTableLoaded) {
- _compTableLoaded = loadCompTable(index);
- if (!_compTableLoaded)
+ if (_file->isOpen() == false) {
+ error("BundleMgr::readFile() File is not open");
return 0;
- }
+ }
- uncompressedBundle = _isUncompressed;
+ if (_curSampleId == -1)
+ _curSampleId = found->index;
- if (_isUncompressed) {
- _file->seek(_bundleTable[index].offset + offset + headerSize, SEEK_SET);
- *compFinal = (byte *)malloc(size);
- assert(*compFinal);
- _file->read(*compFinal, size);
- return size;
- }
+ assert(_curSampleId == found->index);
- firstBlock = (offset + headerSize) / 0x2000;
- lastBlock = (offset + headerSize + size - 1) / 0x2000;
-
- // Clip last_block by the total number of blocks (= "comp items")
- if ((lastBlock >= _numCompItems) && (_numCompItems > 0))
- lastBlock = _numCompItems - 1;
-
- int32 blocksFinalSize = 0x2000 * (1 + lastBlock - firstBlock);
- *compFinal = (byte *)malloc(blocksFinalSize);
- assert(*compFinal);
- finalSize = 0;
-
- skip = (offset + headerSize) % 0x2000;
-
- for (i = firstBlock; i <= lastBlock; i++) {
- if (_lastBlock != i) {
- // CMI hack: one more zero byte at the end of input buffer
- _compInputBuff[_compTable[i].size] = 0;
- _file->seek(_bundleTable[index].offset + _compTable[i].offset, SEEK_SET);
- _file->read(_compInputBuff, _compTable[i].size);
- _outputSize = BundleCodecs::decompressCodec(_compTable[i].codec, _compInputBuff, _compOutputBuff, _compTable[i].size);
- if (_outputSize > 0x2000) {
- error("_outputSize: %d", _outputSize);
- }
- _lastBlock = i;
+ if (!_compTableLoaded) {
+ _compTableLoaded = loadCompTable(found->index);
+ if (!_compTableLoaded)
+ return 0;
}
- outputSize = _outputSize;
+ if (_isUncompressed) {
+ _file->seek(_bundleTable[found->index].offset + _curDecompressedFilePos + headerSize, SEEK_SET);
+ *comp_final = (byte *)malloc(size);
+ assert(*comp_final);
+ _file->read(*comp_final, size);
+ _curDecompressedFilePos += size;
+ return size;
+ }
- if (headerOutside) {
- outputSize -= skip;
- } else {
- if ((headerSize != 0) && (skip >= headerSize))
+ firstBlock = (_curDecompressedFilePos + headerSize) / DIMUSE_BUN_CHUNK_SIZE;
+ lastBlock = (_curDecompressedFilePos + headerSize + size - 1) / DIMUSE_BUN_CHUNK_SIZE;
+
+ // Clip last_block by the total number of blocks (= "comp items")
+ if ((lastBlock >= _numCompItems) && (_numCompItems > 0))
+ lastBlock = _numCompItems - 1;
+
+ int32 blocksFinalSize = DIMUSE_BUN_CHUNK_SIZE * (1 + lastBlock - firstBlock);
+ *comp_final = (byte *)malloc(blocksFinalSize);
+ assert(*comp_final);
+ finalSize = 0;
+
+ skip = (_curDecompressedFilePos + headerSize) % DIMUSE_BUN_CHUNK_SIZE; // Excess length after the last block
+
+ for (i = firstBlock; i <= lastBlock; i++) {
+ if (_lastBlock != i) {
+ // CMI hack: one more zero byte at the end of input buffer
+ _compInputBuff[_compTable[i].size] = 0;
+ _file->seek(_bundleTable[found->index].offset + _compTable[i].offset, SEEK_SET);
+ _file->read(_compInputBuff, _compTable[i].size);
+ _outputSize = BundleCodecs::decompressCodec(_compTable[i].codec, _compInputBuff, _compOutputBuff, _compTable[i].size);
+
+ if (_outputSize > DIMUSE_BUN_CHUNK_SIZE) {
+ error("_outputSize: %d", _outputSize);
+ }
+ _lastBlock = i;
+ }
+
+ outputSize = _outputSize;
+
+ if (header_outside) {
outputSize -= skip;
- }
+ } else {
+ if ((headerSize != 0) && (skip >= headerSize))
+ outputSize -= skip;
+ }
- if ((outputSize + skip) > 0x2000) // workaround
- outputSize -= (outputSize + skip) - 0x2000;
+ if ((outputSize + skip) > DIMUSE_BUN_CHUNK_SIZE) // workaround
+ outputSize -= (outputSize + skip) - DIMUSE_BUN_CHUNK_SIZE;
- if (outputSize > size)
- outputSize = size;
+ if (outputSize > size)
+ outputSize = size;
- assert(finalSize + outputSize <= blocksFinalSize);
+ assert(finalSize + outputSize <= blocksFinalSize);
- memcpy(*compFinal + finalSize, _compOutputBuff + skip, outputSize);
- finalSize += outputSize;
+ memcpy(*comp_final + finalSize, _compOutputBuff + skip, outputSize);
+ finalSize += outputSize;
- size -= outputSize;
- assert(size >= 0);
- if (size == 0)
- break;
+ size -= outputSize;
+ assert(size >= 0);
+ if (size == 0)
+ break;
+
+ skip = 0;
+ }
+ _curDecompressedFilePos += finalSize;
- skip = 0;
+ return finalSize;
}
- return finalSize;
+ debug(2, "BundleMgr::readFile() Failed finding sound %s", name);
+ return final_size;
}
-int32 BundleMgr::decompressSampleByName(const char *name, int32 offset, int32 size, byte **comp_final, bool header_outside,
- bool &uncompressedBundle) {
- int32 final_size = 0;
-
- if (!_file->isOpen()) {
- error("BundleMgr::decompressSampleByName() File is not open");
- return 0;
- }
+bool BundleMgr::isExtCompBun(byte gameId) {
+ bool isExtComp = false;
+ if (gameId == GID_CMI) {
+ bool isExtComp1 = false, isExtComp2 = false, isExtComp3 = false, isExtComp4 = false;
+ this->open("voxdisk1.bun", isExtComp1); this->close();
+ this->open("voxdisk2.bun", isExtComp2); this->close();
+ this->open("musdisk1.bun", isExtComp3); this->close();
+ this->open("musdisk2.bun", isExtComp4); this->close();
- BundleDirCache::IndexNode target;
- strcpy(target.filename, name);
- BundleDirCache::IndexNode *found = (BundleDirCache::IndexNode *)bsearch(&target, _indexTable, _numFiles,
- sizeof(BundleDirCache::IndexNode), (int (*)(const void*, const void*))scumm_stricmp);
- if (found) {
- final_size = decompressSampleByIndex(found->index, offset, size, comp_final, 0, header_outside, uncompressedBundle);
- return final_size;
+ isExtComp = isExtComp1 | isExtComp2 | isExtComp3 | isExtComp4;
+ } else {
+ bool isExtComp1 = false, isExtComp2 = false;
+ this->open("digvoice.bun", isExtComp1); this->close();
+ this->open("digmusic.bun", isExtComp2); this->close();
+ isExtComp = isExtComp1 | isExtComp2;
}
- debug(2, "BundleMgr::decompressSampleByName() Failed finding sound %s", name);
- return final_size;
+ return isExtComp;
}
} // End of namespace Scumm
diff --git a/engines/scumm/imuse_digi/dimuse_bndmgr.h b/engines/scumm/imuse_digi/dimuse_bndmgr.h
index ca7e434bf2..0a45db9f2e 100644
--- a/engines/scumm/imuse_digi/dimuse_bndmgr.h
+++ b/engines/scumm/imuse_digi/dimuse_bndmgr.h
@@ -25,6 +25,7 @@
#include "common/scummsys.h"
#include "common/file.h"
+#include "scumm/imuse_digi/dimuse_defs.h"
namespace Scumm {
@@ -81,7 +82,9 @@ private:
int _numFiles;
int _numCompItems;
+ int _lastBlockDecompressedSize;
int _curSampleId;
+ int _curDecompressedFilePos;
BaseScummFile *_file;
bool _compTableLoaded;
bool _isUncompressed;
@@ -90,7 +93,6 @@ private:
byte *_compInputBuff;
int _outputSize;
int _lastBlock;
-
bool loadCompTable(int32 index);
public:
@@ -98,12 +100,12 @@ public:
BundleMgr(BundleDirCache *_cache);
~BundleMgr();
- bool open(const char *filename, bool &compressed, bool errorFlag = false);
+ bool open(const char *filename, bool &isCompressed, bool errorFlag = false);
void close();
Common::SeekableReadStream *getFile(const char *filename, int32 &offset, int32 &size);
- int32 decompressSampleByName(const char *name, int32 offset, int32 size, byte **compFinal, bool headerOutside, bool &uncompressedBundle);
- int32 decompressSampleByIndex(int32 index, int32 offset, int32 size, byte **compFinal, int header_size, bool headerOutside, bool &uncompressedBundle);
- int32 decompressSampleByCurIndex(int32 offset, int32 size, byte **compFinal, int headerSize, bool headerOutside);
+ int32 seekFile(int32 offset, int size);
+ int32 readFile(const char *name, int32 size, byte **compFinal, bool headerOutside);
+ bool isExtCompBun(byte gameId);
};
} // End of namespace Scumm
diff --git a/engines/scumm/imuse_digi/dimuse_cmds.cpp b/engines/scumm/imuse_digi/dimuse_cmds.cpp
new file mode 100644
index 0000000000..cd88878acc
--- /dev/null
+++ b/engines/scumm/imuse_digi/dimuse_cmds.cpp
@@ -0,0 +1,271 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "scumm/imuse_digi/dimuse_engine.h"
+
+namespace Scumm {
+
+// We have some uintptr arguments as commands 28, 29 and 30 actually require pointer arguments
+// Unfortunately this makes function calls for other command a little less pretty...
+int IMuseDigital::cmdsHandleCmd(int cmd, uint8 *ptr, int a, int b, int c, int d, int e,
+ int f, int g, int h, int i, int j, int k, int l, int m, int n) {
+
+ // Convert the character constant (single quotes '') to string
+ char marker[5];
+ if (!_isEarlyDiMUSE && (cmd == 17 || cmd == 18 || cmd == 19)) {
+ for (int index = 0; index < 4; index++) {
+#if defined SCUMM_BIG_ENDIAN
+ marker[index] = (b >> (8 * index)) & 0xff;
+#elif defined SCUMM_LITTLE_ENDIAN
+ marker[3 - index] = (b >> (8 * index)) & 0xff;
+#endif
+ }
+ marker[4] = '\0';
+ }
+
+ switch (cmd) {
+ case 0:
+ return cmdsInit();
+ case 3:
+ return cmdsPause();
+ case 4:
+ return cmdsResume();
+ case 7:
+ _groupsHandler->setGroupVol(a, b);
+ break;
+ case 8:
+ cmdsStartSound(a, b);
+ break;
+ case 9:
+ cmdsStopSound(a);
+ break;
+ case 10:
+ cmdsStopAllSounds();
+ break;
+ case 11:
+ return cmdsGetNextSound(a);
+ case 12:
+ cmdsSetParam(a, b, c);
+ break;
+ case 13:
+ return cmdsGetParam(a, b);
+ case 14:
+ return _fadesHandler->fadeParam(a, b, c, d);
+ case 15:
+ return cmdsSetHook(a, b);
+ case 16:
+ return cmdsGetHook(a);
+ case 17:
+ return _triggersHandler->setTrigger(a, marker, c, d, e, f, g, h, i, j, k, l, m, n);
+ case 18:
+ return _triggersHandler->checkTrigger(a, marker, c);
+ case 19:
+ return _triggersHandler->clearTrigger(a, marker, c);
+ case 20:
+ return _triggersHandler->deferCommand(a, b, c, d, e, f, g, h, i, j, k, l, m, n);
+ case 25:
+ return waveStartStream(a, b, c);
+ case 26:
+ if (_isEarlyDiMUSE)
+ return waveSwitchStream(a, b, ptr, d, e);
+ else
+ return waveSwitchStream(a, b, c, d, e);
+ case 27:
+ return waveProcessStreams();
+ case 29:
+ return waveFeedStream(a, ptr, c, d);
+ default:
+ debug(5, "IMuseDigital::cmdsHandleCmd(): bogus/unused opcode ignored (%d).", cmd);
+ return -1;
+ }
+
+ return 0;
+}
+
+int IMuseDigital::cmdsInit() {
+ _cmdsRunning60HzCount = 0;
+ _cmdsRunning10HzCount = 0;
+
+ if (_groupsHandler->init() || _fadesHandler->init() ||
+ _triggersHandler->init() || waveInit()) {
+ return -1;
+ }
+
+ _cmdsPauseCount = 0;
+ return 48;
+}
+
+int IMuseDigital::cmdsDeinit() {
+ waveTerminate();
+ waveOutDeinit();
+ _triggersHandler->deinit();
+ _fadesHandler->deinit();
+ _cmdsPauseCount = 0;
+ _cmdsRunning60HzCount = 0;
+ _cmdsRunning10HzCount = 0;
+
+ return 0;
+}
+
+int IMuseDigital::cmdsTerminate() {
+ return 0;
+}
+
+int IMuseDigital::cmdsPause() {
+ int result = 0;
+
+ if (_cmdsPauseCount == 0) {
+ result = wavePause();
+ }
+
+ if (!result) {
+ result = _cmdsPauseCount + 1;
+ }
+ _cmdsPauseCount++;
+
+ return result;
+}
+
+int IMuseDigital::cmdsResume() {
+ int result = 0;
+
+ if (_cmdsPauseCount == 1) {
+ result = waveResume();
+ }
+
+ if (_cmdsPauseCount != 0) {
+ _cmdsPauseCount--;
+ }
+
+ if (!result) {
+ result = _cmdsPauseCount;
+ }
+
+ return result;
+}
+
+void IMuseDigital::cmdsSaveLoad(Common::Serializer &ser) {
+ // Serialize in this order:
+ // - Open files
+ // - Fades
+ // - Triggers
+ // - Pass the control to waveSaveLoad and then tracksSaveLoad, which will serialize:
+ // - Dispatches
+ // - Tracks (with SYNCs, if the game is COMI)
+ // - State and sequence info
+ // - Attributes
+ // - Full Throttle's music cue ID
+
+ _filesHandler->saveLoad(ser);
+ _fadesHandler->saveLoad(ser);
+ _triggersHandler->saveLoad(ser);
+ waveSaveLoad(ser);
+ ser.syncAsSint32LE(_curMusicState, VER(103));
+ ser.syncAsSint32LE(_curMusicSeq, VER(103));
+ ser.syncAsSint32LE(_nextSeqToPlay, VER(103));
+ ser.syncAsByte(_radioChatterSFX, VER(103));
+ ser.syncArray(_attributes, 188, Common::Serializer::Sint32LE, VER(103));
+ ser.syncAsSint32LE(_curMusicCue, VER(103));
+}
+
+int IMuseDigital::cmdsStartSound(int soundId, int priority) {
+ uint8 *src = _filesHandler->getSoundAddrData(soundId);
+
+ if (src == nullptr) {
+ debug(5, "IMuseDigital::cmdsStartSound(): ERROR: resource address for sound %d is NULL", soundId);
+ return -1;
+ }
+
+ // Check for the "Creative Voice File" header
+ if (_isEarlyDiMUSE && READ_BE_UINT32(src) == MKTAG('C', 'r', 'e', 'a'))
+ return waveStartSound(soundId, priority);
+
+ // Check for the "iMUS" header
+ if (READ_BE_UINT32(src) == MKTAG('i', 'M', 'U', 'S'))
+ return waveStartSound(soundId, priority);
+
+ return -1;
+}
+
+int IMuseDigital::cmdsStopSound(int soundId) {
+ int result = _filesHandler->getNextSound(soundId);
+
+ if (result != 2)
+ return -1;
+
+ return waveStopSound(soundId);
+}
+
+int IMuseDigital::cmdsStopAllSounds() {
+ return _triggersHandler->clearAllTriggers() | waveStopAllSounds();
+}
+
+int IMuseDigital::cmdsGetNextSound(int soundId) {
+ return waveGetNextSound(soundId);
+}
+
+int IMuseDigital::cmdsSetParam(int soundId, int subCmd, int value) {
+ int result = _filesHandler->getNextSound(soundId);
+
+ if (result != 2)
+ return -1;
+
+ return waveSetParam(soundId, subCmd, value);
+}
+
+int IMuseDigital::cmdsGetParam(int soundId, int subCmd) {
+ int result = _filesHandler->getNextSound(soundId);
+
+ if (subCmd != 0) {
+ if (subCmd == DIMUSE_P_TRIGS_SNDS) {
+ return _triggersHandler->countPendingSounds(soundId);
+ }
+
+ if (result == 2) {
+ return waveGetParam(soundId, subCmd);
+ }
+
+ result = (subCmd == DIMUSE_P_SND_TRACK_NUM) - 1;
+ }
+
+ return result;
+}
+
+int IMuseDigital::cmdsSetHook(int soundId, int hookId) {
+ int result = _filesHandler->getNextSound(soundId);
+
+ if (result != 2)
+ return -1;
+
+ return waveSetHook(soundId, hookId);
+}
+
+int IMuseDigital::cmdsGetHook(int soundId) {
+ int result = _filesHandler->getNextSound(soundId);
+
+ if (result != 2)
+ return -1;
+
+ return waveGetHook(soundId);
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/imuse_digi/dimuse_codecs.cpp b/engines/scumm/imuse_digi/dimuse_codecs.cpp
index 44c57a6bb7..7d7763137c 100644
--- a/engines/scumm/imuse_digi/dimuse_codecs.cpp
+++ b/engines/scumm/imuse_digi/dimuse_codecs.cpp
@@ -31,25 +31,6 @@ namespace Scumm {
namespace BundleCodecs {
-uint32 decode12BitsSample(const byte *src, byte **dst, uint32 size) {
- uint32 loop_size = size / 3;
- uint32 s_size = loop_size * 4;
- byte *ptr = *dst = (byte *)malloc(s_size);
- assert(ptr);
-
- uint32 tmp;
- while (loop_size--) {
- byte v1 = *src++;
- byte v2 = *src++;
- byte v3 = *src++;
- tmp = ((((v2 & 0x0f) << 8) | v1) << 4) - 0x8000;
- WRITE_BE_UINT16(ptr, tmp); ptr += 2;
- tmp = ((((v2 & 0xf0) << 4) | v3) << 4) - 0x8000;
- WRITE_BE_UINT16(ptr, tmp); ptr += 2;
- }
- return s_size;
-}
-
/*
* The "IMC" codec below (see cases 13 & 15 in decompressCodec) is actually a
* variant of the IMA codec, see also
@@ -305,7 +286,13 @@ int32 decompressADPCM(byte *compInput, byte *compOutput, int channels) {
// Clip outputWord to 16 bit signed, and write it into the destination stream
outputWord = CLIP<int32>(outputWord, -0x8000, 0x7fff);
- WRITE_BE_UINT16(dst + destPos, outputWord);
+
+ // This is being written as-is (LE), without concerns regarding endianness:
+ // this is because the internal DiMUSE mixer handles the data in LE format,
+ // and we'll convert data to the appropriate format using the QueuingAudioStream flags
+ // when flushing the final audio data to the output stream (see IMuseDigital::waveOutWrite())
+ WRITE_UINT16(dst + destPos, outputWord);
+
destPos += channels << 1;
// Adjust the curTablePos
diff --git a/engines/scumm/imuse_digi/dimuse_codecs.h b/engines/scumm/imuse_digi/dimuse_codecs.h
index 163d0250fa..01b9c88c0d 100644
--- a/engines/scumm/imuse_digi/dimuse_codecs.h
+++ b/engines/scumm/imuse_digi/dimuse_codecs.h
@@ -29,7 +29,6 @@ namespace Scumm {
namespace BundleCodecs {
-uint32 decode12BitsSample(const byte *src, byte **dst, uint32 size);
int32 decompressCodec(int32 codec, byte *compInput, byte *compOutput, int32 inputSize);
void initializeImcTables();
diff --git a/engines/scumm/imuse_digi/dimuse_defs.h b/engines/scumm/imuse_digi/dimuse_defs.h
new file mode 100644
index 0000000000..56b8bb2245
--- /dev/null
+++ b/engines/scumm/imuse_digi/dimuse_defs.h
@@ -0,0 +1,215 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#if !defined(SCUMM_IMUSE_DIGI_DEFS_H) && defined(ENABLE_SCUMM_7_8)
+#define SCUMM_IMUSE_DIGI_DEFS_H
+
+namespace Scumm {
+
+#define DIMUSE_MAX_GROUPS 16
+#define DIMUSE_MAX_FADES 16
+#define DIMUSE_MAX_TRIGGERS 8
+#define DIMUSE_MAX_DEFERS 8
+#define DIMUSE_MAX_TRACKS 8
+#define DIMUSE_MAX_MAP_SIZE 2048
+#define DIMUSE_MAX_DISPATCHES 8
+#define DIMUSE_MAX_STREAMZONES 50
+#define DIMUSE_MAX_FADE_VOLUME 8323072
+#define DIMUSE_MAX_STREAMS 3
+
+#define DIMUSE_LARGE_FADES 1
+#define DIMUSE_SMALL_FADES 4
+#define DIMUSE_LARGE_FADE_DIM 350000
+#define DIMUSE_SMALL_FADE_DIM 44100
+
+#define DIMUSE_SAMPLERATE 22050
+#define DIMUSE_NUM_WAVE_BUFS 8
+#define DIMUSE_SMUSH_SOUNDID 12345678
+#define DIMUSE_BUN_CHUNK_SIZE 0x2000
+#define DIMUSE_GROUP_SFX 1
+#define DIMUSE_GROUP_SPEECH 2
+#define DIMUSE_GROUP_MUSIC 3
+#define DIMUSE_GROUP_MUSICEFF 4
+#define DIMUSE_BUFFER_SPEECH 1
+#define DIMUSE_BUFFER_MUSIC 2
+#define DIMUSE_BUFFER_SFX 3
+
+// Parameters IDs
+#define DIMUSE_P_BOGUS_ID 0x0
+#define DIMUSE_P_SND_TRACK_NUM 0x100
+#define DIMUSE_P_TRIGS_SNDS 0x200
+#define DIMUSE_P_MARKER 0x300
+#define DIMUSE_P_GROUP 0x400
+#define DIMUSE_P_PRIORITY 0x500
+#define DIMUSE_P_VOLUME 0x600
+#define DIMUSE_P_PAN 0x700
+#define DIMUSE_P_DETUNE 0x800
+#define DIMUSE_P_TRANSPOSE 0x900
+#define DIMUSE_P_MAILBOX 0xA00
+#define DIMUSE_P_UNKNOWN 0xF00
+#define DIMUSE_P_SND_HAS_STREAM 0x1800
+#define DIMUSE_P_STREAM_BUFID 0x1900
+#define DIMUSE_P_SND_POS_IN_MS 0x1A00
+
+struct IMuseDigiDispatch;
+struct IMuseDigiTrack;
+struct IMuseDigiStreamZone;
+
+typedef struct {
+ int sound;
+ char text[256];
+ int opcode;
+ int a;
+ int b;
+ int c;
+ int d;
+ int e;
+ int f;
+ int g;
+ int h;
+ int i;
+ int j;
+ int clearLater;
+} IMuseDigiTrigger;
+
+typedef struct {
+ int counter;
+ int opcode;
+ int a;
+ int b;
+ int c;
+ int d;
+ int e;
+ int f;
+ int g;
+ int h;
+ int i;
+ int j;
+} IMuseDigiDefer;
+
+typedef struct {
+ int status;
+ int sound;
+ int param;
+ int currentVal;
+ int counter;
+ int length;
+ int slope;
+ int slopeMod;
+ int modOvfloCounter;
+ int nudge;
+} IMuseDigiFade;
+
+struct IMuseDigiTrack {
+ IMuseDigiTrack *prev;
+ IMuseDigiTrack *next;
+ IMuseDigiDispatch *dispatchPtr;
+ int soundId;
+ int marker;
+ int group;
+ int priority;
+ int vol;
+ int effVol;
+ int pan;
+ int detune;
+ int transpose;
+ int pitchShift;
+ int mailbox;
+ int jumpHook;
+ int32 syncSize_0;
+ byte *syncPtr_0;
+ int32 syncSize_1;
+ byte *syncPtr_1;
+ int32 syncSize_2;
+ byte *syncPtr_2;
+ int32 syncSize_3;
+ byte *syncPtr_3;
+};
+
+struct IMuseDigiStreamZone {
+ IMuseDigiStreamZone *prev;
+ IMuseDigiStreamZone *next;
+ int useFlag;
+ int32 offset;
+ int32 size;
+ int fadeFlag;
+};
+
+typedef struct {
+ int soundId;
+ int32 curOffset;
+ int32 endOffset;
+ int bufId;
+ uint8 *buf;
+ int32 bufFreeSize;
+ int32 loadSize;
+ int32 criticalSize;
+ int32 maxRead;
+ int32 loadIndex;
+ int32 readIndex;
+ int paused;
+ int vocLoopFlag;
+ int32 vocLoopTriggerOffset;
+} IMuseDigiStream;
+
+typedef struct {
+ uint8 *buffer;
+ int32 bufSize;
+ int32 loadSize;
+ int32 criticalSize;
+} IMuseDigiSndBuffer;
+
+struct IMuseDigiDispatch {
+ IMuseDigiTrack *trackPtr;
+ int wordSize;
+ int sampleRate;
+ int channelCount;
+ int32 currentOffset;
+ int32 audioRemaining;
+ int32 map[DIMUSE_MAX_MAP_SIZE];
+ IMuseDigiStream *streamPtr;
+ int streamBufID;
+ IMuseDigiStreamZone *streamZoneList;
+ int streamErrFlag;
+ uint8 *fadeBuf;
+ int32 fadeOffset;
+ int32 fadeRemaining;
+ int fadeWordSize;
+ int fadeSampleRate;
+ int fadeChannelCount;
+ int fadeSyncFlag;
+ int32 fadeSyncDelta;
+ int fadeVol;
+ int fadeSlope;
+ int32 vocLoopStartingPoint;
+};
+
+typedef struct {
+ int bytesPerSample;
+ int numChannels;
+ uint8 *mixBuf;
+ int mixBufSize;
+ int sizeSampleKB;
+} waveOutParamsStruct;
+
+} // End of namespace Scumm
+#endif
diff --git a/engines/scumm/imuse_digi/dimuse_dispatch.cpp b/engines/scumm/imuse_digi/dimuse_dispatch.cpp
new file mode 100644
index 0000000000..5f18a4646b
--- /dev/null
+++ b/engines/scumm/imuse_digi/dimuse_dispatch.cpp
@@ -0,0 +1,1743 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "scumm/imuse_digi/dimuse_engine.h"
+#include "scumm/imuse_digi/dimuse_defs.h"
+
+namespace Scumm {
+
+int IMuseDigital::dispatchInit() {
+ _dispatchBuffer = (uint8 *)malloc(DIMUSE_SMALL_FADES * DIMUSE_SMALL_FADE_DIM + DIMUSE_LARGE_FADE_DIM * DIMUSE_LARGE_FADES);
+
+ if (_dispatchBuffer) {
+ _dispatchLargeFadeBufs = _dispatchBuffer;
+ _dispatchSmallFadeBufs = _dispatchBuffer + (DIMUSE_LARGE_FADE_DIM * DIMUSE_LARGE_FADES);
+
+ for (int i = 0; i < DIMUSE_LARGE_FADES; i++) {
+ _dispatchLargeFadeFlags[i] = 0;
+ }
+
+ for (int i = 0; i < DIMUSE_SMALL_FADES; i++) {
+ _dispatchSmallFadeFlags[i] = 0;
+ }
+
+ for (int i = 0; i < DIMUSE_MAX_STREAMZONES; i++) {
+ _streamZones[i].useFlag = 0;
+ _streamZones[i].fadeFlag = 0;
+ _streamZones[i].prev = nullptr;
+ _streamZones[i].next = nullptr;
+ _streamZones[i].size = 0;
+ _streamZones[i].offset = 0;
+ }
+
+ for (int i = 0; i < DIMUSE_MAX_DISPATCHES; i++) {
+ _dispatches[i].trackPtr = nullptr;
+ _dispatches[i].wordSize = 0;
+ _dispatches[i].sampleRate = 0;
+ _dispatches[i].channelCount = 0;
+ _dispatches[i].currentOffset = 0;
+ _dispatches[i].audioRemaining = 0;
+ memset(_dispatches[i].map, 0, sizeof(_dispatches[i].map));
+ _dispatches[i].streamPtr = nullptr;
+ _dispatches[i].streamBufID = 0;
+ _dispatches[i].streamZoneList = nullptr;
+ _dispatches[i].streamErrFlag = 0;
+ _dispatches[i].fadeBuf = nullptr;
+ _dispatches[i].fadeOffset = 0;
+ _dispatches[i].fadeRemaining = 0;
+ _dispatches[i].fadeWordSize = 0;
+ _dispatches[i].fadeSampleRate = 0;
+ _dispatches[i].fadeChannelCount = 0;
+ _dispatches[i].fadeSyncFlag = 0;
+ _dispatches[i].fadeSyncDelta = 0;
+ _dispatches[i].fadeVol = 0;
+ _dispatches[i].fadeSlope = 0;
+ _dispatches[i].vocLoopStartingPoint = 0;
+ }
+
+ } else {
+ debug(5, "IMuseDigital::dispatchInit(): ERROR: couldn't allocate buffers\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+IMuseDigiDispatch *IMuseDigital::dispatchGetDispatchByTrackId(int trackId) {
+ return &_dispatches[trackId];
+}
+
+void IMuseDigital::dispatchSaveLoad(Common::Serializer &ser) {
+
+ for (int l = 0; l < DIMUSE_MAX_DISPATCHES; l++) {
+ ser.syncAsSint32LE(_dispatches[l].wordSize, VER(103));
+ ser.syncAsSint32LE(_dispatches[l].sampleRate, VER(103));
+ ser.syncAsSint32LE(_dispatches[l].channelCount, VER(103));
+ ser.syncAsSint32LE(_dispatches[l].currentOffset, VER(103));
+ ser.syncAsSint32LE(_dispatches[l].audioRemaining, VER(103));
+ ser.syncArray(_dispatches[l].map, 2048, Common::Serializer::Sint32LE, VER(103));
+
+ // This is only needed to signal if the sound originally had a stream associated with it
+ int hasStream = 0;
+ if (ser.isSaving()) {
+ if (_dispatches[l].streamPtr)
+ hasStream = 1;
+ ser.syncAsSint32LE(hasStream, VER(103));
+ } else {
+ ser.syncAsSint32LE(hasStream, VER(103));
+ _dispatches[l].streamPtr = hasStream ? (IMuseDigiStream *)1 : nullptr;
+ }
+
+ ser.syncAsSint32LE(_dispatches[l].streamBufID, VER(103));
+ ser.syncAsSint32LE(_dispatches[l].streamErrFlag, VER(103));
+ ser.syncAsSint32LE(_dispatches[l].fadeOffset, VER(103));
+ ser.syncAsSint32LE(_dispatches[l].fadeRemaining, VER(103));
+ ser.syncAsSint32LE(_dispatches[l].fadeWordSize, VER(103));
+ ser.syncAsSint32LE(_dispatches[l].fadeSampleRate, VER(103));
+ ser.syncAsSint32LE(_dispatches[l].fadeChannelCount, VER(103));
+ ser.syncAsSint32LE(_dispatches[l].fadeSyncFlag, VER(103));
+ ser.syncAsSint32LE(_dispatches[l].fadeSyncDelta, VER(103));
+ ser.syncAsSint32LE(_dispatches[l].fadeVol, VER(103));
+ ser.syncAsSint32LE(_dispatches[l].fadeSlope, VER(103));
+ ser.syncAsSint32LE(_dispatches[l].vocLoopStartingPoint, VER(103));
+ }
+
+ if (ser.isLoading()) {
+ for (int i = 0; i < DIMUSE_LARGE_FADES; i++) {
+ _dispatchLargeFadeFlags[i] = 0;
+ }
+
+ for (int i = 0; i < DIMUSE_SMALL_FADES; i++) {
+ _dispatchSmallFadeFlags[i] = 0;
+ }
+
+ for (int i = 0; i < DIMUSE_MAX_STREAMZONES; i++) {
+ _streamZones[i].useFlag = 0;
+ }
+ }
+}
+
+int IMuseDigital::dispatchRestoreStreamZones() {
+ IMuseDigiDispatch *curDispatchPtr;
+ IMuseDigiStreamZone *curStreamZone;
+ int32 sizeToFeed = _isEarlyDiMUSE ? 0x800 : 0x4000;
+
+ curDispatchPtr = _dispatches;
+ for (int i = 0; i < _trackCount; i++) {
+ curDispatchPtr = &_dispatches[i];
+ curDispatchPtr->fadeBuf = nullptr;
+
+ if (curDispatchPtr->trackPtr->soundId && curDispatchPtr->streamPtr) {
+ // Try allocating the stream
+ curDispatchPtr->streamPtr = streamerAllocateSound(curDispatchPtr->trackPtr->soundId, curDispatchPtr->streamBufID, sizeToFeed);
+
+ if (curDispatchPtr->streamPtr) {
+ streamerSetSoundToStreamFromOffset(curDispatchPtr->streamPtr, curDispatchPtr->trackPtr->soundId, curDispatchPtr->currentOffset);
+
+ if (_isEarlyDiMUSE) {
+ if (curDispatchPtr->vocLoopStartingPoint)
+ streamerSetLoopFlag(curDispatchPtr->streamPtr, curDispatchPtr->currentOffset + curDispatchPtr->audioRemaining);
+ } else if (curDispatchPtr->audioRemaining) {
+ // Try allocating the first streamZone of the dispatch
+ curStreamZone = dispatchAllocateStreamZone();
+ curDispatchPtr->streamZoneList = curStreamZone;
+
+ if (curStreamZone) {
+ curStreamZone->offset = curDispatchPtr->currentOffset;
+ curStreamZone->size = 0;
+ curStreamZone->fadeFlag = 0;
+ } else {
+ debug(5, "IMuseDigital::dispatchRestoreStreamZones(): unable to allocate streamZone during restore");
+ }
+ }
+ } else {
+ debug(5, "IMuseDigital::dispatchRestoreStreamZones(): unable to start stream during restore");
+ }
+ }
+ }
+ return 0;
+}
+
+int IMuseDigital::dispatchAllocateSound(IMuseDigiTrack *trackPtr, int groupId) {
+ IMuseDigiDispatch *trackDispatch;
+ IMuseDigiDispatch *dispatchToDeallocate;
+ IMuseDigiStreamZone *streamZoneList;
+ int navigateMapResult;
+ int32 sizeToFeed = _isEarlyDiMUSE ? 0x800 : 0x4000;
+
+ trackDispatch = trackPtr->dispatchPtr;
+ trackDispatch->currentOffset = 0;
+ trackDispatch->audioRemaining = 0;
+ trackDispatch->fadeBuf = 0;
+
+ if (_isEarlyDiMUSE) {
+ trackDispatch->vocLoopStartingPoint = 0;
+ } else {
+ memset(trackDispatch->map, 0, sizeof(trackDispatch->map));
+ }
+
+ if (groupId) {
+ trackDispatch->streamPtr = streamerAllocateSound(trackPtr->soundId, groupId, sizeToFeed);
+ trackDispatch->streamBufID = groupId;
+
+ if (!trackDispatch->streamPtr) {
+ debug(5, "IMuseDigital::dispatchAllocateSound(): unable to allocate stream for sound %d", trackPtr->soundId);
+ return -1;
+ }
+
+ if (_isEarlyDiMUSE)
+ return 0;
+
+ trackDispatch->streamZoneList = 0;
+ trackDispatch->streamErrFlag = 0;
+ } else {
+ trackDispatch->streamPtr = 0;
+ if (_isEarlyDiMUSE)
+ return dispatchSeekToNextChunk(trackDispatch);
+ }
+
+ navigateMapResult = dispatchNavigateMap(trackDispatch);
+ if (!navigateMapResult || navigateMapResult == -3)
+ return 0;
+
+ // At this point, something went wrong, so deallocate what we have to...
+ debug(5, "IMuseDigital::dispatchAllocateSound(): problem starting sound (%d) in dispatch", trackPtr->soundId);
+
+ // Remove streamZones from list
+ dispatchToDeallocate = trackDispatch->trackPtr->dispatchPtr;
+ if (dispatchToDeallocate->streamPtr) {
+ streamZoneList = dispatchToDeallocate->streamZoneList;
+ streamerClearSoundInStream(dispatchToDeallocate->streamPtr);
+ if (dispatchToDeallocate->streamZoneList) {
+ do {
+ streamZoneList->useFlag = 0;
+ removeStreamZoneFromList(&dispatchToDeallocate->streamZoneList, streamZoneList);
+ } while (streamZoneList);
+ }
+ }
+
+ if (!dispatchToDeallocate->fadeBuf)
+ return -1;
+
+ // Mark the fade corresponding to our fadeBuf as unused
+ dispatchDeallocateFade(dispatchToDeallocate, "dispatchAllocateSound");
+ return -1;
+}
+
+int IMuseDigital::dispatchRelease(IMuseDigiTrack *trackPtr) {
+ IMuseDigiDispatch *dispatchToDeallocate;
+ IMuseDigiStreamZone *streamZoneList;
+
+ dispatchToDeallocate = trackPtr->dispatchPtr;
+
+ // Remove streamZones from list
+ if (dispatchToDeallocate->streamPtr) {
+ streamerClearSoundInStream(dispatchToDeallocate->streamPtr);
+
+ if (_isEarlyDiMUSE)
+ return 0;
+
+ streamZoneList = dispatchToDeallocate->streamZoneList;
+ if (dispatchToDeallocate->streamZoneList) {
+ do {
+ streamZoneList->useFlag = 0;
+ removeStreamZoneFromList(&dispatchToDeallocate->streamZoneList, streamZoneList);
+ } while (dispatchToDeallocate->streamZoneList);
+ }
+ }
+
+ if (!dispatchToDeallocate->fadeBuf)
+ return 0;
+
+ // Mark the fade corresponding to our fadeBuf as unused
+ dispatchDeallocateFade(dispatchToDeallocate, "dispatchRelease");
+ return 0;
+}
+
+int IMuseDigital::dispatchSwitchStream(int oldSoundId, int newSoundId, int fadeLength, int unusedFadeSyncFlag, int offsetFadeSyncFlag) {
+ int32 effFadeLen, effFadeSize, strZnSize;
+ int getMapResult, i;
+ IMuseDigiDispatch *curDispatch = _dispatches;
+
+ effFadeLen = fadeLength;
+
+ if (fadeLength > 2000)
+ effFadeLen = 2000;
+
+ for (i = 0; i < _trackCount; i++) {
+ curDispatch = &_dispatches[i];
+ if (oldSoundId && curDispatch->trackPtr->soundId == oldSoundId && curDispatch->streamPtr) {
+ break;
+ }
+ }
+
+ if (i >= _trackCount) {
+ debug(5, "IMuseDigital::dispatchSwitchStream(): couldn't find sound, index went past _trackCount (%d)", _trackCount);
+ return -1;
+ }
+
+ if (curDispatch->streamZoneList) {
+ if (!curDispatch->wordSize) {
+ debug(5, "IMuseDigital::dispatchSwitchStream(): found streamZoneList but NULL wordSize");
+ return -1;
+ }
+
+ if (curDispatch->fadeBuf) {
+ // Mark the fade corresponding to our fadeBuf as unused
+ dispatchDeallocateFade(curDispatch, "dispatchSwitchStream");
+ }
+
+ _dispatchFadeSize = dispatchGetFadeSize(curDispatch, effFadeLen);
+
+ strZnSize = curDispatch->streamZoneList->size;
+ if (strZnSize >= _dispatchFadeSize)
+ strZnSize = _dispatchFadeSize;
+ _dispatchFadeSize = strZnSize;
+
+ // Validate and adjust the fade dispatch size further;
+ // this should correctly align the dispatch size to avoid starting a fade without
+ // inverting the stereo image by mistake
+ dispatchValidateFadeSize(curDispatch, _dispatchFadeSize, "dispatchSwitchStream");
+
+ curDispatch->fadeBuf = dispatchAllocateFade(_dispatchFadeSize, "dispatchSwitchStream");
+
+ // If we were able to allocate a fade, set up a fade out for the old sound.
+ // We'll copy data from the stream buffer to the fade buffer
+ if (curDispatch->fadeBuf) {
+ curDispatch->fadeOffset = 0;
+ curDispatch->fadeRemaining = 0;
+ curDispatch->fadeWordSize = curDispatch->wordSize;
+ curDispatch->fadeSampleRate = curDispatch->sampleRate;
+ curDispatch->fadeChannelCount = curDispatch->channelCount;
+ curDispatch->fadeSyncFlag = unusedFadeSyncFlag | offsetFadeSyncFlag;
+ curDispatch->fadeSyncDelta = 0;
+ curDispatch->fadeVol = DIMUSE_MAX_FADE_VOLUME;
+ curDispatch->fadeSlope = 0;
+
+ while (curDispatch->fadeRemaining < _dispatchFadeSize) {
+ effFadeSize = _dispatchFadeSize - curDispatch->fadeRemaining;
+ if ((_dispatchFadeSize - curDispatch->fadeRemaining) >= 0x4000)
+ effFadeSize = 0x4000;
+
+ memcpy(&curDispatch->fadeBuf[curDispatch->fadeRemaining], streamerGetStreamBuffer(curDispatch->streamPtr, effFadeSize), effFadeSize);
+
+ curDispatch->fadeRemaining += effFadeSize;
+ }
+ } else {
+ debug(5, "IMuseDigital::dispatchSwitchStream(): WARNING: couldn't allocate fade buffer (from sound %d to sound %d)", oldSoundId, newSoundId);
+ }
+ }
+
+ // Clear fades and triggers for the old soundId
+ char emptyMarker[1] = "";
+ _fadesHandler->clearFadeStatus(curDispatch->trackPtr->soundId, -1);
+ _triggersHandler->clearTrigger(curDispatch->trackPtr->soundId, (char *)emptyMarker, -1);
+
+ // Setup the new soundId
+ curDispatch->trackPtr->soundId = newSoundId;
+
+ streamerSetReadIndex(curDispatch->streamPtr, streamerGetFreeBufferAmount(curDispatch->streamPtr));
+
+ if (offsetFadeSyncFlag && curDispatch->streamZoneList) {
+ // Start the soundId from an offset
+ streamerSetSoundToStreamFromOffset(curDispatch->streamPtr, newSoundId, curDispatch->currentOffset);
+ while (curDispatch->streamZoneList->next) {
+ curDispatch->streamZoneList->next->useFlag = 0;
+ removeStreamZoneFromList(&curDispatch->streamZoneList->next, curDispatch->streamZoneList->next);
+ }
+ curDispatch->streamZoneList->size = 0;
+
+ return 0;
+ } else {
+ // Start the soundId from the beginning
+ streamerSetSoundToStreamFromOffset(curDispatch->streamPtr, newSoundId, 0);
+
+ while (curDispatch->streamZoneList) {
+ curDispatch->streamZoneList->useFlag = 0;
+ removeStreamZoneFromList(&curDispatch->streamZoneList, curDispatch->streamZoneList);
+ }
+
+ curDispatch->currentOffset = 0;
+ curDispatch->audioRemaining = 0;
+ memset(curDispatch->map, 0, sizeof(curDispatch->map));
+
+ getMapResult = dispatchNavigateMap(curDispatch);
+ if (!getMapResult || getMapResult == -3) {
+ return 0;
+ } else {
+ debug(5, "IMuseDigital::dispatchSwitchStream(): problem switching stream in dispatch (from sound %d to sound %d)", oldSoundId, newSoundId);
+ tracksClear(curDispatch->trackPtr);
+ return -1;
+ }
+ }
+}
+
+int IMuseDigital::dispatchSwitchStream(int oldSoundId, int newSoundId, uint8 *crossfadeBuffer, int crossfadeBufferSize, int vocLoopFlag) {
+ IMuseDigiDispatch *dispatchPtr = nullptr;
+ uint8 *streamBuf;
+ int i;
+ int32 effAudioRemaining, audioRemaining, offset;
+
+ for (i = 0; i < _trackCount; i++) {
+ dispatchPtr = &_dispatches[i];
+ if (oldSoundId && dispatchPtr->trackPtr->soundId == oldSoundId && dispatchPtr->streamPtr) {
+ break;
+ }
+ }
+
+ if (i >= _trackCount) {
+ debug(5, "IMuseDigital::dispatchSwitchStream(): couldn't find sound, index went past _trackCount (%d)", _trackCount);
+ return -1;
+ }
+
+ offset = dispatchPtr->currentOffset;
+ audioRemaining = dispatchPtr->audioRemaining;
+ dispatchPtr->trackPtr->soundId = newSoundId;
+ dispatchPtr->fadeBuf = crossfadeBuffer;
+ dispatchPtr->fadeRemaining = 0;
+ dispatchPtr->fadeSyncDelta = 0;
+ dispatchPtr->fadeVol = DIMUSE_MAX_FADE_VOLUME;
+ dispatchPtr->fadeSlope = 0;
+
+ if (crossfadeBufferSize) {
+ do {
+ if (!streamerGetFreeBufferAmount(dispatchPtr->streamPtr)
+ || (!dispatchPtr->audioRemaining && dispatchSeekToNextChunk(dispatchPtr))) {
+ break;
+ }
+
+ effAudioRemaining = dispatchPtr->audioRemaining;
+ if (crossfadeBufferSize - dispatchPtr->fadeRemaining < effAudioRemaining)
+ effAudioRemaining = crossfadeBufferSize - dispatchPtr->fadeRemaining;
+
+ if (effAudioRemaining >= streamerGetFreeBufferAmount(dispatchPtr->streamPtr))
+ effAudioRemaining = streamerGetFreeBufferAmount(dispatchPtr->streamPtr);
+
+ if (effAudioRemaining >= 0x800)
+ effAudioRemaining = 0x800;
+
+ streamBuf = streamerGetStreamBuffer(dispatchPtr->streamPtr, effAudioRemaining);
+ memcpy(&crossfadeBuffer[dispatchPtr->fadeRemaining], streamBuf, effAudioRemaining);
+
+ dispatchPtr->fadeRemaining += effAudioRemaining;
+ dispatchPtr->currentOffset += effAudioRemaining;
+ dispatchPtr->audioRemaining -= effAudioRemaining;
+ } while (dispatchPtr->fadeRemaining < crossfadeBufferSize);
+ }
+
+ streamerSetReadIndex(dispatchPtr->streamPtr, streamerGetFreeBufferAmount(dispatchPtr->streamPtr));
+ streamerSetSoundToStreamFromOffset(dispatchPtr->streamPtr, newSoundId, vocLoopFlag ? offset : 0);
+
+ if (vocLoopFlag) {
+ if (dispatchPtr->vocLoopStartingPoint)
+ streamerSetLoopFlag(dispatchPtr->streamPtr, dispatchPtr->audioRemaining + dispatchPtr->currentOffset);
+ } else {
+ streamerRemoveLoopFlag(dispatchPtr->streamPtr);
+ }
+
+ dispatchPtr->currentOffset = vocLoopFlag ? offset : 0;
+ dispatchPtr->audioRemaining = vocLoopFlag ? audioRemaining : 0;
+
+ if (!vocLoopFlag) {
+ dispatchPtr->vocLoopStartingPoint = 0;
+ }
+
+ return 0;
+}
+
+void IMuseDigital::dispatchProcessDispatches(IMuseDigiTrack *trackPtr, int feedSize, int sampleRate) {
+ IMuseDigiDispatch *dispatchPtr;
+ int32 effFeedSize, effWordSize, effRemainingAudio, effRemainingFade, effSampleRate;
+ int32 inFrameCount, mixVolume, mixStartingPoint, elapsedFadeDelta;
+ int navigateMapResult;
+ uint8 *srcBuf, *soundAddrData;
+
+ dispatchPtr = trackPtr->dispatchPtr;
+ if (dispatchPtr->streamPtr && dispatchPtr->streamZoneList)
+ dispatchPredictStream(dispatchPtr);
+
+ // If a fade has previously been allocated
+ if (dispatchPtr->fadeBuf) {
+ inFrameCount = 8 * dispatchPtr->fadeRemaining / (dispatchPtr->fadeWordSize * dispatchPtr->fadeChannelCount);
+
+ if (_vm->_game.id == GID_DIG) {
+ effSampleRate = dispatchPtr->fadeSampleRate;
+ } else {
+ effSampleRate = (trackPtr->pitchShift * dispatchPtr->fadeSampleRate) >> 8;
+ }
+
+ if (inFrameCount >= effSampleRate * feedSize / sampleRate) {
+ inFrameCount = effSampleRate * feedSize / sampleRate;
+ effFeedSize = feedSize;
+ } else {
+ effFeedSize = sampleRate * inFrameCount / effSampleRate;
+ }
+
+ if (dispatchPtr->fadeWordSize == 12 && dispatchPtr->fadeChannelCount == 1)
+ inFrameCount &= 0xFFFFFFFE;
+
+ // If the fade is still going on
+ if (inFrameCount) {
+ // Update the fade volume
+ effRemainingFade = ((dispatchPtr->fadeWordSize * dispatchPtr->fadeChannelCount) * inFrameCount) / 8;
+ mixVolume = dispatchUpdateFadeMixVolume(dispatchPtr, effRemainingFade);
+
+ // Send it all to the mixer
+ srcBuf = &dispatchPtr->fadeBuf[dispatchPtr->fadeOffset];
+
+ _internalMixer->mix(
+ srcBuf,
+ inFrameCount,
+ dispatchPtr->fadeWordSize,
+ dispatchPtr->fadeChannelCount,
+ effFeedSize,
+ 0,
+ mixVolume,
+ trackPtr->pan);
+
+ dispatchPtr->fadeOffset += effRemainingFade;
+ dispatchPtr->fadeRemaining -= effRemainingFade;
+
+ // Deallocate fade if it ended
+ if (!dispatchPtr->fadeRemaining) {
+ dispatchDeallocateFade(dispatchPtr, "dispatchProcessDispatches");
+ }
+ } else {
+ debug(5, "IMuseDigital::dispatchProcessDispatches(): WARNING: fade for sound %d ends with incomplete frame (or odd 12-bit mono frame)", trackPtr->soundId);
+
+ // Fade ended, deallocate it
+ dispatchDeallocateFade(dispatchPtr, "dispatchProcessDispatches");
+ }
+
+ if (!dispatchPtr->fadeRemaining)
+ dispatchPtr->fadeBuf = nullptr;
+ }
+
+ // This index keeps track of the offset position until which we have
+ // filled the buffer; with each update it is incremented by the effective
+ // feed size.
+ mixStartingPoint = 0;
+
+ while (1) {
+ // If the current region is finished playing
+ // go check for any event on the map for the current offset
+ if (!dispatchPtr->audioRemaining) {
+ _dispatchFadeStartedFlag = 0;
+ navigateMapResult = dispatchNavigateMap(dispatchPtr);
+
+ if (navigateMapResult)
+ break;
+
+ if (_dispatchFadeStartedFlag) {
+ // We reached a JUMP, therefore we have to crossfade to
+ // the destination region: start a fade-out
+ if (_vm->_game.id == GID_DIG) {
+ effSampleRate = dispatchPtr->fadeSampleRate;
+ } else {
+ effSampleRate = (trackPtr->pitchShift * dispatchPtr->fadeSampleRate) >> 8;
+ }
+
+ inFrameCount = 8 * dispatchPtr->fadeRemaining / (dispatchPtr->fadeWordSize * dispatchPtr->fadeChannelCount);
+ if (inFrameCount >= effSampleRate * feedSize / sampleRate) {
+ inFrameCount = effSampleRate * feedSize / sampleRate;
+ effFeedSize = feedSize;
+ } else {
+ effFeedSize = sampleRate * inFrameCount / effSampleRate;
+ }
+
+ if (dispatchPtr->fadeWordSize == 12 && dispatchPtr->fadeChannelCount == 1)
+ inFrameCount &= 0xFFFFFFFE;
+
+ if (!inFrameCount)
+ debug(5, "IMuseDigital::dispatchProcessDispatches(): WARNING: fade for sound %d ends with incomplete frame (or odd 12-bit mono frame)", trackPtr->soundId);
+
+ // Update the fade volume
+ effRemainingFade = (inFrameCount * dispatchPtr->fadeWordSize * dispatchPtr->fadeChannelCount) / 8;
+ mixVolume = dispatchUpdateFadeMixVolume(dispatchPtr, effRemainingFade);
+
+ // Send it all to the mixer
+ srcBuf = &dispatchPtr->fadeBuf[dispatchPtr->fadeOffset];
+
+ _internalMixer->mix(
+ srcBuf,
+ inFrameCount,
+ dispatchPtr->fadeWordSize,
+ dispatchPtr->fadeChannelCount,
+ effFeedSize,
+ mixStartingPoint,
+ mixVolume,
+ trackPtr->pan);
+
+ dispatchPtr->fadeOffset += effRemainingFade;
+ dispatchPtr->fadeRemaining -= effRemainingFade;
+
+ if (!dispatchPtr->fadeRemaining) {
+ // Fade ended, deallocate it
+ dispatchDeallocateFade(dispatchPtr, "dispatchProcessDispatches");
+ }
+ }
+ }
+
+ if (!feedSize)
+ return;
+
+ if (_vm->_game.id == GID_DIG) {
+ effSampleRate = dispatchPtr->sampleRate;
+ } else {
+ effSampleRate = (trackPtr->pitchShift * dispatchPtr->sampleRate) >> 8;
+ }
+
+ effWordSize = dispatchPtr->channelCount * dispatchPtr->wordSize;
+ inFrameCount = effSampleRate * feedSize / sampleRate;
+
+ if (inFrameCount <= (8 * dispatchPtr->audioRemaining / effWordSize)) {
+ effFeedSize = feedSize;
+ } else {
+ inFrameCount = 8 * dispatchPtr->audioRemaining / effWordSize;
+ effFeedSize = sampleRate * (8 * dispatchPtr->audioRemaining / effWordSize) / effSampleRate;
+ }
+
+ if (dispatchPtr->wordSize == 12 && dispatchPtr->channelCount == 1)
+ inFrameCount &= 0xFFFFFFFE;
+
+ if (!inFrameCount) {
+ if (_vm->_game.id == GID_DIG || dispatchPtr->wordSize == 12)
+ debug(5, "IMuseDigital::dispatchProcessDispatches(): WARNING: region in sound %d ends with incomplete frame (or odd 12-bit mono frame)", trackPtr->soundId);
+ tracksClear(trackPtr);
+ return;
+ }
+
+ // Play the audio of the current region
+ effRemainingAudio = (effWordSize * inFrameCount) / 8;
+
+ if (dispatchPtr->streamPtr) {
+ srcBuf = streamerGetStreamBuffer(dispatchPtr->streamPtr, effRemainingAudio);
+ if (!srcBuf) {
+ dispatchPtr->streamErrFlag = 1;
+ if (dispatchPtr->fadeBuf && dispatchPtr->fadeSyncFlag)
+ dispatchPtr->fadeSyncDelta += feedSize;
+
+ streamerQueryStream(
+ dispatchPtr->streamPtr,
+ _dispatchCurStreamBufSize,
+ _dispatchCurStreamCriticalSize,
+ _dispatchCurStreamFreeSpace,
+ _dispatchCurStreamPaused);
+
+ if (_dispatchCurStreamPaused) {
+ debug(5, "IMuseDigital::dispatchProcessDispatches(): WARNING: stopping starving paused stream for sound %d", dispatchPtr->trackPtr->soundId);
+ tracksClear(trackPtr);
+ }
+
+ return;
+ }
+ dispatchPtr->streamZoneList->offset += effRemainingAudio;
+ dispatchPtr->streamZoneList->size -= effRemainingAudio;
+ dispatchPtr->streamErrFlag = 0;
+ } else {
+ soundAddrData = _filesHandler->getSoundAddrData(trackPtr->soundId);
+ if (!soundAddrData) {
+ debug(5, "IMuseDigital::dispatchProcessDispatches(): ERROR: soundAddrData for sound %d is NULL", trackPtr->soundId);
+ // Try to gracefully play nothing instead of getting stuck on an infinite loop
+ dispatchPtr->currentOffset += effRemainingAudio;
+ dispatchPtr->audioRemaining -= effRemainingAudio;
+ return;
+ }
+
+ srcBuf = &soundAddrData[dispatchPtr->currentOffset];
+ }
+
+ if (dispatchPtr->fadeBuf) {
+ // If the fadeSyncFlag is active (e.g. we are crossfading
+ // to another version of the same music piece), do this... thing,
+ // and update the fadeSyncDelta
+ if (dispatchPtr->fadeSyncFlag) {
+ if (dispatchPtr->fadeSyncDelta) {
+ elapsedFadeDelta = effFeedSize;
+ if (effFeedSize >= dispatchPtr->fadeSyncDelta)
+ elapsedFadeDelta = dispatchPtr->fadeSyncDelta;
+
+ dispatchPtr->fadeSyncDelta -= elapsedFadeDelta;
+ effFeedSize -= elapsedFadeDelta;
+
+ if (_vm->_game.id == GID_DIG) {
+ effSampleRate = dispatchPtr->sampleRate;
+ } else {
+ effSampleRate = (trackPtr->pitchShift * dispatchPtr->sampleRate) >> 8;
+ }
+
+ inFrameCount = effFeedSize * effSampleRate / sampleRate;
+
+ if (dispatchPtr->wordSize == 12 && dispatchPtr->channelCount == 1)
+ inFrameCount &= 0xFFFFFFFE;
+
+ srcBuf = &srcBuf[effRemainingAudio - ((dispatchPtr->wordSize * inFrameCount * dispatchPtr->channelCount) / 8)];
+ }
+ }
+
+ // If there's still a fadeBuffer active in our dispatch
+ // we balance the volume of the considered track with
+ // the fade volume, effectively creating a crossfade
+ if (dispatchPtr->fadeBuf) {
+ // Fade-in
+ mixVolume = dispatchUpdateFadeSlope(dispatchPtr);
+ } else {
+ mixVolume = trackPtr->effVol;
+ }
+ } else {
+ mixVolume = trackPtr->effVol;
+ }
+
+ // Real-time lo-fi Radio voice effect
+ if (trackPtr->mailbox)
+ _internalMixer->setRadioChatter();
+
+ _internalMixer->mix(
+ srcBuf,
+ inFrameCount,
+ dispatchPtr->wordSize,
+ dispatchPtr->channelCount,
+ effFeedSize,
+ mixStartingPoint,
+ mixVolume,
+ trackPtr->pan);
+
+ _internalMixer->clearRadioChatter();
+ mixStartingPoint += effFeedSize;
+ feedSize -= effFeedSize;
+
+ dispatchPtr->currentOffset += effRemainingAudio;
+ dispatchPtr->audioRemaining -= effRemainingAudio;
+ }
+
+ // Behavior of errors and STOP marker
+ if (navigateMapResult == -1)
+ tracksClear(trackPtr);
+
+ if (dispatchPtr->fadeBuf && dispatchPtr->fadeSyncFlag)
+ dispatchPtr->fadeSyncDelta += feedSize;
+}
+
+void IMuseDigital::dispatchProcessDispatches(IMuseDigiTrack *trackPtr, int feedSize) {
+ IMuseDigiDispatch *dispatchPtr = nullptr;
+ IMuseDigiStream *streamPtr;
+ uint8 *buffer, *srcBuf;
+ int32 fadeChunkSize = 0;
+ int32 tentativeFeedSize, inFrameCount, fadeSyncDelta, mixStartingPoint, seekResult;
+ int mixVolume;
+
+ dispatchPtr = trackPtr->dispatchPtr;
+ tentativeFeedSize = (dispatchPtr->sampleRate == 22050) ? feedSize : feedSize / 2;
+
+ if (dispatchPtr->fadeBuf) {
+ if (tentativeFeedSize >= dispatchPtr->fadeRemaining) {
+ fadeChunkSize = dispatchPtr->fadeRemaining;
+ } else {
+ fadeChunkSize = tentativeFeedSize;
+ }
+
+ mixVolume = dispatchUpdateFadeMixVolume(dispatchPtr, fadeChunkSize);
+ _internalMixer->mix(dispatchPtr->fadeBuf, fadeChunkSize, 8, 1, feedSize, 0, mixVolume, trackPtr->pan);
+ dispatchPtr->fadeRemaining -= fadeChunkSize;
+ dispatchPtr->fadeBuf += fadeChunkSize;
+ if (dispatchPtr->fadeRemaining == fadeChunkSize)
+ dispatchPtr->fadeBuf = nullptr;
+ }
+
+ mixStartingPoint = 0;
+ while (1) {
+ if (!dispatchPtr->audioRemaining) {
+ seekResult = dispatchSeekToNextChunk(dispatchPtr);
+ if (seekResult)
+ break;
+ }
+
+ if (!tentativeFeedSize)
+ return;
+
+ inFrameCount = dispatchPtr->audioRemaining;
+ if (tentativeFeedSize < inFrameCount)
+ inFrameCount = tentativeFeedSize;
+
+ streamPtr = dispatchPtr->streamPtr;
+ if (streamPtr) {
+ buffer = streamerGetStreamBuffer(streamPtr, inFrameCount);
+ if (!buffer) {
+ if (dispatchPtr->fadeBuf)
+ dispatchPtr->fadeSyncDelta += fadeChunkSize;
+ return;
+ }
+ } else {
+ srcBuf = _filesHandler->getSoundAddrData(trackPtr->soundId);
+ if (!srcBuf)
+ return;
+ buffer = &srcBuf[dispatchPtr->currentOffset];
+ }
+
+ if (dispatchPtr->fadeBuf) {
+ if (dispatchPtr->fadeSyncDelta) {
+ fadeSyncDelta = dispatchPtr->fadeSyncDelta;
+ if (dispatchPtr->fadeSyncDelta >= inFrameCount)
+ fadeSyncDelta = inFrameCount;
+ inFrameCount -= fadeSyncDelta;
+ dispatchPtr->fadeSyncDelta -= fadeSyncDelta;
+ buffer += fadeSyncDelta;
+ dispatchPtr->currentOffset += fadeSyncDelta;
+ dispatchPtr->audioRemaining -= fadeSyncDelta;
+ }
+ }
+
+ if (inFrameCount) {
+ if (dispatchPtr->fadeBuf) {
+ mixVolume = dispatchUpdateFadeSlope(dispatchPtr);
+ } else {
+ mixVolume = trackPtr->effVol;
+ }
+
+ _internalMixer->mix(buffer, inFrameCount, 8, 1, feedSize, mixStartingPoint, mixVolume, trackPtr->pan);
+ mixStartingPoint += inFrameCount;
+ tentativeFeedSize -= inFrameCount;
+ dispatchPtr->currentOffset += inFrameCount;
+ dispatchPtr->audioRemaining -= inFrameCount;
+ }
+ }
+
+ if (seekResult == -1)
+ tracksClear(trackPtr);
+
+ if (dispatchPtr->fadeBuf)
+ dispatchPtr->fadeSyncDelta += fadeChunkSize;
+}
+
+void IMuseDigital::dispatchPredictFirstStream() {
+ Common::StackLock lock(_mutex);
+
+ for (int i = 0; i < _trackCount; i++) {
+ if (_dispatches[i].trackPtr->soundId && _dispatches[i].streamPtr && _dispatches[i].streamZoneList)
+ dispatchPredictStream(&_dispatches[i]);
+ }
+}
+
+int IMuseDigital::dispatchNavigateMap(IMuseDigiDispatch *dispatchPtr) {
+ int32 *mapCurEvent;
+ int32 blockTag, effFadeSize, elapsedFadeSize, regionOffset;
+ char *marker = NULL;
+
+ int getMapResult = dispatchGetMap(dispatchPtr);
+ if (getMapResult)
+ return getMapResult;
+
+ if (dispatchPtr->audioRemaining
+ || (dispatchPtr->streamPtr && dispatchPtr->streamZoneList->offset != dispatchPtr->currentOffset)) {
+ debug(5, "IMuseDigital::dispatchNavigateMap(): ERROR: navigation error in dispatch");
+ return -1;
+ }
+
+ mapCurEvent = NULL;
+ while (1) {
+ mapCurEvent = dispatchGetNextMapEvent(dispatchPtr->map, dispatchPtr->currentOffset, mapCurEvent);
+
+ if (!mapCurEvent) {
+ debug(5, "IMuseDigital::dispatchNavigateMap(): ERROR: no more map events at offset %dx", dispatchPtr->currentOffset);
+ return -1;
+ }
+
+ blockTag = mapCurEvent[0];
+ switch (blockTag) {
+ case MKTAG('J', 'U', 'M', 'P'):
+ // Handle any event found at this offset
+ // Jump block (fixed size: 28 bytes)
+ // - The tag 'JUMP' (4 bytes)
+ // - Block size in bytes minus 8 (4 bytes)
+ // - Block offset (hook position) (4 bytes)
+ // - Jump destination offset (4 bytes)
+ // - Hook ID (4 bytes)
+ // - Fade time in ms (4 bytes)
+ if (!checkHookId(dispatchPtr->trackPtr->jumpHook, mapCurEvent[4])) {
+ // This is the right hookId, let's jump
+ dispatchPtr->currentOffset = mapCurEvent[3];
+ if (dispatchPtr->streamPtr) {
+ if (dispatchPtr->streamZoneList->size || !dispatchPtr->streamZoneList->next) {
+ debug(5, "IMuseDigital::dispatchNavigateMap(): next streamZone is unallocated, calling dispatchPrepareToJump()");
+ dispatchPrepareToJump(dispatchPtr, dispatchPtr->streamZoneList, mapCurEvent, 1);
+ }
+
+ debug(5, "IMuseDigital::dispatchNavigateMap(): \n"
+ "\tJUMP found for sound %d with valid candidateHookId (%d), \n"
+ "\tgoing to offset %d with a crossfade of %d ms",
+ dispatchPtr->trackPtr->soundId, mapCurEvent[4], mapCurEvent[3], mapCurEvent[5]);
+
+ dispatchPtr->streamZoneList->useFlag = 0;
+ removeStreamZoneFromList(&dispatchPtr->streamZoneList, dispatchPtr->streamZoneList);
+
+ if (dispatchPtr->streamZoneList->fadeFlag) {
+ if (dispatchPtr->fadeBuf) {
+ // Mark the fade corresponding to our fadeBuf as unused
+ dispatchDeallocateFade(dispatchPtr, "dispatchNavigateMap");
+ }
+
+ _dispatchJumpFadeSize = dispatchPtr->streamZoneList->size;
+ dispatchPtr->fadeBuf = dispatchAllocateFade(_dispatchJumpFadeSize, "dispatchNavigateMap");
+
+ // If the fade buffer is allocated
+ // set up the fade
+ if (dispatchPtr->fadeBuf) {
+ dispatchPtr->fadeWordSize = dispatchPtr->wordSize;
+ dispatchPtr->fadeSampleRate = dispatchPtr->sampleRate;
+ dispatchPtr->fadeChannelCount = dispatchPtr->channelCount;
+ dispatchPtr->fadeOffset = 0;
+ dispatchPtr->fadeRemaining = 0;
+ dispatchPtr->fadeSyncFlag = 0;
+ dispatchPtr->fadeSyncDelta = 0;
+ dispatchPtr->fadeVol = DIMUSE_MAX_FADE_VOLUME;
+ dispatchPtr->fadeSlope = 0;
+
+ // Clone the old sound in the fade buffer for just the duration of the fade
+ if (_dispatchJumpFadeSize) {
+ do {
+ effFadeSize = _dispatchJumpFadeSize - dispatchPtr->fadeRemaining;
+ if ((_dispatchJumpFadeSize - dispatchPtr->fadeRemaining) >= 0x4000)
+ effFadeSize = 0x4000;
+
+ memcpy(&dispatchPtr->fadeBuf[dispatchPtr->fadeRemaining],
+ streamerGetStreamBuffer(dispatchPtr->streamPtr, effFadeSize),
+ effFadeSize);
+
+ elapsedFadeSize = effFadeSize + dispatchPtr->fadeRemaining;
+ dispatchPtr->fadeRemaining = elapsedFadeSize;
+ } while (_dispatchJumpFadeSize > elapsedFadeSize);
+ }
+ _dispatchFadeStartedFlag = 1;
+ }
+ dispatchPtr->streamZoneList->useFlag = 0;
+ removeStreamZoneFromList(&dispatchPtr->streamZoneList, dispatchPtr->streamZoneList);
+ }
+ }
+ mapCurEvent = nullptr;
+ }
+
+ continue;
+ case MKTAG('S', 'Y', 'N', 'C'):
+ // SYNC block (fixed size: x bytes)
+ // - The tag 'SYNC' (4 bytes)
+ // - SYNC size in bytes (4 bytes)
+ // - SYNC data (variable length)
+
+ // It is possible to gather a total maximum of 4 SYNCs for a single track;
+ // this is not a problem however, as speech files only have one SYNC block,
+ // and the most we get is four (one for each character) in
+ // A Pirate I Was Meant To Be, in Part 3 of COMI
+
+ // Curiously we skip the first four bytes of data, ending up having the first
+ // four bytes of the next block in our syncPtr; but this is exactly what happens
+ // within the interpreter, so I'm not going to argue with it
+
+ if (!dispatchPtr->trackPtr->syncPtr_0) {
+ dispatchPtr->trackPtr->syncPtr_0 = (byte *)malloc(mapCurEvent[1]);
+ memcpy(dispatchPtr->trackPtr->syncPtr_0, mapCurEvent + 3, mapCurEvent[1]);
+ dispatchPtr->trackPtr->syncSize_0 = mapCurEvent[1];
+
+ } else if (!dispatchPtr->trackPtr->syncPtr_1) {
+ dispatchPtr->trackPtr->syncPtr_1 = (byte *)malloc(mapCurEvent[1]);
+ memcpy(dispatchPtr->trackPtr->syncPtr_1, mapCurEvent + 3, mapCurEvent[1]);
+ dispatchPtr->trackPtr->syncSize_1 = mapCurEvent[1];
+
+ } else if (!dispatchPtr->trackPtr->syncPtr_2) {
+ dispatchPtr->trackPtr->syncPtr_2 = (byte *)malloc(mapCurEvent[1]);
+ memcpy(dispatchPtr->trackPtr->syncPtr_2, mapCurEvent + 3, mapCurEvent[1]);
+ dispatchPtr->trackPtr->syncSize_2 = mapCurEvent[1];
+
+ } else if (!dispatchPtr->trackPtr->syncPtr_3) {
+ dispatchPtr->trackPtr->syncPtr_3 = (byte *)malloc(mapCurEvent[1]);
+ memcpy(dispatchPtr->trackPtr->syncPtr_3, mapCurEvent + 3, mapCurEvent[1]);
+ dispatchPtr->trackPtr->syncSize_3 = mapCurEvent[1];
+ }
+
+ continue;
+ case MKTAG('F', 'R', 'M', 'T'):
+ // Format block (fixed size: 28 bytes)
+ // - The tag 'FRMT' (4 bytes)
+ // - Block size in bytes minus 8 (4 bytes)
+ // - Block offset (4 bytes)
+ // - Empty field (4 bytes) (which is set to 1 in Grim Fandango, I suspect this is the endianness)
+ // - Word size between 8, 12 and 16 (4 bytes)
+ // - Sample rate (4 bytes)
+ // - Number of channels (4 bytes)
+ dispatchPtr->wordSize = mapCurEvent[4];
+ dispatchPtr->sampleRate = mapCurEvent[5];
+ dispatchPtr->channelCount = mapCurEvent[6];
+
+ continue;
+ case MKTAG('R', 'E', 'G', 'N'):
+ // Region block (fixed size: 16 bytes)
+ // - The tag 'REGN' (4 bytes)
+ // - Block size in bytes minus 8 (4 bytes)
+ // - Block offset (4 bytes)
+ // - Region length (4 bytes)
+ regionOffset = mapCurEvent[2];
+ if (regionOffset == dispatchPtr->currentOffset) {
+ dispatchPtr->audioRemaining = mapCurEvent[3];
+ return 0;
+ } else {
+ debug(5, "IMuseDigital::dispatchNavigateMap(): ERROR: region offset %d != currentOffset %d", regionOffset, dispatchPtr->currentOffset);
+ return -1;
+ }
+ case MKTAG('S', 'T', 'O', 'P'):
+ // Stop block (fixed size: 12 bytes)
+ // Contains:
+ // - The tag 'STOP' (4 bytes)
+ // - Block size in bytes minus 8 (4 bytes)
+ // - Block offset (4 bytes)
+ return -1;
+ case MKTAG('T', 'E', 'X', 'T'):
+ // Marker block (variable size)
+ // Contains:
+ // - The tag 'TEXT' (4 bytes)
+ // - Block size in bytes minus 8 (4 bytes)
+ // - Block offset (4 bytes)
+ // - A string of characters ending with '\0' (variable length)
+ marker = (char *)mapCurEvent + 12;
+ _triggersHandler->processTriggers(dispatchPtr->trackPtr->soundId, marker);
+ if (dispatchPtr->audioRemaining)
+ return 0;
+
+ continue;
+ default:
+ debug(5, "IMuseDigital::dispatchNavigateMap(): ERROR: Unrecognized map event at offset %dx", dispatchPtr->currentOffset);
+ break;
+ };
+ }
+ return -1;
+}
+
+int IMuseDigital::dispatchGetMap(IMuseDigiDispatch *dispatchPtr) {
+ int32 *dstMap;
+ uint8 *rawMap, *copiedBuf, *soundAddrData;
+ int32 size;
+
+ dstMap = dispatchPtr->map;
+
+ // If there's no map, try to fetch it
+ if (dispatchPtr->map[0] != MKTAG('M', 'A', 'P', ' ')) {
+ if (dispatchPtr->currentOffset) {
+ debug(5, "IMuseDigital::dispatchNavigateMap(): found offset but no map");
+ return -1;
+ }
+
+ // If there's a streamPtr it means that this is a sound loaded
+ // from a bundle (either music or speech)
+ if (dispatchPtr->streamPtr) {
+ copiedBuf = (uint8 *)streamerGetStreamBufferAtOffset(dispatchPtr->streamPtr, 0, 0x10);
+
+ if (!copiedBuf) {
+ return -3;
+ }
+
+ if (READ_BE_UINT32(copiedBuf) == MKTAG('i', 'M', 'U', 'S') && READ_BE_UINT32(copiedBuf + 8) == MKTAG('M', 'A', 'P', ' ')) {
+ size = READ_BE_UINT32(copiedBuf + 12) + 24;
+ if (!streamerGetStreamBufferAtOffset(dispatchPtr->streamPtr, 0, size)) {
+ return -3;
+ }
+ rawMap = (uint8 *)streamerGetStreamBuffer(dispatchPtr->streamPtr, size);
+ if (!rawMap) {
+ debug(5, "IMuseDigital::dispatchGetMap(): ERROR: stream read failed after view succeeded");
+ return -1;
+ }
+
+ dispatchPtr->currentOffset = size;
+ if (dispatchConvertMap(rawMap + 8, (uint8 *)dstMap)) {
+ debug(5, "IMuseDigital::dispatchGetMap(): ERROR: dispatchConvertMap() failed");
+ return -1;
+ }
+
+ if (dispatchPtr->map[2] != MKTAG('F', 'R', 'M', 'T')) {
+ debug(5, "IMuseDigital::dispatchGetMap(): ERROR: expected 'FRMT' at start of map");
+ return -1;
+ }
+
+ if (dispatchPtr->map[4] != dispatchPtr->currentOffset) {
+ debug(5, "IMuseDigital::dispatchGetMap(): ERROR: expected data to follow map");
+ return -1;
+ } else {
+ if (dispatchPtr->streamZoneList) {
+ debug(5, "IMuseDigital::dispatchGetMap(): ERROR: expected NULL streamZoneList");
+ return -1;
+ }
+
+ dispatchPtr->streamZoneList = dispatchAllocateStreamZone();
+ if (!dispatchPtr->streamZoneList) {
+ debug(5, "IMuseDigital::dispatchGetMap(): ERROR: couldn't allocate zone");
+ return -1;
+ }
+
+ dispatchPtr->streamZoneList->offset = dispatchPtr->currentOffset;
+ dispatchPtr->streamZoneList->size = streamerGetFreeBufferAmount(dispatchPtr->streamPtr);
+ dispatchPtr->streamZoneList->fadeFlag = 0;
+ }
+ } else {
+ debug(5, "IMuseDigital::dispatchGetMap(): ERROR: unrecognized file format in stream buffer");
+ return -1;
+ }
+
+ } else {
+ // Otherwise, this is a SFX and we must load it using its resource pointer
+ soundAddrData = _filesHandler->getSoundAddrData(dispatchPtr->trackPtr->soundId);
+
+ if (!soundAddrData) {
+ debug(5, "IMuseDigital::dispatchGetMap(): ERROR: couldn't get sound address");
+ return -1;
+ }
+
+ if (READ_BE_UINT32(soundAddrData) == MKTAG('i', 'M', 'U', 'S') && READ_BE_UINT32(soundAddrData + 8) == MKTAG('M', 'A', 'P', ' ')) {
+ dispatchPtr->currentOffset = READ_BE_UINT32(soundAddrData + 12) + 24;
+ if (dispatchConvertMap((soundAddrData + 8), (uint8 *)dstMap)) {
+ debug(5, "IMuseDigital::dispatchGetMap(): ERROR: dispatchConvertMap() failure");
+ return -1;
+ }
+
+ if (dispatchPtr->map[2] != MKTAG('F', 'R', 'M', 'T')) {
+ debug(5, "IMuseDigital::dispatchGetMap(): ERROR: expected 'FRMT' at start of map");
+ return -1;
+ }
+
+ if (dispatchPtr->map[4] != dispatchPtr->currentOffset) {
+ debug(5, "IMuseDigital::dispatchGetMap(): ERROR: expected data to follow map");
+ return -1;
+ }
+ } else {
+ debug(5, "IMuseDigital::dispatchGetMap(): ERROR: unrecognized file format in stream buffer");
+ return -1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+int IMuseDigital::dispatchConvertMap(uint8 *rawMap, uint8 *destMap) {
+ int32 effMapSize;
+ uint8 *mapCurPos;
+ int32 blockName;
+ uint8 *blockSizePtr;
+ uint32 blockSizeMin8;
+ uint8 *firstChar;
+ uint8 *otherChars;
+ uint32 remainingFieldsNum;
+ int32 bytesUntilEndOfMap;
+ uint8 *endOfMapPtr;
+
+ if (READ_BE_UINT32(rawMap) == MKTAG('M', 'A', 'P', ' ')) {
+ bytesUntilEndOfMap = READ_BE_UINT32(rawMap + 4);
+ effMapSize = bytesUntilEndOfMap + 8;
+ if (((_vm->_game.id == GID_DIG
+ || (_vm->_game.id == GID_CMI && _vm->_game.features & GF_DEMO)) && effMapSize <= 0x400)
+ || (_vm->_game.id == GID_CMI && effMapSize <= 0x2000)) {
+ memcpy(destMap, rawMap, effMapSize);
+
+ // Fill (or rather, swap32) the fields:
+ // - The 4 bytes string 'MAP '
+ // - Size of the map
+ //int dest = READ_BE_UINT32(destMap);
+ *(int32 *)destMap = READ_BE_UINT32(destMap);
+ *((int32 *)destMap + 1) = READ_BE_UINT32(destMap + 4);
+
+ mapCurPos = destMap + 8;
+ endOfMapPtr = &destMap[effMapSize];
+
+ // Swap32 the rest of the map
+ while (mapCurPos < endOfMapPtr) {
+ // Swap32 the 4 characters block name
+ int32 swapped = READ_BE_UINT32(mapCurPos);
+ *(int32 *)mapCurPos = swapped;
+ blockName = swapped;
+
+ // Advance and Swap32 the block size (minus 8) field
+ blockSizePtr = mapCurPos + 4;
+ blockSizeMin8 = READ_BE_UINT32(blockSizePtr);
+ *(int32 *)blockSizePtr = blockSizeMin8;
+ mapCurPos = blockSizePtr + 4;
+
+ // Swapping32 a TEXT block is different:
+ // it also contains single characters, so we skip them
+ // since they're already good like this
+ if (blockName == MKTAG('T', 'E', 'X', 'T')) {
+ // Swap32 the block offset position
+ *(int32 *)mapCurPos = READ_BE_UINT32(mapCurPos);
+
+ // Skip the single characters
+ firstChar = mapCurPos + 4;
+ mapCurPos += 5;
+ if (*firstChar) {
+ do {
+ otherChars = mapCurPos++;
+ } while (*otherChars);
+ }
+ } else if ((blockSizeMin8 & 0xFFFFFFFC) != 0) {
+ // Basically divide by 4 to retrieve the number
+ // of fields to swap
+ remainingFieldsNum = blockSizeMin8 >> 2;
+
+ // ...and swap them of course
+ do {
+ *(int32 *)mapCurPos = READ_BE_UINT32(mapCurPos);
+ mapCurPos += 4;
+ --remainingFieldsNum;
+ } while (remainingFieldsNum);
+ }
+ }
+
+ // Just a sanity check to see if we've parsed the whole map
+ if (&destMap[bytesUntilEndOfMap] - mapCurPos == -8) {
+ return 0;
+ } else {
+ debug(5, "IMuseDigital::dispatchConvertMap(): ERROR: converted wrong number of bytes");
+ return -1;
+ }
+ } else {
+ debug(5, "IMuseDigital::dispatchConvertMap(): ERROR: map is too big (%d)", effMapSize);
+ return -1;
+ }
+ } else {
+ debug(5, "IMuseDigital::dispatchConvertMap(): ERROR: got bogus map");
+ return -1;
+ }
+
+ return 0;
+}
+
+int32 *IMuseDigital::dispatchGetNextMapEvent(int32 *mapPtr, int32 soundOffset, int32 *mapEvent) {
+ if (mapEvent) {
+ // Advance the map to the next block (mapEvent[1] + 8 is the size of the block)
+ mapEvent = (int32 *)((int8 *)mapEvent + mapEvent[1] + 8);
+
+ if ((int8 *)&mapPtr[2] + mapPtr[1] > (int8 *)mapEvent) {
+ if (mapEvent[2] != soundOffset) {
+ debug(5, "IMuseDigital::dispatchGetNextMapEvent(): ERROR: no more events at offset %d", soundOffset);
+ return nullptr;
+ }
+ } else {
+ debug(5, "IMuseDigital::dispatchGetNextMapEvent(): ERROR: map overrun");
+ return nullptr;
+ }
+
+ } else {
+ // Init the current map position starting from the first block
+ // (cells 0 and 1 are the tag 'MAP ' and the map size respectively)
+ mapEvent = &mapPtr[2];
+
+ // Search for the block with the same offset as ours
+ while (mapEvent[2] != soundOffset) {
+ // Check if we've overrun the offset, to make sure
+ // that there actually is an event at our offset
+ mapEvent = (int32 *)((int8 *)mapEvent + mapEvent[1] + 8);
+
+ if ((int8 *)&mapPtr[2] + mapPtr[1] <= (int8 *)mapEvent) {
+ debug(5, "IMuseDigital::dispatchGetNextMapEvent(): ERROR: couldn't find event at offset %d", soundOffset);
+ return nullptr;
+ }
+ }
+ }
+
+ return mapEvent;
+}
+
+void IMuseDigital::dispatchPredictStream(IMuseDigiDispatch *dispatchPtr) {
+ IMuseDigiStreamZone *szTmp, *lastStreamInList, *curStrZn;
+ int32 cumulativeStreamOffset;
+ int32 *jumpParameters;
+
+ if (!dispatchPtr->streamPtr || !dispatchPtr->streamZoneList) {
+ debug(5, "IMuseDigital::dispatchPredictStream(): ERROR: NULL streamId or streamZoneList");
+ return;
+ }
+
+ szTmp = dispatchPtr->streamZoneList;
+
+ // Get the offset which our stream is currently at
+ cumulativeStreamOffset = 0;
+ do {
+ cumulativeStreamOffset += szTmp->size;
+ lastStreamInList = szTmp;
+ szTmp = szTmp->next;
+ } while (szTmp);
+
+ lastStreamInList->size += streamerGetFreeBufferAmount(dispatchPtr->streamPtr) - cumulativeStreamOffset;
+ curStrZn = dispatchPtr->streamZoneList;
+
+ for (_dispatchBufferedHookId = dispatchPtr->trackPtr->jumpHook; curStrZn; curStrZn = curStrZn->next) {
+ if (!curStrZn->fadeFlag) {
+ jumpParameters = dispatchCheckForJump(dispatchPtr->map, curStrZn, _dispatchBufferedHookId);
+ if (jumpParameters) {
+ // If we've reached a JUMP and it's successful, allocate the streamZone of the destination
+ dispatchPrepareToJump(dispatchPtr, curStrZn, jumpParameters, 0);
+ } else {
+ // If we don't have to jump, just play the next streamZone available
+ dispatchStreamNextZone(dispatchPtr, curStrZn);
+ }
+ }
+ }
+}
+
+int32 *IMuseDigital::dispatchCheckForJump(int32 *mapPtr, IMuseDigiStreamZone *strZnPtr, int &candidateHookId) {
+ int32 *curMapPlace = &mapPtr[2];
+ int32 *endOfMap = (int32 *)((int8 *)&mapPtr[2] + mapPtr[1]);
+ int32 mapPlaceTag, jumpHookPos, jumpHookId, bytesUntilNextPlace;
+
+ while (curMapPlace < endOfMap) {
+ mapPlaceTag = curMapPlace[0];
+ bytesUntilNextPlace = curMapPlace[1] + 8;
+
+ if (mapPlaceTag == MKTAG('J', 'U', 'M', 'P')) {
+ jumpHookPos = curMapPlace[2];
+ jumpHookId = curMapPlace[4];
+
+ if (jumpHookPos > strZnPtr->offset && jumpHookPos <= strZnPtr->size + strZnPtr->offset) {
+ if (!checkHookId(candidateHookId, jumpHookId))
+ return curMapPlace;
+ }
+ }
+ // Advance the map to the next place
+ curMapPlace = (int32 *)((int8 *)curMapPlace + bytesUntilNextPlace);
+ }
+
+ return nullptr;
+}
+
+void IMuseDigital::dispatchPrepareToJump(IMuseDigiDispatch *dispatchPtr, IMuseDigiStreamZone *strZnPtr, int32 *jumpParams, int calledFromNavigateMap) {
+ int32 hookPosition, jumpDestination, fadeTime;
+ IMuseDigiStreamZone *nextStreamZone;
+ IMuseDigiStreamZone *zoneForJump = nullptr;
+ IMuseDigiStreamZone *zoneAfterJump;
+ uint32 streamOffset;
+ IMuseDigiStreamZone *zoneCycle;
+
+ // jumpParams format:
+ // jumpParams[0]: four bytes which form the string 'JUMP'
+ // jumpParams[1]: block size in bytes minus 8 (16 for a JUMP block like this one; total == 24 bytes)
+ // jumpParams[2]: hook position
+ // jumpParams[3]: jump destination
+ // jumpParams[4]: hook ID
+ // jumpParams[5]: fade time in milliseconds
+
+ hookPosition = jumpParams[2];
+ jumpDestination = jumpParams[3];
+ fadeTime = jumpParams[5];
+
+ // Edge cases handling
+ if (strZnPtr->size + strZnPtr->offset == hookPosition) {
+ nextStreamZone = strZnPtr->next;
+ if (nextStreamZone) {
+ if (nextStreamZone->fadeFlag) {
+ // Avoid jumping if the next stream zone is already fading
+ // and its ending position is our jump destination.
+ // Basically: cancel the jump if there's already a fade in progress
+ if (nextStreamZone->offset == hookPosition) {
+ if (nextStreamZone->next) {
+ if (nextStreamZone->next->offset == jumpDestination)
+ return;
+ }
+ }
+ } else {
+ // Avoid jumping if we're trying to jump to the next stream zone
+ if (nextStreamZone->offset == jumpDestination)
+ return;
+ }
+ }
+ }
+
+ // Maximum size of the dispatch for the fade (in bytes)
+ _dispatchSize = dispatchGetFadeSize(dispatchPtr, fadeTime);
+
+ // If this function is being called from dispatchPredictStream,
+ // avoid accepting an oversized dispatch
+ if (!calledFromNavigateMap) {
+ if (_dispatchSize > strZnPtr->size + strZnPtr->offset - hookPosition)
+ return;
+ }
+
+ // Cap the dispatch size, if oversized
+ if (_dispatchSize > strZnPtr->size + strZnPtr->offset - hookPosition)
+ _dispatchSize = strZnPtr->size + strZnPtr->offset - hookPosition;
+
+ // This prevents starting a fade with an inverted stereo image
+ dispatchValidateFadeSize(dispatchPtr, _dispatchSize, "dispatchPrepareToJump");
+
+ if (_vm->_game.id == GID_DIG) {
+ if (hookPosition < jumpDestination)
+ _dispatchSize = 0;
+ } else {
+ if (dispatchPtr->fadeRemaining)
+ _dispatchSize = 0;
+ }
+
+ // Try allocating the two streamZones needed for the jump
+ if (_dispatchSize) {
+ zoneForJump = dispatchAllocateStreamZone();
+ if (!zoneForJump) {
+ debug(5, "IMuseDigital::dispatchPrepareToJump(): ERROR: couldn't allocate streamZone");
+ return;
+ }
+ }
+
+ zoneAfterJump = dispatchAllocateStreamZone();
+ if (!zoneAfterJump) {
+ debug(5, "IMuseDigital::dispatchPrepareToJump(): ERROR: couldn't allocate streamZone");
+ return;
+ }
+
+ strZnPtr->size = hookPosition - strZnPtr->offset;
+ streamOffset = hookPosition - strZnPtr->offset + _dispatchSize;
+
+ // Go to the interested stream zone to calculate the stream offset,
+ // and schedule the sound to stream with that offset
+ zoneCycle = dispatchPtr->streamZoneList;
+ while (zoneCycle != strZnPtr) {
+ streamOffset += zoneCycle->size;
+ zoneCycle = zoneCycle->next;
+ }
+
+ streamerSetLoadIndex(dispatchPtr->streamPtr, streamOffset);
+
+ while (strZnPtr->next) {
+ strZnPtr->next->useFlag = 0;
+ removeStreamZoneFromList(&strZnPtr->next, strZnPtr->next);
+ }
+
+ streamerSetSoundToStreamFromOffset(dispatchPtr->streamPtr,
+ dispatchPtr->trackPtr->soundId, jumpDestination);
+
+ // Prepare the fading zone for the jump
+ // and also a subsequent empty dummy zone
+ if (_dispatchSize) {
+ strZnPtr->next = zoneForJump;
+ zoneForJump->prev = strZnPtr;
+ strZnPtr = zoneForJump;
+ zoneForJump->next = 0;
+ zoneForJump->offset = hookPosition;
+ zoneForJump->size = _dispatchSize;
+ zoneForJump->fadeFlag = 1;
+ }
+
+ strZnPtr->next = zoneAfterJump;
+ zoneAfterJump->prev = strZnPtr;
+ zoneAfterJump->next = nullptr;
+ zoneAfterJump->offset = jumpDestination;
+ zoneAfterJump->size = 0;
+ zoneAfterJump->fadeFlag = 0;
+}
+
+void IMuseDigital::dispatchStreamNextZone(IMuseDigiDispatch *dispatchPtr, IMuseDigiStreamZone *strZnPtr) {
+ int32 cumulativeStreamOffset;
+ IMuseDigiStreamZone *szTmp;
+
+ if (strZnPtr->next) {
+ cumulativeStreamOffset = strZnPtr->size;
+ szTmp = dispatchPtr->streamZoneList;
+ while (szTmp != strZnPtr) {
+ cumulativeStreamOffset += szTmp->size;
+ szTmp = szTmp->next;
+ }
+
+ // Set the stream load index of the sound to the new streamZone
+ streamerSetLoadIndex(dispatchPtr->streamPtr, cumulativeStreamOffset);
+
+ // Remove che previous streamZone from the list, we don't need it anymore
+ while (strZnPtr->next->prev) {
+ strZnPtr->next->prev->useFlag = 0;
+ removeStreamZoneFromList(&strZnPtr->next, strZnPtr->next->prev);
+ }
+
+ streamerSetSoundToStreamFromOffset(
+ dispatchPtr->streamPtr,
+ dispatchPtr->trackPtr->soundId,
+ strZnPtr->size + strZnPtr->offset);
+ }
+}
+
+IMuseDigiStreamZone *IMuseDigital::dispatchAllocateStreamZone() {
+ for (int i = 0; i < DIMUSE_MAX_STREAMZONES; i++) {
+ if (_streamZones[i].useFlag == 0) {
+ _streamZones[i].prev = 0;
+ _streamZones[i].next = 0;
+ _streamZones[i].useFlag = 1;
+ _streamZones[i].offset = 0;
+ _streamZones[i].size = 0;
+ _streamZones[i].fadeFlag = 0;
+
+ return &_streamZones[i];
+ }
+ }
+ debug(5, "IMuseDigital::dispatchAllocateStreamZone(): ERROR: out of streamZones");
+ return nullptr;
+}
+
+uint8 *IMuseDigital::dispatchAllocateFade(int32 &fadeSize, const char *function) {
+ uint8 *allocatedFadeBuf = nullptr;
+ if (fadeSize > DIMUSE_LARGE_FADE_DIM) {
+ debug(5, "IMuseDigital::dispatchAllocateFade(): WARNING: requested fade too large (%d) in %s()", fadeSize, function);
+ fadeSize = DIMUSE_LARGE_FADE_DIM;
+ }
+
+ if (fadeSize <= DIMUSE_SMALL_FADE_DIM) { // Small fade
+ for (int i = 0; i <= DIMUSE_SMALL_FADES; i++) {
+ if (i == DIMUSE_SMALL_FADES) {
+ debug(5, "IMuseDigital::dispatchAllocateFade(): couldn't allocate small fade buffer in %s()", function);
+ allocatedFadeBuf = nullptr;
+ break;
+ }
+
+ if (!_dispatchSmallFadeFlags[i]) {
+ _dispatchSmallFadeFlags[i] = 1;
+ allocatedFadeBuf = &_dispatchSmallFadeBufs[DIMUSE_SMALL_FADE_DIM * i];
+ break;
+ }
+ }
+ } else { // Large fade
+ for (int i = 0; i <= DIMUSE_LARGE_FADES; i++) {
+ if (i == DIMUSE_LARGE_FADES) {
+ debug(5, "IMuseDigital::dispatchAllocateFade(): couldn't allocate large fade buffer in %s()", function);
+ allocatedFadeBuf = nullptr;
+ break;
+ }
+
+ if (!_dispatchLargeFadeFlags[i]) {
+ _dispatchLargeFadeFlags[i] = 1;
+ allocatedFadeBuf = &_dispatchLargeFadeBufs[DIMUSE_LARGE_FADE_DIM * i];
+ break;
+ }
+ }
+
+ // Fallback to a small fade if large fades are unavailable
+ if (!allocatedFadeBuf) {
+ for (int i = 0; i <= DIMUSE_SMALL_FADES; i++) {
+ if (i == DIMUSE_SMALL_FADES) {
+ debug(5, "IMuseDigital::dispatchAllocateFade(): couldn't allocate small fade buffer in %s()", function);
+ allocatedFadeBuf = nullptr;
+ break;
+ }
+
+ if (!_dispatchSmallFadeFlags[i]) {
+ _dispatchSmallFadeFlags[i] = 1;
+ allocatedFadeBuf = &_dispatchSmallFadeBufs[DIMUSE_SMALL_FADE_DIM * i];
+ break;
+ }
+ }
+ }
+ }
+
+ return allocatedFadeBuf;
+}
+
+void IMuseDigital::dispatchDeallocateFade(IMuseDigiDispatch *dispatchPtr, const char *function) {
+ // This function flags the fade corresponding to our fadeBuf as unused
+
+ // First, check if our fade buffer is one of the large fade buffers
+ for (int i = 0; i < DIMUSE_LARGE_FADES; i++) {
+ if (_dispatchLargeFadeBufs + (DIMUSE_LARGE_FADE_DIM * i) == dispatchPtr->fadeBuf) { // Found it!
+ if (_dispatchLargeFadeFlags[i] == 0) {
+ debug(5, "IMuseDigital::dispatchDeallocateFade(): redundant large fade buf de-allocation in %s()", function);
+ }
+ _dispatchLargeFadeFlags[i] = 0;
+ return;
+ }
+ }
+
+ // If not, check between the small fade buffers
+ for (int j = 0; j < DIMUSE_SMALL_FADES; j++) {
+ if (_dispatchSmallFadeBufs + (DIMUSE_SMALL_FADE_DIM * j) == dispatchPtr->fadeBuf) { // Found it!
+ if (_dispatchSmallFadeFlags[j] == 0) {
+ debug(5, "IMuseDigital::dispatchDeallocateFade(): redundant small fade buf de-allocation in %s()", function);
+ }
+ _dispatchSmallFadeFlags[j] = 0;
+ return;
+ }
+ }
+
+ debug(5, "IMuseDigital::dispatchDeallocateFade(): couldn't find fade buf to de-allocate in %s()", function);
+}
+
+int IMuseDigital::dispatchGetFadeSize(IMuseDigiDispatch *dispatchPtr, int fadeLength) {
+ return (dispatchPtr->wordSize * dispatchPtr->channelCount * ((dispatchPtr->sampleRate * fadeLength / 1000) & 0xFFFFFFFE)) / 8;
+}
+
+void IMuseDigital::dispatchValidateFadeSize(IMuseDigiDispatch *dispatchPtr, int32 &dispatchSize, const char *function) {
+ int alignmentModDividend;
+ if (_vm->_game.id == GID_DIG || (_vm->_game.id == GID_CMI && _vm->_game.features & GF_DEMO)) {
+ alignmentModDividend = dispatchPtr->channelCount * (dispatchPtr->wordSize == 8 ? 1 : 3);
+ } else {
+ if (dispatchPtr->wordSize == 8) {
+ alignmentModDividend = dispatchPtr->channelCount * 1;
+ } else {
+ alignmentModDividend = dispatchPtr->channelCount * ((dispatchPtr->wordSize == 12) + 2);
+ }
+ }
+
+ if (alignmentModDividend) {
+ dispatchSize -= dispatchSize % alignmentModDividend;
+ } else {
+ debug(5, "IMuseDigital::dispatchValidateFadeSize(): WARNING: tried mod by 0 while validating fade size in %s(), ignored", function);
+ }
+}
+
+int IMuseDigital::dispatchUpdateFadeMixVolume(IMuseDigiDispatch *dispatchPtr, int32 remainingFade) {
+ int mixVolume = (((dispatchPtr->fadeVol / 65536) + 1) * dispatchPtr->trackPtr->effVol) / 128;
+ dispatchPtr->fadeVol += remainingFade * dispatchPtr->fadeSlope;
+
+ if (dispatchPtr->fadeVol < 0)
+ dispatchPtr->fadeVol = 0;
+ if (dispatchPtr->fadeVol > DIMUSE_MAX_FADE_VOLUME)
+ dispatchPtr->fadeVol = DIMUSE_MAX_FADE_VOLUME;
+
+ return mixVolume;
+}
+
+int IMuseDigital::dispatchUpdateFadeSlope(IMuseDigiDispatch *dispatchPtr) {
+ int32 updatedVolume, effRemainingFade;
+
+ updatedVolume = (dispatchPtr->trackPtr->effVol * (128 - (dispatchPtr->fadeVol / 65536))) / 128;
+ if (!dispatchPtr->fadeSlope) {
+ effRemainingFade = dispatchPtr->fadeRemaining;
+ if (effRemainingFade <= 1)
+ effRemainingFade = 2;
+ dispatchPtr->fadeSlope = -(DIMUSE_MAX_FADE_VOLUME / effRemainingFade);
+ }
+
+ return updatedVolume;
+}
+
+void IMuseDigital::dispatchVOCLoopCallback(int soundId) {
+ IMuseDigiDispatch *curDispatchPtr;
+ uint8 *dataBlockTag;
+
+ if (!soundId)
+ return;
+
+ for (int i = 0; i < _trackCount; i++) {
+ curDispatchPtr = &_dispatches[i];
+ if (curDispatchPtr->trackPtr->soundId == soundId) {
+ dataBlockTag = streamerGetStreamBufferAtOffset(curDispatchPtr->streamPtr, curDispatchPtr->audioRemaining, 1);
+ if (dataBlockTag && dataBlockTag[0] == 7) { // End of loop
+ streamerSetLoadIndex(curDispatchPtr->streamPtr, curDispatchPtr->audioRemaining + 1);
+ streamerSetSoundToStreamFromOffset(curDispatchPtr->streamPtr, curDispatchPtr->trackPtr->soundId, curDispatchPtr->vocLoopStartingPoint);
+ }
+ }
+ }
+}
+
+int IMuseDigital::dispatchSeekToNextChunk(IMuseDigiDispatch *dispatchPtr) {
+ uint8 *headerBuf;
+ uint8 *soundAddrData;
+ int32 resSize;
+
+ while (1) {
+ if (dispatchPtr->streamPtr) {
+ headerBuf = streamerGetStreamBufferAtOffset(dispatchPtr->streamPtr, 0, 0x30);
+ if (headerBuf || (headerBuf = streamerGetStreamBufferAtOffset(dispatchPtr->streamPtr, 0, 1)) != 0) {
+ // Little hack: avoid copying stuff from the resource to the
+ // header buffer beyond the resource size limit
+ resSize = _filesHandler->getSoundAddrDataSize(dispatchPtr->trackPtr->soundId, dispatchPtr->streamPtr != nullptr);
+ if ((resSize - dispatchPtr->currentOffset) < 0x30) {
+ memcpy(_currentVOCHeader, headerBuf, resSize - dispatchPtr->currentOffset);
+ } else {
+ memcpy(_currentVOCHeader, headerBuf, 0x30);
+ }
+ } else {
+ return -3;
+ }
+ } else {
+ soundAddrData = _filesHandler->getSoundAddrData(dispatchPtr->trackPtr->soundId);
+ if (soundAddrData) {
+ // Little hack: see above
+ resSize = _filesHandler->getSoundAddrDataSize(dispatchPtr->trackPtr->soundId, dispatchPtr->streamPtr != nullptr);
+ if ((resSize - dispatchPtr->currentOffset) < 0x30) {
+ memcpy(_currentVOCHeader, &soundAddrData[dispatchPtr->currentOffset], resSize - dispatchPtr->currentOffset);
+ } else {
+ memcpy(_currentVOCHeader, &soundAddrData[dispatchPtr->currentOffset], 0x30);
+ }
+ } else {
+ return -1;
+ }
+ }
+
+ if (READ_BE_UINT32(_currentVOCHeader) == MKTAG('C', 'r', 'e', 'a')) {
+ // We expect to find (everything in Little Endian except where noted):
+ // - The string "Creative Voice File" stored in Big Endian format;
+ // - 0x1A, which the interpreter doesn't check, so we don't either
+ // - Total size of the header, which has to be 0x001A (0x1A00 in LE)
+ // - Version tags: 0x0A (minor), 0x01 (major), this corresponds to version 1.10
+ if (_currentVOCHeader[20] == 0x1A && _currentVOCHeader[21] == 0x0 &&
+ _currentVOCHeader[22] == 0xA && _currentVOCHeader[23] == 0x1) {
+ dispatchPtr->currentOffset += 26;
+ if (dispatchPtr->streamPtr)
+ streamerGetStreamBuffer(dispatchPtr->streamPtr, 26);
+ continue;
+ }
+ return -1;
+ } else {
+ uint8 *headerTag = _currentVOCHeader;
+ if (headerTag[0] != 1 && headerTag[0] != 4 && headerTag[0] != 6 && headerTag[0] != 7)
+ headerTag += 2;
+
+ switch (headerTag[0]) {
+ case 1:
+ dispatchPtr->sampleRate = headerTag[4] > 196 ? 22050 : 11025;
+ dispatchPtr->audioRemaining = (READ_LE_UINT32(headerTag) >> 8) - 2;
+ dispatchPtr->currentOffset += 6;
+
+ // Another little hack to avoid click and pops artifacts:
+ // read one audio sample less if this is the last audio chunk of the file
+ resSize = _filesHandler->getSoundAddrDataSize(dispatchPtr->trackPtr->soundId, dispatchPtr->streamPtr != nullptr);
+ if ((resSize - (dispatchPtr->currentOffset + dispatchPtr->audioRemaining)) < 0x30) {
+ dispatchPtr->audioRemaining -= 2;
+ }
+
+ if (dispatchPtr->streamPtr) {
+ streamerGetStreamBuffer(dispatchPtr->streamPtr, 6);
+ if (dispatchPtr->vocLoopStartingPoint)
+ streamerSetLoopFlag(dispatchPtr->streamPtr, dispatchPtr->audioRemaining + dispatchPtr->currentOffset);
+ }
+ return 0;
+ case 4:
+ // Marker, 2 bytes, theoretically used for triggers, but never actually found in the game;
+ // I am keeping this case here, in order to correctly keep track of the offset
+ dispatchPtr->currentOffset += 6;
+ continue;
+ case 6:
+ dispatchPtr->vocLoopStartingPoint = dispatchPtr->currentOffset;
+ dispatchPtr->currentOffset += 6;
+ if (dispatchPtr->streamPtr)
+ streamerGetStreamBuffer(dispatchPtr->streamPtr, 6);
+ continue;
+ case 7:
+ dispatchPtr->currentOffset = dispatchPtr->vocLoopStartingPoint;
+ if (dispatchPtr->streamPtr)
+ streamerGetStreamBuffer(dispatchPtr->streamPtr, 1);
+ continue;
+ default:
+ return -1;
+ }
+ }
+ return -1;
+ }
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/imuse_digi/dimuse_engine.cpp b/engines/scumm/imuse_digi/dimuse_engine.cpp
new file mode 100644
index 0000000000..71d75f0f55
--- /dev/null
+++ b/engines/scumm/imuse_digi/dimuse_engine.cpp
@@ -0,0 +1,755 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/system.h"
+#include "common/timer.h"
+
+#include "scumm/actor.h"
+#include "scumm/scumm_v7.h"
+#include "scumm/sound.h"
+#include "scumm/imuse_digi/dimuse_engine.h"
+#include "scumm/imuse_digi/dimuse_defs.h"
+#include "scumm/imuse_digi/dimuse_bndmgr.h"
+#include "scumm/imuse_digi/dimuse_sndmgr.h"
+#include "scumm/imuse_digi/dimuse_tables.h"
+
+#include "audio/audiostream.h"
+#include "audio/mixer.h"
+
+namespace Scumm {
+
+void IMuseDigital::timer_handler(void *refCon) {
+ IMuseDigital *diMUSE = (IMuseDigital *)refCon;
+ diMUSE->callback();
+}
+
+IMuseDigital::IMuseDigital(ScummEngine_v7 *scumm, Audio::Mixer *mixer)
+ : _vm(scumm), _mixer(mixer) {
+ assert(_vm);
+ assert(mixer);
+
+ // 50 Hz rate for the callback
+ _callbackFps = 50;
+ _usecPerInt = 20000;
+
+ _isEarlyDiMUSE = (_vm->_game.id == GID_FT || (_vm->_game.id == GID_DIG && _vm->_game.features & GF_DEMO));
+
+ if (_isEarlyDiMUSE) {
+ memset(_ftCrossfadeBuffer, 0, sizeof(_ftCrossfadeBuffer));
+ }
+
+ _curMixerMusicVolume = _mixer->getVolumeForSoundType(Audio::Mixer::kMusicSoundType);
+ _curMixerSpeechVolume = _mixer->getVolumeForSoundType(Audio::Mixer::kSpeechSoundType);
+ _curMixerSFXVolume = _mixer->getVolumeForSoundType(Audio::Mixer::kSFXSoundType);
+ _currentSpeechVolume = 0;
+ _currentSpeechFrequency = 0;
+ _currentSpeechPan = 0;
+
+ _waveOutXorTrigger = 0;
+ _waveOutWriteIndex = 0;
+ _waveOutDisableWrite = 0;
+ _waveOutPreferredFeedSize = 0;
+
+ _dispatchFadeSize = 0;
+
+ _stopSequenceFlag = 0;
+ _scriptInitializedFlag = 0;
+ _callbackInterruptFlag = 0;
+
+ _radioChatterSFX = false;
+ _isEngineDisabled = false;
+
+ _audioNames = nullptr;
+ _numAudioNames = 0;
+
+ _emptyMarker[0] = '\0';
+ _internalMixer = new IMuseDigiInternalMixer(mixer);
+ _groupsHandler = new IMuseDigiGroupsHandler(this);
+ _fadesHandler = new IMuseDigiFadesHandler(this);
+ _triggersHandler = new IMuseDigiTriggersHandler(this);
+ _filesHandler = new IMuseDigiFilesHandler(this, scumm);
+
+ diMUSEInitialize();
+ diMUSEInitializeScript();
+ if (_vm->_game.id == GID_CMI) {
+ _filesHandler->allocSoundBuffer(DIMUSE_BUFFER_SPEECH, 176000, 44000, 88000);
+ _filesHandler->allocSoundBuffer(DIMUSE_BUFFER_MUSIC, 528000, 44000, 352000);
+ } else if (_vm->_game.id == GID_DIG && !isFTSoundEngine()) {
+ _filesHandler->allocSoundBuffer(DIMUSE_BUFFER_SPEECH, 88000, 22000, 44000);
+ _filesHandler->allocSoundBuffer(DIMUSE_BUFFER_MUSIC, 528000, 11000, 132000);
+ } else {
+ _filesHandler->allocSoundBuffer(DIMUSE_BUFFER_SPEECH, 110000, 22000, 44000);
+ _filesHandler->allocSoundBuffer(DIMUSE_BUFFER_MUSIC, 220000, 22000, 44000);
+ }
+
+ _filesHandler->allocSoundBuffer(DIMUSE_BUFFER_SFX, 198000, 0, 0);
+
+ _vm->getTimerManager()->installTimerProc(timer_handler, 1000000 / _callbackFps, this, "IMuseDigital");
+}
+
+IMuseDigital::~IMuseDigital() {
+ _vm->getTimerManager()->removeTimerProc(timer_handler);
+ _filesHandler->deallocSoundBuffer(DIMUSE_BUFFER_SPEECH);
+ _filesHandler->deallocSoundBuffer(DIMUSE_BUFFER_MUSIC);
+ _filesHandler->deallocSoundBuffer(DIMUSE_BUFFER_SFX);
+ cmdsDeinit();
+ diMUSETerminate();
+ delete _internalMixer;
+ delete _groupsHandler;
+ delete _fadesHandler;
+ delete _triggersHandler;
+ delete _filesHandler;
+
+ // Deinit the Dispatch module
+ free(_dispatchBuffer);
+ _dispatchBuffer = nullptr;
+
+ // Deinit the WaveOut module
+ free(_waveOutOutputBuffer);
+ _waveOutOutputBuffer = nullptr;
+
+ free(_audioNames);
+}
+
+void IMuseDigital::stopSound(int sound) {
+ diMUSEStopSound(sound);
+}
+
+void IMuseDigital::stopAllSounds() {
+ diMUSEStopAllSounds();
+}
+
+int IMuseDigital::isSoundRunning(int soundId) {
+ return diMUSEGetParam(soundId, DIMUSE_P_SND_TRACK_NUM) > 0;
+}
+
+int IMuseDigital::startVoice(int soundId, const char *soundName, byte speakingActorId) {
+ _filesHandler->closeSoundImmediatelyById(soundId);
+
+ int fileDoesNotExist = 0;
+ if (_vm->_game.id == GID_DIG) {
+ if (!strcmp(soundName, "PIG.018"))
+ fileDoesNotExist = _filesHandler->setCurrentSpeechFilename("PIG.019");
+ else
+ fileDoesNotExist = _filesHandler->setCurrentSpeechFilename(soundName);
+
+ if (fileDoesNotExist)
+ return 1;
+
+ // Workaround for this particular sound file not playing (this is a bug in the original):
+ // this is happening because the sound buffer responsible for speech
+ // is still busy with the previous speech file playing during the SAN
+ // movie. We just stop the SMUSH speech sound before playing NEXUS.029.
+ if (!strcmp(soundName, "NEXUS.029")) {
+ diMUSEStopSound(DIMUSE_SMUSH_SOUNDID + DIMUSE_BUFFER_SPEECH);
+ }
+
+ diMUSEStartStream(kTalkSoundID, 127, DIMUSE_BUFFER_SPEECH);
+ diMUSESetParam(kTalkSoundID, DIMUSE_P_GROUP, DIMUSE_GROUP_SPEECH);
+ if (speakingActorId == _vm->VAR(_vm->VAR_EGO)) {
+ diMUSESetParam(kTalkSoundID, DIMUSE_P_MAILBOX, 0);
+ diMUSESetParam(kTalkSoundID, DIMUSE_P_VOLUME, 127);
+ } else {
+ diMUSESetParam(kTalkSoundID, DIMUSE_P_MAILBOX, _radioChatterSFX);
+ diMUSESetParam(kTalkSoundID, DIMUSE_P_VOLUME, 88);
+ }
+ _filesHandler->closeSound(kTalkSoundID);
+ } else if (_vm->_game.id == GID_CMI) {
+ fileDoesNotExist = _filesHandler->setCurrentSpeechFilename(soundName);
+ if (fileDoesNotExist)
+ return 1;
+
+ diMUSEStartStream(kTalkSoundID, 127, DIMUSE_BUFFER_SPEECH);
+ diMUSESetParam(kTalkSoundID, DIMUSE_P_GROUP, DIMUSE_GROUP_SPEECH);
+
+ // Let's not give the occasion to raise errors here
+ if (_vm->isValidActor(_vm->VAR(_vm->VAR_TALK_ACTOR))) {
+ Actor *a = _vm->derefActor(_vm->VAR(_vm->VAR_TALK_ACTOR), "IMuseDigital::startVoice");
+ if (_vm->VAR(_vm->VAR_VOICE_MODE) == 2)
+ diMUSESetParam(kTalkSoundID, DIMUSE_P_VOLUME, 0);
+ else
+ diMUSESetParam(kTalkSoundID, DIMUSE_P_VOLUME, a->_talkVolume);
+
+ diMUSESetParam(kTalkSoundID, DIMUSE_P_TRANSPOSE, a->_talkFrequency);
+ diMUSESetParam(kTalkSoundID, DIMUSE_P_PAN, a->_talkPan);
+ }
+
+ // The interpreter really calls for processStreams two times in a row,
+ // and who am I to contradict it?
+ diMUSEProcessStreams();
+ diMUSEProcessStreams();
+ }
+
+ return 0;
+}
+
+// Used by FT and DIG demo
+int IMuseDigital::startVoice(const char *fileName, ScummFile *file, uint32 offset, uint32 size) {
+ _filesHandler->setCurrentFtSpeechFile(fileName, file, offset, size);
+ diMUSEStopSound(kTalkSoundID);
+ diMUSEStartStream(kTalkSoundID, 127, DIMUSE_BUFFER_SPEECH);
+ diMUSESetParam(kTalkSoundID, DIMUSE_P_GROUP, DIMUSE_GROUP_SPEECH);
+ return 0;
+}
+
+void IMuseDigital::saveLoadEarly(Common::Serializer &s) {
+ Common::StackLock lock(_mutex, "IMuseDigital::saveLoadEarly()");
+
+ if (s.isLoading()) {
+ diMUSEStopAllSounds();
+ _filesHandler->closeSoundImmediatelyById(kTalkSoundID);
+ }
+
+ if (s.getVersion() < 103) {
+ // Just load the current state and sequence, and play them
+ debug(5, "IMuseDigital::saveLoadEarly(): old savegame detected (version %d), game may load with an undesired audio status", s.getVersion());
+ s.skip(4, VER(31), VER(42)); // _volVoice
+ s.skip(4, VER(31), VER(42)); // _volSfx
+ s.skip(4, VER(31), VER(42)); // _volMusic
+ s.syncAsSint32LE(_curMusicState, VER(31));
+ s.syncAsSint32LE(_curMusicSeq, VER(31));
+ s.syncAsSint32LE(_curMusicCue, VER(31));
+ s.syncAsSint32LE(_nextSeqToPlay, VER(31));
+ s.syncAsByte(_radioChatterSFX, VER(76));
+ s.syncArray(_attributes, 188, Common::Serializer::Sint32LE, VER(31));
+
+ int stateSoundId = 0;
+ int seqSoundId = 0;
+
+ // TODO: Check if FT actually works here
+ if (_vm->_game.id == GID_DIG) {
+ stateSoundId = _digStateMusicTable[_curMusicState].soundId;
+ seqSoundId = _digSeqMusicTable[_curMusicSeq].soundId;
+ } else {
+ if (_vm->_game.features & GF_DEMO) {
+ stateSoundId = _comiDemoStateMusicTable[_curMusicState].soundId;
+ } else {
+ stateSoundId = _comiStateMusicTable[_curMusicState].soundId;
+ seqSoundId = _comiSeqMusicTable[_curMusicSeq].soundId;
+ }
+ }
+
+ _curMusicState = 0;
+ _curMusicSeq = 0;
+ scriptSetSequence(seqSoundId);
+ scriptSetState(stateSoundId);
+ scriptSetCuePoint(_curMusicCue);
+ _curMusicCue = 0;
+ } else {
+ diMUSESaveLoad(s);
+ }
+}
+
+void IMuseDigital::refreshScripts() {
+ if (!_vm->isSmushActive()) {
+ diMUSEProcessStreams();
+ diMUSERefreshScript();
+ }
+}
+
+void IMuseDigital::setRadioChatterSFX(bool state) {
+ _radioChatterSFX = state;
+}
+
+int IMuseDigital::startSfx(int soundId, int priority) {
+ diMUSEStartSound(soundId, priority);
+ diMUSESetParam(soundId, DIMUSE_P_GROUP, DIMUSE_GROUP_SFX);
+ return 0;
+}
+
+void IMuseDigital::callback() {
+ if (_cmdsPauseCount)
+ return;
+
+ if (!_callbackInterruptFlag) {
+ _callbackInterruptFlag = 1;
+ diMUSEHeartbeat();
+ _callbackInterruptFlag = 0;
+ }
+}
+
+void IMuseDigital::diMUSEHeartbeat() {
+ // This is what happens:
+ // - Usual audio stuff like fetching and playing sound (and everything
+ // within waveapi_callback()) happens at a base 50Hz rate;
+ // - Triggers and fades handling happens at a (somewhat hacky) 60Hz rate;
+ // - Music gain reduction happens at a 10Hz rate.
+
+ int soundId, foundGroupId, musicTargetVolume, musicEffVol, musicVol, tempVol, tempEffVol, factor, step;
+
+ waveOutCallback();
+
+ // Update volumes
+
+ if (_curMixerMusicVolume != _mixer->getVolumeForSoundType(Audio::Mixer::kMusicSoundType)) {
+ _curMixerMusicVolume = _mixer->getVolumeForSoundType(Audio::Mixer::kMusicSoundType);
+ diMUSESetMusicGroupVol(CLIP(_mixer->getVolumeForSoundType(Audio::Mixer::kMusicSoundType) / 2, 0, 127));
+ }
+
+ if (_curMixerSpeechVolume != _mixer->getVolumeForSoundType(Audio::Mixer::kSpeechSoundType)) {
+ _curMixerSpeechVolume = _mixer->getVolumeForSoundType(Audio::Mixer::kSpeechSoundType);
+ diMUSESetVoiceGroupVol(CLIP(_mixer->getVolumeForSoundType(Audio::Mixer::kSpeechSoundType) / 2, 0, 127));
+ }
+
+ if (_curMixerSFXVolume != _mixer->getVolumeForSoundType(Audio::Mixer::kSFXSoundType)) {
+ _curMixerSFXVolume = _mixer->getVolumeForSoundType(Audio::Mixer::kSFXSoundType);
+ diMUSESetSFXGroupVol(CLIP(_mixer->getVolumeForSoundType(Audio::Mixer::kSFXSoundType) / 2, 0, 127));
+ }
+
+ // Handle fades and triggers
+
+ _cmdsRunning60HzCount += _usecPerInt;
+ while (_cmdsRunning60HzCount >= 16667) {
+ _cmdsRunning60HzCount -= 16667;
+ _fadesHandler->loop();
+ _triggersHandler->loop();
+ }
+
+ _cmdsRunning10HzCount += _usecPerInt;
+ if (_cmdsRunning10HzCount < 100000)
+ return;
+
+ do {
+ // SPEECH GAIN REDUCTION 10Hz
+ _cmdsRunning10HzCount -= 100000;
+ soundId = 0;
+ musicTargetVolume = _groupsHandler->setGroupVol(DIMUSE_GROUP_MUSIC, -1);
+ while (1) { // Check all tracks to see if there's a speech file playing
+ soundId = waveGetNextSound(soundId);
+ if (!soundId)
+ break;
+
+ foundGroupId = -1;
+ if (_filesHandler->getNextSound(soundId) == 2) {
+ foundGroupId = waveGetParam(soundId, DIMUSE_P_GROUP); // Check the groupId of this sound
+ }
+
+ if (foundGroupId == DIMUSE_GROUP_SPEECH) {
+ // Remember: when a speech file stops playing this block stops
+ // being executed, so musicTargetVolume returns back to its original value
+ factor = _isEarlyDiMUSE ? 82 : 80;
+ musicTargetVolume = (musicTargetVolume * factor) / 128;
+ break;
+ }
+ }
+
+ musicEffVol = _groupsHandler->setGroupVol(DIMUSE_GROUP_MUSICEFF, -1); // MUSIC EFFECTIVE VOLUME GROUP (used for gain reduction)
+ musicVol = _groupsHandler->setGroupVol(DIMUSE_GROUP_MUSIC, -1); // MUSIC VOLUME SUBGROUP (keeps track of original music volume)
+
+ if (musicEffVol < musicTargetVolume) { // If there is gain reduction already going on...
+ tempEffVol = musicEffVol + 3;
+ if (tempEffVol < musicTargetVolume) {
+ if (musicVol <= tempEffVol) {
+ musicVol = tempEffVol;
+ }
+ } else if (musicVol <= musicTargetVolume) { // Bring up the music volume immediately when speech stops playing
+ musicVol = musicTargetVolume;
+ }
+ _groupsHandler->setGroupVol(DIMUSE_GROUP_MUSICEFF, musicVol);
+ } else if (musicEffVol > musicTargetVolume) {
+ // Bring music volume down to target volume with a -18 (or -6 for FT & DIG demo) step
+ // if there's speech playing or else, just cap it to the target if it's out of range
+ step = _isEarlyDiMUSE ? 6 : 18;
+ tempVol = musicEffVol - step;
+ if (tempVol <= musicTargetVolume) {
+ if (musicVol >= musicTargetVolume) {
+ musicVol = musicTargetVolume;
+ }
+ } else {
+ if (musicVol >= tempVol) {
+ musicVol = tempVol;
+ }
+ }
+ _groupsHandler->setGroupVol(DIMUSE_GROUP_MUSICEFF, musicVol);
+ }
+
+ } while (_cmdsRunning10HzCount >= 100000);
+}
+
+void IMuseDigital::setPriority(int soundId, int priority) {
+ diMUSESetParam(soundId, DIMUSE_P_PRIORITY, priority);
+}
+
+void IMuseDigital::setVolume(int soundId, int volume) {
+ diMUSESetParam(soundId, DIMUSE_P_VOLUME, volume);
+ if (soundId == kTalkSoundID)
+ _currentSpeechVolume = volume;
+}
+
+void IMuseDigital::setPan(int soundId, int pan) {
+ diMUSESetParam(soundId, DIMUSE_P_PAN, pan);
+ if (soundId == kTalkSoundID)
+ _currentSpeechPan = pan;
+}
+
+void IMuseDigital::setFrequency(int soundId, int frequency) {
+ diMUSESetParam(soundId, DIMUSE_P_TRANSPOSE, frequency);
+ if (soundId == kTalkSoundID)
+ _currentSpeechFrequency = frequency;
+}
+
+int IMuseDigital::getCurSpeechVolume() const {
+ return _currentSpeechVolume;
+}
+
+int IMuseDigital::getCurSpeechPan() const {
+ return _currentSpeechPan;
+}
+
+int IMuseDigital::getCurSpeechFrequency() const {
+ return _currentSpeechFrequency;
+}
+
+void IMuseDigital::flushTracks() {
+ _filesHandler->flushSounds();
+}
+
+// This is used in order to avoid crash everything
+// if a compressed audio resource file is found
+void IMuseDigital::disableEngine() {
+ _isEngineDisabled = true;
+}
+
+bool IMuseDigital::isEngineDisabled() {
+ return _isEngineDisabled;
+}
+
+bool IMuseDigital::isFTSoundEngine() {
+ return _isEarlyDiMUSE;
+}
+
+int32 IMuseDigital::getCurMusicPosInMs() {
+ int soundId, curSoundId;
+
+ curSoundId = 0;
+ soundId = 0;
+ while (1) {
+ curSoundId = diMUSEGetNextSound(curSoundId);
+ if (!curSoundId)
+ break;
+
+ if (diMUSEGetParam(curSoundId, DIMUSE_P_SND_HAS_STREAM) && diMUSEGetParam(curSoundId, DIMUSE_P_STREAM_BUFID) == DIMUSE_BUFFER_MUSIC) {
+ soundId = curSoundId;
+ return diMUSEGetParam(soundId, DIMUSE_P_SND_POS_IN_MS);
+ }
+ }
+
+ return diMUSEGetParam(soundId, DIMUSE_P_SND_POS_IN_MS);
+}
+
+int32 IMuseDigital::getCurVoiceLipSyncWidth() {
+ int32 width, height;
+ getSpeechLipSyncInfo(width, height);
+ return width;
+}
+
+int32 IMuseDigital::getCurVoiceLipSyncHeight() {
+ int32 width, height;
+ getSpeechLipSyncInfo(width, height);
+ return height;
+}
+
+int32 IMuseDigital::getCurMusicLipSyncWidth(int syncId) {
+ int32 width, height;
+ getMusicLipSyncInfo(syncId, width, height);
+ return width;
+}
+
+int32 IMuseDigital::getCurMusicLipSyncHeight(int syncId) {
+ int32 width, height;
+ getMusicLipSyncInfo(syncId, width, height);
+ return height;
+}
+
+void IMuseDigital::getSpeechLipSyncInfo(int32 &width, int32 &height) {
+ int curSpeechPosInMs;
+
+ width = 0;
+ height = 0;
+
+ if (diMUSEGetParam(kTalkSoundID, DIMUSE_P_SND_TRACK_NUM) > 0) {
+ curSpeechPosInMs = diMUSEGetParam(kTalkSoundID, DIMUSE_P_SND_POS_IN_MS);
+ diMUSELipSync(kTalkSoundID, 0, _vm->VAR(_vm->VAR_SYNC) + curSpeechPosInMs + 50, width, height);
+ }
+}
+
+void IMuseDigital::getMusicLipSyncInfo(int syncId, int32 &width, int32 &height) {
+ int soundId;
+ int speechSoundId;
+ int curSpeechPosInMs;
+
+ soundId = 0;
+ speechSoundId = 0;
+ width = 0;
+ height = 0;
+ while (1) {
+ soundId = diMUSEGetNextSound(soundId);
+ if (!soundId)
+ break;
+ if (diMUSEGetParam(soundId, DIMUSE_P_SND_HAS_STREAM)) {
+ if (diMUSEGetParam(soundId, DIMUSE_P_STREAM_BUFID) == DIMUSE_BUFFER_MUSIC) {
+ speechSoundId = soundId;
+ break;
+ }
+ }
+ }
+
+ if (speechSoundId) {
+ curSpeechPosInMs = diMUSEGetParam(speechSoundId, DIMUSE_P_SND_POS_IN_MS);
+ diMUSELipSync(speechSoundId, syncId, _vm->VAR(_vm->VAR_SYNC) + curSpeechPosInMs + 50, width, height);
+ }
+}
+
+int32 IMuseDigital::getSoundElapsedTimeInMs(int soundId) {
+ if (diMUSEGetParam(soundId, DIMUSE_P_SND_HAS_STREAM)) {
+ return diMUSEGetParam(soundId, DIMUSE_P_SND_POS_IN_MS);
+ }
+ return 0;
+}
+
+void IMuseDigital::pause(bool p) {
+ if (p) {
+ debug(5, "IMuseDigital::pause(): pausing...");
+ diMUSEPause();
+ } else {
+ debug(5, "IMuseDigital::pause(): resuming...");
+ diMUSEResume();
+ }
+}
+
+void IMuseDigital::setAudioNames(int32 num, char *names) {
+ free(_audioNames);
+ _numAudioNames = num;
+ _audioNames = names;
+}
+
+int IMuseDigital::getSoundIdByName(const char *soundName) {
+ if (soundName && soundName[0] != 0) {
+ for (int r = 0; r < _numAudioNames; r++) {
+ if (strcmp(soundName, &_audioNames[r * 9]) == 0) {
+ return r;
+ }
+ }
+ }
+
+ return 0;
+}
+
+void IMuseDigital::parseScriptCmds(int cmd, int soundId, int sub_cmd, int d, int e, int f, int g, int h, int i, int j, int k, int l, int m, int n, int o, int p) {
+ int b = soundId;
+ int c = sub_cmd;
+ int id;
+ switch (cmd) {
+ case 0x1000:
+ // SetState
+ diMUSESetState(soundId);
+ break;
+ case 0x1001:
+ // SetSequence
+ diMUSESetSequence(soundId);
+ break;
+ case 0x1002:
+ // SetCuePoint
+ diMUSESetCuePoint(soundId);
+ break;
+ case 0x1003:
+ // SetAttribute
+ diMUSESetAttribute(b, c);
+ break;
+ case 0x2000:
+ // SetGroupSfxVolume
+ diMUSESetSFXGroupVol(CLIP(_mixer->getVolumeForSoundType(Audio::Mixer::kSFXSoundType) / 2, 0, 127));
+ break;
+ case 0x2001:
+ // SetGroupVoiceVolume
+ diMUSESetVoiceGroupVol(CLIP(_mixer->getVolumeForSoundType(Audio::Mixer::kSpeechSoundType) / 2, 0, 127));
+ break;
+ case 0x2002:
+ // SetGroupMusicVolume
+ diMUSESetMusicGroupVol(CLIP(_mixer->getVolumeForSoundType(Audio::Mixer::kMusicSoundType) / 2, 0, 127));
+ break;
+ case 10: // StopAllSounds
+ case 12: // SetParam
+ case 14: // FadeParam
+ cmdsHandleCmd(cmd, nullptr, soundId, sub_cmd, d, e, f, g, h, i, j, k, l, m, n, o);
+ break;
+ case 25: // OpenSound
+ if (_vm->_game.id == GID_FT) {
+ id = getSoundIdByName("kstand");
+ _filesHandler->openSound(id);
+ } else if (_vm->_game.id == GID_DIG && _vm->_game.features & GF_DEMO) {
+ // Special opcode used in place of the first setState instruction
+ _filesHandler->openSound(soundId);
+ diMUSEStartStream(soundId, 126, DIMUSE_BUFFER_MUSIC);
+ }
+
+ break;
+ case 26:
+ // Special opcode used in place of successive setState instructions
+ if (_vm->_game.id == GID_DIG && _vm->_game.features & GF_DEMO) {
+ _filesHandler->openSound(c);
+ diMUSESwitchStream(soundId, c, _ftCrossfadeBuffer, sizeof(_ftCrossfadeBuffer), 0);
+ _filesHandler->closeSound(soundId);
+ }
+ break;
+ default:
+ debug("IMuseDigital::parseScriptCmds(): WARNING: unhandled command %d", cmd);
+ }
+}
+
+int IMuseDigital::diMUSETerminate() {
+ if (_scriptInitializedFlag) {
+ diMUSEStopAllSounds();
+ _filesHandler->closeAllSounds();
+ }
+
+ return 0;
+}
+
+int IMuseDigital::diMUSEInitialize() {
+ return cmdsHandleCmd(0);
+}
+
+int IMuseDigital::diMUSEPause() {
+ return cmdsHandleCmd(3);
+}
+
+int IMuseDigital::diMUSEResume() {
+ return cmdsHandleCmd(4);
+}
+
+void IMuseDigital::diMUSESaveLoad(Common::Serializer &ser) {
+ cmdsSaveLoad(ser);
+}
+
+int IMuseDigital::diMUSESetGroupVol(int groupId, int volume) {
+ return cmdsHandleCmd(7, nullptr, groupId, volume);
+}
+
+int IMuseDigital::diMUSEStartSound(int soundId, int priority) {
+ return cmdsHandleCmd(8, nullptr, soundId, priority);
+}
+
+int IMuseDigital::diMUSEStopSound(int soundId) {
+ debug(5, "IMuseDigital::diMUSEStopSound(): %d", soundId);
+ return cmdsHandleCmd(9, nullptr, soundId);
+}
+
+int IMuseDigital::diMUSEStopAllSounds() {
+ debug(5, "IMuseDigital::diMUSEStopAllSounds()");
+ return cmdsHandleCmd(10);
+}
+
+int IMuseDigital::diMUSEGetNextSound(int soundId) {
+ return cmdsHandleCmd(11, nullptr, soundId);
+}
+
+int IMuseDigital::diMUSESetParam(int soundId, int paramId, int value) {
+ return cmdsHandleCmd(12, nullptr, soundId, paramId, value);
+}
+
+int IMuseDigital::diMUSEGetParam(int soundId, int paramId) {
+ return cmdsHandleCmd(13, nullptr, soundId, paramId);
+}
+
+int IMuseDigital::diMUSEFadeParam(int soundId, int opcode, int destValue, int fadeLength) {
+ return cmdsHandleCmd(14, nullptr, soundId, opcode, destValue, fadeLength);
+}
+
+int IMuseDigital::diMUSESetHook(int soundId, int hookId) {
+ return cmdsHandleCmd(15, nullptr, soundId, hookId);
+}
+
+int IMuseDigital::diMUSESetTrigger(int soundId, int marker, int opcode, int d, int e, int f, int g, int h, int i, int j, int k, int l, int m, int n) {
+ return cmdsHandleCmd(17, nullptr, soundId, marker, opcode, d, e, f, g, h, i, j, k, l, m, n);
+}
+
+int IMuseDigital::diMUSEStartStream(int soundId, int priority, int bufferId) {
+ return cmdsHandleCmd(25, nullptr, soundId, priority, bufferId);
+}
+
+int IMuseDigital::diMUSESwitchStream(int oldSoundId, int newSoundId, int fadeDelay, int fadeSyncFlag2, int fadeSyncFlag1) {
+ return cmdsHandleCmd(26, nullptr, oldSoundId, newSoundId, fadeDelay, fadeSyncFlag2, fadeSyncFlag1);
+}
+
+// Variation for FT and DIG demo
+int IMuseDigital::diMUSESwitchStream(int oldSoundId, int newSoundId, uint8 *crossfadeBuffer, int crossfadeBufferSize, int vocLoopFlag) {
+ return cmdsHandleCmd(26, crossfadeBuffer, oldSoundId, newSoundId, -1, crossfadeBufferSize, vocLoopFlag);
+}
+
+int IMuseDigital::diMUSEProcessStreams() {
+ return cmdsHandleCmd(27);
+}
+
+int IMuseDigital::diMUSEFeedStream(int soundId, uint8 *srcBuf, int32 sizeToFeed, int paused) {
+ return cmdsHandleCmd(29, srcBuf, soundId, -1, sizeToFeed, paused);
+}
+
+int IMuseDigital::diMUSELipSync(int soundId, int syncId, int msPos, int32 &width, int32 &height) {
+ return waveLipSync(soundId, syncId, msPos, width, height);
+}
+
+int IMuseDigital::diMUSESetMusicGroupVol(int volume) {
+ debug(5, "IMuseDigital::diMUSESetMusicGroupVol(): %d", volume);
+ return diMUSESetGroupVol(3, volume);
+}
+
+int IMuseDigital::diMUSESetSFXGroupVol(int volume) {
+ debug(5, "IMuseDigital::diMUSESetSFXGroupVol(): %d", volume);
+ return diMUSESetGroupVol(1, volume);
+}
+
+int IMuseDigital::diMUSESetVoiceGroupVol(int volume) {
+ debug(5, "IMuseDigital::diMUSESetVoiceGroupVol(): %d", volume);
+ return diMUSESetGroupVol(2, volume);
+}
+
+void IMuseDigital::diMUSEUpdateGroupVolumes() {
+ waveUpdateGroupVolumes();
+}
+
+int IMuseDigital::diMUSEInitializeScript() {
+ return scriptParse(0, -1, -1);
+}
+
+void IMuseDigital::diMUSERefreshScript() {
+ diMUSEProcessStreams();
+ scriptParse(4, -1, -1);
+}
+
+int IMuseDigital::diMUSESetState(int soundId) {
+ return scriptParse(5, soundId, -1);
+}
+
+int IMuseDigital::diMUSESetSequence(int soundId) {
+ return scriptParse(6, soundId, -1);
+}
+
+int IMuseDigital::diMUSESetCuePoint(int cueId) {
+ return scriptParse(7, cueId, -1);
+}
+
+int IMuseDigital::diMUSESetAttribute(int attrIndex, int attrVal) {
+ return scriptParse(8, attrIndex, attrVal);
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/imuse_digi/dimuse_engine.h b/engines/scumm/imuse_digi/dimuse_engine.h
new file mode 100644
index 0000000000..da0d1bb659
--- /dev/null
+++ b/engines/scumm/imuse_digi/dimuse_engine.h
@@ -0,0 +1,391 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#if !defined(SCUMM_IMUSE_DIGI_H) && defined(ENABLE_SCUMM_7_8)
+#define SCUMM_IMUSE_DIGI_H
+
+#include "common/scummsys.h"
+#include "common/mutex.h"
+#include "common/serializer.h"
+#include "common/textconsole.h"
+#include "common/util.h"
+
+#include "scumm/scumm_v7.h"
+#include "scumm/music.h"
+#include "scumm/sound.h"
+#include "scumm/file.h"
+
+#include "scumm/imuse_digi/dimuse_defs.h"
+#include "scumm/imuse_digi/dimuse_internalmixer.h"
+#include "scumm/imuse_digi/dimuse_groups.h"
+#include "scumm/imuse_digi/dimuse_fades.h"
+#include "scumm/imuse_digi/dimuse_files.h"
+#include "scumm/imuse_digi/dimuse_triggers.h"
+#include "scumm/imuse_digi/dimuse_bndmgr.h"
+#include "scumm/imuse_digi/dimuse_sndmgr.h"
+#include "scumm/imuse_digi/dimuse_tables.h"
+
+#include "audio/mixer.h"
+#include "audio/decoders/raw.h"
+
+namespace Audio {
+class AudioStream;
+class Mixer;
+class QueuingAudioStream;
+}
+
+namespace Scumm {
+class ScummEngine_v7;
+
+struct imuseDigTable;
+struct imuseComiTable;
+struct IMuseDigiDispatch;
+struct IMuseDigiTrack;
+struct IMuseDigiStreamZone;
+
+class IMuseDigital : public MusicEngine {
+private:
+ Common::Mutex _mutex;
+ ScummEngine_v7 *_vm;
+ Audio::Mixer *_mixer;
+
+ IMuseDigiInternalMixer *_internalMixer;
+ IMuseDigiGroupsHandler *_groupsHandler;
+ IMuseDigiFadesHandler *_fadesHandler;
+ IMuseDigiTriggersHandler *_triggersHandler;
+ IMuseDigiFilesHandler *_filesHandler;
+
+ int _callbackFps;
+ static void timer_handler(void *refConf);
+ void callback();
+
+ bool _isEarlyDiMUSE;
+ bool _isEngineDisabled;
+
+ // These three are manipulated in the waveOut functions
+ uint8 *_outputAudioBuffer;
+ int _outputFeedSize;
+ int _outputSampleRate;
+
+ int _currentSpeechVolume, _currentSpeechFrequency, _currentSpeechPan;
+ int _curMixerMusicVolume, _curMixerSpeechVolume, _curMixerSFXVolume;
+ bool _radioChatterSFX;
+
+ int32 _attributes[188]; // internal attributes for each music file to store and check later
+ int32 _nextSeqToPlay;
+ int32 _curMusicState;
+ int32 _curMusicSeq;
+ int32 _curMusicCue;
+
+ char *_audioNames; // filenames of sound SFX used in FT
+ int32 _numAudioNames; // number of above filenames
+ uint8 _currentVOCHeader[52]; // Header for the current sound for early DiMUSE
+
+ int _stopSequenceFlag;
+ int _scriptInitializedFlag;
+ char _emptyMarker[1];
+
+ int _usecPerInt; // Microseconds between each callback (will be set to 50 Hz)
+ int _callbackInterruptFlag;
+ void diMUSEHeartbeat();
+
+ void setFtMusicState(int stateId);
+ void setFtMusicSequence(int seqId);
+ void playFtMusic(const char *songName, int opcode, int volume);
+ void setDigMusicState(int stateId);
+ void setDigMusicSequence(int seqId);
+ void playDigMusic(const char *songName, const imuseDigTable *table, int attribPos, bool sequence);
+ void setComiMusicState(int stateId);
+ void setComiMusicSequence(int seqId);
+ void playComiMusic(const char *songName, const imuseComiTable *table, int attribPos, bool sequence);
+ void playComiDemoMusic(const char *songName, const imuseComiTable *table, int attribPos, bool sequence);
+ int getSoundIdByName(const char *soundName);
+
+ // Script
+ int scriptParse(int cmd, int a, int b);
+ int scriptInit();
+ int scriptTerminate();
+ void scriptRefresh();
+ void scriptSetState(int soundId);
+ void scriptSetSequence(int soundId);
+ void scriptSetCuePoint(int cueId);
+ int scriptSetAttribute(int attrIndex, int attrVal);
+
+ // CMDs
+ int _cmdsPauseCount;
+ int _cmdsRunning60HzCount;
+ int _cmdsRunning10HzCount;
+
+ int cmdsInit();
+ int cmdsDeinit();
+ int cmdsTerminate();
+ int cmdsPause();
+ int cmdsResume();
+ void cmdsSaveLoad(Common::Serializer &ser);
+ int cmdsStartSound(int soundId, int priority);
+ int cmdsStopSound(int soundId);
+ int cmdsStopAllSounds();
+ int cmdsGetNextSound(int soundId);
+ int cmdsSetParam(int soundId, int opcode, int value);
+ int cmdsGetParam(int soundId, int opcode);
+ int cmdsSetHook(int soundId, int hookId);
+ int cmdsGetHook(int soundId);
+
+ // Streamer
+ IMuseDigiStream _streams[DIMUSE_MAX_STREAMS];
+ IMuseDigiStream *_lastStreamLoaded;
+ int _streamerBailFlag;
+
+ int streamerInit();
+ IMuseDigiStream *streamerAllocateSound(int soundId, int bufId, int32 maxRead);
+ int streamerClearSoundInStream(IMuseDigiStream *streamPtr);
+ int streamerProcessStreams();
+ uint8 *streamerGetStreamBuffer(IMuseDigiStream *streamPtr, int size);
+ uint8 *streamerGetStreamBufferAtOffset(IMuseDigiStream *streamPtr, int32 offset, int size);
+ int streamerSetReadIndex(IMuseDigiStream *streamPtr, int offset);
+ int streamerSetLoadIndex(IMuseDigiStream *streamPtr, int offset);
+ int streamerGetFreeBufferAmount(IMuseDigiStream *streamPtr);
+ int streamerSetSoundToStreamFromOffset(IMuseDigiStream *streamPtr, int soundId, int32 offset);
+ int streamerQueryStream(IMuseDigiStream *streamPtr, int32 &bufSize, int32 &criticalSize, int32 &freeSpace, int &paused);
+ int streamerFeedStream(IMuseDigiStream *streamPtr, uint8 *srcBuf, int32 sizeToFeed, int paused);
+ int streamerFetchData(IMuseDigiStream *streamPtr);
+ void streamerSetLoopFlag(IMuseDigiStream *streamPtr, int offset);
+ void streamerRemoveLoopFlag(IMuseDigiStream *streamPtr);
+
+ // Tracks
+ IMuseDigiTrack _tracks[DIMUSE_MAX_TRACKS];
+ IMuseDigiTrack *_trackList;
+
+ int _trackCount;
+ int _tracksPauseTimer;
+ int _tracksPrefSampleRate;
+ int _tracksMicroSecsToFeed;
+
+ int tracksInit();
+ void tracksPause();
+ void tracksResume();
+ void tracksSaveLoad(Common::Serializer &ser);
+ void tracksSetGroupVol();
+ void tracksCallback();
+ int tracksStartSound(int soundId, int tryPriority, int group);
+ int tracksStopSound(int soundId);
+ int tracksStopAllSounds();
+ int tracksGetNextSound(int soundId);
+ int tracksFeedStream(int soundId, uint8 *srcBuf, int32 sizeToFeed, int paused);
+ void tracksClear(IMuseDigiTrack *trackPtr);
+ int tracksSetParam(int soundId, int opcode, int value);
+ int tracksGetParam(int soundId, int opcode);
+ int tracksLipSync(int soundId, int syncId, int msPos, int32 &width, int32 &height);
+ int tracksSetHook(int soundId, int hookId);
+ int tracksGetHook(int soundId);
+ IMuseDigiTrack *tracksReserveTrack(int priority);
+ void tracksDeinit();
+
+ // Dispatch
+ IMuseDigiDispatch _dispatches[DIMUSE_MAX_DISPATCHES];
+ IMuseDigiStreamZone _streamZones[DIMUSE_MAX_STREAMZONES];
+ uint8 *_dispatchBuffer;
+ uint8 _ftCrossfadeBuffer[30000]; // Used by FT & DIG demo
+ int32 _dispatchSize;
+ uint8 *_dispatchSmallFadeBufs;
+ uint8 *_dispatchLargeFadeBufs;
+ int32 _dispatchFadeSize;
+ int _dispatchLargeFadeFlags[DIMUSE_LARGE_FADES];
+ int _dispatchSmallFadeFlags[DIMUSE_SMALL_FADES];
+ int _dispatchFadeStartedFlag;
+ int _dispatchBufferedHookId;
+ int32 _dispatchJumpFadeSize;
+ int32 _dispatchCurStreamBufSize;
+ int32 _dispatchCurStreamCriticalSize;
+ int32 _dispatchCurStreamFreeSpace;
+ int _dispatchCurStreamPaused;
+
+ int dispatchInit();
+ IMuseDigiDispatch *dispatchGetDispatchByTrackId(int trackId);
+ void dispatchSaveLoad(Common::Serializer &ser);
+ int dispatchRestoreStreamZones();
+ int dispatchAllocateSound(IMuseDigiTrack *trackPtr, int groupId);
+ int dispatchRelease(IMuseDigiTrack *trackPtr);
+ int dispatchSwitchStream(int oldSoundId, int newSoundId, int fadeLength, int unusedFadeSyncFlag, int offsetFadeSyncFlag);
+ int dispatchSwitchStream(int oldSoundId, int newSoundId, uint8 *crossfadeBuffer, int crossfadeBufferSize, int vocLoopFlag);
+ void dispatchProcessDispatches(IMuseDigiTrack *trackPtr, int feedSize, int sampleRate);
+ void dispatchProcessDispatches(IMuseDigiTrack *trackPtr, int feedSize);
+ void dispatchPredictFirstStream();
+ int dispatchNavigateMap(IMuseDigiDispatch *dispatchPtr);
+ int dispatchGetMap(IMuseDigiDispatch *dispatchPtr);
+ int dispatchConvertMap(uint8 *rawMap, uint8 *destMap);
+ int32 *dispatchGetNextMapEvent(int32 *mapPtr, int32 soundOffset, int32 *mapEvent);
+ void dispatchPredictStream(IMuseDigiDispatch *dispatchPtr);
+ int32 *dispatchCheckForJump(int32 *mapPtr, IMuseDigiStreamZone *strZnPtr, int &candidateHookId);
+ void dispatchPrepareToJump(IMuseDigiDispatch *dispatchPtr, IMuseDigiStreamZone *strZnPtr, int32 *jumpParamsFromMap, int calledFromGetNextMapEvent);
+ void dispatchStreamNextZone(IMuseDigiDispatch *dispatchPtr, IMuseDigiStreamZone *strZnPtr);
+ IMuseDigiStreamZone *dispatchAllocateStreamZone();
+ uint8 *dispatchAllocateFade(int32 &fadeSize, const char *functionName);
+ void dispatchDeallocateFade(IMuseDigiDispatch *dispatchPtr, const char *functionName);
+ int dispatchGetFadeSize(IMuseDigiDispatch *dispatchPtr, int fadeLength);
+ void dispatchValidateFadeSize(IMuseDigiDispatch *dispatchPtr, int32 &dispatchSize, const char *functionName);
+ int dispatchUpdateFadeMixVolume(IMuseDigiDispatch *dispatchPtr, int32 remainingFade);
+ int dispatchUpdateFadeSlope(IMuseDigiDispatch *dispatchPtr);
+ void dispatchVOCLoopCallback(int soundId);
+ int dispatchSeekToNextChunk(IMuseDigiDispatch *dispatchPtr);
+
+ // Wave (mainly a wrapper for Tracks functions)
+ int waveInit();
+ int waveTerminate();
+ int wavePause();
+ int waveResume();
+ void waveSaveLoad(Common::Serializer &ser);
+ void waveUpdateGroupVolumes();
+ int waveStartSound(int soundId, int priority);
+ int waveStopSound(int soundId);
+ int waveStopAllSounds();
+ int waveGetNextSound(int soundId);
+ int waveSetParam(int soundId, int opcode, int value);
+ int waveGetParam(int soundId, int opcode);
+ int waveSetHook(int soundId, int hookId);
+ int waveGetHook(int soundId);
+ int waveStartStream(int soundId, int priority, int groupId);
+ int waveSwitchStream(int oldSoundId, int newSoundId, int fadeLengthMs, int fadeSyncFlag2, int fadeSyncFlag1);
+ int waveSwitchStream(int oldSoundId, int newSoundId, uint8 *crossfadeBuffer, int crossfadeBufferSize, int vocLoopFlag);
+ int waveProcessStreams();
+ int waveFeedStream(int soundId, uint8 *srcBuf, int32 sizeToFeed, int paused);
+ int waveLipSync(int soundId, int syncId, int msPos, int32 &width, int32 &height);
+
+ // Waveapi
+ waveOutParamsStruct waveOutSettings;
+
+ int _waveOutSampleRate;
+ int _waveOutBytesPerSample;
+ int _waveOutNumChannels;
+ int _waveOutZeroLevel;
+ int _waveOutPreferredFeedSize;
+ uint8 *_waveOutMixBuffer;
+ uint8 *_waveOutOutputBuffer;
+
+ int _waveOutXorTrigger;
+ int _waveOutWriteIndex;
+ int _waveOutDisableWrite;
+
+ int waveOutInit(int sampleRate, waveOutParamsStruct *waveOutSettings);
+ void waveOutWrite(uint8 **audioBuffer, int &feedSize, int &sampleRate);
+ int waveOutDeinit();
+ void waveOutCallback();
+ byte waveOutGetStreamFlags();
+
+public:
+ IMuseDigital(ScummEngine_v7 *scumm, Audio::Mixer *mixer);
+ ~IMuseDigital() override;
+
+ // Wrapper functions used by the main engine
+
+ void startSound(int sound) override { error("IMuseDigital::startSound(int) should be never called"); }
+ void setMusicVolume(int vol) override {}
+ void stopSound(int sound) override;
+ void stopAllSounds() override;
+ int getSoundStatus(int sound) const override { return 0; }
+ int isSoundRunning(int soundId); // Needed because getSoundStatus is a const function, and I needed a workaround
+ int startVoice(int soundId, const char *soundName, byte speakingActorId);
+ int startVoice(const char *fileName, ScummFile *file, uint32 offset, uint32 size);
+ void saveLoadEarly(Common::Serializer &ser);
+ void setRadioChatterSFX(bool state);
+ void setAudioNames(int32 num, char *names);
+ int startSfx(int soundId, int priority) ;
+ void setPriority(int soundId, int priority);
+ void setVolume(int soundId, int volume);
+ void setPan(int soundId, int pan);
+ void setFrequency(int soundId, int frequency);
+ int getCurSpeechVolume() const;
+ int getCurSpeechPan() const;
+ int getCurSpeechFrequency() const;
+ void pause(bool pause);
+ void parseScriptCmds(int cmd, int soundId, int sub_cmd, int d, int e, int f, int g, int h, int i, int j, int k, int l, int m, int n, int o, int p);
+ void refreshScripts();
+ void flushTracks();
+ void disableEngine();
+ bool isEngineDisabled();
+
+ bool isFTSoundEngine(); // Used in the handlers to check if we're using the FT version of the engine
+
+ int32 getCurMusicPosInMs();
+ int32 getCurVoiceLipSyncWidth();
+ int32 getCurVoiceLipSyncHeight();
+ int32 getCurMusicLipSyncWidth(int syncId);
+ int32 getCurMusicLipSyncHeight(int syncId);
+ void getSpeechLipSyncInfo(int32 &width, int32 &height);
+ void getMusicLipSyncInfo(int syncId, int32 &width, int32 &height);
+ int32 getSoundElapsedTimeInMs(int soundId);
+
+ // General engine functions
+ int diMUSETerminate();
+ int diMUSEInitialize();
+ int diMUSEPause();
+ int diMUSEResume();
+ void diMUSESaveLoad(Common::Serializer &ser);
+ int diMUSESetGroupVol(int groupId, int volume);
+ int diMUSEStartSound(int soundId, int priority);
+ int diMUSEStopSound(int soundId);
+ int diMUSEStopAllSounds();
+ int diMUSEGetNextSound(int soundId);
+ int diMUSESetParam(int soundId, int paramId, int value);
+ int diMUSEGetParam(int soundId, int paramId);
+ int diMUSEFadeParam(int soundId, int opcode, int destValue, int fadeLength);
+ int diMUSESetHook(int soundId, int hookId);
+ int diMUSESetTrigger(int soundId, int marker, int opcode, int d, int e, int f, int g, int h, int i, int j, int k, int l, int m, int n);
+ int diMUSEStartStream(int soundId, int priority, int groupId);
+ int diMUSESwitchStream(int oldSoundId, int newSoundId, int fadeDelay, int fadeSyncFlag2, int fadeSyncFlag1);
+ int diMUSESwitchStream(int oldSoundId, int newSoundId, uint8 *crossfadeBuffer, int crossfadeBufferSize, int vocLoopFlag);
+ int diMUSEProcessStreams();
+ int diMUSEFeedStream(int soundId, uint8 *srcBuf, int32 sizeToFeed, int paused);
+ int diMUSELipSync(int soundId, int syncId, int msPos, int32 &width, int32 &height);
+ int diMUSESetMusicGroupVol(int volume);
+ int diMUSESetSFXGroupVol(int volume);
+ int diMUSESetVoiceGroupVol(int volume);
+ void diMUSEUpdateGroupVolumes();
+ int diMUSEInitializeScript();
+ void diMUSERefreshScript();
+ int diMUSESetState(int soundId);
+ int diMUSESetSequence(int soundId);
+ int diMUSESetCuePoint(int cueId);
+ int diMUSESetAttribute(int attrIndex, int attrVal);
+
+ // Utils
+ int addTrackToList(IMuseDigiTrack **listPtr, IMuseDigiTrack *listPtr_Item);
+ int removeTrackFromList(IMuseDigiTrack **listPtr, IMuseDigiTrack *itemPtr);
+ int addStreamZoneToList(IMuseDigiStreamZone **listPtr, IMuseDigiStreamZone *listPtr_Item);
+ int removeStreamZoneFromList(IMuseDigiStreamZone **listPtr, IMuseDigiStreamZone *itemPtr);
+ int clampNumber(int value, int minValue, int maxValue);
+ int clampTuning(int value, int minValue, int maxValue);
+ int checkHookId(int &trackHookId, int sampleHookId);
+
+ // CMDs
+ int cmdsHandleCmd(int cmd, uint8 *ptr = nullptr,
+ int a = -1, int b = -1, int c = -1, int d = -1, int e = -1,
+ int f = -1, int g = -1, int h = -1, int i = -1, int j = -1,
+ int k = -1, int l = -1, int m = -1, int n = -1);
+
+ // Script
+ int scriptTriggerCallback(char *marker);
+};
+
+} // End of namespace Scumm
+
+#endif
diff --git a/engines/scumm/imuse_digi/dimuse_fades.cpp b/engines/scumm/imuse_digi/dimuse_fades.cpp
new file mode 100644
index 0000000000..12e9b2a44f
--- /dev/null
+++ b/engines/scumm/imuse_digi/dimuse_fades.cpp
@@ -0,0 +1,168 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "scumm/imuse_digi/dimuse_engine.h"
+#include "scumm/imuse_digi/dimuse_fades.h"
+
+namespace Scumm {
+
+IMuseDigiFadesHandler::IMuseDigiFadesHandler(IMuseDigital *engine) {
+ _engine = engine;
+}
+
+IMuseDigiFadesHandler::~IMuseDigiFadesHandler() {}
+
+int IMuseDigiFadesHandler::init() {
+ clearAllFades();
+ return 0;
+}
+
+int IMuseDigiFadesHandler::fadeParam(int soundId, int opcode, int destinationValue, int fadeLength) {
+ if (!soundId || fadeLength < 0)
+ return -5;
+ if (opcode != DIMUSE_P_PRIORITY && opcode != DIMUSE_P_VOLUME && opcode != DIMUSE_P_PAN && opcode != DIMUSE_P_DETUNE && opcode != DIMUSE_P_UNKNOWN && opcode != 17)
+ return -5;
+
+ for (int l = 0; l < DIMUSE_MAX_FADES; l++) {
+ if (_fades[l].status && (_fades[l].sound == soundId) && (_fades[l].param == opcode || opcode == -1)) {
+ _fades[l].status = 0;
+ }
+ }
+
+ if (!fadeLength) {
+ debug(5, "IMuseDigiFadesHandler::fadeParam(): WARNING: allocated fade with zero length for sound %d", soundId);
+ if (opcode != DIMUSE_P_VOLUME || destinationValue) {
+ _engine->diMUSESetParam(soundId, opcode, destinationValue);
+ return 0;
+ } else {
+ _engine->diMUSEStopSound(soundId);
+ return 0;
+ }
+ }
+
+ for (int l = 0; l < DIMUSE_MAX_FADES; l++) {
+ if (!_fades[l].status) {
+ _fades[l].sound = soundId;
+ _fades[l].param = opcode;
+ _fades[l].currentVal = _engine->diMUSEGetParam(soundId, opcode);
+ _fades[l].length = fadeLength;
+ _fades[l].counter = fadeLength;
+ _fades[l].slope = (destinationValue - _fades[l].currentVal) / fadeLength;
+ _fades[l].modOvfloCounter = 0;
+ _fades[l].status = 1;
+ _fadesOn = 1;
+
+ if ((destinationValue - _fades[l].currentVal) < 0) {
+ _fades[l].nudge = -1;
+ _fades[l].slopeMod = (-(destinationValue - _fades[l].currentVal) % fadeLength);
+ } else {
+ _fades[l].nudge = 1;
+ _fades[l].slopeMod = (destinationValue - _fades[l].currentVal) % fadeLength;
+ }
+
+ return 0;
+ }
+ }
+
+ debug(5, "IMuseDigiFadesHandler::fadeParam(): unable to allocate fade for sound %d", soundId);
+ return -6;
+}
+
+void IMuseDigiFadesHandler::clearFadeStatus(int soundId, int opcode) {
+ for (int l = 0; l < DIMUSE_MAX_FADES; l++) {
+ if (_fades[l].status
+ && _fades[l].sound == soundId
+ && (_fades[l].param == opcode || opcode == -1)) {
+ _fades[l].status = 0;
+ }
+ }
+}
+
+void IMuseDigiFadesHandler::loop() {
+ if (!_fadesOn)
+ return;
+ _fadesOn = 0;
+
+ for (int l = 0; l < DIMUSE_MAX_FADES; l++) {
+ if (_fades[l].status) {
+ _fadesOn = 1;
+ if (--_fades[l].counter == 0) {
+ _fades[l].status = 0;
+ }
+
+ int currentVolume = _fades[l].currentVal + _fades[l].slope;
+ int currentSlopeMod = _fades[l].modOvfloCounter + _fades[l].slopeMod;
+ _fades[l].modOvfloCounter += _fades[l].slopeMod;
+
+ if (_fades[l].length <= currentSlopeMod) {
+ _fades[l].modOvfloCounter = currentSlopeMod - _fades[l].length;
+ currentVolume += _fades[l].nudge;
+ }
+
+ if (_fades[l].currentVal != currentVolume) {
+ _fades[l].currentVal = currentVolume;
+
+ if ((_fades[l].counter % 6) == 0) {
+ debug(5, "IMuseDigiFadesHandler::loop(): running fade for sound %d with id %d, currently at volume %d", _fades[l].sound, l, currentVolume);
+ if ((_fades[l].param != DIMUSE_P_VOLUME) || currentVolume != 0) {
+ _engine->diMUSESetParam(_fades[l].sound, _fades[l].param, currentVolume);
+ continue;
+ } else {
+ _engine->diMUSEStopSound(_fades[l].sound);
+ }
+ }
+ }
+ }
+ }
+}
+
+void IMuseDigiFadesHandler::deinit() {
+ clearAllFades();
+}
+
+void IMuseDigiFadesHandler::saveLoad(Common::Serializer &ser) {
+ for (int l = 0; l < DIMUSE_MAX_FADES; l++) {
+ ser.syncAsSint32LE(_fades[l].status, VER(103));
+ ser.syncAsSint32LE(_fades[l].sound, VER(103));
+ ser.syncAsSint32LE(_fades[l].param, VER(103));
+ ser.syncAsSint32LE(_fades[l].currentVal, VER(103));
+ ser.syncAsSint32LE(_fades[l].counter, VER(103));
+ ser.syncAsSint32LE(_fades[l].length, VER(103));
+ ser.syncAsSint32LE(_fades[l].slope, VER(103));
+ ser.syncAsSint32LE(_fades[l].slopeMod, VER(103));
+ ser.syncAsSint32LE(_fades[l].modOvfloCounter, VER(103));
+ ser.syncAsSint32LE(_fades[l].nudge, VER(103));
+ }
+
+ if (ser.isLoading())
+ _fadesOn = 1;
+}
+
+void IMuseDigiFadesHandler::clearAllFades() {
+ for (int l = 0; l < DIMUSE_MAX_FADES; l++) {
+ _fades[l].status = 0;
+ _fades[l].sound = 0;
+ }
+ _fadesOn = 0;
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/imuse_digi/dimuse_fades.h b/engines/scumm/imuse_digi/dimuse_fades.h
new file mode 100644
index 0000000000..954f8b9907
--- /dev/null
+++ b/engines/scumm/imuse_digi/dimuse_fades.h
@@ -0,0 +1,54 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#if !defined(SCUMM_IMUSE_DIGI_FADES_H) && defined(ENABLE_SCUMM_7_8)
+#define SCUMM_IMUSE_DIGI_FADES_H
+
+#include "common/scummsys.h"
+#include "common/textconsole.h"
+#include "common/util.h"
+
+namespace Scumm {
+
+class IMuseDigiFadesHandler {
+
+private:
+ IMuseDigital *_engine;
+ IMuseDigiFade _fades[DIMUSE_MAX_FADES];
+ int _fadesOn;
+
+ void clearAllFades();
+public:
+ IMuseDigiFadesHandler(IMuseDigital *engine);
+ ~IMuseDigiFadesHandler();
+
+ int init();
+ void deinit();
+ void saveLoad(Common::Serializer &ser);
+ int fadeParam(int soundId, int opcode, int destinationValue, int fadeLength);
+ void clearFadeStatus(int soundId, int opcode);
+ void loop();
+
+};
+
+} // End of namespace Scumm
+#endif
diff --git a/engines/scumm/imuse_digi/dimuse_files.cpp b/engines/scumm/imuse_digi/dimuse_files.cpp
new file mode 100644
index 0000000000..b22bebd31e
--- /dev/null
+++ b/engines/scumm/imuse_digi/dimuse_files.cpp
@@ -0,0 +1,439 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "scumm/imuse_digi/dimuse_engine.h"
+#include "scumm/imuse_digi/dimuse_files.h"
+#include "scumm/file.h"
+
+namespace Scumm {
+
+IMuseDigiFilesHandler::IMuseDigiFilesHandler(IMuseDigital *engine, ScummEngine_v7 *vm) {
+ _engine = engine;
+ _sound = new ImuseDigiSndMgr(vm);
+ assert(_sound);
+ _vm = vm;
+
+ _ftSpeechFilename[0] = '\0';
+ _ftSpeechSubFileOffset = 0;
+ _ftSpeechFileSize = 0;
+ _ftSpeechFileCurPos = 0;
+ _ftSpeechFile = nullptr;
+
+ for (int i = 0; i < 4; i++) {
+ IMuseDigiSndBuffer *selectedSoundBuf = &_soundBuffers[i];
+ selectedSoundBuf->buffer = nullptr;
+ selectedSoundBuf->bufSize = 0;
+ selectedSoundBuf->loadSize = 0;
+ selectedSoundBuf->criticalSize = 0;
+ }
+}
+
+IMuseDigiFilesHandler::~IMuseDigiFilesHandler() {
+ if (_ftSpeechFile)
+ _ftSpeechFile->close();
+ delete _sound;
+}
+
+void IMuseDigiFilesHandler::saveLoad(Common::Serializer &ser) {
+ int curSound = 0;
+ ImuseDigiSndMgr::SoundDesc *sounds = _sound->getSounds();
+
+ ser.syncArray(_currentSpeechFilename, 60, Common::Serializer::SByte, VER(103));
+ if (ser.isSaving()) {
+ for (int l = 0; l < MAX_IMUSE_SOUNDS; l++) {
+ ser.syncAsSint32LE(sounds[l].soundId, VER(103));
+ }
+ if (_engine->isFTSoundEngine()) {
+ ser.syncAsSint32LE(_ftSpeechFileCurPos, VER(103));
+ ser.syncAsSint32LE(_ftSpeechFileSize, VER(103));
+ ser.syncAsSint32LE(_ftSpeechSubFileOffset, VER(103));
+ ser.syncArray(_ftSpeechFilename, sizeof(_ftSpeechFilename), Common::Serializer::SByte, VER(103));
+ }
+ }
+
+ if (ser.isLoading()) {
+ // Close prior sounds if we're reloading (needed for edge cases like the recipe book in COMI)
+ for (int l = 0; l < MAX_IMUSE_SOUNDS; l++) {
+ _sound->closeSound(&sounds[l]);
+ }
+
+ for (int l = 0; l < MAX_IMUSE_SOUNDS; l++) {
+ ser.syncAsSint32LE(curSound, VER(103));
+ if (curSound) {
+ openSound(curSound);
+ if (curSound != kTalkSoundID)
+ closeSound(curSound);
+ }
+ }
+
+ if (_engine->isFTSoundEngine()) {
+ ser.syncAsSint32LE(_ftSpeechFileCurPos, VER(103));
+ ser.syncAsSint32LE(_ftSpeechFileSize, VER(103));
+ ser.syncAsSint32LE(_ftSpeechSubFileOffset, VER(103));
+ ser.syncArray(_ftSpeechFilename, sizeof(_ftSpeechFilename), Common::Serializer::SByte, VER(103));
+ _ftSpeechFile = _vm->_sound->restoreDiMUSESpeechFile(_ftSpeechFilename);
+ }
+ }
+}
+
+uint8 *IMuseDigiFilesHandler::getSoundAddrData(int soundId) {
+ if (_engine->isEngineDisabled())
+ return nullptr;
+
+ Common::StackLock lock(_mutex);
+ // This function is always used for SFX (tracks which do not
+ // have a stream pointer), hence the use of the resource address
+ if (soundId != 0) {
+ _vm->_res->lock(rtSound, soundId);
+ byte *ptr = _vm->getResourceAddress(rtSound, soundId);
+ if (!ptr) {
+ _vm->_res->unlock(rtSound, soundId);
+ return nullptr;
+ }
+ return ptr;
+
+ }
+ debug(5, "IMuseDigiFilesHandler::getSoundAddrData(): soundId is 0 or out of range");
+ return nullptr;
+}
+
+int IMuseDigiFilesHandler::getSoundAddrDataSize(int soundId, bool hasStream) {
+ if (_engine->isEngineDisabled())
+ return 0;
+
+ if (hasStream) {
+ ImuseDigiSndMgr::SoundDesc *s = _sound->findSoundById(soundId);
+ if (s) {
+ if (soundId != kTalkSoundID) {
+ return s->resSize;
+ }
+ } else if (soundId == kTalkSoundID) {
+ return _ftSpeechFileSize;
+ }
+ } else {
+ return _vm->getResourceSize(rtSound, soundId);
+ }
+
+ return 0;
+}
+
+int IMuseDigiFilesHandler::getNextSound(int soundId) {
+ int foundSoundId = 0;
+ do {
+ foundSoundId = _engine->diMUSEGetNextSound(foundSoundId);
+ if (!foundSoundId)
+ return -1;
+ } while (foundSoundId != soundId);
+ return 2;
+}
+
+int IMuseDigiFilesHandler::seek(int soundId, int32 offset, int mode, int bufId) {
+ // This function and files_read() are used for sounds for which a stream is needed (speech
+ // and music), therefore they will always refer to sounds in a bundle file for DIG and COMI
+ // The seeked position is in reference to the decompressed sound
+
+ if (_engine->isEngineDisabled())
+ return 0;
+
+ char fileName[60] = "";
+ getFilenameFromSoundId(soundId, fileName, sizeof(fileName));
+
+ ImuseDigiSndMgr::SoundDesc *s = _sound->findSoundById(soundId);
+ if (s || (_engine->isFTSoundEngine() && soundId == kTalkSoundID)) {
+ if (soundId != 0) {
+ if (_engine->isFTSoundEngine()) {
+ switch (mode) {
+ case SEEK_END:
+ if (soundId != kTalkSoundID) {
+ return s->resSize;
+ } else {
+ return _ftSpeechFileSize;
+ }
+ case SEEK_SET:
+ default:
+ if (soundId != kTalkSoundID) {
+ if (offset <= s->resSize) {
+ s->resCurOffset = offset;
+ return offset;
+ }
+ } else {
+ if (offset <= _ftSpeechFileSize) {
+ _ftSpeechFileCurPos = offset;
+ return _ftSpeechFileCurPos;
+ }
+ }
+ }
+ } else {
+ // A soundId > 10000 is a SAN cutscene
+ if ((_vm->_game.id == GID_DIG && !(_vm->_game.features & GF_DEMO)) && (soundId > kTalkSoundID))
+ return 0;
+
+ return s->bundle->seekFile(offset, mode);
+ }
+ } else {
+ debug(5, "IMuseDigiFilesHandler::seek(): soundId is 0 or out of range");
+ }
+ } else {
+ debug(5, "IMuseDigiFilesHandler::seek(): can't find sound %d (%s); did you forget to open it?", soundId, fileName);
+ }
+
+ return 0;
+}
+
+int IMuseDigiFilesHandler::read(int soundId, uint8 *buf, int32 size, int bufId) {
+ // This function and files_seek() are used for sounds for which a stream is needed (speech
+ // and music), therefore they will always refer to sounds in a bundle file for DIG and COMI
+
+ if (_engine->isEngineDisabled())
+ return 0;
+
+ if (soundId != 0) {
+ uint8 *tmpBuf = nullptr;
+ int32 resultingSize;
+
+ // We don't have SoundDesc objects for FT & DIG demo speech files
+ if (_engine->isFTSoundEngine() && soundId == kTalkSoundID) {
+ _ftSpeechFile->seek(_ftSpeechSubFileOffset + _ftSpeechFileCurPos, SEEK_SET);
+ resultingSize = size > _ftSpeechFileSize ? (_ftSpeechFileSize - _ftSpeechFileCurPos) : size;
+ return _ftSpeechFile->read(buf, resultingSize);
+ }
+
+ char fileName[60] = "";
+ getFilenameFromSoundId(soundId, fileName, sizeof(fileName));
+
+ ImuseDigiSndMgr::SoundDesc *s = _sound->getSounds();
+ ImuseDigiSndMgr::SoundDesc *curSnd = nullptr;
+ for (int i = 0; i < MAX_IMUSE_SOUNDS; i++) {
+ curSnd = &s[i];
+ if (curSnd->inUse) {
+ if (curSnd->soundId == soundId) {
+ if (_engine->isFTSoundEngine()) { // FT & DIG demo
+ resultingSize = size > (curSnd->resSize - curSnd->resCurOffset) ? (curSnd->resSize - curSnd->resCurOffset) : size;
+ tmpBuf = &curSnd->resPtr[curSnd->resCurOffset];
+
+ if (resultingSize != size)
+ debug(5, "IMuseDigiFilesHandler::read(): WARNING: tried to read %d bytes, got %d instead (soundId %d (%s))", size, resultingSize, soundId, fileName);
+
+ memcpy(buf, tmpBuf, resultingSize); // We don't free tmpBuf: it's the resource pointer
+ return resultingSize;
+ } else { // DIG & COMI
+ resultingSize = curSnd->bundle->readFile(fileName, size, &tmpBuf, ((_vm->_game.id == GID_CMI) && !(_vm->_game.features & GF_DEMO)));
+
+ if (resultingSize != size)
+ debug(5, "IMuseDigiFilesHandler::read(): WARNING: tried to read %d bytes, got %d instead (soundId %d (%s))", size, resultingSize, soundId, fileName);
+
+ memcpy(buf, tmpBuf, resultingSize);
+ free(tmpBuf);
+ return resultingSize;
+ }
+ }
+ }
+ }
+
+ debug(5, "IMuseDigiFilesHandler::read(): can't find sound %d (%s); did you forget to open it?", soundId, fileName);
+
+ } else {
+ debug(5, "IMuseDigiFilesHandler::read(): soundId is 0 or out of range");
+ }
+
+ return 0;
+}
+
+IMuseDigiSndBuffer *IMuseDigiFilesHandler::getBufInfo(int bufId) {
+ if (bufId > 0 && bufId <= 4) {
+ return &_soundBuffers[bufId];
+ }
+
+ debug(5, "IMuseDigiFilesHandler::getBufInfo(): ERROR: invalid buffer id");
+ return nullptr;
+}
+
+int IMuseDigiFilesHandler::openSound(int soundId) {
+ if (_engine->isEngineDisabled())
+ return 1;
+
+ ImuseDigiSndMgr::SoundDesc *s = nullptr;
+ if (!_engine->isFTSoundEngine()) {
+ char fileName[60] = "";
+ getFilenameFromSoundId(soundId, fileName, sizeof(fileName));
+
+ int groupId = soundId == kTalkSoundID ? IMUSE_VOLGRP_VOICE : IMUSE_VOLGRP_MUSIC;
+ s = _sound->openSound(soundId, fileName, IMUSE_BUNDLE, groupId, -1);
+ if (!s)
+ s = _sound->openSound(soundId, fileName, IMUSE_BUNDLE, groupId, 1);
+ if (!s)
+ s = _sound->openSound(soundId, fileName, IMUSE_BUNDLE, groupId, 2);
+ if (!s) {
+ debug(5, "IMuseDigiFilesHandler::openSound(): can't open sound %d (%s)", soundId, fileName);
+ return 1;
+ }
+ } else {
+ s = _sound->findSoundById(soundId);
+ if (!s)
+ s = _sound->openSound(soundId, "", IMUSE_RESOURCE, -1, -1);
+ if (!s)
+ s = _sound->openSound(soundId, "", IMUSE_RESOURCE, -1, 1);
+ if (!s)
+ s = _sound->openSound(soundId, "", IMUSE_RESOURCE, -1, 2);
+ if (!s) {
+ debug(5, "IMuseDigiFilesHandler::openSound(): can't open sound %d", soundId);
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+void IMuseDigiFilesHandler::closeSound(int soundId) {
+ if (_engine->isEngineDisabled())
+ return;
+
+ _sound->scheduleSoundForDeallocation(soundId);
+}
+
+void IMuseDigiFilesHandler::closeAllSounds() {
+ ImuseDigiSndMgr::SoundDesc *s = _sound->getSounds();
+ for (int i = 0; i < MAX_IMUSE_SOUNDS; i++) {
+ if (s[i].inUse) {
+ closeSound((&s[i])->soundId);
+ }
+ }
+
+ _engine->flushTracks();
+}
+
+void IMuseDigiFilesHandler::getFilenameFromSoundId(int soundId, char *fileName, size_t size) {
+ if (_engine->isFTSoundEngine())
+ return;
+
+ int i = 0;
+
+ if (soundId == kTalkSoundID) {
+ Common::strlcpy(fileName, _currentSpeechFilename, size);
+ return;
+ }
+
+ if (_vm->_game.id == GID_CMI) {
+ if (_vm->_game.features & GF_DEMO) {
+ while (_comiDemoStateMusicTable[i].soundId != -1) {
+ if (_comiDemoStateMusicTable[i].soundId == soundId) {
+ Common::strlcpy(fileName, _comiDemoStateMusicTable[i].filename, size);
+ return;
+ }
+ i++;
+ }
+ } else {
+ if (soundId < 2000) {
+ while (_comiStateMusicTable[i].soundId != -1) {
+ if (_comiStateMusicTable[i].soundId == soundId) {
+ Common::strlcpy(fileName, _comiStateMusicTable[i].filename, size);
+ return;
+ }
+ i++;
+ }
+ } else {
+ while (_comiSeqMusicTable[i].soundId != -1) {
+ if (_comiSeqMusicTable[i].soundId == soundId) {
+ Common::strlcpy(fileName, _comiSeqMusicTable[i].filename, size);
+ return;
+ }
+ i++;
+ }
+ }
+ }
+ } else if (_vm->_game.id == GID_DIG) {
+ if (soundId < 2000) {
+ while (_digStateMusicTable[i].soundId != -1) {
+ if (_digStateMusicTable[i].soundId == soundId) {
+ Common::strlcpy(fileName, _digStateMusicTable[i].filename, size);
+ return;
+ }
+ i++;
+ }
+ } else {
+ while (_digSeqMusicTable[i].soundId != -1) {
+ if (_digSeqMusicTable[i].soundId == soundId) {
+ Common::strlcpy(fileName, _digSeqMusicTable[i].filename, size);
+ return;
+ }
+ i++;
+ }
+ }
+ }
+}
+
+void IMuseDigiFilesHandler::allocSoundBuffer(int bufId, int32 size, int32 loadSize, int32 criticalSize) {
+ IMuseDigiSndBuffer *selectedSoundBuf;
+
+ selectedSoundBuf = &_soundBuffers[bufId];
+ selectedSoundBuf->buffer = (uint8 *)malloc(size);
+ selectedSoundBuf->bufSize = size;
+ selectedSoundBuf->loadSize = loadSize;
+ selectedSoundBuf->criticalSize = criticalSize;
+}
+
+void IMuseDigiFilesHandler::deallocSoundBuffer(int bufId) {
+ IMuseDigiSndBuffer *selectedSoundBuf;
+
+ selectedSoundBuf = &_soundBuffers[bufId];
+ free(selectedSoundBuf->buffer);
+ selectedSoundBuf->buffer = nullptr;
+}
+
+void IMuseDigiFilesHandler::flushSounds() {
+ if (_engine->isEngineDisabled())
+ return;
+
+ ImuseDigiSndMgr::SoundDesc *s = _sound->getSounds();
+ for (int i = 0; i < MAX_IMUSE_SOUNDS; i++) {
+ ImuseDigiSndMgr::SoundDesc *curSnd = &s[i];
+ if (curSnd && curSnd->inUse) {
+ if (curSnd->scheduledForDealloc)
+ if (!_engine->diMUSEGetParam(curSnd->soundId, DIMUSE_P_SND_TRACK_NUM) && !_engine->diMUSEGetParam(curSnd->soundId, DIMUSE_P_TRIGS_SNDS))
+ _sound->closeSound(curSnd);
+ }
+ }
+}
+
+int IMuseDigiFilesHandler::setCurrentSpeechFilename(const char *fileName) {
+ Common::strlcpy(_currentSpeechFilename, fileName, sizeof(_currentSpeechFilename));
+ if (openSound(kTalkSoundID))
+ return 1;
+
+ return 0;
+}
+
+void IMuseDigiFilesHandler::setCurrentFtSpeechFile(const char *fileName, ScummFile *file, uint32 offset, uint32 size) {
+ Common::strlcpy(_ftSpeechFilename, fileName, sizeof(_ftSpeechFilename));
+ _ftSpeechFile = file;
+ _ftSpeechSubFileOffset = offset;
+ _ftSpeechFileSize = size;
+}
+
+void IMuseDigiFilesHandler::closeSoundImmediatelyById(int soundId) {
+ if (_engine->isEngineDisabled())
+ return;
+
+ _sound->closeSoundById(soundId);
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/imuse_digi/dimuse_files.h b/engines/scumm/imuse_digi/dimuse_files.h
new file mode 100644
index 0000000000..4fea1fbd97
--- /dev/null
+++ b/engines/scumm/imuse_digi/dimuse_files.h
@@ -0,0 +1,75 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#if !defined(SCUMM_IMUSE_DIGI_FILES_H) && defined(ENABLE_SCUMM_7_8)
+#define SCUMM_IMUSE_DIGI_FILES_H
+
+#include "common/scummsys.h"
+#include "common/textconsole.h"
+#include "common/util.h"
+#include "scumm/resource.h"
+#include "scumm/file.h"
+#include "scumm/imuse_digi/dimuse_bndmgr.h"
+#include "scumm/imuse_digi/dimuse_sndmgr.h"
+
+namespace Scumm {
+
+class IMuseDigiFilesHandler {
+
+private:
+ IMuseDigital *_engine;
+ ImuseDigiSndMgr *_sound;
+ ScummEngine_v7 *_vm;
+ Common::Mutex _mutex;
+ IMuseDigiSndBuffer _soundBuffers[4];
+ char _currentSpeechFilename[60];
+ ScummFile *_ftSpeechFile;
+ char _ftSpeechFilename[160];
+ int _ftSpeechSubFileOffset;
+ int _ftSpeechFileSize;
+ int _ftSpeechFileCurPos;
+
+ void getFilenameFromSoundId(int soundId, char *fileName, size_t size);
+public:
+ IMuseDigiFilesHandler(IMuseDigital *engine, ScummEngine_v7 *vm);
+ ~IMuseDigiFilesHandler();
+
+ uint8 *getSoundAddrData(int soundId);
+ int getSoundAddrDataSize(int soundId, bool hasStream);
+ int getNextSound(int soundId);
+ int seek(int soundId, int32 offset, int mode, int bufId);
+ int read(int soundId, uint8 *buf, int32 size, int bufId);
+ IMuseDigiSndBuffer *getBufInfo(int bufId);
+ int openSound(int soundId);
+ void closeSound(int soundId);
+ void closeAllSounds();
+ void allocSoundBuffer(int bufId, int32 size, int32 loadSize, int32 criticalSize);
+ void deallocSoundBuffer(int bufId);
+ void flushSounds();
+ int setCurrentSpeechFilename(const char *fileName);
+ void setCurrentFtSpeechFile(const char *fileName, ScummFile *file, uint32 offset, uint32 size);
+ void closeSoundImmediatelyById(int soundId);
+ void saveLoad(Common::Serializer &ser);
+};
+
+} // End of namespace Scumm
+#endif
diff --git a/engines/scumm/imuse_digi/dimuse_groups.cpp b/engines/scumm/imuse_digi/dimuse_groups.cpp
new file mode 100644
index 0000000000..76ff458597
--- /dev/null
+++ b/engines/scumm/imuse_digi/dimuse_groups.cpp
@@ -0,0 +1,80 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "scumm/imuse_digi/dimuse_engine.h"
+#include "scumm/imuse_digi/dimuse_groups.h"
+
+namespace Scumm {
+
+IMuseDigiGroupsHandler::IMuseDigiGroupsHandler(IMuseDigital *engine) {
+ _engine = engine;
+}
+
+IMuseDigiGroupsHandler::~IMuseDigiGroupsHandler() {}
+
+int IMuseDigiGroupsHandler::init() {
+ for (int i = 0; i < DIMUSE_MAX_GROUPS; i++) {
+ _effVols[i] = 127;
+ _vols[i] = 127;
+ }
+ return 0;
+}
+
+int IMuseDigiGroupsHandler::setGroupVol(int id, int volume) {
+ int l;
+
+ if (id >= DIMUSE_MAX_GROUPS) {
+ return -5;
+ }
+
+ if (volume == -1) {
+ return _vols[id];
+ }
+
+ if (volume > 127)
+ return -5;
+
+ if (id) {
+ _vols[id] = volume;
+ _effVols[id] = (_vols[0] * (volume + 1)) / 128;
+ } else {
+ _effVols[0] = volume;
+ _vols[0] = volume;
+
+ for (l = 1; l < DIMUSE_MAX_GROUPS; l++) {
+ _effVols[l] = (volume * (_vols[id] + 1)) / 128;
+ }
+ }
+
+ _engine->diMUSEUpdateGroupVolumes();
+ return _vols[id];
+}
+
+int IMuseDigiGroupsHandler::getGroupVol(int id) {
+ if (id >= DIMUSE_MAX_GROUPS) {
+ return -5;
+ }
+
+ return _effVols[id];
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/imuse_digi/dimuse_groups.h b/engines/scumm/imuse_digi/dimuse_groups.h
new file mode 100644
index 0000000000..f8e787f7eb
--- /dev/null
+++ b/engines/scumm/imuse_digi/dimuse_groups.h
@@ -0,0 +1,50 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#if !defined(SCUMM_IMUSE_DIGI_GROUPS_H) && defined(ENABLE_SCUMM_7_8)
+#define SCUMM_IMUSE_DIGI_GROUPS_H
+
+#include "common/scummsys.h"
+#include "common/mutex.h"
+#include "common/serializer.h"
+#include "common/textconsole.h"
+#include "common/util.h"
+#include "scumm/imuse_digi/dimuse_engine.h"
+
+namespace Scumm {
+
+class IMuseDigiGroupsHandler {
+
+private:
+ IMuseDigital *_engine;
+ int _effVols[DIMUSE_MAX_GROUPS];
+ int _vols[DIMUSE_MAX_GROUPS];
+public:
+ IMuseDigiGroupsHandler(IMuseDigital *engine);
+ ~IMuseDigiGroupsHandler();
+ int init();
+ int setGroupVol(int id, int volume);
+ int getGroupVol(int id);
+};
+
+} // End of namespace Scumm
+#endif
diff --git a/engines/scumm/imuse_digi/dimuse_internalmixer.cpp b/engines/scumm/imuse_digi/dimuse_internalmixer.cpp
new file mode 100644
index 0000000000..1d8cf798d5
--- /dev/null
+++ b/engines/scumm/imuse_digi/dimuse_internalmixer.cpp
@@ -0,0 +1,1104 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/scummsys.h"
+#include "common/mutex.h"
+#include "common/serializer.h"
+#include "common/textconsole.h"
+#include "common/util.h"
+
+#include "scumm/imuse_digi/dimuse_engine.h"
+#include "scumm/imuse_digi/dimuse_internalmixer.h"
+
+namespace Scumm {
+
+IMuseDigiInternalMixer::IMuseDigiInternalMixer(Audio::Mixer *mixer) {
+ _stream = Audio::makeQueuingAudioStream(22050, true);
+ _mixer = mixer;
+ _radioChatter = 0;
+ _amp8Table = nullptr;
+}
+
+IMuseDigiInternalMixer::~IMuseDigiInternalMixer() {
+ free(_amp8Table);
+ _amp8Table = nullptr;
+}
+
+// Lookup table for a linear volume ramp (0 to 16) accounting for panning (-8 to 8)
+static const int8 _stereoVolumeTable[284] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 0, 0, 0, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 1, 1, 1, 1, 2, 2, 2,
+ 2, 2, 3, 3, 3, 3, 3, 3, 0, 0, 1, 1, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 0,
+ 0, 1, 1, 2, 2, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 5, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5,
+ 5, 5, 6, 6, 6, 6, 6, 0, 1, 1, 2, 3, 3, 4, 4, 5, 5, 6, 6, 6, 7, 7, 7, 7, 0, 1,
+ 2, 2, 3, 4, 4, 5, 6, 6, 7, 7, 7, 8, 8, 8, 8, 0, 1, 2, 3, 3, 4, 5, 6, 6, 7, 7,
+ 8, 8, 9, 9, 9, 9, 0, 1, 2, 3, 4, 5, 6, 6, 7, 8, 8, 9, 9, 10, 10, 10, 10, 0, 1, 2,
+ 3, 4, 5, 6, 7, 8, 9, 9, 10, 10, 11, 11, 11, 11, 0, 1, 2, 3, 5, 6, 7, 8, 8, 9, 10, 11,
+ 11, 11, 12, 12, 12, 0, 1, 3, 4, 5, 6, 7, 8, 9, 10, 11, 11, 12, 12, 13, 13, 13, 0, 1, 3, 4,
+ 5, 7, 8, 9, 10, 11, 12, 12, 13, 13, 14, 14, 14, 0, 1, 3, 4, 6, 7, 8, 10, 11, 12, 12, 13, 14,
+ 14, 15, 15, 15, 0, 2, 3, 5, 6, 8, 9, 10, 11, 12, 13, 14, 15, 15, 16, 16, 16, 0, 0, 0
+};
+
+int IMuseDigiInternalMixer::init(int bytesPerSample, int numChannels, uint8 *mixBuf, int mixBufSize, int sizeSampleKB, int mixChannelsNum) {
+ int amplitudeValue;
+ int waveMixChannelsCount;
+ int softLdenominator;
+ int softLnumerator;
+ int softLcurValue;
+ int zeroCenterOffset;
+
+ _outWordSize = bytesPerSample;
+ _outChannelCount = numChannels;
+ _mixBufSize = mixBufSize;
+ _stereoReverseFlag = sizeSampleKB;
+ _mixBuf = mixBuf;
+
+ waveMixChannelsCount = mixChannelsNum;
+
+ // We're allocating:
+ // - The amplitude quantization table for 8-bit audio ((17 * 256) int16 == 2176 int32)
+ // - The amplitude quantization table for 12-bit/16-bit audio ((17 * 4096) int16 == 34816 int32)
+ // - The pseudologarithmic curve for volume fade ((8 * 4096) int16 == 16384 int32)
+ //
+ // For the first two tables 17 represents the volume range (0-16), while
+ // 256 and 4096 are respectively the dynamic range for 8-bit and 12-bit audio;
+ //
+ // Every table is initialized with int16 values, but stored as int32 and manipulated
+ // at different levels of granularity depending on the case (as uint32, uint16 or uint8).
+ // The latter is populated from the middle, inside-out; hence _softLMID.
+
+ int32 *tableMalloc = (int32 *)malloc(53376 * sizeof(int32));
+
+ _amp8Table = tableMalloc; // Dim: 2176
+ _amp12Table = tableMalloc + 2176; // Dim: 34816
+ _softLTable = tableMalloc + 36992; // Dim: 16384
+ _softLMID = tableMalloc + 45184; // Mid table
+
+ if (tableMalloc) {
+ // Any unpopulated cell is expected to be set to 0
+ memset(tableMalloc, 0, 53376 * sizeof(int32));
+ zeroCenterOffset = 0;
+ for (int i = 0; i < 17; i++) {
+ amplitudeValue = -2048 * zeroCenterOffset;
+ for (int j = 0; j < 256; j++) {
+ ((int16 *)&_amp8Table[i * 128])[j] = (int16)(amplitudeValue / 127);
+ amplitudeValue += 16 * zeroCenterOffset;
+ }
+
+ zeroCenterOffset += 8;
+ if (zeroCenterOffset == 8)
+ zeroCenterOffset = 7;
+ }
+
+ zeroCenterOffset = 0;
+ for (int i = 0; i < 17; i++) {
+ amplitudeValue = -2048 * zeroCenterOffset;
+ for (int j = 0; j < 4096; j++) {
+ ((int16 *)&_amp12Table[i * 2048])[j] = (int16)(amplitudeValue / 127);
+ amplitudeValue += zeroCenterOffset;
+ }
+
+ zeroCenterOffset += 8;
+ if (zeroCenterOffset == 8)
+ zeroCenterOffset = 7;
+ }
+
+ if (_outWordSize == 8) {
+ if (waveMixChannelsCount * 1024 > 0) {
+ softLnumerator = 0;
+ softLdenominator = 2047 * waveMixChannelsCount;
+ for (int i = 0; i < waveMixChannelsCount * 2048; i++) {
+ softLcurValue = softLnumerator / softLdenominator + 1;
+ softLdenominator += waveMixChannelsCount - 1;
+ softLnumerator += 254 * waveMixChannelsCount;
+ softLcurValue /= 2;
+
+ ((int8 *)_softLMID)[i] = (int8)softLcurValue + 128;
+ ((int8 *)_softLMID)[-i] = 127 - (int8)softLcurValue;
+ }
+ }
+ } else if (waveMixChannelsCount * 2048 > 0) {
+ softLdenominator = 2047 * waveMixChannelsCount;
+ softLnumerator = 0;
+ for (int i = 0; i < waveMixChannelsCount * 2048; i++) {
+ softLcurValue = softLnumerator / softLdenominator + 1;
+ softLcurValue /= 2;
+ softLdenominator += waveMixChannelsCount - 1;
+ softLnumerator += 65534 * waveMixChannelsCount;
+ ((int16 *)_softLMID)[i] = (int16)softLcurValue;
+ ((int16 *)_softLMID)[-i - 1] = -1 - (int16)softLcurValue;
+ }
+ }
+ _mixer->playStream(Audio::Mixer::kPlainSoundType, &_channelHandle, _stream, -1, Audio::Mixer::kMaxChannelVolume, false);
+ return 0;
+ } else {
+ debug(5, "DiMUSE_InternalMixer::init(): ERROR: couldn't allocate mixer tables");
+ return -1;
+ }
+}
+
+void IMuseDigiInternalMixer::setRadioChatter() {
+ _radioChatter = 1;
+}
+void IMuseDigiInternalMixer::clearRadioChatter() {
+ _radioChatter = 0;
+}
+
+int IMuseDigiInternalMixer::clearMixerBuffer() {
+ if (!_mixBuf)
+ return -1;
+
+ memset(_mixBuf, 0, _mixBufSize);
+
+ return 0;
+}
+
+void IMuseDigiInternalMixer::mix(uint8 *srcBuf, int32 inFrameCount, int wordSize, int channelCount, int feedSize, int32 mixBufStartIndex, int volume, int pan) {
+ int32 *ampTable;
+ int rightChannelVolume;
+ int leftChannelVolume;
+ int channelVolume;
+ int channelPan;
+
+ if (_mixBuf) {
+ if (srcBuf) {
+ if (inFrameCount) {
+ if (channelCount == 1 && _outChannelCount == 2) {
+ channelVolume = volume / 8;
+ if (volume)
+ ++channelVolume;
+ if (channelVolume >= 17)
+ channelVolume = 16;
+ channelPan = (pan / 8) - 8;
+ if (pan > 64)
+ ++channelPan;
+
+ channelPan = channelVolume == 0 ? 0 : channelPan;
+
+ // Linear volume quantization from the lookup table
+ rightChannelVolume = _stereoVolumeTable[17 * channelVolume + channelPan];
+ leftChannelVolume = _stereoVolumeTable[17 * channelVolume - channelPan];
+ if (wordSize == 8) {
+ mixBits8ConvertToStereo(
+ srcBuf,
+ inFrameCount,
+ feedSize,
+ mixBufStartIndex,
+ &_amp8Table[leftChannelVolume * 128],
+ &_amp8Table[rightChannelVolume * 128]);
+ } else if (wordSize == 12) {
+ mixBits12ConvertToStereo(
+ srcBuf,
+ inFrameCount,
+ feedSize,
+ mixBufStartIndex,
+ &_amp12Table[leftChannelVolume * 2048],
+ &_amp12Table[rightChannelVolume * 2048]);
+ } else {
+ mixBits16ConvertToStereo(
+ srcBuf,
+ inFrameCount,
+ feedSize,
+ mixBufStartIndex,
+ &_amp12Table[leftChannelVolume * 2048],
+ &_amp12Table[rightChannelVolume * 2048]);
+ }
+ } else {
+ channelVolume = volume / 8;
+ if (volume)
+ channelVolume++;
+ if (channelVolume >= 17)
+ channelVolume = 16;
+
+ if (wordSize == 8)
+ ampTable = &_amp8Table[channelVolume * 128];
+ else
+ ampTable = &_amp12Table[channelVolume * 2048];
+
+ if (_outChannelCount == 1) {
+ if (channelCount == 1) {
+ if (wordSize == 8) {
+ mixBits8Mono(srcBuf, inFrameCount, feedSize, mixBufStartIndex, ampTable);
+ } else if (wordSize == 12) {
+ mixBits12Mono(srcBuf, inFrameCount, feedSize, mixBufStartIndex, ampTable);
+ } else {
+ mixBits16Mono(srcBuf, inFrameCount, feedSize, mixBufStartIndex, ampTable);
+ }
+ } else if (wordSize == 8) {
+ mixBits8ConvertToMono(srcBuf, inFrameCount, feedSize, mixBufStartIndex, ampTable);
+ } else if (wordSize == 12) {
+ mixBits12ConvertToMono(srcBuf, inFrameCount, feedSize, mixBufStartIndex, ampTable);
+ } else {
+ mixBits16ConvertToMono(srcBuf, inFrameCount, feedSize, mixBufStartIndex, ampTable);
+ }
+ } else if (wordSize == 8) {
+ mixBits8Stereo(srcBuf, inFrameCount, feedSize, mixBufStartIndex, ampTable);
+ } else if (wordSize == 12) {
+ mixBits12Stereo(srcBuf, inFrameCount, feedSize, mixBufStartIndex, ampTable);
+ } else {
+ mixBits16Stereo(srcBuf, inFrameCount, feedSize, mixBufStartIndex, ampTable);
+ }
+ }
+ }
+ }
+ }
+}
+
+int IMuseDigiInternalMixer::loop(uint8 **destBuffer, int len) {
+ int16 *mixBuffer = (int16 *)_mixBuf;
+ uint8 *destBuffer_tmp = *destBuffer;
+
+ if (!_mixBuf || !destBuffer || !len)
+ return -1;
+
+ if (_outChannelCount == 2)
+ len *= 2;
+
+ if (!_stereoReverseFlag || _outChannelCount == 1) {
+ if (_outWordSize != 16) {
+ if (len) {
+ for (int i = 0; i < len; i++) {
+ destBuffer_tmp[i] = ((uint8 *)_softLMID)[mixBuffer[i]];
+ }
+ }
+ }
+
+ if (len) {
+ for (int i = 0; i < len; i++) {
+ ((uint16 *)destBuffer_tmp)[i] = ((uint16 *)_softLMID)[mixBuffer[i]];
+ }
+ }
+ } else {
+ len /= 2;
+ if (_outWordSize == 16) {
+ if (len) {
+ for (int i = 0; i < len; i += 2) {
+ ((uint16 *)destBuffer_tmp)[i] = ((uint16 *)_softLMID)[mixBuffer[i + 1]];
+ ((uint16 *)destBuffer_tmp)[i + 1] = ((uint16 *)_softLMID)[mixBuffer[i]];
+ }
+ }
+ }
+
+ if (len) {
+ for (int i = 0; i < len; i += 2) {
+ destBuffer_tmp[i] = ((uint8 *)_softLMID)[mixBuffer[i + 1]];
+ destBuffer_tmp[i + 1] = ((uint8 *)_softLMID)[mixBuffer[i]];
+ }
+ }
+ }
+ return 0;
+}
+
+void IMuseDigiInternalMixer::mixBits8Mono(uint8 *srcBuf, int32 inFrameCount, int feedSize, int32 mixBufStartIndex, int32 *ampTable) {
+ uint16 *mixBufCurCell;
+ uint8 *srcBuf_ptr;
+ uint8 *ptr;
+ int residualLength;
+ int value;
+
+ mixBufCurCell = (uint16 *)(&_mixBuf[2 * mixBufStartIndex]);
+ srcBuf_ptr = srcBuf;
+ if (inFrameCount == feedSize) {
+ if (_radioChatter) {
+ ptr = srcBuf + 4;
+ value = srcBuf[0] - 128 + srcBuf[1] - 128 + srcBuf[2] - 128 + srcBuf[3] - 128;
+ if (feedSize) {
+ for (int i = 0; i < feedSize; i++) {
+ mixBufCurCell[i] += 4 * *((uint16 *)ampTable + (srcBuf_ptr[i] - (value >> 2)));
+ value += ptr[i] - srcBuf_ptr[i];
+ }
+ }
+ } else {
+ if (feedSize) {
+ for (int i = 0; i < feedSize; i++) {
+ mixBufCurCell[i] += *((uint16 *)ampTable + srcBuf_ptr[i]);
+ }
+ }
+ }
+ } else if (2 * inFrameCount == feedSize) {
+ if (inFrameCount - 1 != 0) {
+ for (int i = 0, j = 0; i < inFrameCount - 1; i++, j += 2) {
+ mixBufCurCell[j] += *((uint16 *)ampTable + srcBuf_ptr[i]);
+ mixBufCurCell[j + 1] += (*((int16 *)ampTable + srcBuf_ptr[i]) + *((int16 *)ampTable + srcBuf_ptr[i + 1])) >> 1;
+ }
+ }
+ mixBufCurCell[inFrameCount] += *((uint16 *)ampTable + srcBuf_ptr[inFrameCount]);
+ mixBufCurCell[inFrameCount + 1] += *((uint16 *)ampTable + srcBuf_ptr[inFrameCount]);
+ } else if (2 * feedSize == inFrameCount) {
+ if (feedSize) {
+ for (int i = 0, j = 0; i < feedSize; i++, j += 2) {
+ mixBufCurCell[i] += *((uint16 *)ampTable + srcBuf_ptr[j]);
+ }
+ }
+ } else {
+ residualLength = -inFrameCount;
+ if (feedSize) {
+ for (int i = 0; i < feedSize; i++) {
+ mixBufCurCell[i] += *((uint16 *)ampTable + srcBuf_ptr[0]);
+ // Seek the next srcBuf element until there's excess length
+ for (residualLength += inFrameCount; residualLength >= 0; ++srcBuf_ptr)
+ residualLength -= feedSize;
+ }
+ }
+ }
+}
+
+void IMuseDigiInternalMixer::mixBits12Mono(uint8 *srcBuf, int32 inFrameCount, int feedSize, int32 mixBufStartIndex, int32 *ampTable) {
+ uint16 *mixBufCurCell;
+ uint8 *srcBuf_ptr;
+ int value;
+ int xorFlag;
+ int residualLength;
+ int term_1;
+ int term_2;
+
+ if ((inFrameCount & 1) != 0) {
+ inFrameCount &= 0xFFFFFFFE;
+ debug(5, "DiMUSE_InternalMixer::mixBits12Mono(): WARNING: odd inFrameCount with 12-bit data");
+ }
+
+ mixBufCurCell = (uint16 *)(&_mixBuf[2 * mixBufStartIndex]);
+ if (feedSize == inFrameCount) {
+ if (inFrameCount / 2) {
+ srcBuf_ptr = srcBuf;
+ for (int i = 0; i < inFrameCount / 2; i++) {
+ mixBufCurCell[0] += *((uint16 *)ampTable + (srcBuf_ptr[0] | ((srcBuf_ptr[1] & 0xF) << 8)));
+ mixBufCurCell[1] += *((uint16 *)ampTable + (srcBuf_ptr[2] | ((srcBuf_ptr[1] & 0xF0) << 4)));
+ srcBuf_ptr += 3;
+ mixBufCurCell += 2;
+ }
+ }
+ } else if (2 * inFrameCount == feedSize) {
+ srcBuf_ptr = srcBuf;
+ if ((inFrameCount / 2) - 1) {
+ for (int i = 0; i < (inFrameCount / 2) - 1; i++) {
+ value = *((uint16 *)ampTable + (srcBuf_ptr[2] | ((srcBuf_ptr[1] & 0xF0) << 4)));
+
+ mixBufCurCell[0] += *((uint16 *)ampTable + (srcBuf_ptr[0] | ((srcBuf_ptr[1] & 0xF) << 8)));
+ mixBufCurCell[1] += (value + *((uint16 *)ampTable + value)) >> 1;
+ mixBufCurCell[2] += value;
+ mixBufCurCell[3] += ((int16)value + *((int16 *)ampTable + (srcBuf_ptr[3] | ((srcBuf_ptr[4] & 0xF) << 8)))) >> 1;
+ srcBuf_ptr += 3;
+ mixBufCurCell += 4;
+ }
+ }
+ mixBufCurCell[0] += *((uint16 *)ampTable + (srcBuf_ptr[0] | ((srcBuf_ptr[1] & 0xF) << 8)));
+ mixBufCurCell[1] += (*((uint16 *)ampTable + (srcBuf_ptr[0] | ((srcBuf_ptr[1] & 0xF) << 8)))
+ + *((uint16 *)ampTable + (srcBuf_ptr[2] | ((srcBuf_ptr[1] & 0xF0) << 4)))) >> 1;
+ mixBufCurCell[2] += *((uint16 *)ampTable + (srcBuf_ptr[2] | ((srcBuf_ptr[1] & 0xF0) << 4)));
+ mixBufCurCell[3] += *((uint16 *)ampTable + (srcBuf_ptr[2] | ((srcBuf_ptr[1] & 0xF0) << 4)));
+ } else if (2 * feedSize == inFrameCount) {
+ if (feedSize) {
+ srcBuf_ptr = srcBuf;
+ for (int i = 0; i < feedSize; i++) {
+ mixBufCurCell[i] += *((uint16 *)ampTable + (srcBuf_ptr[0] | ((srcBuf_ptr[1] & 0xF) << 8)));
+ srcBuf_ptr += 3;
+ }
+ }
+ } else {
+ xorFlag = 0;
+ residualLength = -inFrameCount;
+ if (feedSize) {
+ srcBuf_ptr = srcBuf;
+ for (int i = 0; i < feedSize; i++) {
+ if (xorFlag) {
+ term_1 = (srcBuf_ptr[1] & 0xF0) << 4;
+ term_2 = srcBuf_ptr[2];
+ } else {
+ term_1 = (srcBuf_ptr[1] & 0xF) << 8;
+ term_2 = srcBuf_ptr[0];
+ }
+
+ mixBufCurCell[i] += *((uint16 *)ampTable + (term_2 | term_1));
+ residualLength += inFrameCount;
+ while (residualLength >= 0) {
+ residualLength -= feedSize;
+ xorFlag ^= 1u;
+ if (!xorFlag)
+ srcBuf_ptr += 3;
+ }
+ }
+ }
+ }
+}
+
+void IMuseDigiInternalMixer::mixBits16Mono(uint8 *srcBuf, int32 inFrameCount, int feedSize, int32 mixBufStartIndex, int32 *ampTable) {
+ uint16 *mixBufCurCell;
+ uint16 *srcBuf_ptr;
+ int residualLength;
+
+ mixBufCurCell = (uint16 *)(&_mixBuf[2 * mixBufStartIndex]);
+ if (feedSize == inFrameCount) {
+ if (feedSize) {
+ srcBuf_ptr = (uint16 *)srcBuf;
+ for (int i = 0; i < feedSize; i++) {
+ mixBufCurCell[i] += *(uint16 *)((uint8 *)ampTable + (((int16)srcBuf_ptr[i] & (int16)0xFFF7) >> 3) + 4096);
+ }
+ }
+ } else if (2 * inFrameCount == feedSize) {
+ srcBuf_ptr = (uint16 *)srcBuf;
+ int i = 0;
+ if (inFrameCount - 1 != 0) {
+ for (i = 0; i < inFrameCount - 1; i++) {
+ mixBufCurCell[0] += *(uint16 *)((uint8 *)ampTable + (((int16)srcBuf_ptr[i] & (int16)0xFFF7) >> 3) + 4096);
+ mixBufCurCell[1] += (*(int16 *)((uint8 *)ampTable + (((int16)srcBuf_ptr[i] & (int16)0xFFF7) >> 3) + 4096)
+ + *(int16 *)((uint8 *)ampTable + (((int16)srcBuf_ptr[i + 1] & (int16) 0xFFF7) >> 3) + 4096)) >> 1;
+ mixBufCurCell += 2;
+ }
+ }
+ mixBufCurCell[0] += *(uint16 *)((uint8 *)ampTable + (((int16)srcBuf_ptr[i] & (int16)0xFFF7) >> 3) + 4096);
+ mixBufCurCell[1] += *(uint16 *)((uint8 *)ampTable + (((int16)srcBuf_ptr[i] & (int16)0xFFF7) >> 3) + 4096);
+ } else {
+ if (2 * feedSize == inFrameCount) {
+ if (feedSize) {
+ srcBuf_ptr = (uint16 *)srcBuf;
+ for (int i = 0; i < feedSize; i++) {
+ mixBufCurCell[i] += *(uint16 *)((uint8 *)ampTable + (((int16)srcBuf_ptr[0] & (int16)0xFFF7) >> 3) + 4096);
+ srcBuf_ptr += 2;
+ }
+ }
+ } else {
+ residualLength = -inFrameCount;
+
+ if (feedSize) {
+ srcBuf_ptr = (uint16 *)srcBuf;
+ for (int i = 0; i < feedSize; i++) {
+ mixBufCurCell[i] += *(uint16 *)((uint8 *)ampTable + (((int16)srcBuf_ptr[0] & (int16)0xFFF7) >> 3) + 4096);
+
+ for (residualLength += inFrameCount; residualLength >= 0; ++srcBuf_ptr)
+ residualLength -= feedSize;
+ }
+ }
+ }
+ }
+}
+
+void IMuseDigiInternalMixer::mixBits8ConvertToMono(uint8 *srcBuf, int32 inFrameCount, int feedSize, int32 mixBufStartIndex, int32 *ampTable) {
+ uint8 *srcBuf_ptr;
+ int residualLength;
+ uint16 *mixBufCurCell;
+
+ mixBufCurCell = (uint16 *)(&_mixBuf[2 * mixBufStartIndex]);
+ srcBuf_ptr = srcBuf;
+ if (inFrameCount == feedSize) {
+ if (feedSize) {
+ for (int i = 0, j = 0; i < feedSize; i++, j += 2) {
+ mixBufCurCell[i] += (2 * *((int16 *)ampTable + srcBuf_ptr[j])) >> 1;
+ }
+ }
+ } else if (2 * inFrameCount == feedSize) {
+ if (inFrameCount != 1) {
+ for (int i = 0; i < inFrameCount - 1; i++) {
+ mixBufCurCell[0] += (*((int16 *)ampTable + srcBuf_ptr[1]) + *((int16 *)ampTable + srcBuf_ptr[0])) >> 1;
+ int term_1 = (*((int16 *)ampTable + srcBuf_ptr[1]) + *((int16 *)ampTable + srcBuf_ptr[3])) >> 1;
+ int term_2 = *((int16 *)ampTable + srcBuf_ptr[0]) + *((int16 *)ampTable + srcBuf_ptr[2]);
+ mixBufCurCell[1] += ((term_2 >> 1) + term_1) >> 1;
+
+ srcBuf_ptr += 2;
+ mixBufCurCell += 2;
+ }
+ }
+ mixBufCurCell[0] += (*((int16 *)ampTable + srcBuf_ptr[1]) + *((int16 *)ampTable + srcBuf_ptr[0])) >> 1;
+ mixBufCurCell[1] += (*((int16 *)ampTable + srcBuf_ptr[1]) + *((int16 *)ampTable + srcBuf_ptr[0])) >> 1;
+ } else if (2 * feedSize == inFrameCount) {
+ if (feedSize) {
+ for (int i = 0, j = 0; i < feedSize; i++, j += 4) {
+ mixBufCurCell[i] += (2 * *((int16 *)ampTable + srcBuf_ptr[j])) >> 1;
+ }
+ }
+ } else {
+ residualLength = -inFrameCount;
+ if (feedSize) {
+ for (int i = 0; i < feedSize; i++) {
+ mixBufCurCell[i] += (*((int16 *)ampTable + srcBuf_ptr[0]) + *((int16 *)ampTable + srcBuf_ptr[1])) >> 1;
+ // Skip srcBuf elements until there's excess length
+ for (residualLength += inFrameCount; residualLength >= 0; srcBuf_ptr += 2)
+ residualLength -= feedSize;
+ }
+ }
+ }
+}
+
+void IMuseDigiInternalMixer::mixBits12ConvertToMono(uint8 *srcBuf, int32 inFrameCount, int feedSize, int32 mixBufStartIndex, int32 *ampTable) {
+ uint8 *srcBuf_ptr;
+ int residualLength;
+ uint16 *mixBufCurCell;
+ int term_1;
+ int term_2;
+
+ mixBufCurCell = (uint16 *)(&_mixBuf[2 * mixBufStartIndex]);
+ if (feedSize == inFrameCount) {
+ if (feedSize) {
+ srcBuf_ptr = srcBuf;
+
+ for (int i = 0; i < feedSize; i++) {
+ mixBufCurCell[i] += (*((int16 *)ampTable + (srcBuf_ptr[0] | ((srcBuf_ptr[1] & 0xF) << 8))) +
+ *((int16 *)ampTable + (srcBuf_ptr[2] | ((srcBuf_ptr[1] & 0xF0) << 4)))) >> 1;
+ srcBuf_ptr += 3;
+ }
+ }
+ } else if (2 * inFrameCount == feedSize) {
+ srcBuf_ptr = srcBuf;
+ if (inFrameCount - 1 != 0) {
+ for (int i = 0; i < inFrameCount - 1; i++) {
+ term_1 = *((int16 *)ampTable + (srcBuf_ptr[0] | ((srcBuf_ptr[1] & 0xF0) << 8)));
+ term_2 = *((int16 *)ampTable + (srcBuf_ptr[2] | ((srcBuf_ptr[1] & 0xF) << 4)));
+
+ mixBufCurCell[0] += (term_1 + term_2) >> 1;
+ mixBufCurCell[1] += (((term_1 + *((int16 *)ampTable + (srcBuf_ptr[3] | ((srcBuf_ptr[4] & 0xF) << 8)))) >> 1)
+ + ((term_2 + *((int16 *)ampTable + (srcBuf_ptr[5] | ((srcBuf_ptr[4] & 0xF0) << 4)))) >> 1)) >> 1;
+
+ srcBuf_ptr += 3;
+ mixBufCurCell += 2;
+ }
+ }
+
+ mixBufCurCell[0] += (*((int16 *)ampTable + (srcBuf_ptr[0] | ((srcBuf_ptr[1] & 0xF) << 8)))
+ + *((int16 *)ampTable + (srcBuf_ptr[2] | ((srcBuf_ptr[1] & 0xF0) << 4)))) >> 1;
+ mixBufCurCell[1] += (*((int16 *)ampTable + (srcBuf_ptr[0] | ((srcBuf_ptr[1] & 0xF) << 8)))
+ + *((int16 *)ampTable + (srcBuf_ptr[2] | ((srcBuf_ptr[1] & 0xF0) << 4)))) >> 1;
+
+
+ } else if (2 * feedSize == inFrameCount) {
+ if (feedSize) {
+ srcBuf_ptr = srcBuf;
+
+ for (int i = 0; i < feedSize; i++) {
+
+ mixBufCurCell[i] += (*((int16 *)ampTable + (srcBuf_ptr[0] | ((srcBuf_ptr[1] & 0xF) << 8)))
+ + *((int16 *)ampTable + (srcBuf_ptr[2] | ((srcBuf_ptr[1] & 0xF0) << 4)))) >> 1;
+
+ srcBuf_ptr += 6;
+ }
+ }
+ } else {
+ residualLength = -inFrameCount;
+ if (feedSize) {
+ srcBuf_ptr = srcBuf;
+ for (int i = 0; i < feedSize; i++) {
+ mixBufCurCell[i] += (*((int16 *)ampTable + (srcBuf_ptr[0] | ((srcBuf_ptr[1] & 0xF) << 8)))
+ + *((int16 *)ampTable + (srcBuf_ptr[2] | ((srcBuf_ptr[1] & 0xF0) << 4)))) >> 1;
+
+ for (residualLength += inFrameCount; residualLength >= 0; srcBuf_ptr += 3)
+ residualLength -= feedSize;
+ }
+ }
+ }
+}
+
+void IMuseDigiInternalMixer::mixBits16ConvertToMono(uint8 *srcBuf, int32 inFrameCount, int feedSize, int32 mixBufStartIndex, int32 *ampTable) {
+ uint16 *mixBufCurCell;
+ uint16 *srcBuf_ptr;
+ int residualLength;
+
+ mixBufCurCell = (uint16 *)(&_mixBuf[2 * mixBufStartIndex]);
+ if (feedSize == inFrameCount) {
+ if (feedSize) {
+ srcBuf_ptr = (uint16 *)srcBuf;
+ for (int i = 0; i < feedSize; i++) {
+ mixBufCurCell[i] += (*(int16 *)((uint8 *)ampTable + (((int16)srcBuf_ptr[0] & (int16)0xFFF7) >> 3) + 4096)
+ + *(int16 *)((uint8 *)ampTable + (((int16)srcBuf_ptr[1] & (int16)0xFFF7) >> 3) + 4096)) >> 1;
+ srcBuf_ptr += 2;
+ }
+ }
+ } else if (2 * inFrameCount == feedSize) {
+ srcBuf_ptr = (uint16 *)srcBuf;
+ if (inFrameCount - 1 != 0) {
+ for (int i = 0; i < inFrameCount - 1; i++) {
+ mixBufCurCell[0] += (*(int16 *)((uint8 *)ampTable + (((int16)srcBuf_ptr[1] & (int16)0xFFF7) >> 3) + 4096)
+ + *(int16 *)((uint8 *)ampTable + (((int16)srcBuf_ptr[0] & (int16)0xFFF7) >> 3) + 4096)) >> 1;
+
+ mixBufCurCell[1] += (((*(int16 *)((uint8 *)ampTable + (((int16)srcBuf_ptr[0] & (int16)0xFFF7) >> 3) + 4096)
+ + *(int16 *)((uint8 *)ampTable + (((int16)srcBuf_ptr[2] & (int16)0xFFF7) >> 3) + 4096)) >> 1)
+ + ((*(int16 *)((uint8 *)ampTable + (((int16)srcBuf_ptr[1] & (int16)0xFFF7) >> 3) + 4096)
+ + *(int16 *)((uint8 *)ampTable + (((int16)srcBuf_ptr[3] & (int16)0xFFF7) >> 3) + 4096)) >> 1)) >> 1;
+ mixBufCurCell += 2;
+ srcBuf_ptr += 2;
+ }
+ }
+
+ mixBufCurCell[0] += (*(int16 *)((uint8 *)ampTable + (((int16)srcBuf_ptr[1] & (int16)0xFFF7) >> 3) + 4096)
+ + *(int16 *)((uint8 *)ampTable + (((int16)srcBuf_ptr[0] & (int16)0xFFF7) >> 3) + 4096)) >> 1;
+ mixBufCurCell[1] += (*(int16 *)((uint8 *)ampTable + (((int16)srcBuf_ptr[1] & (int16)0xFFF7) >> 3) + 4096)
+ + *(int16 *)((uint8 *)ampTable + (((int16)srcBuf_ptr[0] & (int16)0xFFF7) >> 3) + 4096)) >> 1;
+ } else if (2 * feedSize == inFrameCount) {
+ if (feedSize) {
+ srcBuf_ptr = (uint16 *)srcBuf;
+ for (int i = 0; i < feedSize; i++) {
+ mixBufCurCell[i] += (*(int16 *)((uint8 *)ampTable + (((int16)srcBuf_ptr[0] & (int16)0xFFF7) >> 3) + 4096)
+ + *(int16 *)((uint8 *)ampTable + (((int16)srcBuf_ptr[1] & (int16)0xFFF7) >> 3) + 4096)) >> 1;
+ srcBuf_ptr += 4;
+ }
+ }
+ } else {
+ residualLength = -inFrameCount;
+
+ if (feedSize) {
+ srcBuf_ptr = (uint16 *)srcBuf;
+ for (int i = 0; i < feedSize; i++) {
+ mixBufCurCell[i] += (*(int16 *)((uint8 *)ampTable + (((int16)srcBuf_ptr[0] & (int16)0xFFF7) >> 3) + 4096)
+ + *(int16 *)((uint8 *)ampTable + (((int16)srcBuf_ptr[1] & (int16)0xFFF7) >> 3) + 4096)) >> 1;
+
+ for (residualLength += inFrameCount; residualLength >= 0; srcBuf_ptr += 2)
+ residualLength -= feedSize;
+ }
+ }
+ }
+}
+
+void IMuseDigiInternalMixer::mixBits8ConvertToStereo(uint8 *srcBuf, int32 inFrameCount, int feedSize, int32 mixBufStartIndex, int32 *leftAmpTable, int32 *rightAmpTable) {
+ uint16 *mixBufCurCell;
+ uint8 *srcBuf_ptr;
+ uint8 *ptr;
+ int value;
+ int residualLength;
+
+ mixBufCurCell = (uint16 *)(&_mixBuf[2 * mixBufStartIndex]);
+ if (feedSize == inFrameCount) {
+ if (_radioChatter) {
+ srcBuf_ptr = srcBuf;
+ ptr = srcBuf + 4;
+ value = srcBuf[0] - 128 + srcBuf[1] - 128 + srcBuf[2] - 128 + srcBuf[3] - 128;
+ if (feedSize) {
+ for (int i = 0; i < feedSize; i++) {
+ mixBufCurCell[0] += 4 * *((uint16 *)leftAmpTable + (srcBuf_ptr[i] - (value >> 2)));
+ mixBufCurCell[1] += 4 * *((uint16 *)rightAmpTable + (srcBuf_ptr[i] - (value >> 2)));
+ value += ptr[i] - srcBuf_ptr[i];
+ mixBufCurCell += 2;
+ }
+ }
+ } else {
+ if (feedSize) {
+ srcBuf_ptr = srcBuf;
+ for (int i = 0; i < feedSize; i++) {
+ mixBufCurCell[0] += *((uint16 *)leftAmpTable + srcBuf_ptr[i]);
+ mixBufCurCell[1] += *((uint16 *)rightAmpTable + srcBuf_ptr[i]);
+ mixBufCurCell += 2;
+ }
+ }
+ }
+ } else if (2 * inFrameCount == feedSize) {
+ srcBuf_ptr = srcBuf;
+ int i = 0;
+ if (inFrameCount - 1 != 0) {
+ for (i = 0; i < inFrameCount - 1; i++) {
+ mixBufCurCell[0] += *((uint16 *)leftAmpTable + srcBuf_ptr[i]);
+ mixBufCurCell[1] += *((uint16 *)rightAmpTable + srcBuf_ptr[i]);
+ mixBufCurCell[2] += (*((int16 *)leftAmpTable + srcBuf_ptr[i]) + *((int16 *)leftAmpTable + srcBuf_ptr[i + 1])) >> 1;
+ mixBufCurCell[3] += (*((int16 *)rightAmpTable + srcBuf_ptr[i]) + *((int16 *)rightAmpTable + srcBuf_ptr[i + 1])) >> 1;
+ mixBufCurCell += 4;
+ }
+ }
+ mixBufCurCell[0] += *((uint16 *)leftAmpTable + srcBuf_ptr[i]);
+ mixBufCurCell[1] += *((uint16 *)rightAmpTable + srcBuf_ptr[i]);
+ mixBufCurCell[2] += *((uint16 *)leftAmpTable + srcBuf_ptr[i]);
+ mixBufCurCell[3] += *((uint16 *)rightAmpTable + srcBuf_ptr[i]);
+ } else if (2 * feedSize == inFrameCount) {
+ if (feedSize) {
+ srcBuf_ptr = srcBuf;
+ for (int i = 0; i < feedSize; i++) {
+ mixBufCurCell[0] += *((uint16 *)leftAmpTable + srcBuf_ptr[0]);
+ mixBufCurCell[1] += *((uint16 *)rightAmpTable + srcBuf_ptr[0]);
+ mixBufCurCell += 2;
+ srcBuf_ptr += 2;
+ }
+ }
+ } else {
+ residualLength = -inFrameCount;
+ if (feedSize) {
+ srcBuf_ptr = srcBuf;
+ for (int i = 0; i < feedSize; i++) {
+ mixBufCurCell[0] += *((uint16 *)leftAmpTable + srcBuf_ptr[0]);
+ mixBufCurCell[1] += *((uint16 *)rightAmpTable + srcBuf_ptr[0]);
+ mixBufCurCell += 2;
+
+ for (residualLength += inFrameCount; residualLength > 0; ++srcBuf_ptr)
+ residualLength -= feedSize;
+ }
+ }
+ }
+}
+
+void IMuseDigiInternalMixer::mixBits12ConvertToStereo(uint8 *srcBuf, int32 inFrameCount, int feedSize, int32 mixBufStartIndex, int32 *leftAmpTable, int32 *rightAmpTable) {
+ uint16 *mixBufCurCell;
+ uint8 *srcBuf_ptr;
+
+ int xorFlag;
+ int residualLength;
+
+ int term_1;
+ int term_2;
+ int term_3;
+ int term_4;
+
+ mixBufCurCell = (uint16 *)(&_mixBuf[4 * mixBufStartIndex]);
+ if (feedSize == inFrameCount) {
+ if (inFrameCount / 2) {
+ srcBuf_ptr = srcBuf;
+ for (int i = 0; i < (inFrameCount / 2); i++) {
+ mixBufCurCell[0] += *((uint16 *)leftAmpTable + (srcBuf_ptr[0] | ((srcBuf_ptr[1] & 0xF) << 8)));
+ mixBufCurCell[1] += *((uint16 *)rightAmpTable + (srcBuf_ptr[0] | ((srcBuf_ptr[1] & 0xF) << 8)));
+ mixBufCurCell[2] += *((uint16 *)leftAmpTable + (srcBuf_ptr[2] | ((srcBuf_ptr[1] & 0xF0) << 4)));
+ mixBufCurCell[3] += *((uint16 *)rightAmpTable + (srcBuf_ptr[2] | ((srcBuf_ptr[1] & 0xF0) << 4)));
+ srcBuf_ptr += 3;
+ mixBufCurCell += 4;
+ }
+ }
+ } else if (2 * inFrameCount == feedSize) {
+ srcBuf_ptr = srcBuf;
+ if ((inFrameCount / 2) - 1 != 0) {
+ for (int i = 0; i < (inFrameCount / 2) - 1; i++) {
+ mixBufCurCell[0] += *((uint16 *)leftAmpTable + (srcBuf_ptr[0] | ((srcBuf_ptr[1] & 0xF) << 8)));
+ mixBufCurCell[1] += *((uint16 *)rightAmpTable + (srcBuf_ptr[0] | ((srcBuf_ptr[1] & 0xF) << 8)));
+
+ mixBufCurCell[2] += (*((int16 *)leftAmpTable + (srcBuf_ptr[2] | ((srcBuf_ptr[1] & 0xF0) << 4)))
+ + *((int16 *)leftAmpTable + (srcBuf_ptr[0] | ((srcBuf_ptr[1] & 0xF) << 8)))) >> 1;
+
+ mixBufCurCell[3] += (*((int16 *)rightAmpTable + (srcBuf_ptr[2] | ((srcBuf_ptr[1] & 0xF0) << 4)))
+ + *((int16 *)rightAmpTable + (srcBuf_ptr[0] | ((srcBuf_ptr[1] & 0xF) << 8)))) >> 1;
+
+ mixBufCurCell[4] += *((uint16 *)leftAmpTable + (srcBuf_ptr[2] | ((srcBuf_ptr[1] & 0xF0) << 4)));
+ mixBufCurCell[5] += *((uint16 *)rightAmpTable + (srcBuf_ptr[2] | ((srcBuf_ptr[1] & 0xF0) << 4)));
+
+ mixBufCurCell[6] += (*((int16 *)leftAmpTable + (srcBuf_ptr[2] | ((srcBuf_ptr[1] & 0xF0) << 4))) +
+ *((int16 *)leftAmpTable + (srcBuf_ptr[3] | ((srcBuf_ptr[4] & 0xF) << 8)))) >> 1;
+
+ mixBufCurCell[7] += (*((int16 *)rightAmpTable + (srcBuf_ptr[2] | ((srcBuf_ptr[1] & 0xF0) << 4))) +
+ *((int16 *)rightAmpTable + (srcBuf_ptr[3] | ((srcBuf_ptr[4] & 0xF) << 8)))) >> 1;
+
+ srcBuf_ptr += 3;
+ mixBufCurCell += 8;
+ }
+ }
+ mixBufCurCell[0] += *((uint16 *)leftAmpTable + (srcBuf_ptr[0] | ((srcBuf_ptr[1] & 0xF) << 8)));
+ mixBufCurCell[1] += *((uint16 *)rightAmpTable + (srcBuf_ptr[0] | ((srcBuf_ptr[1] & 0xF) << 8)));
+
+ mixBufCurCell[2] += (*((int16 *)leftAmpTable + (srcBuf_ptr[0] | ((srcBuf_ptr[1] & 0xF) << 8)))
+ + *((int16 *)leftAmpTable + (srcBuf_ptr[2] | ((srcBuf_ptr[1] & 0xF0) << 4)))) >> 1;
+
+ mixBufCurCell[3] += (*((int16 *)rightAmpTable + (srcBuf_ptr[0] | ((srcBuf_ptr[1] & 0xF) << 8)))
+ + *((int16 *)rightAmpTable + (srcBuf_ptr[2] | ((srcBuf_ptr[1] & 0xF0) << 4)))) >> 1;
+
+ mixBufCurCell[4] += *((uint16 *)leftAmpTable + (srcBuf_ptr[2] | ((srcBuf_ptr[1] & 0xF0) << 4)));
+ mixBufCurCell[5] += *((uint16 *)rightAmpTable + (srcBuf_ptr[2] | ((srcBuf_ptr[1] & 0xF0) << 4)));
+ mixBufCurCell[6] += *((uint16 *)leftAmpTable + (srcBuf_ptr[2] | ((srcBuf_ptr[1] & 0xF0) << 4)));
+ mixBufCurCell[7] += *((uint16 *)rightAmpTable + (srcBuf_ptr[2] | ((srcBuf_ptr[1] & 0xF0) << 4)));
+ } else if (2 * feedSize == inFrameCount) {
+ if (feedSize) {
+ srcBuf_ptr = srcBuf;
+ for (int i = 0; i < feedSize; i++) {
+ mixBufCurCell[0] += *((uint16 *)leftAmpTable + (srcBuf_ptr[0] | ((srcBuf_ptr[1] & 0xF) << 8)));
+ mixBufCurCell[1] += *((uint16 *)rightAmpTable + (srcBuf_ptr[0] | ((srcBuf_ptr[1] & 0xF) << 8)));
+ srcBuf_ptr += 3;
+ mixBufCurCell += 2;
+ }
+ }
+ } else {
+ xorFlag = 0;
+ residualLength = -inFrameCount;
+ if (feedSize) {
+ srcBuf_ptr = srcBuf;
+ for (int i = 0; i < feedSize; i++) {
+ if (xorFlag) {
+ term_2 = (srcBuf_ptr[1] & 0xF0) << 4;
+ term_1 = srcBuf_ptr[2];
+ } else {
+ term_2 = (srcBuf_ptr[1] & 0xF) << 8;
+ term_1 = srcBuf_ptr[0];
+ }
+ mixBufCurCell[0] += *((uint16 *)leftAmpTable + (term_1 | term_2));
+
+ if (xorFlag) {
+ term_4 = (srcBuf_ptr[1] & 0xF0) << 4;
+ term_3 = srcBuf_ptr[2];
+ } else {
+ term_4 = (srcBuf_ptr[1] & 0xF) << 8;
+ term_3 = srcBuf_ptr[0];
+ }
+ mixBufCurCell[1] += *((uint16 *)rightAmpTable + (term_3 | term_4));
+
+ residualLength += inFrameCount;
+ while (residualLength > 0) {
+ residualLength -= feedSize;
+ xorFlag ^= 1u;
+ if (!xorFlag)
+ srcBuf_ptr += 3;
+ }
+
+ mixBufCurCell += 2;
+ }
+ }
+ }
+}
+
+void IMuseDigiInternalMixer::mixBits16ConvertToStereo(uint8 *srcBuf, int32 inFrameCount, int feedSize, int32 mixBufStartIndex, int32 *leftAmpTable, int32 *rightAmpTable) {
+ uint16* mixBufCurCell;
+ uint16 *srcBuf_tmp;
+ int residualLength;
+
+ mixBufCurCell = (uint16*)(&_mixBuf[2 * mixBufStartIndex]);
+
+ if (feedSize == inFrameCount) {
+ if (feedSize) {
+ srcBuf_tmp = (uint16 *)srcBuf;
+ for (int i = 0; i < feedSize; i++) {
+ mixBufCurCell[0] += *(uint16 *)((uint8 *)leftAmpTable + (((int16)srcBuf_tmp[i] & (int16)0xFFF7) >> 3) + 4096);
+ mixBufCurCell[1] += *(uint16 *)((uint8 *)rightAmpTable + (((int16)srcBuf_tmp[i] & (int16)0xFFF7) >> 3) + 4096);
+ mixBufCurCell += 2;
+ }
+ }
+ } else if (2 * inFrameCount == feedSize) {
+ srcBuf_tmp = (uint16 *)srcBuf;
+ int i = 0;
+ if (inFrameCount - 1 != 0) {
+ for (i = 0; i < inFrameCount - 1; i++) {
+ mixBufCurCell[0] += *(uint16 *)((uint8 *)leftAmpTable + (((int16)srcBuf_tmp[i] & (int16)0xFFF7) >> 3) + 4096);
+ mixBufCurCell[1] += *(uint16 *)((uint8 *)rightAmpTable + (((int16)srcBuf_tmp[i] & (int16)0xFFF7) >> 3) + 4096);
+
+ mixBufCurCell[2] += (*(int16 *)((uint8 *)leftAmpTable + (((int16)srcBuf_tmp[i] & (int16)0xFFF7) >> 3) + 4096)
+ + *(int16 *)((uint8 *)leftAmpTable + (((int16)srcBuf_tmp[i + 1] & (int16)0xFFF7) >> 3) + 4096)) >> 1;
+
+ mixBufCurCell[3] += (*(int16 *)((uint8 *)rightAmpTable + (((int16)srcBuf_tmp[i] & (int16)0xFFF7) >> 3) + 4096)
+ + *(int16 *)((uint8 *)rightAmpTable + (((int16)srcBuf_tmp[i + 1] & (int16)0xFFF7) >> 3) + 4096)) >> 1;
+ mixBufCurCell += 4;
+ }
+ }
+ mixBufCurCell[0] += *(uint16 *)((uint8 *)leftAmpTable + (((int16)srcBuf_tmp[i] & (int16)0xFFF7) >> 3) + 4096);
+ mixBufCurCell[1] += *(uint16 *)((uint8 *)rightAmpTable + (((int16)srcBuf_tmp[i] & (int16)0xFFF7) >> 3) + 4096);
+ mixBufCurCell[2] += *(uint16 *)((uint8 *)leftAmpTable + (((int16)srcBuf_tmp[i] & (int16)0xFFF7) >> 3) + 4096);
+ mixBufCurCell[3] += *(uint16 *)((uint8 *)rightAmpTable + (((int16)srcBuf_tmp[i] & (int16)0xFFF7) >> 3) + 4096);
+ } else if (2 * feedSize == inFrameCount) {
+ if (feedSize) {
+ srcBuf_tmp = (uint16 *)srcBuf;
+ for (int i = 0; i < feedSize; i++) {
+ mixBufCurCell[0] += *(uint16 *)((uint8 *)leftAmpTable + (((int16)srcBuf_tmp[0] & (int16)0xFFF7) >> 3) + 4096);
+ mixBufCurCell[1] += *(uint16 *)((uint8 *)rightAmpTable + (((int16)srcBuf_tmp[0] & (int16)0xFFF7) >> 3) + 4096);
+ srcBuf_tmp += 2;
+ mixBufCurCell += 2;
+ }
+ }
+ } else {
+ residualLength = -inFrameCount;
+ if (feedSize) {
+ srcBuf_tmp = (uint16 *)srcBuf;
+ for (int i = 0; i < feedSize; i++) {
+ mixBufCurCell[0] += *(uint16 *)((uint8 *)leftAmpTable + (((int16)srcBuf_tmp[0] & (int16)0xFFF7) >> 3) + 4096);
+ mixBufCurCell[1] += *(uint16 *)((uint8 *)rightAmpTable + (((int16)srcBuf_tmp[0] & (int16)0xFFF7) >> 3) + 4096);
+
+ for (residualLength += inFrameCount; residualLength > 0; ++srcBuf_tmp)
+ residualLength -= feedSize;
+
+ mixBufCurCell += 2;
+ }
+ }
+ }
+}
+
+void IMuseDigiInternalMixer::mixBits8Stereo(uint8 *srcBuf, int32 inFrameCount, int feedSize, int32 mixBufStartIndex, int32 *ampTable) {
+ uint16 *mixBufCurCell;
+ uint8 *srcBuf_ptr;
+ int residualLength;
+
+ mixBufCurCell = (uint16*)(&_mixBuf[4 * mixBufStartIndex]);
+ if (feedSize == inFrameCount) {
+ if (feedSize) {
+ srcBuf_ptr = srcBuf;
+ for (int i = 0; i < feedSize; i++) {
+ mixBufCurCell[0] += *((uint16 *)ampTable + srcBuf_ptr[0]);
+ mixBufCurCell[1] += *((uint16 *)ampTable + srcBuf_ptr[1]);
+ srcBuf_ptr += 2;
+ mixBufCurCell += 2;
+ }
+ }
+ } else if (2 * inFrameCount == feedSize) {
+ srcBuf_ptr = srcBuf;
+ if (inFrameCount - 1 != 0) {
+ for (int i = 0; i < inFrameCount - 1; i++) {
+ mixBufCurCell[0] += *((uint16 *)ampTable + srcBuf_ptr[0]);
+ mixBufCurCell[1] += *((uint16 *)ampTable + srcBuf_ptr[1]);
+ mixBufCurCell[2] += (*((int16 *)ampTable + srcBuf_ptr[0]) + *((int16 *)ampTable + srcBuf_ptr[2])) >> 1;
+ mixBufCurCell[3] += (*((int16 *)ampTable + srcBuf_ptr[1]) + *((int16 *)ampTable + srcBuf_ptr[3])) >> 1;
+ mixBufCurCell += 4;
+ srcBuf_ptr += 2;
+ }
+ }
+ mixBufCurCell[0] += *((uint16 *)ampTable + srcBuf_ptr[0]);
+ mixBufCurCell[1] += *((uint16 *)ampTable + srcBuf_ptr[1]);
+ mixBufCurCell[2] += *((uint16 *)ampTable + srcBuf_ptr[0]);
+ mixBufCurCell[3] += *((uint16 *)ampTable + srcBuf_ptr[1]);
+ } else if (2 * feedSize == inFrameCount) {
+ if (feedSize) {
+ srcBuf_ptr = srcBuf;
+ for (int i = 0; i < feedSize; i++) {
+ mixBufCurCell[0] += *((uint16 *)ampTable + srcBuf_ptr[0]);
+ mixBufCurCell[1] += *((uint16 *)ampTable + srcBuf_ptr[1]);
+ srcBuf_ptr += 4;
+ mixBufCurCell += 2;
+ }
+ }
+ } else {
+ residualLength = -inFrameCount;
+ if (feedSize) {
+ srcBuf_ptr = srcBuf;
+ for (int i = 0; i < feedSize; i++) {
+ mixBufCurCell[0] += *((uint16 *)ampTable + srcBuf_ptr[0]);
+ mixBufCurCell[1] += *((uint16 *)ampTable + srcBuf_ptr[1]);
+ mixBufCurCell += 2;
+ for (residualLength += inFrameCount; residualLength >= 0; srcBuf_ptr += 2)
+ residualLength -= feedSize;
+ }
+ }
+ }
+}
+
+void IMuseDigiInternalMixer::mixBits12Stereo(uint8 *srcBuf, int32 inFrameCount, int feedSize, int32 mixBufStartIndex, int32 *ampTable) {
+ uint16 *mixBufCurCell;
+ uint8 *srcBuf_ptr;
+ int residualLength;
+
+ mixBufCurCell = (uint16 *)(&_mixBuf[4 * mixBufStartIndex]);
+ if (feedSize == inFrameCount) {
+ if (feedSize) {
+ srcBuf_ptr = srcBuf;
+
+ for (int i = 0; i < feedSize; i++) {
+ mixBufCurCell[0] += *((uint16 *)ampTable + (srcBuf_ptr[0] | ((srcBuf_ptr[1] & 0xF) << 8)));
+ mixBufCurCell[1] += *((uint16 *)ampTable + (srcBuf_ptr[2] | ((srcBuf_ptr[1] & 0xF0) << 4)));
+
+ srcBuf_ptr += 3;
+ mixBufCurCell += 2;
+ }
+ }
+ } else if (2 * inFrameCount == feedSize) {
+ srcBuf_ptr = srcBuf;
+ if (inFrameCount - 1 != 0) {
+ for (int i = 0; i < inFrameCount - 1; i++) {
+ mixBufCurCell[0] += *((uint16 *)ampTable + (srcBuf_ptr[0] | ((srcBuf_ptr[1] & 0xF) << 8)));
+ mixBufCurCell[1] += *((uint16 *)ampTable + (srcBuf_ptr[2] | ((srcBuf_ptr[1] & 0xF0) << 4)));
+ mixBufCurCell[2] += (*((int16 *)ampTable + (srcBuf_ptr[0] | ((srcBuf_ptr[1] & 0xF) << 8)))
+ + *((int16 *)ampTable + (srcBuf_ptr[3] | ((srcBuf_ptr[4] & 0xF) << 8)))) >> 1;
+ mixBufCurCell[3] += (*((int16 *)ampTable + (srcBuf_ptr[2] | ((srcBuf_ptr[1] & 0xF0) << 4)))
+ + *((int16 *)ampTable + (srcBuf_ptr[5] | ((srcBuf_ptr[4] & 0xF0) << 4)))) >> 1;
+
+ srcBuf_ptr += 3;
+ mixBufCurCell += 4;
+ }
+ }
+ mixBufCurCell[0] += *((uint16 *)ampTable + (srcBuf_ptr[0] | ((srcBuf_ptr[1] & 0xF) << 8)));
+ mixBufCurCell[1] += *((uint16 *)ampTable + (srcBuf_ptr[2] | ((srcBuf_ptr[1] & 0xF0) << 4)));
+ mixBufCurCell[2] += *((uint16 *)ampTable + (srcBuf_ptr[0] | ((srcBuf_ptr[1] & 0xF) << 8)));
+ mixBufCurCell[3] += *((uint16 *)ampTable + (srcBuf_ptr[2] | ((srcBuf_ptr[1] & 0xF0) << 4)));
+ } else if (2 * feedSize == inFrameCount) {
+ if (feedSize) {
+ srcBuf_ptr = srcBuf;
+
+ for (int i = 0; i < feedSize; i++) {
+ mixBufCurCell[0] += *((uint16 *)ampTable + (srcBuf_ptr[0] | ((srcBuf_ptr[1] & 0xF) << 8)));
+ mixBufCurCell[1] += *((uint16 *)ampTable + (srcBuf_ptr[2] | ((srcBuf_ptr[1] & 0xF0) << 4)));
+
+ srcBuf_ptr += 6;
+ mixBufCurCell += 2;
+ }
+ }
+ } else {
+ residualLength = -inFrameCount;
+ if (feedSize) {
+ srcBuf_ptr = srcBuf;
+
+ for (int i = 0; i < feedSize; i++) {
+ mixBufCurCell[0] += *((uint16 *)ampTable + (srcBuf_ptr[0] | ((srcBuf_ptr[1] & 0xF) << 8)));
+ mixBufCurCell[1] += *((uint16 *)ampTable + (srcBuf_ptr[2] | ((srcBuf_ptr[1] & 0xF0) << 4)));
+
+ mixBufCurCell += 2;
+ for (residualLength += inFrameCount; residualLength >= 0; srcBuf_ptr += 3)
+ residualLength -= feedSize;
+ }
+ }
+ }
+}
+
+void IMuseDigiInternalMixer::mixBits16Stereo(uint8 *srcBuf, int32 inFrameCount, int feedSize, int32 mixBufStartIndex, int32 *ampTable) {
+ uint16 *mixBufCurCell;
+ uint16 *srcBuf_ptr;
+ int residualLength;
+
+ mixBufCurCell = (uint16*)(&_mixBuf[4 * mixBufStartIndex]);
+ if (feedSize == inFrameCount) {
+ if (feedSize) {
+ srcBuf_ptr = (uint16 *)srcBuf;
+
+ for (int i = 0; i < feedSize; i++) {
+ mixBufCurCell[0] += *(uint16 *)((int8 *)ampTable + (((int16)srcBuf_ptr[0] & (int16)0xFFF7) >> 3) + 4096);
+ mixBufCurCell[1] += *(uint16 *)((int8 *)ampTable + (((int16)srcBuf_ptr[1] & (int16)0xFFF7) >> 3) + 4096);
+ srcBuf_ptr += 2;
+ mixBufCurCell += 2;
+ }
+ }
+ } else if (2 * inFrameCount == feedSize) {
+ srcBuf_ptr = (uint16 *)srcBuf;
+
+ if (inFrameCount - 1 != 0) {
+ for (int i = 0; i < inFrameCount - 1; i++) {
+ mixBufCurCell[0] += *(uint16 *)((int8 *)ampTable + (((int16)srcBuf_ptr[0] & (int16)0xFFF7) >> 3) + 4096);
+ mixBufCurCell[1] += *(uint16 *)((int8 *)ampTable + (((int16)srcBuf_ptr[1] & (int16)0xFFF7) >> 3) + 4096);
+
+ mixBufCurCell[2] += (*(int16 *)((int8 *)ampTable + (((int16)srcBuf_ptr[0] & (int16)0xFFF7) >> 3) + 4096)
+ + *(int16 *)((int8 *)ampTable + (((int16)srcBuf_ptr[2] & (int16)0xFFF7) >> 3) + 4096)) >> 1;
+
+ mixBufCurCell[4] += (*(int16 *)((int8 *)ampTable + (((int16)(((uint8 *)srcBuf_ptr)[2] | (((uint8 *)srcBuf_ptr)[3] << 8)) & (int16)0xFFF7) >> 3) + 4096)
+ + *(int16 *)((int8 *)ampTable + (((int16)srcBuf_ptr[3] & (int16)0xFFF7) >> 3) + 4096)) >> 1;
+ mixBufCurCell += 4;
+ srcBuf_ptr += 2;
+ }
+ }
+ mixBufCurCell[0] += *(int16 *)((int8 *)ampTable + (((int16)srcBuf_ptr[0] & (int16)0xFFF7) >> 3) + 4096);
+ mixBufCurCell[1] += *(int16 *)((int8 *)ampTable + (((int16)srcBuf_ptr[1] & (int16)0xFFF7) >> 3) + 4096);
+ mixBufCurCell[2] += *(int16 *)((int8 *)ampTable + (((int16)srcBuf_ptr[0] & (int16)0xFFF7) >> 3) + 4096);
+ mixBufCurCell[3] += *(int16 *)((int8 *)ampTable + (((int16)srcBuf_ptr[1] & (int16)0xFFF7) >> 3) + 4096);
+ } else {
+ if (2 * feedSize == inFrameCount) {
+ if (feedSize) {
+ srcBuf_ptr = (uint16 *)srcBuf;
+ for (int i = 0; i < feedSize; i++) {
+ mixBufCurCell[0] += *(uint16 *)((int8 *)ampTable + ((((int16)srcBuf_ptr[0] & (int16)0xFFF7)) >> 3) + 4096);
+ mixBufCurCell[1] += *(uint16 *)((int8 *)ampTable + ((((int16)srcBuf_ptr[1] & (int16)0xFFF7)) >> 3) + 4096);
+ mixBufCurCell += 2;
+ srcBuf_ptr += 4;
+ }
+ }
+ } else {
+ residualLength = -inFrameCount;
+ if (feedSize) {
+ srcBuf_ptr = (uint16 *)srcBuf;
+ for (int i = 0; i < feedSize; i++) {
+ mixBufCurCell[0] += *(uint16 *)((int8 *)ampTable + (((int16)srcBuf_ptr[0] & (int16)0xFFF7) >> 3) + 4096);
+ mixBufCurCell[1] += *(uint16 *)((int8 *)ampTable + (((int16)srcBuf_ptr[1] & (int16)0xFFF7) >> 3) + 4096);
+
+ for (residualLength += inFrameCount; residualLength >= 0; srcBuf_ptr += 2)
+ residualLength -= feedSize;
+ }
+ }
+ }
+ }
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/imuse_digi/dimuse_internalmixer.h b/engines/scumm/imuse_digi/dimuse_internalmixer.h
new file mode 100644
index 0000000000..a4e94e49d2
--- /dev/null
+++ b/engines/scumm/imuse_digi/dimuse_internalmixer.h
@@ -0,0 +1,95 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#if !defined(SCUMM_IMUSE_DIGI_V2_MIXER_H) && defined(ENABLE_SCUMM_7_8)
+#define SCUMM_IMUSE_DIGI_V2_MIXER_H
+
+#include "common/scummsys.h"
+#include "common/mutex.h"
+#include "common/serializer.h"
+#include "common/textconsole.h"
+#include "common/util.h"
+
+#include "scumm/imuse_digi/dimuse_engine.h"
+#include "scumm/music.h"
+#include "scumm/sound.h"
+#include "audio/mixer.h"
+#include "audio/audiostream.h"
+
+namespace Audio {
+class AudioStream;
+class Mixer;
+class QueuingAudioStream;
+}
+
+namespace Scumm {
+
+class IMuseDigiInternalMixer {
+
+private:
+ int32 *_amp8Table;
+ int32 *_amp12Table;
+ int32 *_softLMID;
+ int32 *_softLTable;
+
+ uint8 *_mixBuf;
+
+ Audio::Mixer *_mixer;
+ Audio::SoundHandle _channelHandle;
+ int _mixBufSize;
+ int _radioChatter;
+ int _outWordSize;
+ int _outChannelCount;
+ int _stereoReverseFlag;
+
+ void mixBits8Mono(uint8 *srcBuf, int32 inFrameCount, int feedSize, int32 mixBufStartIndex, int32 *ampTable);
+ void mixBits12Mono(uint8 *srcBuf, int32 inFrameCount, int feedSize, int32 mixBufStartIndex, int32 *ampTable);
+ void mixBits16Mono(uint8 *srcBuf, int32 inFrameCount, int feedSize, int32 mixBufStartIndex, int32 *ampTable);
+
+ void mixBits8ConvertToMono(uint8 *srcBuf, int32 inFrameCount, int feedSize, int32 mixBufStartIndex, int32 *ampTable);
+ void mixBits12ConvertToMono(uint8 *srcBuf, int32 inFrameCount, int feedSize, int32 mixBufStartIndex, int32 *ampTable);
+ void mixBits16ConvertToMono(uint8 *srcBuf, int32 inFrameCount, int feedSize, int32 mixBufStartIndex, int32 *ampTable);
+
+ void mixBits8ConvertToStereo(uint8 *srcBuf, int32 inFrameCount, int feedSize, int32 mixBufStartIndex, int32 *leftAmpTable, int32 *rightAmpTable);
+ void mixBits12ConvertToStereo(uint8 *srcBuf, int32 inFrameCount, int feedSize, int32 mixBufStartIndex, int32 *leftAmpTable, int32 *rightAmpTable);
+ void mixBits16ConvertToStereo(uint8 *srcBuf, int32 inFrameCount, int feedSize, int32 mixBufStartIndex, int32 *leftAmpTable, int32 *rightAmpTable);
+
+ void mixBits8Stereo(uint8 *srcBuf, int32 inFrameCount, int feedSize, int32 mixBufStartIndex, int32 *ampTable);
+ void mixBits12Stereo(uint8 *srcBuf, int32 inFrameCount, int feedSize, int32 mixBufStartIndex, int32 *ampTable);
+ void mixBits16Stereo(uint8 *srcBuf, int32 inFrameCount, int feedSize, int32 mixBufStartIndex, int32 *ampTable);
+
+public:
+ IMuseDigiInternalMixer(Audio::Mixer *mixer);
+ ~IMuseDigiInternalMixer();
+ int init(int bytesPerSample, int numChannels, uint8 *mixBuf, int mixBufSize, int sizeSampleKB, int mixChannelsNum);
+ void setRadioChatter();
+ void clearRadioChatter();
+ int clearMixerBuffer();
+
+ void mix(uint8 *srcBuf, int32 inFrameCount, int wordSize, int channelCount, int feedSize, int32 mixBufStartIndex, int volume, int pan);
+ int loop(uint8 **destBuffer, int len);
+ Audio::QueuingAudioStream *_stream;
+};
+
+} // End of namespace Scumm
+
+#endif
diff --git a/engines/scumm/imuse_digi/dimuse_music.cpp b/engines/scumm/imuse_digi/dimuse_music.cpp
deleted file mode 100644
index 1984c9b6f7..0000000000
--- a/engines/scumm/imuse_digi/dimuse_music.cpp
+++ /dev/null
@@ -1,542 +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 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-
-#include "common/scummsys.h"
-#include "scumm/scumm.h"
-#include "scumm/imuse_digi/dimuse.h"
-#include "scumm/imuse_digi/dimuse_tables.h"
-
-namespace Scumm {
-
-#define DIG_STATE_OFFSET 11
-#define DIG_SEQ_OFFSET (DIG_STATE_OFFSET + 65)
-#define COMI_STATE_OFFSET 3
-
-void IMuseDigital::setDigMusicState(int stateId) {
- int l, num = -1;
-
- for (l = 0; _digStateMusicTable[l].soundId != -1; l++) {
- if ((_digStateMusicTable[l].soundId == stateId)) {
- debug(5, "Set music state: %s, %s", _digStateMusicTable[l].name, _digStateMusicTable[l].filename);
- num = l;
- break;
- }
- }
-
- if (num == -1) {
- for (l = 0; _digStateMusicMap[l].roomId != -1; l++) {
- if ((_digStateMusicMap[l].roomId == stateId)) {
- break;
- }
- }
- num = l;
-
- int offset = _attributes[_digStateMusicMap[num].offset];
- if (offset == 0) {
- if (_attributes[_digStateMusicMap[num].attribPos] != 0) {
- num = _digStateMusicMap[num].stateIndex3;
- } else {
- num = _digStateMusicMap[num].stateIndex1;
- }
- } else {
- int stateIndex2 = _digStateMusicMap[num].stateIndex2;
- if (stateIndex2 == 0) {
- num = _digStateMusicMap[num].stateIndex1 + offset;
- } else {
- num = stateIndex2;
- }
- }
- }
-
- debug(5, "Set music state: %s, %s", _digStateMusicTable[num].name, _digStateMusicTable[num].filename);
-
- if (_curMusicState == num)
- return;
-
- if (_curMusicSeq == 0) {
- if (num == 0)
- playDigMusic(NULL, &_digStateMusicTable[0], num, false);
- else
- playDigMusic(_digStateMusicTable[num].name, &_digStateMusicTable[num], num, false);
- }
-
- _curMusicState = num;
-}
-
-void IMuseDigital::setDigMusicSequence(int seqId) {
- int l, num = -1;
-
- if (seqId == 0)
- seqId = 2000;
-
- for (l = 0; _digSeqMusicTable[l].soundId != -1; l++) {
- if ((_digSeqMusicTable[l].soundId == seqId)) {
- debug(5, "Set music sequence: %s, %s", _digSeqMusicTable[l].name, _digSeqMusicTable[l].filename);
- num = l;
- break;
- }
- }
-
- if (num == -1)
- return;
-
- if (_curMusicSeq == num)
- return;
-
- if (num != 0) {
- if (_curMusicSeq && ((_digSeqMusicTable[_curMusicSeq].transitionType == 4)
- || (_digSeqMusicTable[_curMusicSeq].transitionType == 6))) {
- _nextSeqToPlay = num;
- return;
- } else {
- playDigMusic(_digSeqMusicTable[num].name, &_digSeqMusicTable[num], 0, true);
- _nextSeqToPlay = 0;
- _attributes[DIG_SEQ_OFFSET + num] = 1; // _attributes[COMI_SEQ_OFFSET] in Comi are not used as it doesn't have 'room' attributes table
- }
- } else {
- if (_nextSeqToPlay != 0) {
- playDigMusic(_digSeqMusicTable[_nextSeqToPlay].name, &_digSeqMusicTable[_nextSeqToPlay], 0, true);
- _attributes[DIG_SEQ_OFFSET + _nextSeqToPlay] = 1; // _attributes[COMI_SEQ_OFFSET] in Comi are not used as it doesn't have 'room' attributes table
- num = _nextSeqToPlay;
- _nextSeqToPlay = 0;
- } else {
- if (_curMusicState != 0) {
- playDigMusic(_digStateMusicTable[_curMusicState].name, &_digStateMusicTable[_curMusicState], _curMusicState, true);
- } else
- playDigMusic(NULL, &_digStateMusicTable[0], _curMusicState, true);
- num = 0;
- }
- }
-
- _curMusicSeq = num;
-}
-
-void IMuseDigital::playDigMusic(const char *songName, const imuseDigTable *table, int attribPos, bool sequence) {
- int hookId = 0;
-
- if (songName != NULL) {
- if ((_attributes[DIG_SEQ_OFFSET + 38]) && (!_attributes[DIG_SEQ_OFFSET + 41])) {
- if ((attribPos == 43) || (attribPos == 44))
- hookId = 3;
- }
-
- if ((_attributes[DIG_SEQ_OFFSET + 46] != 0) && (_attributes[DIG_SEQ_OFFSET + 48] == 0)) {
- if ((attribPos == 38) || (attribPos == 39))
- hookId = 3;
- }
-
- if ((_attributes[DIG_SEQ_OFFSET + 53] != 0)) {
- if ((attribPos == 50) || (attribPos == 51))
- hookId = 3;
- }
-
- if ((attribPos != 0) && (hookId == 0)) {
- if (table->attribPos != 0)
- attribPos = table->attribPos;
- hookId = _attributes[DIG_STATE_OFFSET + attribPos];
- if (table->hookId != 0) {
- if ((hookId != 0) && (table->hookId > 1)) {
- _attributes[DIG_STATE_OFFSET + attribPos] = 2;
- } else {
- _attributes[DIG_STATE_OFFSET + attribPos] = hookId + 1;
- if (table->hookId < hookId + 1)
- _attributes[DIG_STATE_OFFSET + attribPos] = 1;
- }
- }
- }
- }
-
- if (!songName) {
- fadeOutMusic(120);
- return;
- }
-
- switch (table->transitionType) {
- case 0:
- case 5:
- default:
- break;
- case 3:
- case 4:
- if (table->filename[0] == 0) {
- fadeOutMusic(60);
- return;
- }
- if (table->transitionType == 4)
- _stopingSequence = 1;
- if ((!sequence) && (table->attribPos != 0) &&
- (table->attribPos == _digStateMusicTable[_curMusicState].attribPos)) {
- fadeOutMusicAndStartNew(108, table->filename, table->soundId);
- } else {
- fadeOutMusic(108);
- startMusic(table->filename, table->soundId, hookId, 127);
- }
- break;
- case 6:
- _stopingSequence = 1;
- break;
- }
-}
-
-void IMuseDigital::setComiMusicState(int stateId) {
- int l, num = -1;
-
- if (stateId == 4) // look into #3604 bug, ignore stateId == 4 it's seems needed after all
- return;
-
- if (stateId == 0)
- stateId = 1000;
-
- for (l = 0; _comiStateMusicTable[l].soundId != -1; l++) {
- if ((_comiStateMusicTable[l].soundId == stateId)) {
- debug(5, "Set music state: %s, %s", _comiStateMusicTable[l].name, _comiStateMusicTable[l].filename);
- num = l;
- break;
- }
- }
-
- if (num == -1)
- return;
-
- if (_curMusicState == num)
- return;
-
- if (_curMusicSeq == 0) {
- if (num == 0)
- playComiMusic(NULL, &_comiStateMusicTable[0], num, false);
- else
- playComiMusic(_comiStateMusicTable[num].name, &_comiStateMusicTable[num], num, false);
- }
-
- _curMusicState = num;
-}
-
-void IMuseDigital::setComiMusicSequence(int seqId) {
- int l, num = -1;
-
- if (seqId == 0)
- seqId = 2000;
-
- for (l = 0; _comiSeqMusicTable[l].soundId != -1; l++) {
- if ((_comiSeqMusicTable[l].soundId == seqId)) {
- debug(5, "Set music sequence: %s, %s", _comiSeqMusicTable[l].name, _comiSeqMusicTable[l].filename);
- num = l;
- break;
- }
- }
-
- if (num == -1)
- return;
-
- if (_curMusicSeq == num)
- return;
-
- if (num != 0) {
- if (_curMusicSeq && ((_comiSeqMusicTable[_curMusicSeq].transitionType == 4)
- || (_comiSeqMusicTable[_curMusicSeq].transitionType == 6))) {
- _nextSeqToPlay = num;
- return;
- } else {
- playComiMusic(_comiSeqMusicTable[num].name, &_comiSeqMusicTable[num], 0, true);
- _nextSeqToPlay = 0;
- }
- } else {
- if (_nextSeqToPlay != 0) {
- playComiMusic(_comiSeqMusicTable[_nextSeqToPlay].name, &_comiSeqMusicTable[_nextSeqToPlay], 0, true);
- num = _nextSeqToPlay;
- _nextSeqToPlay = 0;
- } else {
- if (_curMusicState != 0) {
- playComiMusic(_comiStateMusicTable[_curMusicState].name, &_comiStateMusicTable[_curMusicState], _curMusicState, true);
- } else
- playComiMusic(NULL, &_comiStateMusicTable[0], _curMusicState, true);
- num = 0;
- }
- }
-
- _curMusicSeq = num;
-}
-
-void IMuseDigital::setComiDemoMusicState(int stateNum) {
- if (stateNum == -1)
- return;
-
- if (_curMusicState == stateNum)
- return;
-
- if (stateNum != 0 && stateNum != 2 && stateNum != 4 && stateNum != 8 && stateNum != 9 && stateNum != 16) {
- debug(5, "Tried to set music state to num: %d, defaulting to 0", stateNum);
- stateNum = 0;
- }
-
- if (_curMusicSeq == 0) {
- if (stateNum == 0)
- playComiDemoMusic(NULL, &_comiDemoStateMusicTable[0], stateNum);
- else
- playComiDemoMusic(_comiDemoStateMusicTable[stateNum].name, &_comiDemoStateMusicTable[stateNum], stateNum);
- }
-
- _curMusicState = stateNum;
-}
-
-void IMuseDigital::playComiMusic(const char *songName, const imuseComiTable *table, int attribPos, bool sequence) {
- int hookId = 0;
-
- if ((songName != NULL) && (attribPos != 0)) {
- if (table->attribPos != 0)
- attribPos = table->attribPos;
- hookId = _attributes[COMI_STATE_OFFSET + attribPos];
- if (table->hookId != 0) {
- if ((hookId != 0) && (table->hookId > 1)) {
- _attributes[COMI_STATE_OFFSET + attribPos] = 2;
- } else {
- _attributes[COMI_STATE_OFFSET + attribPos] = hookId + 1;
- if (table->hookId < hookId + 1)
- _attributes[COMI_STATE_OFFSET + attribPos] = 1;
- }
- }
- }
-
- if (!songName) {
- fadeOutMusic(120);
- return;
- }
-
- TriggerParams trigger;
-
- switch (table->transitionType) {
- case 0:
- default:
- break;
- case 8:
- setHookIdForMusic(table->hookId);
- break;
- case 9:
- // Setup the trigger
- strcpy(trigger.marker, "_end"); trigger.fadeOutDelay = table->fadeOutDelay;
- strcpy(trigger.filename, table->filename); trigger.soundId = table->soundId;
- trigger.hookId = table->hookId; trigger.volume = 127;
- setTrigger(&trigger);
-
- setHookIdForMusic(table->hookId);
- break;
- case 4:
- if (table->filename[0] == 0) {
- fadeOutMusic(60);
- return;
- }
- if (getCurMusicSoundId() == table->soundId)
- return;
-
- // Setup the trigger
- strcpy(trigger.marker, "_end"); trigger.fadeOutDelay = table->fadeOutDelay;
- strcpy(trigger.filename, table->filename); trigger.soundId = table->soundId;
- trigger.hookId = table->hookId; trigger.volume = 127;
- setTrigger(&trigger);
-
- fadeOutMusic(table->fadeOutDelay);
- startMusic(table->filename, table->soundId, hookId, 127);
- break;
- case 2:
- case 3:
- case 12:
- if (table->filename[0] == 0) {
- fadeOutMusic(60);
- return;
- }
- if (getCurMusicSoundId() == table->soundId)
- return;
- if (table->transitionType == 2) {
- fadeOutMusic(table->fadeOutDelay);
- startMusic(table->filename, table->soundId, table->hookId, 127);
- return;
- }
- if ((!sequence) && (table->attribPos != 0) &&
- (table->attribPos == _comiStateMusicTable[_curMusicState].attribPos)) {
- fadeOutMusicAndStartNew(table->fadeOutDelay, table->filename, table->soundId);
- } else if (table->transitionType == 12) {
- // Setup the trigger
- strcpy(trigger.marker, "exit"); trigger.fadeOutDelay = table->fadeOutDelay;
- strcpy(trigger.filename, table->filename); trigger.soundId = table->soundId;
- trigger.hookId = table->hookId; trigger.volume = 127;
- setTrigger(&trigger);
- } else {
- fadeOutMusic(table->fadeOutDelay);
- startMusic(table->filename, table->soundId, hookId, 127);
- }
- break;
- }
-}
-
-void IMuseDigital::playComiDemoMusic(const char *songName, const imuseComiTable *table, int attribPos) {
- int hookId = 0;
-
- if ((songName != NULL) && (attribPos != 0)) {
- if (table->attribPos != 0)
- attribPos = table->attribPos;
- hookId = _attributes[COMI_STATE_OFFSET + attribPos];
- if (table->hookId != 0) {
- if ((hookId != 0) && (table->hookId > 1)) {
- _attributes[COMI_STATE_OFFSET + attribPos] = 2;
- }
- else {
- _attributes[COMI_STATE_OFFSET + attribPos] = hookId + 1;
- if (table->hookId < hookId + 1)
- _attributes[COMI_STATE_OFFSET + attribPos] = 1;
- }
- }
- }
-
- if (!songName) {
- fadeOutMusic(120);
- return;
- }
-
- switch (table->transitionType) {
- case 0:
- default:
- break;
- case 3:
- if (table->filename[0] == 0) {
- fadeOutMusic(60);
- return;
- }
- if (getCurMusicSoundId() == table->soundId)
- return;
- if ((table->attribPos != 0) &&
- (table->attribPos == _comiDemoStateMusicTable[_curMusicState].attribPos)) {
- fadeOutMusicAndStartNew(table->fadeOutDelay, table->filename, table->soundId);
- } else {
- fadeOutMusic(table->fadeOutDelay);
- startMusic(table->filename, table->soundId, hookId, 127);
- }
- break;
- }
-}
-
-void IMuseDigital::setFtMusicState(int stateId) {
- if (stateId > 48)
- return;
-
- debug(5, "State music: %s, %s", _ftStateMusicTable[stateId].name, _ftStateMusicTable[stateId].audioName);
-
- if (_curMusicState == stateId)
- return;
-
- if (_curMusicSeq == 0) {
- if (stateId == 0)
- playFtMusic(NULL, 0, 0);
- else
- playFtMusic(_ftStateMusicTable[stateId].audioName, _ftStateMusicTable[stateId].transitionType, _ftStateMusicTable[stateId].volume);
- }
-
- _curMusicState = stateId;
-}
-
-void IMuseDigital::setFtMusicSequence(int seqId) {
- if (seqId > 52)
- return;
-
- debug(5, "Sequence music: %s", _ftSeqNames[seqId].name);
-
- if (_curMusicSeq == seqId)
- return;
-
- if (seqId == 0) {
- if (_curMusicState == 0)
- playFtMusic(NULL, 0, 0);
- else {
- playFtMusic(_ftStateMusicTable[_curMusicState].audioName, _ftStateMusicTable[_curMusicState].transitionType, _ftStateMusicTable[_curMusicState].volume);
- }
- } else {
- int seq = (seqId - 1) * 4;
- playFtMusic(_ftSeqMusicTable[seq].audioName, _ftSeqMusicTable[seq].transitionType, _ftSeqMusicTable[seq].volume);
- }
-
- _curMusicSeq = seqId;
- _curMusicCue = 0;
-}
-
-void IMuseDigital::setFtMusicCuePoint(int cueId) {
- if (cueId > 3)
- return;
-
- debug(5, "Cue point sequence: %d", cueId);
-
- if (_curMusicSeq == 0)
- return;
-
- if (_curMusicCue == cueId)
- return;
-
- if (cueId == 0)
- playFtMusic(NULL, 0, 0);
- else {
- int seq = ((_curMusicSeq - 1) * 4) + cueId;
- playFtMusic(_ftSeqMusicTable[seq].audioName, _ftSeqMusicTable[seq].transitionType, _ftSeqMusicTable[seq].volume);
- }
-
- _curMusicCue = cueId;
-}
-
-void IMuseDigital::setAudioNames(int32 num, char *names) {
- free(_audioNames);
- _numAudioNames = num;
- _audioNames = names;
-}
-
-int IMuseDigital::getSoundIdByName(const char *soundName) {
- if (soundName && soundName[0] != 0) {
- for (int r = 0; r < _numAudioNames; r++) {
- if (strcmp(soundName, &_audioNames[r * 9]) == 0) {
- return r;
- }
- }
- }
-
- return -1;
-}
-
-void IMuseDigital::playFtMusic(const char *songName, int opcode, int volume) {
- fadeOutMusic(200);
-
- switch (opcode) {
- case 0:
- case 4:
- default:
- break;
- case 1:
- case 2:
- case 3:
- {
- int soundId = getSoundIdByName(songName);
- if (soundId != -1) {
- startMusic(soundId, volume);
- }
- }
- break;
- }
-}
-
-
-} // End of namespace Scumm
diff --git a/engines/scumm/imuse_digi/dimuse_script.cpp b/engines/scumm/imuse_digi/dimuse_script.cpp
deleted file mode 100644
index d15a62b721..0000000000
--- a/engines/scumm/imuse_digi/dimuse_script.cpp
+++ /dev/null
@@ -1,435 +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 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-
-#include "common/system.h"
-#include "common/timer.h"
-
-#include "scumm/actor.h"
-#include "scumm/scumm_v7.h"
-#include "scumm/sound.h"
-#include "scumm/imuse_digi/dimuse.h"
-#include "scumm/imuse_digi/dimuse_bndmgr.h"
-#include "scumm/imuse_digi/dimuse_track.h"
-
-#include "audio/audiostream.h"
-#include "audio/mixer.h"
-
-namespace Scumm {
-
-void IMuseDigital::parseScriptCmds(int cmd, int b, int c, int d, int e, int f, int g, int h) {
- int soundId = b;
- int sub_cmd = c;
-
- if (!cmd)
- return;
-
- switch (cmd) {
- case 10: // ImuseStopAllSounds
- stopAllSounds();
- break;
- case 12: // ImuseSetParam
- switch (sub_cmd) {
- case 0x400: // select group volume
- selectVolumeGroup(soundId, d);
- break;
- case 0x500: // set priority
- setPriority(soundId, d);
- break;
- case 0x600: // set volume
- if (d >= 0 && d <= 127)
- setVolume(soundId, d);
- break;
- case 0x700: // set pan
- if (d >= 0 && d <= 127)
- setPan(soundId, d);
- break;
- default:
- warning("IMuseDigital::doCommand SetParam DEFAULT command %d", sub_cmd);
- break;
- }
- break;
- case 14: // ImuseFadeParam
- switch (sub_cmd) {
- case 0x600: // set volume fading
- if ((d != 0) && (e == 0))
- setVolume(soundId, d);
- else if ((d == 0) && (e == 0))
- stopSound(soundId);
- else
- setFade(soundId, d, e);
- break;
- default:
- warning("IMuseDigital::doCommand FadeParam DEFAULT sub command %d", sub_cmd);
- break;
- }
- break;
- case 25: // ImuseStartStream
- debug(3, "ImuseStartStream (%d, %d, %d)", soundId, c, d);
- break;
- case 26: // ImuseSwitchStream
- debug(3, "ImuseSwitchStream (%d, %d, %d, %d, %d)", soundId, c, d, e, f);
- break;
- case 0x1000: // ImuseSetState
- debug(5, "ImuseSetState (%d)", b);
- if ((_vm->_game.id == GID_DIG) && (_vm->_game.features & GF_DEMO)) {
- if (b == 1) {
- fadeOutMusic(200);
- startMusic(1, 127);
- } else {
- if (getSoundStatus(2) == 0) {
- fadeOutMusic(200);
- startMusic(2, 127);
- }
- }
- } else if ((_vm->_game.id == GID_CMI) && (_vm->_game.features & GF_DEMO)) {
- setComiDemoMusicState(b);
- } else if (_vm->_game.id == GID_DIG) {
- setDigMusicState(b);
- } else if (_vm->_game.id == GID_CMI) {
- setComiMusicState(b);
- } else if (_vm->_game.id == GID_FT) {
- setFtMusicState(b);
- }
- break;
- case 0x1001: // ImuseSetSequence
- debug(5, "ImuseSetSequence (%d)", b);
- if (_vm->_game.id == GID_DIG) {
- setDigMusicSequence(b);
- } else if (_vm->_game.id == GID_CMI) {
- setComiMusicSequence(b);
- } else if (_vm->_game.id == GID_FT) {
- setFtMusicSequence(b);
- }
- break;
- case 0x1002: // ImuseSetCuePoint
- debug(5, "ImuseSetCuePoint (%d)", b);
- if (_vm->_game.id == GID_FT) {
- setFtMusicCuePoint(b);
- }
- break;
- case 0x1003: // ImuseSetAttribute
- debug(5, "ImuseSetAttribute (%d, %d)", b, c);
- assert((_vm->_game.id == GID_DIG) || (_vm->_game.id == GID_FT));
- if (_vm->_game.id == GID_DIG) {
- _attributes[b] = c;
- }
- break;
- case 0x2000: // ImuseSetGroupSfxVolume
- break;
- case 0x2001: // ImuseSetGroupVoiceVolume
- break;
- case 0x2002: // ImuseSetGroupMusicVolume
- break;
- default:
- error("IMuseDigital::doCommand DEFAULT command %d", cmd);
- }
-}
-
-void IMuseDigital::flushTrack(Track *track) {
- track->toBeRemoved = true;
-
- if (track->souStreamUsed) {
- _mixer->stopHandle(track->mixChanHandle);
- } else if (track->stream) {
- debug(5, "flushTrack(trackId: %d) - soundId:%d", track->trackId, track->soundId);
- // Finalize the appendable stream, then remove our reference to it.
- // Note that there might still be some data left in the buffers of the
- // appendable stream. We play it nice and wait till all of it
- // played. The audio mixer will take care of it afterwards (and dispose it).
- track->stream->finish();
- track->stream = 0;
- if (track->soundDesc) {
- _sound->closeSound(track->soundDesc);
- track->soundDesc = 0;
- }
- }
-
- if (!_mixer->isSoundHandleActive(track->mixChanHandle)) {
- track->reset();
- }
-}
-
-void IMuseDigital::flushTracks() {
- Common::StackLock lock(_mutex, "IMuseDigital::flushTracks()");
- debug(6, "flushTracks()");
- for (int l = 0; l < MAX_DIGITAL_TRACKS + MAX_DIGITAL_FADETRACKS; l++) {
- Track *track = _track[l];
- if (track->used && track->toBeRemoved && !_mixer->isSoundHandleActive(track->mixChanHandle)) {
- debug(5, "flushTracks() - trackId:%d, soundId:%d", track->trackId, track->soundId);
- track->reset();
- }
- }
-}
-
-void IMuseDigital::refreshScripts() {
- Common::StackLock lock(_mutex, "IMuseDigital::refreshScripts()");
- debug(6, "refreshScripts()");
-
- if (_stopingSequence) {
- // prevent start new music, only fade out old one
- if (_vm->isSmushActive()) {
- fadeOutMusic(60);
- return;
- }
- // small delay, it seems help for fix bug #3325
- if (_stopingSequence++ > 120) {
- debug(5, "refreshScripts() Force restore music state");
- parseScriptCmds(0x1001, 0, 0, 0, 0, 0, 0, 0);
- _stopingSequence = 0;
- }
- }
-
- bool found = false;
- for (int l = 0; l < MAX_DIGITAL_TRACKS; l++) {
- Track *track = _track[l];
- if (track->used && !track->toBeRemoved && (track->volGroupId == IMUSE_VOLGRP_MUSIC)) {
- found = true;
- break;
- }
- }
-
- if (!found && _curMusicState) {
- debug(5, "refreshScripts() Restore music state");
- parseScriptCmds(0x1001, 0, 0, 0, 0, 0, 0, 0);
- }
-}
-
-int IMuseDigital::startVoice(int soundId, Audio::AudioStream *input) {
- debug(5, "startVoiceStream(%d)", soundId);
- return startSound(soundId, "", 0, IMUSE_VOLGRP_VOICE, input, 0, 127, 127, NULL);
-}
-
-int IMuseDigital::startVoice(int soundId, const char *soundName) {
- debug(5, "startVoiceBundle(%s, %d)", soundName, soundId);
- return startSound(soundId, soundName, IMUSE_BUNDLE, IMUSE_VOLGRP_VOICE, NULL, 0, 127, 127, NULL);
-}
-
-int IMuseDigital::startMusic(int soundId, int volume) {
- debug(5, "startMusicResource(%d)", soundId);
- return startSound(soundId, "", IMUSE_RESOURCE, IMUSE_VOLGRP_MUSIC, NULL, 0, volume, 126, NULL);
-}
-
-int IMuseDigital::startMusic(const char *soundName, int soundId, int hookId, int volume) {
- debug(5, "startMusicBundle(%s, soundId:%d, hookId:%d)", soundName, soundId, hookId);
- return startSound(soundId, soundName, IMUSE_BUNDLE, IMUSE_VOLGRP_MUSIC, NULL, hookId, volume, 126, NULL);
-}
-
-int IMuseDigital::startMusicWithOtherPos(const char *soundName, int soundId, int hookId, int volume, Track *otherTrack) {
- debug(5, "startMusicWithOtherPos(%s, soundId:%d, hookId:%d, oldSoundId:%d)", soundName, soundId, hookId, otherTrack->soundId);
- return startSound(soundId, soundName, IMUSE_BUNDLE, IMUSE_VOLGRP_MUSIC, NULL, hookId, volume, 126, otherTrack);
-}
-
-int IMuseDigital::startSfx(int soundId, int priority) {
- debug(5, "startSfx(%d)", soundId);
- return startSound(soundId, "", IMUSE_RESOURCE, IMUSE_VOLGRP_SFX, NULL, 0, 127, priority, NULL);
-}
-
-void IMuseDigital::getLipSync(int soundId, int syncId, int32 msPos, int32 &width, int32 &height) {
- int32 sync_size;
- byte *sync_ptr;
-
- msPos /= 16;
- if (msPos < 65536) {
- Common::StackLock lock(_mutex, "IMuseDigital::getLipSync()");
- for (int l = 0; l < MAX_DIGITAL_TRACKS; l++) {
- Track *track = _track[l];
- if (track->used && !track->toBeRemoved && (track->soundId == soundId)) {
- _sound->getSyncSizeAndPtrById(track->soundDesc, syncId, sync_size, &sync_ptr);
- if ((sync_size != 0) && (sync_ptr != NULL)) {
- sync_size /= 4;
- while (sync_size--) {
- if (READ_BE_UINT16(sync_ptr) >= msPos)
- break;
- sync_ptr += 4;
- }
- if (sync_size < 0)
- sync_ptr -= 4;
- else
- if (READ_BE_UINT16(sync_ptr) > msPos)
- sync_ptr -= 4;
-
- width = sync_ptr[2];
- height = sync_ptr[3];
- return;
- }
- }
- }
- }
-}
-
-int32 IMuseDigital::getPosInMs(int soundId) {
- Common::StackLock lock(_mutex, "IMuseDigital::getPosInMs()");
- for (int l = 0; l < MAX_DIGITAL_TRACKS; l++) {
- Track *track = _track[l];
- if (track->used && !track->toBeRemoved && (track->soundId == soundId)) {
- int32 pos = (5 * (track->dataOffset + track->regionOffset)) / (track->feedSize / 200);
- return pos;
- }
- }
-
- return 0;
-}
-
-int IMuseDigital::getSoundStatus(int soundId) const {
- Common::StackLock lock(_mutex, "IMuseDigital::getSoundStatus()");
- debug(5, "IMuseDigital::getSoundStatus(%d)", soundId);
- for (int l = 0; l < MAX_DIGITAL_TRACKS; l++) {
- Track *track = _track[l];
- // Note: We do not check track->toBeRemoved here on purpose (I *think*, at least).
- // After all, tracks which are about to stop still are running (if only for a brief time).
- if ((track->soundId == soundId) && track->used) {
- if (_mixer->isSoundHandleActive(track->mixChanHandle)) {
- return 1;
- }
- }
- }
-
- return 0;
-}
-
-void IMuseDigital::stopSound(int soundId) {
- Common::StackLock lock(_mutex, "IMuseDigital::stopSound()");
- debug(5, "IMuseDigital::stopSound(%d)", soundId);
- for (int l = 0; l < MAX_DIGITAL_TRACKS; l++) {
- Track *track = _track[l];
- if (track->used && !track->toBeRemoved && (track->soundId == soundId)) {
- debug(5, "IMuseDigital::stopSound(%d) - stopping sound", soundId);
- flushTrack(track);
- }
- }
-}
-
-int32 IMuseDigital::getCurMusicPosInMs() {
- Common::StackLock lock(_mutex, "IMuseDigital::getCurMusicPosInMs()");
- int soundId = -1;
-
- for (int l = 0; l < MAX_DIGITAL_TRACKS; l++) {
- Track *track = _track[l];
- if (track->used && !track->toBeRemoved && (track->volGroupId == IMUSE_VOLGRP_MUSIC)) {
- soundId = track->soundId;
- }
- }
-
- int32 msPos = getPosInMs(soundId);
- debug(6, "IMuseDigital::getCurMusicPosInMs(%d) = %d", soundId, msPos);
- return msPos;
-}
-
-int32 IMuseDigital::getCurVoiceLipSyncWidth() {
- Common::StackLock lock(_mutex, "IMuseDigital::getCurVoiceLipSyncWidth()");
- int32 msPos = getPosInMs(kTalkSoundID) + 50;
- int32 width = 0, height = 0;
-
- debug(6, "IMuseDigital::getCurVoiceLipSyncWidth(%d)", kTalkSoundID);
- getLipSync(kTalkSoundID, 0, msPos, width, height);
- return width;
-}
-
-int32 IMuseDigital::getCurVoiceLipSyncHeight() {
- Common::StackLock lock(_mutex, "IMuseDigital::getCurVoiceLipSyncHeight()");
- int32 msPos = getPosInMs(kTalkSoundID) + 50;
- int32 width = 0, height = 0;
-
- debug(6, "IMuseDigital::getCurVoiceLipSyncHeight(%d)", kTalkSoundID);
- getLipSync(kTalkSoundID, 0, msPos, width, height);
- return height;
-}
-
-int32 IMuseDigital::getCurMusicLipSyncWidth(int syncId) {
- Common::StackLock lock(_mutex, "IMuseDigital::getCurMusicLipSyncWidth()");
- int soundId = -1;
-
- for (int l = 0; l < MAX_DIGITAL_TRACKS; l++) {
- Track *track = _track[l];
- if (track->used && !track->toBeRemoved && (track->volGroupId == IMUSE_VOLGRP_MUSIC)) {
- soundId = track->soundId;
- }
- }
-
- int32 msPos = getPosInMs(soundId) + 50;
- int32 width = 0, height = 0;
-
- debug(6, "IMuseDigital::getCurVoiceLipSyncWidth(%d, %d)", soundId, msPos);
- getLipSync(soundId, syncId, msPos, width, height);
- return width;
-}
-
-int32 IMuseDigital::getCurMusicLipSyncHeight(int syncId) {
- Common::StackLock lock(_mutex, "IMuseDigital::getCurMusicLipSyncHeight()");
- int soundId = -1;
-
- for (int l = 0; l < MAX_DIGITAL_TRACKS; l++) {
- Track *track = _track[l];
- if (track->used && !track->toBeRemoved && (track->volGroupId == IMUSE_VOLGRP_MUSIC)) {
- soundId = track->soundId;
- }
- }
-
- int32 msPos = getPosInMs(soundId) + 50;
- int32 width = 0, height = 0;
-
- debug(6, "IMuseDigital::getCurVoiceLipSyncHeight(%d, %d)", soundId, msPos);
- getLipSync(soundId, syncId, msPos, width, height);
- return height;
-}
-
-int32 IMuseDigital::getSoundElapsedTimeInMs(int soundId) {
- Common::StackLock lock(_mutex, "IMuseDigital::getPosInMs()");
- for (int l = 0; l < MAX_DIGITAL_TRACKS; l++) {
- Track *track = _track[l];
- if (track->used && !track->toBeRemoved && (track->soundId == soundId)) {
- int32 pos = (_mixer->getSoundElapsedTime(track->mixChanHandle));
- return pos;
- }
- }
-
- return 0;
-}
-
-void IMuseDigital::stopAllSounds() {
- Common::StackLock lock(_mutex, "IMuseDigital::stopAllSounds()");
- debug(5, "IMuseDigital::stopAllSounds");
-
- for (int l = 0; l < MAX_DIGITAL_TRACKS + MAX_DIGITAL_FADETRACKS; l++) {
- Track *track = _track[l];
- if (track->used) {
- // Stop the sound output, *now*. No need to use toBeRemoved etc.
- // as we are protected by a mutex, and this method is never called
- // from IMuseDigital::callback either.
- _mixer->stopHandle(track->mixChanHandle);
- if (track->soundDesc) {
- debug(5, "IMuseDigital::stopAllSounds - stopping sound(%d)", track->soundId);
- _sound->closeSound(track->soundDesc);
- }
-
- // Mark the track as unused
- track->reset();
- }
- }
-}
-
-void IMuseDigital::pause(bool p) {
- _pause = p;
-}
-
-} // End of namespace Scumm
diff --git a/engines/scumm/imuse_digi/dimuse_scripts.cpp b/engines/scumm/imuse_digi/dimuse_scripts.cpp
new file mode 100644
index 0000000000..a6411867b1
--- /dev/null
+++ b/engines/scumm/imuse_digi/dimuse_scripts.cpp
@@ -0,0 +1,909 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "scumm/imuse_digi/dimuse_engine.h"
+
+namespace Scumm {
+
+#define DIG_STATE_OFFSET 11
+#define DIG_SEQ_OFFSET (DIG_STATE_OFFSET + 65)
+#define COMI_STATE_OFFSET 3
+
+int IMuseDigital::scriptParse(int cmd, int a, int b) {
+ if (_scriptInitializedFlag || !cmd) {
+ switch (cmd) {
+ case 0:
+ if (_scriptInitializedFlag) {
+ debug(5, "IMuseDigital::scriptParse(): script module already initialized");
+ return -1;
+ } else {
+ _scriptInitializedFlag = 1;
+ return scriptInit();
+ }
+ case 1:
+ _scriptInitializedFlag = 0;
+ return scriptTerminate();
+ case 2: // script_save(a, b);
+ case 3: // script_restore(a);
+ break;
+ case 4:
+ scriptRefresh();
+ return 0;
+ case 5:
+ scriptSetState(a);
+ return 0;
+ case 6:
+ scriptSetSequence(a);
+ return 0;
+ case 7:
+ scriptSetCuePoint(a);
+ return 0;
+ case 8:
+ return scriptSetAttribute(a, b);
+ default:
+ debug(5, "IMuseDigital::scriptParse(): unrecognized opcode (%d)", cmd);
+ return -1;
+ }
+ } else {
+ debug(5, "IMuseDigital::scriptParse(): script module not initialized");
+ return -1;
+ }
+
+ return -1;
+}
+
+int IMuseDigital::scriptInit() {
+ _curMusicState = 0;
+ _curMusicSeq = 0;
+ _nextSeqToPlay = 0;
+ _curMusicCue = 0;
+ memset(_attributes, 0, sizeof(_attributes));
+ return 0;
+}
+
+int IMuseDigital::scriptTerminate() {
+ diMUSETerminate();
+
+ _curMusicState = 0;
+ _curMusicSeq = 0;
+ _nextSeqToPlay = 0;
+ _curMusicCue = 0;
+ memset(_attributes, 0, sizeof(_attributes));
+ return 0;
+}
+
+void IMuseDigital::scriptRefresh() {
+ int soundId;
+ int nextSound;
+
+ if (_stopSequenceFlag) {
+ scriptSetSequence(0);
+ _stopSequenceFlag = 0;
+ }
+
+ soundId = 0;
+
+ while (1) {
+ nextSound = diMUSEGetNextSound(soundId);
+ soundId = nextSound;
+
+ if (!nextSound)
+ break;
+
+ if (diMUSEGetParam(nextSound, DIMUSE_P_SND_HAS_STREAM) && diMUSEGetParam(soundId, DIMUSE_P_STREAM_BUFID) == DIMUSE_BUFFER_MUSIC) {
+ if (soundId)
+ return;
+ break;
+ }
+ }
+
+ if (_curMusicSeq)
+ scriptSetSequence(0);
+
+ flushTracks();
+}
+
+void IMuseDigital::scriptSetState(int soundId) {
+ if (_vm->_game.id == GID_DIG && !_isEarlyDiMUSE) {
+ setDigMusicState(soundId);
+ } else if (_vm->_game.id == GID_CMI) {
+ setComiMusicState(soundId);
+ } else {
+ setFtMusicState(soundId);
+ }
+}
+
+void IMuseDigital::scriptSetSequence(int soundId) {
+ if (_vm->_game.id == GID_DIG && !_isEarlyDiMUSE) {
+ setDigMusicSequence(soundId);
+ } else if (_vm->_game.id == GID_CMI) {
+ setComiMusicSequence(soundId);
+ } else {
+ setFtMusicSequence(soundId);
+ }
+}
+
+void IMuseDigital::scriptSetCuePoint(int cueId) {
+ if (!_isEarlyDiMUSE)
+ return;
+
+ if (cueId > 3)
+ return;
+
+ debug(5, "IMuseDigital::scriptSetCuePoint(): Cue point sequence: %d", cueId);
+
+ if (_curMusicSeq && _curMusicCue != cueId) {
+ if (cueId == 0)
+ playFtMusic(nullptr, 0, 0);
+ else {
+ int seq = ((_curMusicSeq - 1) * 4) + cueId;
+ playFtMusic(_ftSeqMusicTable[seq].audioName, _ftSeqMusicTable[seq].transitionType, _ftSeqMusicTable[seq].volume);
+ }
+ }
+
+ _curMusicCue = cueId;
+}
+
+int IMuseDigital::scriptSetAttribute(int attrIndex, int attrVal) {
+ // FT appears to set a single attribute to 1 at start-up and
+ // never use it again, so we currently ignore that behavior
+ if (_vm->_game.id == GID_DIG) {
+ _attributes[attrIndex] = attrVal;
+ }
+ return 0;
+}
+
+int IMuseDigital::scriptTriggerCallback(char *marker) {
+ if (marker[0] != '_') {
+ debug(5, "IMuseDigital::scriptTriggerCallback(): got marker != '_end', callback ignored");
+ return -1;
+ }
+ _stopSequenceFlag = 1;
+ return 0;
+}
+
+void Scumm::IMuseDigital::setFtMusicState(int stateId) {
+ if (stateId > 48)
+ return;
+
+ debug(5, "IMuseDigital::setFtMusicState(): State music: %s, %s", _ftStateMusicTable[stateId].name, _ftStateMusicTable[stateId].audioName);
+
+ if (_curMusicState == stateId)
+ return;
+
+ if (_curMusicSeq == 0) {
+ if (stateId == 0) {
+ playFtMusic(nullptr, 0, 0);
+ } else {
+ playFtMusic(_ftStateMusicTable[stateId].audioName, _ftStateMusicTable[stateId].transitionType, _ftStateMusicTable[stateId].volume);
+ }
+ }
+
+ _curMusicState = stateId;
+}
+
+void IMuseDigital::setFtMusicSequence(int seqId) {
+ if (seqId > 52)
+ return;
+
+ debug(5, "IMuseDigital::setFtMusicSequence(): Sequence music: %s", _ftSeqNames[seqId].name);
+
+ if (_curMusicSeq != seqId) {
+ if (seqId == 0) {
+ if (_curMusicState == 0) {
+ playFtMusic(nullptr, 0, 0);
+ } else {
+ playFtMusic(_ftStateMusicTable[_curMusicState].audioName, _ftStateMusicTable[_curMusicState].transitionType, _ftStateMusicTable[_curMusicState].volume);
+ }
+ } else {
+ int seq = (seqId - 1) * 4;
+ playFtMusic(_ftSeqMusicTable[seq].audioName, _ftSeqMusicTable[seq].transitionType, _ftSeqMusicTable[seq].volume);
+ }
+ }
+
+ _curMusicSeq = seqId;
+ _curMusicCue = 0;
+}
+
+
+void IMuseDigital::setDigMusicState(int stateId) {
+ int l, num = -1;
+
+ for (l = 0; _digStateMusicTable[l].soundId != -1; l++) {
+ if ((_digStateMusicTable[l].soundId == stateId)) {
+ debug(5, "IMuseDigital::setDigMusicState(): Set music state: %s, %s", _digStateMusicTable[l].name, _digStateMusicTable[l].filename);
+ num = l;
+ break;
+ }
+ }
+
+ if (num == -1) {
+ for (l = 0; _digStateMusicMap[l].roomId != -1; l++) {
+ if ((_digStateMusicMap[l].roomId == stateId)) {
+ break;
+ }
+ }
+ num = l;
+
+ int offset = _attributes[_digStateMusicMap[num].offset];
+ if (offset == 0) {
+ if (_attributes[_digStateMusicMap[num].attribPos] != 0) {
+ num = _digStateMusicMap[num].stateIndex3;
+ } else {
+ num = _digStateMusicMap[num].stateIndex1;
+ }
+ } else {
+ int stateIndex2 = _digStateMusicMap[num].stateIndex2;
+ if (stateIndex2 == 0) {
+ num = _digStateMusicMap[num].stateIndex1 + offset;
+ } else {
+ num = stateIndex2;
+ }
+ }
+ }
+
+ debug(5, "IMuseDigital::setDigMusicState(): Set music state: %s, %s", _digStateMusicTable[num].name, _digStateMusicTable[num].filename);
+
+ if (_curMusicState == num)
+ return;
+
+ if (_curMusicSeq == 0) {
+ if (num == 0)
+ playDigMusic(nullptr, &_digStateMusicTable[0], num, false);
+ else
+ playDigMusic(_digStateMusicTable[num].name, &_digStateMusicTable[num], num, false);
+ }
+
+ _curMusicState = num;
+}
+
+void IMuseDigital::setDigMusicSequence(int seqId) {
+ int l, num = -1;
+
+ if (seqId == 0)
+ seqId = 2000;
+
+ for (l = 0; _digSeqMusicTable[l].soundId != -1; l++) {
+ if ((_digSeqMusicTable[l].soundId == seqId)) {
+ debug(5, "IMuseDigital::setDigMusicSequence(): Set music sequence: %s, %s", _digSeqMusicTable[l].name, _digSeqMusicTable[l].filename);
+ num = l;
+ break;
+ }
+ }
+
+ if (num == -1)
+ return;
+
+ if (_curMusicSeq == num)
+ return;
+
+ if (num != 0) {
+ if (_curMusicSeq && ((_digSeqMusicTable[_curMusicSeq].transitionType == 4)
+ || (_digSeqMusicTable[_curMusicSeq].transitionType == 6))) {
+ _nextSeqToPlay = num;
+ return;
+ } else {
+ playDigMusic(_digSeqMusicTable[num].name, &_digSeqMusicTable[num], 0, true);
+ _nextSeqToPlay = 0;
+ _attributes[DIG_SEQ_OFFSET + num] = 1;
+ }
+ } else {
+ if (_nextSeqToPlay != 0) {
+ playDigMusic(_digSeqMusicTable[_nextSeqToPlay].name, &_digSeqMusicTable[_nextSeqToPlay], 0, true);
+ _attributes[DIG_SEQ_OFFSET + _nextSeqToPlay] = 1;
+ num = _nextSeqToPlay;
+ _nextSeqToPlay = 0;
+ } else {
+ if (_curMusicState != 0) {
+ playDigMusic(_digStateMusicTable[_curMusicState].name, &_digStateMusicTable[_curMusicState], _curMusicState, true);
+ } else {
+ playDigMusic(nullptr, &_digStateMusicTable[0], _curMusicState, true);
+ }
+
+ num = 0;
+ }
+ }
+
+ _curMusicSeq = num;
+}
+
+void IMuseDigital::setComiMusicState(int stateId) {
+ int l, num = -1;
+
+ if (stateId == 0)
+ stateId = 1000;
+
+ if ((_vm->_game.features & GF_DEMO) && stateId == 1000)
+ stateId = 0;
+
+ if (!(_vm->_game.features & GF_DEMO)) {
+ for (l = 0; _comiStateMusicTable[l].soundId != -1; l++) {
+ if ((_comiStateMusicTable[l].soundId == stateId)) {
+ debug(5, "IMuseDigital::setComiMusicState(): Set music state: %s, %s", _comiStateMusicTable[l].name, _comiStateMusicTable[l].filename);
+ num = l;
+ break;
+ }
+ }
+ }
+
+ if (num == -1 && !(_vm->_game.features & GF_DEMO))
+ return;
+
+ if (!(_vm->_game.features & GF_DEMO) && _curMusicState == num) {
+ return;
+ } else if ((_vm->_game.features & GF_DEMO) && _curMusicState == stateId) {
+ return;
+ }
+
+ if (_curMusicSeq == 0) {
+ if (_vm->_game.features & GF_DEMO) {
+ if (_curMusicSeq == 0) {
+ if (stateId == 0)
+ playComiDemoMusic(nullptr, &_comiDemoStateMusicTable[0], stateId, false);
+ else
+ playComiDemoMusic(_comiDemoStateMusicTable[stateId].name, &_comiDemoStateMusicTable[stateId], stateId, false);
+ }
+ } else {
+ if (num == 0)
+ playComiMusic(nullptr, &_comiStateMusicTable[0], num, false);
+ else
+ playComiMusic(_comiStateMusicTable[num].name, &_comiStateMusicTable[num], num, false);
+ }
+ }
+
+ if (!(_vm->_game.features & GF_DEMO)) {
+ _curMusicState = num;
+ } else {
+ _curMusicState = stateId;
+ }
+}
+
+void IMuseDigital::setComiMusicSequence(int seqId) {
+ int l, num = -1;
+
+ if (seqId == 0)
+ seqId = 2000;
+
+ for (l = 0; _comiSeqMusicTable[l].soundId != -1; l++) {
+ if ((_comiSeqMusicTable[l].soundId == seqId)) {
+ debug(5, "IMuseDigital::setComiMusicSequence(): Set music sequence: %s, %s", _comiSeqMusicTable[l].name, _comiSeqMusicTable[l].filename);
+ num = l;
+ break;
+ }
+ }
+
+ if (num == -1)
+ return;
+
+ if (_curMusicSeq == num)
+ return;
+
+ if (num != 0) {
+ if (_curMusicSeq && ((_comiSeqMusicTable[_curMusicSeq].transitionType == 4)
+ || (_comiSeqMusicTable[_curMusicSeq].transitionType == 6))) {
+ _nextSeqToPlay = num;
+ return;
+ } else {
+ playComiMusic(_comiSeqMusicTable[num].name, &_comiSeqMusicTable[num], 0, true);
+ _nextSeqToPlay = 0;
+ }
+ } else {
+ if (_nextSeqToPlay != 0) {
+ playComiMusic(_comiSeqMusicTable[_nextSeqToPlay].name, &_comiSeqMusicTable[_nextSeqToPlay], 0, true);
+ num = _nextSeqToPlay;
+ _nextSeqToPlay = 0;
+ } else {
+ if (_curMusicState != 0) {
+ playComiMusic(_comiStateMusicTable[_curMusicState].name, &_comiStateMusicTable[_curMusicState], _curMusicState, true);
+ } else {
+ playComiMusic(nullptr, &_comiStateMusicTable[0], _curMusicState, true);
+ }
+ num = 0;
+ }
+ }
+
+ _curMusicSeq = num;
+}
+
+void IMuseDigital::playFtMusic(const char *songName, int transitionType, int volume) {
+ int oldSoundId = 0;
+ int soundId;
+
+ // Check for any music piece which is played as a SFX (without an associated stream)
+ // and fade it out
+ for (int i = diMUSEGetNextSound(0); i; i = diMUSEGetNextSound(i)) {
+ if (diMUSEGetParam(i, DIMUSE_P_GROUP) == DIMUSE_GROUP_MUSICEFF && !diMUSEGetParam(i, DIMUSE_P_SND_HAS_STREAM))
+ diMUSEFadeParam(i, DIMUSE_P_VOLUME, 0, 200);
+ }
+
+ // Now grab the current standard music soundId: it will either be crossfaded by SwitchStream,
+ // or faded out
+ for (int j = diMUSEGetNextSound(0); j; j = diMUSEGetNextSound(j)) {
+ if (diMUSEGetParam(j, DIMUSE_P_GROUP) == DIMUSE_GROUP_MUSICEFF && diMUSEGetParam(j, DIMUSE_P_SND_HAS_STREAM))
+ oldSoundId = j;
+ }
+
+ if (songName) {
+ switch (transitionType) {
+ case 0:
+ debug(5, "IMuseDigital::playFtMusic(): NULL transition, ignored");
+ return;
+ case 1:
+ soundId = getSoundIdByName(songName);
+ _filesHandler->openSound(soundId);
+ if (!soundId) {
+ debug(5, "IMuseDigital::playFtMusic(): failed to retrieve soundId for sound \"%s\"", songName);
+ break;
+ }
+
+ if (diMUSEStartSound(soundId, 126))
+ debug(5, "IMuseDigital::playFtMusic(): transition 1, failed to start sound \"%s\"(%d)", songName, soundId);
+
+ _filesHandler->closeSound(soundId);
+ diMUSESetParam(soundId, DIMUSE_P_GROUP, DIMUSE_GROUP_MUSICEFF);
+ diMUSESetParam(soundId, DIMUSE_P_VOLUME, volume);
+ break;
+ case 2:
+ case 3:
+ soundId = getSoundIdByName(songName);
+ if (soundId)
+ _filesHandler->openSound(soundId);
+
+ if (soundId) {
+ if (oldSoundId) {
+ if (oldSoundId != soundId || transitionType == 2) {
+ diMUSESwitchStream(oldSoundId, soundId, _ftCrossfadeBuffer, sizeof(_ftCrossfadeBuffer), 0);
+ }
+
+ // WORKAROUND for bug in the original: at the beginning of the game, going in
+ // and out of the bar a couple of times breaks and temporarily stops the music
+ // Here, we override the fade out, just like the remastered does
+ if (oldSoundId == soundId && soundId == 622) {
+ diMUSEFadeParam(soundId, DIMUSE_P_VOLUME, volume, 200);
+ }
+ } else if (diMUSEStartStream(soundId, 126, DIMUSE_BUFFER_MUSIC)) {
+ debug(5, "IMuseDigital::playFtMusic(): failed to start the stream for \"%s\" (%d)", songName, soundId);
+ }
+
+ _filesHandler->closeSound(soundId);
+ diMUSESetParam(soundId, DIMUSE_P_GROUP, DIMUSE_GROUP_MUSICEFF);
+ diMUSESetParam(soundId, DIMUSE_P_VOLUME, volume);
+ } else {
+ debug(5, "IMuseDigital::playFtMusic(): failed to retrieve soundId for sound \"%s\" (%d)", songName, soundId);
+ }
+ break;
+ case 4:
+ if (oldSoundId)
+ diMUSEFadeParam(oldSoundId, DIMUSE_P_VOLUME, 0, 200);
+ return;
+ default:
+ debug(5, "IMuseDigital::playFtMusic(): bogus transition type, ignored");
+ return;
+ }
+ } else {
+ if (oldSoundId)
+ diMUSEFadeParam(oldSoundId, DIMUSE_P_VOLUME, 0, 200);
+ }
+}
+
+void IMuseDigital::playDigMusic(const char *songName, const imuseDigTable *table, int attribPos, bool sequence) {
+ int hookId = 0;
+
+ if (songName != nullptr) {
+ if ((_attributes[DIG_SEQ_OFFSET + 38]) && (!_attributes[DIG_SEQ_OFFSET + 41])) {
+ if ((attribPos == 43) || (attribPos == 44))
+ hookId = 3;
+ }
+
+ if ((_attributes[DIG_SEQ_OFFSET + 46] != 0) && (_attributes[DIG_SEQ_OFFSET + 48] == 0)) {
+ if ((attribPos == 38) || (attribPos == 39))
+ hookId = 3;
+ }
+
+ if ((_attributes[DIG_SEQ_OFFSET + 53] != 0)) {
+ if ((attribPos == 50) || (attribPos == 51))
+ hookId = 3;
+ }
+
+ if ((attribPos != 0) && (hookId == 0)) {
+ if (table->attribPos != 0)
+ attribPos = table->attribPos;
+ hookId = _attributes[DIG_STATE_OFFSET + attribPos];
+ if (table->hookId != 0) {
+ if ((hookId != 0) && (table->hookId > 1)) {
+ _attributes[DIG_STATE_OFFSET + attribPos] = 2;
+ } else {
+ _attributes[DIG_STATE_OFFSET + attribPos] = hookId + 1;
+ if (table->hookId < hookId + 1)
+ _attributes[DIG_STATE_OFFSET + attribPos] = 1;
+ }
+ }
+ }
+ }
+
+ int nextSoundId = 0;
+ while (1) {
+ nextSoundId = diMUSEGetNextSound(nextSoundId);
+ if (!nextSoundId)
+ break;
+
+ // If a sound is found (and its stream is active), fade it out if it's a music track
+ if (diMUSEGetParam(nextSoundId, DIMUSE_P_GROUP) == DIMUSE_GROUP_MUSICEFF && !diMUSEGetParam(nextSoundId, DIMUSE_P_SND_HAS_STREAM))
+ diMUSEFadeParam(nextSoundId, DIMUSE_P_VOLUME, 0, 120);
+ }
+
+ int oldSoundId = 0;
+ nextSoundId = 0;
+ while (1) {
+ nextSoundId = diMUSEGetNextSound(nextSoundId);
+ if (!nextSoundId)
+ break;
+
+ if (diMUSEGetParam(nextSoundId, DIMUSE_P_SND_HAS_STREAM) && (diMUSEGetParam(nextSoundId, DIMUSE_P_STREAM_BUFID) == DIMUSE_BUFFER_MUSIC)) {
+ oldSoundId = nextSoundId;
+ break;
+ }
+ }
+
+ if (!songName) {
+ if (oldSoundId)
+ diMUSEFadeParam(oldSoundId, DIMUSE_P_VOLUME, 0, 120);
+ return;
+ }
+
+ switch (table->transitionType) {
+ case 0:
+ debug(5, "IMuseDigital::playDigMusic(): NULL transition, ignored");
+ break;
+ case 1:
+ _filesHandler->openSound(table->soundId);
+ if (table->soundId) {
+ if (diMUSEStartSound(table->soundId, 126))
+ debug(5, "IMuseDigital::playDigMusic(): transition 1, failed to start the sound (%d)", table->soundId);
+ diMUSESetParam(table->soundId, DIMUSE_P_VOLUME, 1);
+ diMUSEFadeParam(table->soundId, DIMUSE_P_VOLUME, 127, 120);
+ _filesHandler->closeSound(table->soundId);
+ diMUSESetParam(table->soundId, DIMUSE_P_GROUP, DIMUSE_GROUP_MUSICEFF);
+ } else {
+ debug(5, "IMuseDigital::playDigMusic(): transition 1, empty soundId, ignored");
+ }
+
+ break;
+ case 2:
+ case 3:
+ case 4:
+ _filesHandler->openSound(table->soundId);
+ if (table->filename[0] == 0 || table->soundId == 0) {
+ if (oldSoundId)
+ diMUSEFadeParam(oldSoundId, DIMUSE_P_VOLUME, 0, 60);
+ return;
+ }
+
+ if (table->transitionType == 4) {
+ _stopSequenceFlag = 0;
+ diMUSESetTrigger(table->soundId, MKTAG('_', 'e', 'n', 'd'), 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1);
+ }
+
+ if (oldSoundId) {
+ if (table->transitionType == 2) {
+ diMUSESwitchStream(oldSoundId, table->soundId, 1800, 0, 0);
+ diMUSESetParam(table->soundId, DIMUSE_P_VOLUME, 127);
+ diMUSESetParam(table->soundId, DIMUSE_P_GROUP, DIMUSE_GROUP_MUSICEFF);
+ diMUSESetHook(table->soundId, hookId);
+ diMUSEProcessStreams();
+ diMUSESetParam(table->soundId, DIMUSE_P_GROUP, DIMUSE_GROUP_MUSICEFF); // Repeated intentionally
+ return;
+ }
+
+ if (oldSoundId != table->soundId) {
+ if ((!sequence) && (table->attribPos != 0) &&
+ (table->attribPos == _digStateMusicTable[_curMusicState].attribPos)) {
+ diMUSESwitchStream(oldSoundId, table->soundId, 1800, 0, 1);
+ diMUSESetParam(table->soundId, DIMUSE_P_VOLUME, 127);
+ diMUSESetParam(table->soundId, DIMUSE_P_GROUP, DIMUSE_GROUP_MUSICEFF);
+ diMUSEProcessStreams();
+ } else {
+ diMUSESwitchStream(oldSoundId, table->soundId, 1800, 0, 0);
+ diMUSESetParam(table->soundId, DIMUSE_P_VOLUME, 127);
+ diMUSESetParam(table->soundId, DIMUSE_P_GROUP, DIMUSE_GROUP_MUSICEFF);
+ diMUSESetHook(table->soundId, hookId);
+ diMUSEProcessStreams();
+ _filesHandler->closeSound(table->soundId);
+ diMUSESetParam(table->soundId, DIMUSE_P_GROUP, DIMUSE_GROUP_MUSICEFF); // Repeated intentionally
+ }
+ }
+ } else {
+ if (diMUSEStartStream(table->soundId, 126, DIMUSE_BUFFER_MUSIC))
+ debug(5, "IMuseDigital::playDigMusic(): failed to start the stream for sound %d", table->soundId);
+ diMUSESetParam(table->soundId, DIMUSE_P_VOLUME, 127);
+ diMUSESetParam(table->soundId, DIMUSE_P_GROUP, DIMUSE_GROUP_MUSICEFF);
+ diMUSESetHook(table->soundId, hookId);
+ }
+ _filesHandler->closeSound(table->soundId);
+ diMUSESetParam(table->soundId, DIMUSE_P_GROUP, DIMUSE_GROUP_MUSICEFF); // Repeated intentionally
+ break;
+ case 5:
+ debug(5, "IMuseDigital::playDigMusic(): no-op transition type (5), ignored");
+ break;
+ case 6:
+ _stopSequenceFlag = 0;
+ diMUSESetTrigger(DIMUSE_SMUSH_SOUNDID + DIMUSE_BUFFER_MUSIC, MKTAG('_', 'e', 'n', 'd'), 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1);
+ break;
+ case 7:
+ if (oldSoundId)
+ diMUSEFadeParam(oldSoundId, DIMUSE_P_VOLUME, 0, 60);
+ break;
+ default:
+ debug(5, "IMuseDigital::playDigMusic(): bogus transition type, ignored");
+ break;
+ }
+}
+
+void IMuseDigital::playComiDemoMusic(const char *songName, const imuseComiTable *table, int attribPos, bool sequence) {
+ // This is a stripped down version of playDigMusic
+ int hookId = 0;
+
+ if (songName != nullptr) {
+ if (attribPos != 0) {
+ if (table->attribPos != 0)
+ attribPos = table->attribPos;
+ }
+ }
+
+ int nextSoundId = 0;
+ while (1) {
+ nextSoundId = diMUSEGetNextSound(nextSoundId);
+ if (!nextSoundId)
+ break;
+
+ // If a sound is found (and its stream is active), fade it out if it's a music track
+ if (diMUSEGetParam(nextSoundId, DIMUSE_P_GROUP) == DIMUSE_GROUP_MUSICEFF && !diMUSEGetParam(nextSoundId, DIMUSE_P_SND_HAS_STREAM))
+ diMUSEFadeParam(nextSoundId, DIMUSE_P_VOLUME, 0, 120);
+ }
+
+ int oldSoundId = 0;
+ nextSoundId = 0;
+ while (1) {
+ nextSoundId = diMUSEGetNextSound(nextSoundId);
+ if (!nextSoundId)
+ break;
+
+ if (diMUSEGetParam(nextSoundId, DIMUSE_P_SND_HAS_STREAM) && (diMUSEGetParam(nextSoundId, DIMUSE_P_STREAM_BUFID) == DIMUSE_BUFFER_MUSIC)) {
+ oldSoundId = nextSoundId;
+ break;
+ }
+ }
+
+ if (!songName) {
+ if (oldSoundId)
+ diMUSEFadeParam(oldSoundId, DIMUSE_P_VOLUME, 0, 120);
+ return;
+ }
+
+ switch (table->transitionType) {
+ case 3:
+ _filesHandler->openSound(table->soundId);
+ if (table->filename[0] == 0 || table->soundId == 0) {
+ if (oldSoundId)
+ diMUSEFadeParam(oldSoundId, DIMUSE_P_VOLUME, 0, 60);
+ return;
+ }
+
+ if (oldSoundId) {
+ if (oldSoundId != table->soundId) {
+ if ((!sequence) && (table->attribPos != 0) &&
+ (table->attribPos == _comiDemoStateMusicTable[_curMusicState].attribPos)) {
+ diMUSESwitchStream(oldSoundId, table->soundId, 1800, 0, 1);
+ diMUSESetParam(table->soundId, DIMUSE_P_VOLUME, 127);
+ diMUSESetParam(table->soundId, DIMUSE_P_GROUP, DIMUSE_GROUP_MUSICEFF);
+ diMUSEProcessStreams();
+ } else {
+ diMUSESwitchStream(oldSoundId, table->soundId, 1800, 0, 0);
+ diMUSESetParam(table->soundId, DIMUSE_P_VOLUME, 127);
+ diMUSESetParam(table->soundId, DIMUSE_P_GROUP, DIMUSE_GROUP_MUSICEFF);
+ diMUSESetHook(table->soundId, hookId);
+ diMUSEProcessStreams();
+ _filesHandler->closeSound(table->soundId);
+ diMUSESetParam(table->soundId, DIMUSE_P_GROUP, DIMUSE_GROUP_MUSICEFF); // Repeated intentionally
+ }
+ }
+ } else {
+ if (diMUSEStartStream(table->soundId, 126, DIMUSE_BUFFER_MUSIC))
+ debug(5, "IMuseDigital::playComiDemoMusic(): failed to start the stream for sound %d", table->soundId);
+ diMUSESetParam(table->soundId, DIMUSE_P_VOLUME, 127);
+ diMUSESetParam(table->soundId, DIMUSE_P_GROUP, DIMUSE_GROUP_MUSICEFF);
+ diMUSESetHook(table->soundId, hookId);
+ }
+ _filesHandler->closeSound(table->soundId);
+ diMUSESetParam(table->soundId, DIMUSE_P_GROUP, DIMUSE_GROUP_MUSICEFF); // Repeated intentionally
+ break;
+ default:
+ debug(5, "IMuseDigital::playDigMusic(): bogus or unused transition type, ignored");
+ break;
+ }
+}
+
+void IMuseDigital::playComiMusic(const char *songName, const imuseComiTable *table, int attribPos, bool sequence) {
+ int hookId = 0;
+ int fadeDelay = 0;
+
+ if ((songName != nullptr) && (attribPos != 0)) {
+ if (table->attribPos != 0)
+ attribPos = table->attribPos;
+ hookId = _attributes[COMI_STATE_OFFSET + attribPos];
+
+ if (table->hookId != 0) {
+ if ((hookId != 0) && (table->hookId > 1)) {
+ _attributes[COMI_STATE_OFFSET + attribPos] = 2;
+ } else {
+ _attributes[COMI_STATE_OFFSET + attribPos] = hookId + 1;
+ if (table->hookId < hookId + 1)
+ _attributes[COMI_STATE_OFFSET + attribPos] = 1;
+ }
+ }
+ }
+
+ int nextSoundId = 0;
+ while (1) {
+ nextSoundId = diMUSEGetNextSound(nextSoundId);
+ if (!nextSoundId)
+ break;
+
+ // If a sound is found (and its stream is active), fade it out if it's a music track
+ if (diMUSEGetParam(nextSoundId, DIMUSE_P_GROUP) == DIMUSE_GROUP_MUSICEFF && !diMUSEGetParam(nextSoundId, DIMUSE_P_SND_HAS_STREAM))
+ diMUSEFadeParam(nextSoundId, DIMUSE_P_VOLUME, 0, 120);
+ }
+
+ int oldSoundId = 0;
+ nextSoundId = 0;
+ while (1) {
+ nextSoundId = diMUSEGetNextSound(nextSoundId);
+ if (!nextSoundId)
+ break;
+
+ if (diMUSEGetParam(nextSoundId, DIMUSE_P_SND_HAS_STREAM) && (diMUSEGetParam(nextSoundId, DIMUSE_P_STREAM_BUFID) == DIMUSE_BUFFER_MUSIC)) {
+ oldSoundId = nextSoundId;
+ break;
+ }
+ }
+
+ if (!songName) {
+ if (oldSoundId)
+ diMUSEFadeParam(oldSoundId, DIMUSE_P_VOLUME, 0, 120);
+ return;
+ }
+
+ switch (table->transitionType) {
+ case 0:
+ debug(5, "IMuseDigital::playComiMusic(): NULL transition, ignored");
+ break;
+ case 1:
+ _filesHandler->openSound(table->soundId);
+ if (table->soundId) {
+ if (diMUSEStartSound(table->soundId, 126))
+ debug(5, "IMuseDigital::playComiMusic(): transition 1, failed to start the sound (%d)", table->soundId);
+ diMUSESetParam(table->soundId, DIMUSE_P_VOLUME, 1);
+ diMUSEFadeParam(table->soundId, DIMUSE_P_VOLUME, 127, 120);
+ _filesHandler->closeSound(table->soundId);
+ diMUSESetParam(table->soundId, DIMUSE_P_GROUP, DIMUSE_GROUP_MUSICEFF);
+ } else {
+ debug(5, "IMuseDigital::playComiMusic(): transition 1, empty soundId, ignored");
+ }
+ break;
+ case 2:
+ case 3:
+ case 4:
+ case 12:
+ _filesHandler->openSound(table->soundId);
+ if (table->filename[0] == 0 || table->soundId == 0) {
+ if (oldSoundId)
+ diMUSEFadeParam(oldSoundId, DIMUSE_P_VOLUME, 0, 60);
+ break;
+ }
+
+ if (table->transitionType == 4) {
+ _stopSequenceFlag = 0;
+ diMUSESetTrigger(table->soundId, MKTAG('_', 'e', 'n', 'd'), 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1);
+ }
+
+ if (oldSoundId) {
+ fadeDelay = table->fadeOutDelay;
+ if (!fadeDelay)
+ fadeDelay = 1000;
+ else
+ fadeDelay = (fadeDelay * 100) / 6; // Set dimuse_table fade out time to millisecond scale
+
+ if (table->transitionType == 2) {
+ diMUSESwitchStream(oldSoundId, table->soundId, fadeDelay, 0, 0);
+ diMUSESetParam(table->soundId, DIMUSE_P_VOLUME, 127);
+ diMUSESetParam(table->soundId, DIMUSE_P_GROUP, DIMUSE_GROUP_MUSICEFF);
+ diMUSESetHook(table->soundId, table->hookId);
+ diMUSEProcessStreams();
+ } else if (oldSoundId != table->soundId) {
+ if ((!sequence) && (table->attribPos != 0) &&
+ (table->attribPos == _comiStateMusicTable[_curMusicState].attribPos)) {
+
+ debug(5, "IMuseDigital::playComiMusic(): Starting new sound (%s) with same attribute as old sound (%s)",
+ table->name, _comiStateMusicTable[_curMusicState].name);
+
+ diMUSESwitchStream(oldSoundId, table->soundId, fadeDelay, 0, 1);
+ diMUSESetParam(table->soundId, DIMUSE_P_VOLUME, 127);
+ diMUSESetParam(table->soundId, DIMUSE_P_GROUP, DIMUSE_GROUP_MUSICEFF);
+ diMUSEProcessStreams();
+ } else {
+ switch (table->transitionType) {
+ case 12:
+ diMUSESetHook(oldSoundId, table->hookId);
+ diMUSESetTrigger(oldSoundId, MKTAG('e', 'x', 'i', 't'), 26, oldSoundId, table->soundId, fadeDelay, 1, 0, -1, -1, -1, -1, -1, -1);
+ diMUSESetTrigger(oldSoundId, MKTAG('e', 'x', 'i', 't'), 12, table->soundId, DIMUSE_P_VOLUME, 127, -1, -1, -1, -1, -1, -1, -1, -1);
+ diMUSESetTrigger(oldSoundId, MKTAG('e', 'x', 'i', 't'), 12, table->soundId, DIMUSE_P_GROUP, 4, -1, -1, -1, -1, -1, -1, -1, -1);
+ diMUSESetTrigger(oldSoundId, MKTAG('e', 'x', 'i', 't'), 15, table->soundId, hookId, -1, -1, -1, -1, -1, -1, -1, -1, -1);
+ diMUSEProcessStreams();
+ break;
+ default:
+ diMUSESwitchStream(oldSoundId, table->soundId, fadeDelay, 0, 0);
+ diMUSESetParam(table->soundId, DIMUSE_P_VOLUME, 127);
+ diMUSESetParam(table->soundId, DIMUSE_P_GROUP, 4);
+ diMUSESetHook(table->soundId, hookId);
+ diMUSEProcessStreams();
+ break;
+ }
+ }
+ }
+ } else {
+ if (diMUSEStartStream(table->soundId, 126, DIMUSE_BUFFER_MUSIC))
+ debug(5, "IMuseDigital::playComiMusic(): failed to start the stream for sound %d", table->soundId);
+ diMUSESetParam(table->soundId, DIMUSE_P_VOLUME, 127);
+ diMUSESetParam(table->soundId, DIMUSE_P_GROUP, DIMUSE_GROUP_MUSICEFF);
+ diMUSESetHook(table->soundId, hookId);
+ }
+ _filesHandler->closeSound(table->soundId);
+ diMUSESetParam(table->soundId, DIMUSE_P_GROUP, DIMUSE_GROUP_MUSICEFF);
+ break;
+ case 5:
+ debug(5, "IMuseDigital::playComiMusic(): no-op transition type (5), ignored");
+ break;
+ case 6:
+ _stopSequenceFlag = 0;
+ diMUSESetTrigger(DIMUSE_SMUSH_SOUNDID + DIMUSE_BUFFER_MUSIC, MKTAG('_', 'e', 'n', 'd'), 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1);
+ break;
+ case 7:
+ if (oldSoundId)
+ diMUSEFadeParam(oldSoundId, DIMUSE_P_VOLUME, 0, 60);
+ break;
+ case 8:
+ if (oldSoundId)
+ diMUSESetHook(oldSoundId, table->hookId);
+ break;
+ case 9:
+ if (oldSoundId)
+ diMUSESetHook(oldSoundId, table->hookId);
+ _stopSequenceFlag = 0;
+ diMUSESetTrigger(oldSoundId, MKTAG('_', 'e', 'n', 'd'), 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1);
+ break;
+ default:
+ debug(5, "IMuseDigital::playComiMusic(): bogus transition type, ignored");
+ break;
+ }
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/imuse_digi/dimuse_sndmgr.cpp b/engines/scumm/imuse_digi/dimuse_sndmgr.cpp
index d3f00e01ee..976c33c24d 100644
--- a/engines/scumm/imuse_digi/dimuse_sndmgr.cpp
+++ b/engines/scumm/imuse_digi/dimuse_sndmgr.cpp
@@ -57,260 +57,11 @@ ImuseDigiSndMgr::~ImuseDigiSndMgr() {
BundleCodecs::releaseImcTables();
}
-void ImuseDigiSndMgr::countElements(byte *ptr, int &numRegions, int &numJumps, int &numSyncs, int &numMarkers) {
- uint32 tag;
- int32 size = 0;
-
- do {
- tag = READ_BE_UINT32(ptr); ptr += 4;
- switch (tag) {
- case MKTAG('S','T','O','P'):
- case MKTAG('F','R','M','T'):
- case MKTAG('D','A','T','A'):
- size = READ_BE_UINT32(ptr); ptr += size + 4;
- break;
- case MKTAG('T','E','X','T'):
- if (!scumm_stricmp((const char *)(ptr + 8), "exit") || _vm->_game.id == GID_CMI)
- numMarkers++;
- size = READ_BE_UINT32(ptr); ptr += size + 4;
- break;
- case MKTAG('R','E','G','N'):
- numRegions++;
- size = READ_BE_UINT32(ptr); ptr += size + 4;
- break;
- case MKTAG('J','U','M','P'):
- numJumps++;
- size = READ_BE_UINT32(ptr); ptr += size + 4;
- break;
- case MKTAG('S','Y','N','C'):
- numSyncs++;
- size = READ_BE_UINT32(ptr); ptr += size + 4;
- break;
- default:
- error("ImuseDigiSndMgr::countElements() Unknown sfx header '%s'", tag2str(tag));
- }
- } while (tag != MKTAG('D','A','T','A'));
-}
-
-void ImuseDigiSndMgr::prepareSoundFromRMAP(Common::SeekableReadStream *file, SoundDesc *sound, int32 offset, int32 size) {
- int l;
-
- file->seek(offset, SEEK_SET);
- uint32 tag = file->readUint32BE();
- assert(tag == MKTAG('R','M','A','P'));
- int32 version = file->readUint32BE();
- if (version != 3) {
- if (version == 2) {
- warning("ImuseDigiSndMgr::prepareSoundFromRMAP: Wrong version of compressed *.bun file, expected 3, but it's 2");
- warning("Suggested to recompress with latest tool from daily builds");
- } else
- error("ImuseDigiSndMgr::prepareSoundFromRMAP: Wrong version number, expected 3, but it's: %d", version);
- }
- sound->bits = file->readUint32BE();
- sound->freq = file->readUint32BE();
- sound->channels = file->readUint32BE();
- sound->numRegions = file->readUint32BE();
- sound->numJumps = file->readUint32BE();
- sound->numSyncs = file->readUint32BE();
- if (version >= 3)
- sound->numMarkers = file->readUint32BE();
- else
- sound->numMarkers = 0;
-
- sound->region = new Region[sound->numRegions];
- assert(sound->region);
- sound->jump = new Jump[sound->numJumps];
- assert(sound->jump);
- sound->sync = new Sync[sound->numSyncs];
- assert(sound->sync);
- sound->marker = new Marker[sound->numMarkers];
- assert(sound->marker);
-
- for (l = 0; l < sound->numRegions; l++) {
- sound->region[l].offset = file->readUint32BE();
- sound->region[l].length = file->readUint32BE();
- }
- for (l = 0; l < sound->numJumps; l++) {
- sound->jump[l].offset = file->readUint32BE();
- sound->jump[l].dest = file->readUint32BE();
- sound->jump[l].hookId = file->readUint32BE();
- sound->jump[l].fadeDelay = file->readUint32BE();
- }
- for (l = 0; l < sound->numSyncs; l++) {
- sound->sync[l].size = file->readUint32BE();
- sound->sync[l].ptr = new byte[sound->sync[l].size];
- file->read(sound->sync[l].ptr, sound->sync[l].size);
- }
- if (version >= 3) {
- for (l = 0; l < sound->numMarkers; l++) {
- sound->marker[l].pos = file->readUint32BE();
- sound->marker[l].length = file->readUint32BE();
- sound->marker[l].ptr = new char[sound->marker[l].length];
- file->read(sound->marker[l].ptr, sound->marker[l].length);
- }
- }
-}
-
-void ImuseDigiSndMgr::prepareSound(byte *ptr, SoundDesc *sound, bool rawBundle) {
- if (READ_BE_UINT32(ptr) == MKTAG('C','r','e','a')) {
- bool quit = false;
- int len;
-
- int32 offset = READ_LE_UINT16(ptr + 20);
- int16 code = READ_LE_UINT16(ptr + 24);
-
- sound->numRegions = 0;
- sound->region = new Region[70];
- assert(sound->region);
-
- sound->numJumps = 0;
- sound->jump = new Jump[1];
- assert(sound->jump);
-
- sound->numSyncs = 0;
-
- sound->resPtr = ptr;
- sound->bits = 8;
- sound->channels = 1;
-
- while (!quit) {
- len = READ_LE_UINT32(ptr + offset);
- code = len & 0xFF;
- if ((code != 0) && (code != 1) && (code != 6) && (code != 7)) {
- // try again with 2 bytes forward (workaround for some FT sounds (ex.362, 363)
- offset += 2;
- len = READ_LE_UINT32(ptr + offset);
- code = len & 0xFF;
- if ((code != 0) && (code != 1) && (code != 6) && (code != 7)) {
- error("Invalid code in VOC file : %d", code);
- }
- }
- offset += 4;
- len >>= 8;
- switch (code) {
- case 0:
- quit = true;
- break;
- case 1:
- {
- int time_constant = ptr[offset];
- offset += 2;
- len -= 2;
- sound->freq = Audio::getSampleRateFromVOCRate(time_constant);
- sound->region[sound->numRegions].offset = offset;
- sound->region[sound->numRegions].length = len;
- sound->numRegions++;
- }
- break;
- case 6: // begin of loop
- sound->jump[0].dest = offset + 8;
- sound->jump[0].hookId = 0;
- sound->jump[0].fadeDelay = 0;
- break;
- case 7: // end of loop
- sound->jump[0].offset = offset - 4;
- sound->numJumps++;
- sound->region[sound->numRegions].offset = offset - 4;
- sound->region[sound->numRegions].length = 0;
- sound->numRegions++;
- break;
- default:
- error("Invalid code in VOC file : %d", code);
- quit = true;
- break;
- }
- offset += len;
- }
- } else if (READ_BE_UINT32(ptr) == MKTAG('i','M','U','S')) {
- uint32 tag;
- int32 size = 0;
- byte *s_ptr = ptr;
- ptr += 16;
-
- int curIndexRegion = 0;
- int curIndexJump = 0;
- int curIndexSync = 0;
- int curIndexMarker = 0;
-
- sound->numRegions = 0;
- sound->numJumps = 0;
- sound->numSyncs = 0;
- sound->numMarkers = 0;
- countElements(ptr, sound->numRegions, sound->numJumps, sound->numSyncs, sound->numMarkers);
- sound->region = new Region[sound->numRegions];
- assert(sound->region);
- sound->jump = new Jump[sound->numJumps];
- assert(sound->jump);
- sound->sync = new Sync[sound->numSyncs];
- assert(sound->sync);
- sound->marker = new Marker[sound->numMarkers];
- assert(sound->marker);
-
- do {
- tag = READ_BE_UINT32(ptr); ptr += 4;
- switch (tag) {
- case MKTAG('F','R','M','T'):
- ptr += 12;
- sound->littleEndian = READ_BE_UINT32(ptr) == 16 && rawBundle;
-
- sound->bits = READ_BE_UINT32(ptr); ptr += 4;
- sound->freq = READ_BE_UINT32(ptr); ptr += 4;
- sound->channels = READ_BE_UINT32(ptr); ptr += 4;
- break;
- case MKTAG('T','E','X','T'):
- if (!scumm_stricmp((const char *)(ptr + 8), "exit") || _vm->_game.id == GID_CMI) {
- sound->marker[curIndexMarker].pos = READ_BE_UINT32(ptr + 4);
- sound->marker[curIndexMarker].length = strlen((const char *)(ptr + 8)) + 1;
- sound->marker[curIndexMarker].ptr = new char[sound->marker[curIndexMarker].length];
- assert(sound->marker[curIndexMarker].ptr);
- strcpy(sound->marker[curIndexMarker].ptr, (const char *)(ptr + 8));
- curIndexMarker++;
- }
- size = READ_BE_UINT32(ptr); ptr += size + 4;
- break;
- case MKTAG('S','T','O','P'):
- size = READ_BE_UINT32(ptr); ptr += size + 4;
- break;
- case MKTAG('R','E','G','N'):
- ptr += 4;
- sound->region[curIndexRegion].offset = READ_BE_UINT32(ptr); ptr += 4;
- sound->region[curIndexRegion].length = READ_BE_UINT32(ptr); ptr += 4;
- curIndexRegion++;
- break;
- case MKTAG('J','U','M','P'):
- ptr += 4;
- sound->jump[curIndexJump].offset = READ_BE_UINT32(ptr); ptr += 4;
- sound->jump[curIndexJump].dest = READ_BE_UINT32(ptr); ptr += 4;
- sound->jump[curIndexJump].hookId = READ_BE_UINT32(ptr); ptr += 4;
- sound->jump[curIndexJump].fadeDelay = READ_BE_UINT32(ptr); ptr += 4;
- curIndexJump++;
- break;
- case MKTAG('S','Y','N','C'):
- size = READ_BE_UINT32(ptr); ptr += 4;
- sound->sync[curIndexSync].size = size;
- sound->sync[curIndexSync].ptr = new byte[size];
- assert(sound->sync[curIndexSync].ptr);
- memcpy(sound->sync[curIndexSync].ptr, ptr, size);
- curIndexSync++;
- ptr += size;
- break;
- case MKTAG('D','A','T','A'):
- ptr += 4;
- break;
- default:
- error("ImuseDigiSndMgr::prepareSound(%d/%s) Unknown sfx header '%s'", sound->soundId, sound->name, tag2str(tag));
- }
- } while (tag != MKTAG('D','A','T','A'));
- sound->offsetData = ptr - s_ptr;
- } else {
- error("ImuseDigiSndMgr::prepareSound(): Unknown sound format");
- }
-}
-
ImuseDigiSndMgr::SoundDesc *ImuseDigiSndMgr::allocSlot() {
for (int l = 0; l < MAX_IMUSE_SOUNDS; l++) {
if (!_sounds[l].inUse) {
_sounds[l].inUse = true;
+ _sounds[l].scheduledForDealloc = false;
return &_sounds[l];
}
}
@@ -320,31 +71,32 @@ ImuseDigiSndMgr::SoundDesc *ImuseDigiSndMgr::allocSlot() {
bool ImuseDigiSndMgr::openMusicBundle(SoundDesc *sound, int &disk) {
bool result = false;
+ bool compressed = false;
sound->bundle = new BundleMgr(_cacheBundleDir);
assert(sound->bundle);
if (_vm->_game.id == GID_CMI) {
if (_vm->_game.features & GF_DEMO) {
- result = sound->bundle->open("music.bun", sound->compressed);
+ result = sound->bundle->open("music.bun", compressed);
} else {
char musicfile[20];
if (disk == -1)
disk = _vm->VAR(_vm->VAR_CURRENTDISK);
sprintf(musicfile, "musdisk%d.bun", disk);
// if (_disk != _vm->VAR(_vm->VAR_CURRENTDISK)) {
-// _vm->_imuseDigital->parseScriptCmds(0x1000, 0, 0, 0, 0, 0, 0, 0);
-// _vm->_imuseDigital->parseScriptCmds(0x2000, 0, 0, 0, 0, 0, 0, 0);
-// _vm->_imuseDigital->stopAllSounds();
+// _vm->_DiMUSE_v1->parseScriptCmds(0x1000, 0, 0, 0, 0, 0, 0, 0);
+// _vm->_DiMUSE_v1->parseScriptCmds(0x2000, 0, 0, 0, 0, 0, 0, 0);
+// _vm->_DiMUSE_v1->stopAllSounds();
// sound->bundle->closeFile();
// }
- result = sound->bundle->open(musicfile, sound->compressed, true);
+ result = sound->bundle->open(musicfile, compressed);
// FIXME: Shouldn't we only set _disk if result == true?
_disk = (byte)_vm->VAR(_vm->VAR_CURRENTDISK);
}
} else if (_vm->_game.id == GID_DIG)
- result = sound->bundle->open("digmusic.bun", sound->compressed, true);
+ result = sound->bundle->open("digmusic.bun", compressed);
else
error("ImuseDigiSndMgr::openMusicBundle() Don't know which bundle file to load");
@@ -355,31 +107,32 @@ bool ImuseDigiSndMgr::openMusicBundle(SoundDesc *sound, int &disk) {
bool ImuseDigiSndMgr::openVoiceBundle(SoundDesc *sound, int &disk) {
bool result = false;
+ bool compressed = false;
sound->bundle = new BundleMgr(_cacheBundleDir);
assert(sound->bundle);
if (_vm->_game.id == GID_CMI) {
if (_vm->_game.features & GF_DEMO) {
- result = sound->bundle->open("voice.bun", sound->compressed);
+ result = sound->bundle->open("voice.bun", compressed);
} else {
char voxfile[20];
if (disk == -1)
disk = _vm->VAR(_vm->VAR_CURRENTDISK);
sprintf(voxfile, "voxdisk%d.bun", disk);
// if (_disk != _vm->VAR(_vm->VAR_CURRENTDISK)) {
-// _vm->_imuseDigital->parseScriptCmds(0x1000, 0, 0, 0, 0, 0, 0, 0);
-// _vm->_imuseDigital->parseScriptCmds(0x2000, 0, 0, 0, 0, 0, 0, 0);
-// _vm->_imuseDigital->stopAllSounds();
+// _vm->_DiMUSE_v1->parseScriptCmds(0x1000, 0, 0, 0, 0, 0, 0, 0);
+// _vm->_DiMUSE_v1->parseScriptCmds(0x2000, 0, 0, 0, 0, 0, 0, 0);
+// _vm->_DiMUSE_v1->stopAllSounds();
// sound->bundle->closeFile();
// }
- result = sound->bundle->open(voxfile, sound->compressed);
+ result = sound->bundle->open(voxfile, compressed);
// FIXME: Shouldn't we only set _disk if result == true?
_disk = (byte)_vm->VAR(_vm->VAR_CURRENTDISK);
}
} else if (_vm->_game.id == GID_DIG)
- result = sound->bundle->open("digvoice.bun", sound->compressed);
+ result = sound->bundle->open("digvoice.bun", compressed);
else
error("ImuseDigiSndMgr::openVoiceBundle() Don't know which bundle file to load");
@@ -391,7 +144,6 @@ bool ImuseDigiSndMgr::openVoiceBundle(SoundDesc *sound, int &disk) {
ImuseDigiSndMgr::SoundDesc *ImuseDigiSndMgr::openSound(int32 soundId, const char *soundName, int soundType, int volGroupId, int disk) {
assert(soundId >= 0);
assert(soundType);
- bool rawBundle = false;
SoundDesc *sound = allocSlot();
if (!sound) {
@@ -406,7 +158,6 @@ ImuseDigiSndMgr::SoundDesc *ImuseDigiSndMgr::openSound(int32 soundId, const char
case IMUSE_RESOURCE:
assert(soundName[0] == 0); // Paranoia check
- _vm->ensureResourceLoaded(rtSound, soundId);
_vm->_res->lock(rtSound, soundId);
ptr = _vm->getResourceAddress(rtSound, soundId);
if (ptr == nullptr) {
@@ -414,6 +165,7 @@ ImuseDigiSndMgr::SoundDesc *ImuseDigiSndMgr::openSound(int32 soundId, const char
return nullptr;
}
sound->resPtr = ptr;
+ sound->resSize = _vm->getResourceSize(rtSound, soundId) - 8;
break;
case IMUSE_BUNDLE:
if (volGroupId == IMUSE_VOLGRP_VOICE)
@@ -426,35 +178,15 @@ ImuseDigiSndMgr::SoundDesc *ImuseDigiSndMgr::openSound(int32 soundId, const char
closeSound(sound);
return nullptr;
}
- if (sound->compressed) {
- char fileName[24];
- int32 offset = 0, size = 0;
- sprintf(fileName, "%s.map", soundName);
- Common::SeekableReadStream *rmapFile = sound->bundle->getFile(fileName, offset, size);
- if (!rmapFile) {
- closeSound(sound);
- return nullptr;
- }
- prepareSoundFromRMAP(rmapFile, sound, offset, size);
- strcpy(sound->name, soundName);
- sound->soundId = soundId;
- sound->type = soundType;
- sound->volGroupId = volGroupId;
- sound->disk = disk;
- return sound;
- } else if (soundName[0] == 0) {
- if (sound->bundle->decompressSampleByIndex(soundId, 0, 0x2000, &ptr, 0, header_outside, rawBundle) == 0 || ptr == nullptr) {
- closeSound(sound);
- free(ptr);
- return nullptr;
- }
- } else {
- if (sound->bundle->decompressSampleByName(soundName, 0, 0x2000, &ptr, header_outside, rawBundle) == 0 || ptr == nullptr) {
+
+ if (soundName[0] != 0) {
+ if (sound->bundle->readFile(soundName, 0x2000, &ptr, header_outside) == 0 || ptr == nullptr) {
closeSound(sound);
free(ptr);
return nullptr;
}
}
+
sound->resPtr = nullptr;
break;
default:
@@ -463,11 +195,8 @@ ImuseDigiSndMgr::SoundDesc *ImuseDigiSndMgr::openSound(int32 soundId, const char
strcpy(sound->name, soundName);
sound->soundId = soundId;
- sound->type = soundType;
- sound->volGroupId = volGroupId;
- sound->disk = _disk;
- prepareSound(ptr, sound, rawBundle);
- if ((soundType == IMUSE_BUNDLE) && !sound->compressed) {
+
+ if (soundType == IMUSE_BUNDLE) {
free(ptr);
}
return sound;
@@ -486,278 +215,68 @@ void ImuseDigiSndMgr::closeSound(SoundDesc *soundDesc) {
_vm->_res->unlock(rtSound, soundDesc->soundId);
}
- delete soundDesc->compressedStream;
delete soundDesc->bundle;
- for (int r = 0; r < soundDesc->numSyncs; r++)
- delete[] soundDesc->sync[r].ptr;
- for (int r = 0; r < soundDesc->numMarkers; r++)
- delete[] soundDesc->marker[r].ptr;
- delete[] soundDesc->region;
- delete[] soundDesc->jump;
- delete[] soundDesc->sync;
- delete[] soundDesc->marker;
memset(soundDesc, 0, sizeof(SoundDesc));
}
-ImuseDigiSndMgr::SoundDesc *ImuseDigiSndMgr::cloneSound(SoundDesc *soundDesc) {
- ImuseDigiSndMgr::SoundDesc *desc;
- assert(checkForProperHandle(soundDesc));
-
- desc = openSound(soundDesc->soundId, soundDesc->name, soundDesc->type, soundDesc->volGroupId, soundDesc->disk);
- if (!desc)
- desc = openSound(soundDesc->soundId, soundDesc->name, soundDesc->type, soundDesc->volGroupId, 1);
- if (!desc)
- desc = openSound(soundDesc->soundId, soundDesc->name, soundDesc->type, soundDesc->volGroupId, 2);
- return desc;
-}
-
-bool ImuseDigiSndMgr::checkForProperHandle(SoundDesc *soundDesc) {
- if (!soundDesc)
- return false;
- for (int l = 0; l < MAX_IMUSE_SOUNDS; l++) {
- if (soundDesc == &_sounds[l])
- return true;
+ImuseDigiSndMgr::SoundDesc *ImuseDigiSndMgr::findSoundById(int soundId) {
+ SoundDesc *soundDesc = nullptr;
+ for (int i = 0; i < MAX_IMUSE_SOUNDS; i++) {
+ if (_sounds[i].soundId == soundId) {
+ soundDesc = &_sounds[i];
+ break;
+ }
}
- return false;
+ return soundDesc;
}
-bool ImuseDigiSndMgr::isSndDataExtComp(SoundDesc *soundDesc) {
- assert(checkForProperHandle(soundDesc));
- return soundDesc->compressed;
+ImuseDigiSndMgr::SoundDesc *ImuseDigiSndMgr::getSounds() {
+ return _sounds;
}
-int ImuseDigiSndMgr::getFreq(SoundDesc *soundDesc) {
- assert(checkForProperHandle(soundDesc));
- return soundDesc->freq;
-}
-
-int ImuseDigiSndMgr::getBits(SoundDesc *soundDesc) {
- assert(checkForProperHandle(soundDesc));
- return soundDesc->bits;
-}
-
-int ImuseDigiSndMgr::getChannels(SoundDesc *soundDesc) {
- assert(checkForProperHandle(soundDesc));
- return soundDesc->channels;
-}
-
-bool ImuseDigiSndMgr::isEndOfRegion(SoundDesc *soundDesc, int region) {
- assert(checkForProperHandle(soundDesc));
- assert(region >= 0 && region < soundDesc->numRegions);
- return soundDesc->endFlag;
-}
-
-int ImuseDigiSndMgr::getNumRegions(SoundDesc *soundDesc) {
- assert(checkForProperHandle(soundDesc));
- return soundDesc->numRegions;
-}
-
-int ImuseDigiSndMgr::getNumJumps(SoundDesc *soundDesc) {
- assert(checkForProperHandle(soundDesc));
- return soundDesc->numJumps;
-}
-
-int ImuseDigiSndMgr::getRegionOffset(SoundDesc *soundDesc, int region) {
- debug(5, "getRegionOffset() region:%d", region);
- assert(checkForProperHandle(soundDesc));
- assert(region >= 0 && region < soundDesc->numRegions);
- return soundDesc->region[region].offset;
-}
-
-int ImuseDigiSndMgr::getJumpIdByRegionAndHookId(SoundDesc *soundDesc, int region, int hookId) {
- debug(5, "getJumpIdByRegionAndHookId() region:%d, hookId:%d", region, hookId);
- assert(checkForProperHandle(soundDesc));
- assert(region >= 0 && region < soundDesc->numRegions);
- int32 offset = soundDesc->region[region].offset;
- int jumpIdCandidate = -1;
- for (int l = 0; l < soundDesc->numJumps; l++) {
- if (offset == soundDesc->jump[l].offset) {
- jumpIdCandidate = l;
- if (soundDesc->jump[l].hookId == hookId)
- return l;
+void ImuseDigiSndMgr::scheduleSoundForDeallocation(int soundId) {
+ SoundDesc *soundDesc = nullptr;
+ for (int i = 0; i < MAX_IMUSE_SOUNDS; i++) {
+ if (_sounds[i].soundId == soundId) {
+ soundDesc = &_sounds[i];
}
}
- // We missed the jump because we didn't have the right hookId...
- // ...but if that jump led to a region with a "start" marker we have to enforce it anyway!
- // This fixes edge-cases (like bug #11956) where a different hookId than the one expected prevents us
- // from playing the music track from the "start" marker, effectively playing also the
- // silence (or ambient noise) which precedes it.
- if (_vm->_game.id == GID_CMI)
- if (jumpIdCandidate != -1) {
- offset = soundDesc->jump[jumpIdCandidate].dest;
- // Element 2 in marker[] should always be "start"
- if (offset == soundDesc->marker[2].pos && !scumm_stricmp(soundDesc->marker[2].ptr, "start"))
- return jumpIdCandidate;
- }
- return -1;
-}
-
-bool ImuseDigiSndMgr::checkForTriggerByRegionAndMarker(SoundDesc *soundDesc, int region, const char *marker) {
- debug(5, "checkForTriggerByRegionAndMarker() region:%d, marker:%s", region, marker);
assert(checkForProperHandle(soundDesc));
- assert(region >= 0 && region < soundDesc->numRegions);
- assert(marker);
- int32 offset = soundDesc->region[region].offset;
- for (int l = 0; l < soundDesc->numMarkers; l++) {
- if (offset == soundDesc->marker[l].pos) {
- if (!scumm_stricmp(soundDesc->marker[l].ptr, marker))
- return true;
- }
- }
-
- return false;
-}
-void ImuseDigiSndMgr::getSyncSizeAndPtrById(SoundDesc *soundDesc, int number, int32 &sync_size, byte **sync_ptr) {
- assert(checkForProperHandle(soundDesc));
- assert(number >= 0);
- if (number < soundDesc->numSyncs) {
- sync_size = soundDesc->sync[number].size;
- *sync_ptr = soundDesc->sync[number].ptr;
- } else {
- sync_size = 0;
- *sync_ptr = nullptr;
- }
+ soundDesc->scheduledForDealloc = true;
}
-int ImuseDigiSndMgr::getRegionIdByJumpId(SoundDesc *soundDesc, int jumpId) {
- debug(5, "getRegionIdByJumpId() jumpId:%d", jumpId);
- assert(checkForProperHandle(soundDesc));
- assert(jumpId >= 0 && jumpId < soundDesc->numJumps);
- int32 dest = soundDesc->jump[jumpId].dest;
- for (int l = 0; l < soundDesc->numRegions; l++) {
- if (dest == soundDesc->region[l].offset) {
- return l;
+void ImuseDigiSndMgr::closeSoundById(int soundId) {
+ SoundDesc *soundDesc = nullptr;
+ for (int i = 0; i < MAX_IMUSE_SOUNDS; i++) {
+ if (_sounds[i].soundId == soundId) {
+ soundDesc = &_sounds[i];
}
}
- return -1;
-}
-
-int ImuseDigiSndMgr::getJumpHookId(SoundDesc *soundDesc, int number) {
- debug(5, "getJumpHookId() number:%d", number);
- assert(checkForProperHandle(soundDesc));
- assert(number >= 0 && number < soundDesc->numJumps);
- return soundDesc->jump[number].hookId;
-}
+ if (soundDesc) {
+ assert(checkForProperHandle(soundDesc));
-int ImuseDigiSndMgr::getJumpFade(SoundDesc *soundDesc, int number) {
- debug(5, "getJumpFade() number:%d", number);
- assert(checkForProperHandle(soundDesc));
- assert(number >= 0 && number < soundDesc->numJumps);
- return soundDesc->jump[number].fadeDelay;
-}
+ if (soundDesc->resPtr) {
+ _vm->_res->unlock(rtSound, soundDesc->soundId);
+ }
-int32 ImuseDigiSndMgr::getDataFromRegion(SoundDesc *soundDesc, int region, byte **buf, int32 offset, int32 size) {
- debug(6, "getDataFromRegion() region:%d, offset:%d, size:%d, numRegions:%d", region, offset, size, soundDesc->numRegions);
- assert(checkForProperHandle(soundDesc));
+ delete soundDesc->bundle;
- // In COMI we allow at least -size*2 as offset, since music
- // tracks need that in order to be realigned after crossfades
- if (_vm->_game.id == GID_CMI)
- assert(buf && offset >= -(size * 2) && size >= 0);
- else
- assert(buf && offset >= 0 && size >= 0);
- assert(region >= 0 && region < soundDesc->numRegions);
-
- int32 region_offset = soundDesc->region[region].offset;
- int32 region_length = soundDesc->region[region].length;
- int32 offset_data = soundDesc->offsetData;
- int32 start = region_offset - offset_data;
-
- if (offset + size + offset_data > region_length) {
- size = region_length - offset;
- soundDesc->endFlag = true;
- } else {
- soundDesc->endFlag = false;
+ memset(soundDesc, 0, sizeof(SoundDesc));
}
+}
- int header_size = soundDesc->offsetData;
- bool header_outside = ((_vm->_game.id == GID_CMI) && !(_vm->_game.features & GF_DEMO));
- if ((soundDesc->bundle) && (!soundDesc->compressed)) {
- size = soundDesc->bundle->decompressSampleByCurIndex(start + offset, size, buf, header_size, header_outside);
- } else if (soundDesc->resPtr) {
- *buf = (byte *)malloc(size);
- assert(*buf);
- memcpy(*buf, soundDesc->resPtr + start + offset + header_size, size);
- } else if ((soundDesc->bundle) && (soundDesc->compressed)) {
- *buf = (byte *)malloc(size);
- assert(*buf);
- char fileName[26];
- int offsetMs = (((offset * 8 * 10) / soundDesc->bits) / (soundDesc->channels * soundDesc->freq)) * 100;
- sprintf(fileName, "%s_reg%03d", soundDesc->name, region);
- if (scumm_stricmp(fileName, soundDesc->lastFileName) != 0) {
- int32 offs = 0, len = 0;
- Common::SeekableReadStream *cmpFile;
-#if defined(USE_FLAC) || defined(USE_VORBIS) || defined(USE_MAD)
- uint8 soundMode = 0;
-#endif
-
- sprintf(fileName, "%s_reg%03d.fla", soundDesc->name, region);
- cmpFile = soundDesc->bundle->getFile(fileName, offs, len);
- if (len) {
-#ifndef USE_FLAC
- error("FLAC library compiled support needed");
-#else
- soundMode = 3;
-#endif
- }
- if (!len) {
- sprintf(fileName, "%s_reg%03d.ogg", soundDesc->name, region);
- cmpFile = soundDesc->bundle->getFile(fileName, offs, len);
- if (len) {
-#ifndef USE_VORBIS
- error("Vorbis library compiled support needed");
-#else
- soundMode = 2;
-#endif
- }
- }
- if (!len) {
- sprintf(fileName, "%s_reg%03d.mp3", soundDesc->name, region);
- cmpFile = soundDesc->bundle->getFile(fileName, offs, len);
- if (len) {
-#ifndef USE_MAD
- error("Mad library compiled support needed");
-#else
- soundMode = 1;
-#endif
- }
- }
- assert(len);
-
- if (!soundDesc->compressedStream) {
- Common::SeekableReadStream *tmp = cmpFile->readStream(len);
- assert(tmp);
-#ifdef USE_FLAC
- if (soundMode == 3)
- soundDesc->compressedStream = Audio::makeFLACStream(tmp, DisposeAfterUse::YES);
-#endif
-#ifdef USE_VORBIS
- if (soundMode == 2)
- soundDesc->compressedStream = Audio::makeVorbisStream(tmp, DisposeAfterUse::YES);
-#endif
-#ifdef USE_MAD
- if (soundMode == 1)
- soundDesc->compressedStream = Audio::makeMP3Stream(tmp, DisposeAfterUse::YES);
-#endif
- assert(soundDesc->compressedStream);
- soundDesc->compressedStream->seek(offsetMs);
- }
- strcpy(soundDesc->lastFileName, fileName);
- }
- size = soundDesc->compressedStream->readBuffer((int16 *)*buf, size / 2) * 2;
- if (soundDesc->compressedStream->endOfData() || soundDesc->endFlag) {
- delete soundDesc->compressedStream;
- soundDesc->compressedStream = nullptr;
- soundDesc->lastFileName[0] = 0;
- soundDesc->endFlag = true;
- }
+bool ImuseDigiSndMgr::checkForProperHandle(SoundDesc *soundDesc) {
+ if (!soundDesc)
+ return false;
+ for (int l = 0; l < MAX_IMUSE_SOUNDS; l++) {
+ if (soundDesc == &_sounds[l])
+ return true;
}
-
- return size;
+ return false;
}
} // End of namespace Scumm
diff --git a/engines/scumm/imuse_digi/dimuse_sndmgr.h b/engines/scumm/imuse_digi/dimuse_sndmgr.h
index a3f65c194c..6a06a9f240 100644
--- a/engines/scumm/imuse_digi/dimuse_sndmgr.h
+++ b/engines/scumm/imuse_digi/dimuse_sndmgr.h
@@ -52,74 +52,25 @@ public:
#define IMUSE_VOLGRP_SFX 2
#define IMUSE_VOLGRP_MUSIC 3
-private:
- struct Region {
- int32 offset; // offset of region
- int32 length; // length of region
- };
-
- struct Jump {
- int32 offset; // jump offset position
- int32 dest; // jump to dest position
- byte hookId; // id of hook
- int16 fadeDelay; // fade delay in ms
- };
-
- struct Sync {
- int32 size; // size of sync
- byte *ptr; // pointer to sync
- };
-
- struct Marker {
- int32 pos; // position Markaer in sound data
- int32 length; // length of marker string
- char *ptr; // pointer to string
- };
-
public:
struct SoundDesc {
- uint16 freq; // frequency
- byte channels; // stereo or mono
- byte bits; // 8, 12, 16
- bool littleEndian; // Endianness: default is big for original files and native for recompressed ones
-
- int numJumps; // number of Jumps
- Region *region;
-
- int numRegions; // number of Regions
- Jump *jump;
-
- int numSyncs; // number of Syncs
- Sync *sync;
-
- int numMarkers; // number of Markers
- Marker *marker;
-
- bool endFlag;
bool inUse;
- byte *allData;
- int32 offsetData;
+ bool scheduledForDealloc;
+
byte *resPtr;
+ int resSize;
+ int resCurOffset;
char name[15];
int16 soundId;
BundleMgr *bundle;
- int type;
- int volGroupId;
- int disk;
- Audio::SeekableAudioStream *compressedStream;
- bool compressed;
- char lastFileName[24];
};
private:
SoundDesc _sounds[MAX_IMUSE_SOUNDS];
-
bool checkForProperHandle(SoundDesc *soundDesc);
SoundDesc *allocSlot();
- void prepareSound(byte *ptr, SoundDesc *sound, bool uncompressedBundle);
- void prepareSoundFromRMAP(Common::SeekableReadStream *file, SoundDesc *sound, int32 offset, int32 size);
ScummEngine *_vm;
byte _disk;
@@ -128,8 +79,6 @@ private:
bool openMusicBundle(SoundDesc *sound, int &disk);
bool openVoiceBundle(SoundDesc *sound, int &disk);
- void countElements(byte *ptr, int &numRegions, int &numJumps, int &numSyncs, int &numMarkers);
-
public:
ImuseDigiSndMgr(ScummEngine *scumm);
@@ -137,24 +86,11 @@ public:
SoundDesc *openSound(int32 soundId, const char *soundName, int soundType, int volGroupId, int disk);
void closeSound(SoundDesc *soundDesc);
- SoundDesc *cloneSound(SoundDesc *soundDesc);
-
- bool isSndDataExtComp(SoundDesc *soundDesc);
- int getFreq(SoundDesc *soundDesc);
- int getBits(SoundDesc *soundDesc);
- int getChannels(SoundDesc *soundDesc);
- bool isEndOfRegion(SoundDesc *soundDesc, int region);
- int getNumRegions(SoundDesc *soundDesc);
- int getNumJumps(SoundDesc *soundDesc);
- int getRegionOffset(SoundDesc *soundDesc, int region);
- int getJumpIdByRegionAndHookId(SoundDesc *soundDesc, int region, int hookId);
- bool checkForTriggerByRegionAndMarker(SoundDesc *soundDesc, int region, const char *marker);
- int getRegionIdByJumpId(SoundDesc *soundDesc, int jumpId);
- int getJumpHookId(SoundDesc *soundDesc, int number);
- int getJumpFade(SoundDesc *soundDesc, int number);
- void getSyncSizeAndPtrById(SoundDesc *soundDesc, int number, int32 &sync_size, byte **sync_ptr);
-
- int32 getDataFromRegion(SoundDesc *soundDesc, int region, byte **buf, int32 offset, int32 size);
+ void closeSoundById(int soundId);
+ SoundDesc *findSoundById(int soundId);
+ SoundDesc *getSounds();
+ void scheduleSoundForDeallocation(int soundId);
+
};
} // End of namespace Scumm
diff --git a/engines/scumm/imuse_digi/dimuse_streamer.cpp b/engines/scumm/imuse_digi/dimuse_streamer.cpp
new file mode 100644
index 0000000000..1050657113
--- /dev/null
+++ b/engines/scumm/imuse_digi/dimuse_streamer.cpp
@@ -0,0 +1,386 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "scumm/imuse_digi/dimuse_engine.h"
+
+namespace Scumm {
+
+int IMuseDigital::streamerInit() {
+ for (int l = 0; l < DIMUSE_MAX_STREAMS; l++) {
+ _streams[l].soundId = 0;
+ }
+ _lastStreamLoaded = nullptr;
+ return 0;
+}
+
+IMuseDigiStream *IMuseDigital::streamerAllocateSound(int soundId, int bufId, int32 maxRead) {
+ IMuseDigiSndBuffer *bufInfoPtr = _filesHandler->getBufInfo(bufId);
+ if (!bufInfoPtr) {
+ debug(5, "IMuseDigital::streamerAlloc(): ERROR: couldn't get buffer info");
+ return nullptr;
+ }
+
+ if ((bufInfoPtr->bufSize / 4) <= maxRead) {
+ debug(5, "IMuseDigital::streamerAlloc(): ERROR: maxRead too big for buffer");
+ return nullptr;
+ }
+
+ for (int l = 0; l < DIMUSE_MAX_STREAMS; l++) {
+ if (_streams[l].soundId && _streams[l].bufId == bufId) {
+ debug(5, "IMuseDigital::streamerAlloc(): ERROR: stream bufId %d already in use", bufId);
+ return nullptr;
+ }
+ }
+
+ for (int l = 0; l < DIMUSE_MAX_STREAMS; l++) {
+ if (!_streams[l].soundId) {
+ _streams[l].endOffset = _filesHandler->seek(soundId, 0, SEEK_END, bufId);
+ _streams[l].curOffset = 0;
+ _streams[l].soundId = soundId;
+ _streams[l].bufId = bufId;
+ _streams[l].buf = bufInfoPtr->buffer;
+ _streams[l].bufFreeSize = bufInfoPtr->bufSize - maxRead - (_isEarlyDiMUSE ? 0 : 4);
+ _streams[l].loadSize = bufInfoPtr->loadSize;
+ _streams[l].criticalSize = bufInfoPtr->criticalSize;
+ _streams[l].maxRead = maxRead;
+ _streams[l].loadIndex = 0;
+ _streams[l].readIndex = 0;
+ _streams[l].paused = 0;
+ _streams[l].vocLoopFlag = 0;
+ _streams[l].vocLoopTriggerOffset = 0;
+ return &_streams[l];
+ }
+ }
+ debug(5, "IMuseDigital::streamerAlloc(): ERROR: no spare streams");
+ return nullptr;
+}
+
+int IMuseDigital::streamerClearSoundInStream(IMuseDigiStream *streamPtr) {
+ streamPtr->soundId = 0;
+ if (_lastStreamLoaded == streamPtr) {
+ _lastStreamLoaded = 0;
+ }
+ return 0;
+}
+
+int IMuseDigital::streamerProcessStreams() {
+ if (!_isEarlyDiMUSE)
+ dispatchPredictFirstStream();
+
+ IMuseDigiStream *stream1 = nullptr;
+ IMuseDigiStream *stream2 = nullptr;
+
+ for (int l = 0; l < DIMUSE_MAX_STREAMS; l++) {
+ if ((_streams[l].soundId) && (!_streams[l].paused)) {
+ if (stream2) {
+ if (stream1) {
+ debug(5, "IMuseDigital::streamerProcessStreams(): WARNING: three streams in use");
+ } else {
+ stream1 = &_streams[l];
+ }
+ } else {
+ stream2 = &_streams[l];
+ }
+ }
+ }
+
+ if (!stream1) {
+ if (stream2)
+ streamerFetchData(stream2);
+ return 0;
+ }
+
+ if (!stream2) {
+ if (stream1)
+ streamerFetchData(stream1);
+ return 0;
+ }
+
+ // Check if we've reached or surpassed criticalSize
+ bool critical1 = (streamerGetFreeBufferAmount(stream1) >= stream1->criticalSize);
+ bool critical2 = (streamerGetFreeBufferAmount(stream2) >= stream2->criticalSize);
+
+ if (!critical1) {
+ if (!critical2) {
+ if (stream1 == _lastStreamLoaded) {
+ streamerFetchData(stream1);
+ streamerFetchData(stream2);
+ return 0;
+ } else {
+ streamerFetchData(stream2);
+ streamerFetchData(stream1);
+ return 0;
+ }
+ } else {
+ streamerFetchData(stream1);
+ return 0;
+ }
+ }
+
+ if (!critical2) {
+ streamerFetchData(stream2);
+ return 0;
+ } else {
+ if (stream1 == _lastStreamLoaded) {
+ streamerFetchData(stream1);
+ } else {
+ streamerFetchData(stream2);
+ }
+ return 0;
+ }
+}
+
+uint8 *IMuseDigital::streamerGetStreamBuffer(IMuseDigiStream *streamPtr, int size) {
+ if (size > streamerGetFreeBufferAmount(streamPtr) || streamPtr->maxRead < size)
+ return 0;
+
+ if (size > streamPtr->bufFreeSize - streamPtr->readIndex) {
+ memcpy(&streamPtr->buf[streamPtr->bufFreeSize],
+ streamPtr->buf,
+ size - streamPtr->bufFreeSize + streamPtr->readIndex + (_isEarlyDiMUSE ? 0 : 4));
+ }
+
+ int32 readIndex_tmp = streamPtr->readIndex;
+ uint8 *ptr = &streamPtr->buf[readIndex_tmp];
+ streamPtr->readIndex = readIndex_tmp + size;
+ if (streamPtr->bufFreeSize <= readIndex_tmp + size)
+ streamPtr->readIndex = readIndex_tmp + size - streamPtr->bufFreeSize;
+
+ return ptr;
+}
+
+uint8 *IMuseDigital::streamerGetStreamBufferAtOffset(IMuseDigiStream *streamPtr, int32 offset, int size) {
+ if (offset + size > streamerGetFreeBufferAmount(streamPtr) || streamPtr->maxRead < size)
+ return 0;
+
+ int32 offsetReadIndex = offset + streamPtr->readIndex;
+ if (offsetReadIndex >= streamPtr->bufFreeSize) {
+ offsetReadIndex -= streamPtr->bufFreeSize;
+ }
+
+ if (size > streamPtr->bufFreeSize - offsetReadIndex) {
+ memcpy(&streamPtr->buf[streamPtr->bufFreeSize], streamPtr->buf, size + offsetReadIndex - streamPtr->bufFreeSize);
+ }
+
+ return &streamPtr->buf[offsetReadIndex];
+}
+
+int IMuseDigital::streamerSetReadIndex(IMuseDigiStream *streamPtr, int offset) {
+ _streamerBailFlag = 1;
+ if (offset > streamerGetFreeBufferAmount(streamPtr))
+ return -1;
+
+ streamPtr->readIndex += offset;
+ if (streamPtr->readIndex >= streamPtr->bufFreeSize) {
+ streamPtr->readIndex -= streamPtr->bufFreeSize;
+ }
+ return 0;
+}
+
+int IMuseDigital::streamerSetLoadIndex(IMuseDigiStream *streamPtr, int offset) {
+ _streamerBailFlag = 1;
+ if (offset > streamerGetFreeBufferAmount(streamPtr))
+ return -1;
+
+ streamPtr->loadIndex = streamPtr->readIndex + offset;
+ if (streamPtr->loadIndex >= streamPtr->bufFreeSize) {
+ streamPtr->loadIndex -= streamPtr->bufFreeSize;
+ }
+ return 0;
+}
+
+int IMuseDigital::streamerGetFreeBufferAmount(IMuseDigiStream *streamPtr) {
+ int32 freeBufferSize = streamPtr->loadIndex - streamPtr->readIndex;
+ if (freeBufferSize < 0)
+ freeBufferSize += streamPtr->bufFreeSize;
+ return freeBufferSize;
+}
+
+int IMuseDigital::streamerSetSoundToStreamFromOffset(IMuseDigiStream *streamPtr, int soundId, int32 offset) {
+ _streamerBailFlag = 1;
+ streamPtr->soundId = soundId;
+ streamPtr->curOffset = offset;
+ streamPtr->endOffset = _isEarlyDiMUSE ? _filesHandler->seek(streamPtr->soundId, 0, SEEK_END, streamPtr->bufId) : 0;
+ streamPtr->paused = 0;
+ if (_lastStreamLoaded == streamPtr) {
+ _lastStreamLoaded = nullptr;
+ }
+ return 0;
+}
+
+int IMuseDigital::streamerQueryStream(IMuseDigiStream *streamPtr, int32 &bufSize, int32 &criticalSize, int32 &freeSpace, int &paused) {
+ dispatchPredictFirstStream();
+ bufSize = streamPtr->bufFreeSize;
+ criticalSize = streamPtr->criticalSize;
+ freeSpace = streamerGetFreeBufferAmount(streamPtr);
+ paused = streamPtr->paused;
+ return 0;
+}
+
+int IMuseDigital::streamerFeedStream(IMuseDigiStream *streamPtr, uint8 *srcBuf, int32 sizeToFeed, int paused) {
+ int32 size = streamPtr->readIndex - streamPtr->loadIndex;
+ if (size <= 0)
+ size += streamPtr->bufFreeSize;
+ if (sizeToFeed > (size - 4)) {
+ // Don't worry, judging from the intro of The Dig, this is totally expected
+ // to happen, and when it does, the output matches the EXE behavior
+ debug(5, "IMuseDigital::streamerFeedStream(): WARNING: buffer overflow");
+ _streamerBailFlag = 1;
+
+ int32 newTentativeSize = sizeToFeed - (size - 4) - (sizeToFeed - (size - 4)) % 12 + 12;
+ size = streamPtr->loadIndex - streamPtr->readIndex;
+ if (size < 0)
+ size += streamPtr->bufFreeSize;
+ if (size >= newTentativeSize) {
+ streamPtr->readIndex = newTentativeSize + streamPtr->readIndex;
+ if (streamPtr->bufFreeSize <= streamPtr->readIndex)
+ streamPtr->readIndex -= streamPtr->bufFreeSize;
+ }
+ }
+
+ if (sizeToFeed > 0) {
+ do {
+ size = streamPtr->bufFreeSize - streamPtr->loadIndex;
+ if (size >= sizeToFeed) {
+ size = sizeToFeed;
+ }
+ sizeToFeed -= size;
+ memcpy(&streamPtr->buf[streamPtr->loadIndex], srcBuf, size);
+ srcBuf += size;
+
+ int val = size + streamPtr->loadIndex;
+ streamPtr->curOffset += size;
+ streamPtr->loadIndex += size;
+ if (val >= streamPtr->bufFreeSize) {
+ streamPtr->loadIndex = val - streamPtr->bufFreeSize;
+ }
+ } while (sizeToFeed > 0);
+ }
+
+ streamPtr->paused = paused;
+ return 0;
+}
+
+int IMuseDigital::streamerFetchData(IMuseDigiStream *streamPtr) {
+ if (!_isEarlyDiMUSE && streamPtr->endOffset == 0) {
+ streamPtr->endOffset = _filesHandler->seek(streamPtr->soundId, 0, SEEK_END, streamPtr->bufId);
+ }
+
+ int32 size = streamPtr->readIndex - streamPtr->loadIndex;
+ if (size <= 0)
+ size += streamPtr->bufFreeSize;
+
+ int32 loadSize = streamPtr->loadSize;
+ int32 remainingSize = streamPtr->endOffset - streamPtr->curOffset;
+
+ if (loadSize >= size - (_isEarlyDiMUSE ? 1 : 4)) {
+ loadSize = size - (_isEarlyDiMUSE ? 1 : 4);
+ }
+
+ if (loadSize >= remainingSize) {
+ loadSize = remainingSize;
+ }
+
+ if (remainingSize <= 0) {
+ streamPtr->paused = 1;
+
+ if (!_isEarlyDiMUSE) {
+ // Pad the buffer
+ streamPtr->buf[streamPtr->loadIndex] = 127;
+ streamPtr->loadIndex++;
+ streamPtr->buf[streamPtr->loadIndex] = 127;
+ streamPtr->loadIndex++;
+ streamPtr->buf[streamPtr->loadIndex] = 127;
+ streamPtr->loadIndex++;
+ streamPtr->buf[streamPtr->loadIndex] = 127;
+ streamPtr->loadIndex++;
+ }
+ }
+
+ int32 actualAmount;
+ int32 requestedAmount;
+
+ while (1) {
+ if (!_isEarlyDiMUSE && loadSize <= 0)
+ return 0;
+
+ requestedAmount = streamPtr->bufFreeSize - streamPtr->loadIndex;
+ if (requestedAmount >= loadSize) {
+ requestedAmount = loadSize;
+ }
+
+ if (_filesHandler->seek(streamPtr->soundId, streamPtr->curOffset, SEEK_SET, streamPtr->bufId) != streamPtr->curOffset) {
+ debug(5, "IMuseDigital::streamerFetchData(): ERROR: invalid seek in streamer (%d), pausing stream...", streamPtr->curOffset);
+ streamPtr->paused = 1;
+ return 0;
+ }
+
+ _streamerBailFlag = 0;
+
+ Common::StackLock lock(_mutex);
+ actualAmount = _filesHandler->read(streamPtr->soundId, &streamPtr->buf[streamPtr->loadIndex], requestedAmount, streamPtr->bufId);
+ Common::StackLock unlock(_mutex);
+
+ // FT has no bailFlag
+ if (!_isEarlyDiMUSE && _streamerBailFlag)
+ return 0;
+
+ loadSize -= actualAmount;
+ streamPtr->curOffset += actualAmount;
+ _lastStreamLoaded = streamPtr;
+
+ int32 newLoadIndex = actualAmount + streamPtr->loadIndex;
+ streamPtr->loadIndex = newLoadIndex;
+ if (newLoadIndex >= streamPtr->bufFreeSize) {
+ streamPtr->loadIndex = newLoadIndex - streamPtr->bufFreeSize;
+ }
+
+ if (_isEarlyDiMUSE && streamPtr->vocLoopFlag) {
+ if (streamPtr->vocLoopTriggerOffset <= streamPtr->curOffset) {
+ dispatchVOCLoopCallback(streamPtr->soundId);
+ streamPtr->vocLoopFlag = 0;
+ }
+ }
+
+ if (actualAmount != requestedAmount)
+ break;
+
+ // FT checks for negative or zero loadSize here
+ if (_isEarlyDiMUSE && loadSize <= 0)
+ return 0;
+ }
+
+ debug(5, "IMuseDigital::streamerFetchData(): ERROR: unable to load the correct amount of data (req=%d, act=%d)", requestedAmount, actualAmount);
+ _lastStreamLoaded = nullptr;
+ return 0;
+}
+
+void IMuseDigital::streamerSetLoopFlag(IMuseDigiStream *streamPtr, int offset) {
+ streamPtr->vocLoopFlag = 1;
+ streamPtr->vocLoopTriggerOffset = offset;
+}
+
+void IMuseDigital::streamerRemoveLoopFlag(IMuseDigiStream *streamPtr) {
+ streamPtr->vocLoopFlag = 0;
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/imuse_digi/dimuse_tables.cpp b/engines/scumm/imuse_digi/dimuse_tables.cpp
index fdd91f9f64..966160805d 100644
--- a/engines/scumm/imuse_digi/dimuse_tables.cpp
+++ b/engines/scumm/imuse_digi/dimuse_tables.cpp
@@ -20,8 +20,6 @@
*
*/
-
-//#include "scumm/imuse_digi/dimuse.h"
#include "scumm/imuse_digi/dimuse_tables.h"
namespace Scumm {
@@ -853,7 +851,7 @@ const imuseFtSeqTable _ftSeqMusicTable[] = {
{"", 0, 0 },
{"funeral", 2, 127},
- {"", 2, 127},
+ {"", 0, 0 },
{"moshop", 3, 64 },
{"", 0, 0 },
diff --git a/engines/scumm/imuse_digi/dimuse_track.cpp b/engines/scumm/imuse_digi/dimuse_track.cpp
deleted file mode 100644
index 391df09255..0000000000
--- a/engines/scumm/imuse_digi/dimuse_track.cpp
+++ /dev/null
@@ -1,568 +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 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- */
-
-#include "common/config-manager.h"
-#include "common/timer.h"
-
-#include "scumm/actor.h"
More information about the Scummvm-git-logs
mailing list