aboutsummaryrefslogtreecommitdiff
path: root/engines/sci/sfx
diff options
context:
space:
mode:
authorFilippos Karapetis2009-11-15 14:14:49 +0000
committerFilippos Karapetis2009-11-15 14:14:49 +0000
commit544daa5c315f2d598ebeefe43de675fdad1be18a (patch)
treeab1d3d13886c32ffaaebdadcaba7b94a08039552 /engines/sci/sfx
parent3e34b1bbab49aa5b0e4a701589fcfe93c03e93d7 (diff)
downloadscummvm-rg350-544daa5c315f2d598ebeefe43de675fdad1be18a.tar.gz
scummvm-rg350-544daa5c315f2d598ebeefe43de675fdad1be18a.tar.bz2
scummvm-rg350-544daa5c315f2d598ebeefe43de675fdad1be18a.zip
- Started importing the music code from Greg's SCI engine
- Simplified some sound version checks svn-id: r45923
Diffstat (limited to 'engines/sci/sfx')
-rw-r--r--engines/sci/sfx/music.cpp705
-rw-r--r--engines/sci/sfx/music.h207
-rw-r--r--engines/sci/sfx/soundcmd.cpp50
-rw-r--r--engines/sci/sfx/soundcmd.h6
4 files changed, 960 insertions, 8 deletions
diff --git a/engines/sci/sfx/music.cpp b/engines/sci/sfx/music.cpp
new file mode 100644
index 0000000000..ad0fe25b00
--- /dev/null
+++ b/engines/sci/sfx/music.cpp
@@ -0,0 +1,705 @@
+/* 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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "sci/sci.h"
+#include "sci/resource.h"
+#include "sci/sfx/music.h"
+#include "sound/audiostream.h"
+#include "common/config-manager.h"
+
+namespace Sci {
+
+static const int nMidiParams[] = { 2, 2, 2, 2, 1, 1, 2, 0 };
+
+static int f_compare(const void *arg1, const void *arg2) {
+ return ((sciSound *)arg2)->prio - ((sciSound *)arg1)->prio;
+}
+
+SciMusic::SciMusic() {
+}
+
+SciMusic::~SciMusic() {
+ if (_pMidiDrv) {
+ _pMidiDrv->close();
+ delete _pMidiDrv;
+ }
+}
+//----------------------------------------
+void SciMusic::init() {
+ // system init
+ _pMixer = g_system->getMixer();
+ _pMixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, ConfMan.getInt(
+ "sfx_volume"));
+ _pMixer->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType,
+ ConfMan.getInt("speech_volume"));
+ _pMixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType,
+ ConfMan.getInt("music_volume"));
+ // SCI sound init
+ _dwTempo = 0;
+ _midiType = MidiDriver::detectMusicDriver(MDT_MIDI | MDT_ADLIB | MDT_PCSPK
+ | MDT_PREFER_MIDI);
+ _pMidiDrv = MidiDriver::createMidi(_midiType);
+ if (_pMidiDrv) {
+ _pMidiDrv->open();
+ _pMidiDrv->setTimerCallback(this, &miditimerCallback);
+ _dwTempo = _pMidiDrv->getBaseTempo();
+ } else
+ warning("Can't initialise music driver");
+ _bMultiMidi = ConfMan.getBool("multi_midi");
+}
+//----------------------------------------
+bool SciMusic::saveState(Common::OutSaveFile *pFile) {
+ // TODO
+#if 0
+ pFile->writeString("AUDIO\n");
+ // palylist
+ int sz = _playList.size();
+ pFile->writeUint16LE(sz);
+ for(int i = 0; i < sz; i++) {
+ pFile->writeUint16LE(ptr2heap((byte*)_playList[i]));
+ // member variable
+ pFile->writeUint16LE(_audVolNum);
+ pFile->writeByte(_langID);
+ pFile->writeUint16LE(_audioType);
+ pFile->writeUint16LE(_audioRate);
+ // TODO: current playing stream (hCurrentAud) info
+ }
+#endif
+ return true;
+}
+
+//----------------------------------------
+bool SciMusic::restoreState(Common::InSaveFile *pFile){
+ // TODO
+#if 0
+ if (pFile->readLine() != "AUDIO")
+ return false;
+#endif
+ return true;
+}
+//----------------------------------------
+void SciMusic::stopAll() {
+ _pMixer->stopAll();
+ //audioStop();
+ for(uint i = 0; i < _playList.size(); i++){
+ soundStop(_playList[i]);
+ soundKill(_playList[i]);
+ }
+}
+//----------------------------------------
+void SciMusic::miditimerCallback(void *p) {
+ SciMusic* aud = (SciMusic *)p;
+ aud->onTimer();
+}
+//----------------------------------------
+uint16 SciMusic::soundGetVoices() {
+ uint16 res;
+ switch (_midiType) {
+ case MD_PCSPK:
+ res = 1;
+ break;
+ case MD_PCJR:
+ res = 3;
+ break;
+ case MD_ADLIB:
+ res = 8;
+ break;
+ case MD_MT32:
+ res = 16;
+ break;
+ default:
+ res = 1;
+ break;
+ }
+ return res;
+}
+//----------------------------------------
+void SciMusic::sortPlayList() {
+ sciSound ** pData = _playList.begin();
+ qsort(pData, _playList.size(), sizeof(sciSound *), &f_compare);
+}
+
+void SciMusic::patchSysEx(byte * addr, byte *pdata, int len) {
+ byte *buff = new byte[7 + len + 1];
+ uint16 chk = 0;
+ int i;
+
+ buff[0] = 0x41;
+ buff[1] = 0x10;
+ buff[2] = 0x16;
+ buff[3] = 0x12;
+ buff[4] = addr[0];
+ buff[5] = addr[1];
+ buff[6] = addr[2];
+ for (i = 0; i < len; i++) {
+ buff[7 + i] = pdata[i];
+ chk += pdata[i];
+ }
+ chk += addr[0] + addr[1] + addr[2];
+ buff[7 + i] = 128 - chk % 128;
+ _pMidiDrv->sysEx(buff, len + 8);
+ delete[] buff;
+}
+
+void SciMusic::patchUpdateAddr(byte *addr, int len) {
+ addr[2] += len;
+ if (addr[2] >= 0x7F) {
+ addr[1]++;
+ addr[2] -= 0x80;
+ }
+}
+void SciMusic::loadPatch() {
+ if (_midiType == MD_MT32)
+ loadPatchMT32();
+}
+
+// currently loads patch 1.pat for Roland/MT-32 device
+void SciMusic::loadPatchMT32() {
+ //byte sysText[] = { 0x20, 0, 0 };
+ byte sysMem[] = { 0x5, 0, 0 }; // patch memory
+ byte sysRhytm[] = { 0x3, 0x1, 0x10 }; // rhytm
+ byte sysMsg3[15] = { 0x41, 0x10, 0x16, 0x12, 0x52, 0, 0xA, 0x16, 0x16,
+ 0x16, 0x16, 0x16, 0x16, 0x20, 0x80 };
+ byte sysTimbre[] = { 0x8, 0, 0 }; // timbre memory
+ byte sysSystem[] = { 0x10, 0, 4 }; // partial reserve & midi channel
+ byte arr[3][11];
+
+ Resource *res = ((SciEngine *)g_engine)->getResourceManager()->findResource(ResourceId(kResourceTypePatch, 1), 0);
+
+ if (res) {
+ byte *pData = res->data, *p;
+ // welcome message
+ //patchSysEx(sysText, pData + 20, 20);
+ // reading reverb mode, time and level
+ p = pData + 74;
+ for (int i = 0; i < 11; i++) {
+ arr[0][i] = *p++;
+ arr[1][i] = *p++;
+ arr[2][i] = *p++;
+ }
+ // sub_657 - patch memory
+ for (int i = 0; i < 48; i++) {
+ patchSysEx(sysMem, p, 8);
+ patchUpdateAddr(sysMem, 8);
+ p += 8;
+ }
+ // sub_696 - timbre
+ byte dl = *p++, cl = 0;
+ while (dl--) {
+ patchSysEx(sysTimbre, p, 14); // common area
+ patchUpdateAddr(sysTimbre, 14);
+ patchSysEx(sysTimbre, p + 14, 58);// partial 1
+ patchUpdateAddr(sysTimbre, 58);
+ patchSysEx(sysTimbre, p + 72, 58);// partial 2
+ patchUpdateAddr(sysTimbre, 58);
+ patchSysEx(sysTimbre, p + 130, 58);// partial 3
+ patchUpdateAddr(sysTimbre, 58);
+ patchSysEx(sysTimbre, p + 188, 58);// partial 4
+ patchUpdateAddr(sysTimbre, 58);
+ p += 246;
+ cl += 2;
+ sysTimbre[1] = cl;
+ sysTimbre[2] = 0;
+ }
+ // patch memory or rhytm
+ uint16 flag = READ_BE_UINT16(p);
+ p += 2;
+ if (flag == 0xABCD) {
+ // sub_657
+ for (int i = 0; i < 48; i++) {
+ patchSysEx(sysMem, p, 8);
+ patchUpdateAddr(sysMem, 8);
+ p += 8;
+ }
+ } else if (flag == 0xDCBA) {
+ // sub_756
+ for (int i = 0; i < 64; i++) {
+ patchSysEx(sysRhytm, p, 4);
+ patchUpdateAddr(sysRhytm, 4);
+ p += 4;
+ }
+ patchSysEx(sysSystem, p, 18);
+ }
+ // after-init text message
+ //patchSysEx(sysText, pData, 20);
+ // some final sysex
+ _pMidiDrv->sysEx(sysMsg3, 15);
+ // releasing patch resource
+ //g_sci->ResMgr.ResUnload(SCI_RES_PATCH, 1);
+ debug("MT-32 patch loaded");
+ }
+}
+//----------------------------------------
+void SciMusic::soundInitSnd(SoundRes*res, sciSound *pSnd) {
+ //_mutex.lock();
+ SoundRes::tagTrack *pTrack = NULL;
+ switch (_midiType) {
+ case MD_PCSPK:
+ pTrack = res->getTrackByType(SoundRes::kTrackSpeaker);
+ break;
+ case MD_PCJR:
+ pTrack = res->getTrackByType(SoundRes::kTrackTandy);
+ break;
+ case MD_ADLIB:
+ pTrack = res->getTrackByType(SoundRes::kTrackAdlib);
+ break;
+ case MD_MT32:
+ pTrack = res->getTrackByType(SoundRes::kTrackMT32);
+ break;
+ }
+ // attempting to select default MT-32/Roland track
+ if (!pTrack)
+ pTrack = res->getTrackByType(SoundRes::kTrackMT32);
+ if (pTrack) {
+ // if MIDI device is selected but there is no digital track in sound resource
+ // try to use adlib's digital sample if possible
+ if (_midiType <= MD_MT32 && pTrack->nDigital == 0xFF && _bMultiMidi) {
+ if (res->getTrackByType(SoundRes::kTrackAdlib)->nDigital != 0xFF)
+ pTrack = res->getTrackByType(SoundRes::kTrackAdlib);
+ }
+ // play digital sample
+ if (pTrack->nDigital != 0xFF) {
+ byte *pdata = pTrack->aChannels[pTrack->nDigital].ptr;
+ int rate = READ_LE_UINT16(pdata);
+ uint32 size = READ_LE_UINT16(pdata + 2);
+ assert(READ_LE_UINT16(pdata + 4) == 0); // Possibly a compression flag
+ //assert(READ_LE_UINT16(pdata + 6) == size);
+ if (pSnd->pStreamAud)
+ delete pSnd->pStreamAud;
+ pSnd->pStreamAud = Audio::makeLinearInputStream(pdata + 8, size, rate,
+ Audio::Mixer::FLAG_UNSIGNED, 0, 0);
+ pSnd->hCurrentAud = Audio::SoundHandle();
+ } else {// play MIDI track
+ if (pSnd->pMidiParser == NULL) {
+ pSnd->pMidiParser = new MidiParser_SCI();
+ pSnd->pMidiParser->setMidiDriver(_pMidiDrv);
+ pSnd->pMidiParser->setTimerRate(_dwTempo);
+ }
+ pSnd->pMidiParser->loadMusic(pTrack, pSnd);
+ }
+ }
+ //_mutex.unlock();
+}
+//----------------------------------------
+void SciMusic::onTimer() {
+ _mutex.lock();
+ uint sz = _playList.size();
+ for (uint i = 0; i < sz; i++) {
+ if (_playList[i]->sndStatus != kPlaying)
+ continue;
+ if (_playList[i]->pMidiParser) {
+ if (_playList[i]->FadeStep)
+ doFade(_playList[i]);
+ _playList[i]->pMidiParser->onTimer();
+ _playList[i]->ticker = (uint16)_playList[i]->pMidiParser->getTick();
+ } else if (_playList[i]->pStreamAud) {
+ if (_pMixer->isSoundHandleActive(_playList[i]->hCurrentAud)
+ == false) {
+ _playList[i]->ticker = 0xFFFF;
+ _playList[i]->signal = 0xFFFF;
+ _playList[i]->sndStatus = kStopped;
+ } else
+ _playList[i]->ticker = (uint16)(_pMixer->getSoundElapsedTime(
+ _playList[i]->hCurrentAud) * 0.06);
+ }
+ }//for()
+ _mutex.unlock();
+}
+//---------------------------------------------
+void SciMusic::doFade(sciSound *pSnd) {
+ if (pSnd->FadeTicker)
+ pSnd->FadeTicker--;
+ else {
+ pSnd->FadeTicker = pSnd->FadeTickerStep;
+ if (pSnd->volume + pSnd->FadeStep > pSnd->FadeTo) {
+ pSnd->volume = pSnd->FadeTo;
+ pSnd->FadeStep = 0;
+ } else
+ pSnd->volume += pSnd->FadeStep;
+ pSnd->pMidiParser->setVolume(pSnd->volume);
+ }
+}
+
+//---------------------------------------------
+void SciMusic::soundPlay(sciSound *pSnd) {
+ //_mutex.lock();
+ uint sz = _playList.size(), i;
+ // searching if sound is already in _playList
+ for (i = 0; i < sz && _playList[i] != pSnd; i++)
+ ;
+ if (i == sz) {// not found
+ _playList.push_back(pSnd);
+ sortPlayList();
+ }
+
+ if (pSnd->pStreamAud && _pMixer->isSoundHandleActive(pSnd->hCurrentAud)
+ == false)
+ _pMixer->playInputStream(Audio::Mixer::kSFXSoundType, &pSnd->hCurrentAud,
+ pSnd->pStreamAud, -1, pSnd->volume, 0, false);
+ else if (pSnd->pMidiParser) {
+ pSnd->pMidiParser->setVolume(pSnd->volume);
+ if (pSnd->sndStatus == kStopped)
+ pSnd->pMidiParser->jumpToTick(0);
+ }
+ pSnd->sndStatus = kPlaying;
+ //_mutex.unlock();
+}
+//---------------------------------------------
+void SciMusic::soundStop(sciSound *pSnd) {
+ //_mutex.lock();
+ pSnd->sndStatus = kStopped;
+ if (pSnd->pStreamAud)
+ _pMixer->stopHandle(pSnd->hCurrentAud);
+ if (pSnd->pMidiParser)
+ pSnd->pMidiParser->stop();
+ //_mutex.unlock();
+}
+//---------------------------------------------
+void SciMusic::soundSetVolume(sciSound *pSnd, byte volume) {
+ if (pSnd->pStreamAud)
+ _pMixer->setChannelVolume(pSnd->hCurrentAud, volume);
+ else if (pSnd->pMidiParser)
+ pSnd->pMidiParser->setVolume(volume);
+}
+//---------------------------------------------
+void SciMusic::soundSetPriority(sciSound *pSnd, byte prio) {
+ pSnd->prio = prio;
+ sortPlayList();
+}
+//---------------------------------------------
+void SciMusic::soundKill(sciSound *pSnd) {
+ //_mutex.lock();
+ pSnd->sndStatus = kStopped;
+ if (pSnd->pMidiParser) {
+ pSnd->pMidiParser->unloadMusic();
+ delete pSnd->pMidiParser;
+ pSnd->pMidiParser = NULL;
+ }
+ if (pSnd->pStreamAud) {
+ _pMixer->stopHandle(pSnd->hCurrentAud);
+ pSnd->pStreamAud = NULL;
+ }
+
+ uint sz = _playList.size(), i;
+ // searching if sound is already in _playList
+ for (i = 0; i < sz; i++) {
+ if (_playList[i] == pSnd) {
+ _playList.remove_at(i);
+ break;
+ }
+ }
+ //_mutex.unlock();
+}
+//---------------------------------------------
+void SciMusic::soundPause(sciSound *pSnd) {
+ pSnd->sndStatus = kPaused;
+ if (pSnd->pStreamAud)
+ _pMixer->pauseHandle(pSnd->hCurrentAud, true);
+ else if (pSnd->pMidiParser)
+ pSnd->pMidiParser->pause();
+}
+
+//---------------------------------------------
+uint16 SciMusic::soundGetMasterVolume() {
+ return _pMixer->getVolumeForSoundType(Audio::Mixer::kMusicSoundType) * 0xF
+ / Audio::Mixer::kMaxMixerVolume;
+}
+//---------------------------------------------
+void SciMusic::soundSetMasterVolume(uint16 vol) {
+ vol = vol & 0xF; // 0..15
+ vol = vol * Audio::Mixer::kMaxMixerVolume / 0xF;
+ // TODO:balance volume to prevent music to be too loud
+ _pMixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, vol);
+ _pMixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, vol);
+ _pMixer->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType, vol);
+ _pMixer->setVolumeForSoundType(Audio::Mixer::kPlainSoundType, vol);
+}
+
+//---------------------------------------------
+// MidiParser_SCI
+//
+MidiParser_SCI::MidiParser_SCI() :
+ MidiParser() {
+ _pMidiData = NULL;
+ // mididata contains delta in 1/60th second
+ // values of ppqn and tempo are found experimentally and may be wrong
+ _ppqn = 1;
+ setTempo(16667);
+}
+//---------------------------------------------
+MidiParser_SCI::~MidiParser_SCI() {
+ unloadMusic();
+}
+//---------------------------------------------
+bool MidiParser_SCI::loadMusic(SoundRes::tagTrack *ptrack, sciSound *psnd) {
+ unloadMusic();
+ _pTrack = ptrack;
+ _pSnd = psnd;
+ setVolume(psnd->volume);
+ midiMixChannels();
+
+ _num_tracks = 1;
+ _tracks[0] = _pMidiData;
+ setTrack(0);
+ _loopTick = 0;
+ return true;
+}
+
+void MidiParser_SCI::unloadMusic() {
+ allNotesOff();
+ resetTracking();
+ _num_tracks = 0;
+ if (_pMidiData) {
+ delete[] _pMidiData;
+ _pMidiData = NULL;
+ }
+}
+
+void MidiParser_SCI::parseNextEvent(EventInfo &info) {
+ info.start = _position._play_pos;
+ info.delta = *(_position._play_pos++);
+
+ // Process the next info.
+ if ((_position._play_pos[0] & 0xF0) >= 0x80)
+ info.event = *(_position._play_pos++);
+ else
+ info.event = _position._running_status;
+ if (info.event < 0x80)
+ return;
+
+ _position._running_status = info.event;
+ switch (info.command()) {
+ case 0xC:
+ info.basic.param1 = *(_position._play_pos++);
+ info.basic.param2 = 0;
+ if (info.channel() == 0xF) {// SCI special case
+ if (info.basic.param1 != 0x7F)
+ _pSnd->signal = info.basic.param1;
+ else
+ _loopTick = _position._play_tick;
+ }
+ break;
+ case 0xD:
+ info.basic.param1 = *(_position._play_pos++);
+ info.basic.param2 = 0;
+ break;
+
+ case 0xB:
+ info.basic.param1 = *(_position._play_pos++);
+ info.basic.param2 = *(_position._play_pos++);
+ if (info.channel() == 0xF) {// SCI special
+ if (info.basic.param1 == 0x60)
+ _pSnd->dataInc++;
+ // BF 50 x - set reverb to x
+ // BF 60 x - dataInc++
+ // BF 52 x - bHold=x
+ }
+ if (info.basic.param1 == 7) // channel volume change -scale it
+ info.basic.param2 = info.basic.param2 * _volume / 0x7F;
+ info.length = 0;
+ break;
+
+ case 0x8:
+ case 0x9:
+ case 0xA:
+ case 0xE:
+ info.basic.param1 = *(_position._play_pos++);
+ info.basic.param2 = *(_position._play_pos++);
+ if (info.command() == 0x9 && info.basic.param2 == 0)
+ info.event = info.channel() | 0x80;
+ info.length = 0;
+ break;
+
+ case 0xF: // System Common, Meta or SysEx event
+ switch (info.event & 0x0F) {
+ case 0x2: // Song Position Pointer
+ info.basic.param1 = *(_position._play_pos++);
+ info.basic.param2 = *(_position._play_pos++);
+ break;
+
+ case 0x3: // Song Select
+ info.basic.param1 = *(_position._play_pos++);
+ info.basic.param2 = 0;
+ break;
+
+ case 0x6:
+ case 0x8:
+ case 0xA:
+ case 0xB:
+ case 0xC:
+ case 0xE:
+ info.basic.param1 = info.basic.param2 = 0;
+ break;
+
+ case 0x0: // SysEx
+ info.length = readVLQ(_position._play_pos);
+ info.ext.data = _position._play_pos;
+ _position._play_pos += info.length;
+ break;
+
+ case 0xF: // META event
+ info.ext.type = *(_position._play_pos++);
+ info.length = readVLQ(_position._play_pos);
+ info.ext.data = _position._play_pos;
+ _position._play_pos += info.length;
+ if (info.ext.type == 0x2F) {// end of track reached
+ if (_pSnd->loop) {
+ jumpToTick(_loopTick);
+ _pSnd->loop--;
+ } else {
+ _pSnd->sndStatus = kStopped;
+ _pSnd->signal = 0xFFFF;
+ }
+ }
+ break;
+ default:
+ warning(
+ "MidiParser_SCI::parseNextEvent: Unsupported event code %x",
+ info.event);
+ } // // System Common, Meta or SysEx event
+ }// switch (info.command())
+}
+
+//----------------------------------------
+byte MidiParser_SCI::midiGetNextChannel(long ticker) {
+ byte curr = 0xFF;
+ long closest = ticker + 1000000, next = 0;
+ for (int i = 0; i < _pTrack->nChannels; i++) {
+ if (_pTrack->aChannels[i].time == -1) // channel ended
+ continue;
+ next = *_pTrack->aChannels[i].ptr; // when the next event shoudl occur
+ if (next == 0xF8) // 0xF8 means 240 ticks delay
+ next = 240;
+ next += _pTrack->aChannels[i].time;
+ if (next < closest) {
+ curr = i;
+ closest = next;
+ }
+ }
+ return curr;
+}
+//----------------------------------------
+byte *MidiParser_SCI::midiMixChannels() {
+ int lSize = 0;
+ byte **pDataPtr = new byte *[_pTrack->nChannels];
+ for (int i = 0; i < _pTrack->nChannels; i++) {
+ pDataPtr[i] = _pTrack->aChannels[i].ptr;
+ _pTrack->aChannels[i].time = 0;
+ _pTrack->aChannels[i].prev = 0;
+ lSize += _pTrack->aChannels[i].size;
+ }
+ byte *pOutData = new byte[lSize * 2]; // FIXME: creates overhead and still may be not enough to hold all data
+ _pMidiData = pOutData;
+ long ticker = 0;
+ byte curr, delta;
+ byte cmd, par1, global_prev = 0;
+ long new_delta;
+ SoundRes::tagChannel *pCh;
+ while ((curr = midiGetNextChannel(ticker)) != 0xFF) { // there is still active channel
+ pCh = &_pTrack->aChannels[curr];
+ delta = *pCh->ptr++;
+ pCh->time += (delta == 0xF8 ? 240 : delta); // when the comamnd is supposed to occur
+ if (delta == 0xF8)
+ continue;
+ new_delta = pCh->time - ticker;
+ ticker += new_delta;
+
+ cmd = *pCh->ptr++;
+ if (cmd != 0xFC) {
+ // output new delta
+ while (new_delta > 240) {
+ *pOutData++ = 0xF8;
+ new_delta -= 240;
+ }
+ *pOutData++ = (byte)new_delta;
+ }
+ switch (cmd) {
+ case 0xF0: // sysEx
+ *pOutData++ = cmd;
+ do {
+ par1 = *pCh->ptr++;
+ *pOutData++ = par1; // out
+ } while (par1 != 0xF7);
+ break;
+ case 0xFC: // end channel
+ pCh->time = -1; // FIXME
+ break;
+ default: // MIDI command
+ if (cmd & 0x80)
+ par1 = *pCh->ptr++;
+ else {// running status
+ par1 = cmd;
+ cmd = pCh->prev;
+ }
+ if (cmd != global_prev)
+ *pOutData++ = cmd; // out cmd
+ *pOutData++ = par1;// pout par1
+ if (nMidiParams[(cmd >> 4) - 8] == 2)
+ *pOutData++ = *pCh->ptr++; // out par2
+ pCh->prev = cmd;
+ global_prev = cmd;
+ }// switch(cmd)
+ }// while (curr)
+ // mixing finished. inserting stop event
+ *pOutData++ = 0;
+ *pOutData++ = 0xFF;
+ *pOutData++ = 0x2F;
+ *pOutData++ = 0x00;
+ *pOutData++ = 0x00;
+
+ for (int i = 0; i < _pTrack->nChannels; i++)
+ _pTrack->aChannels[i].ptr = pDataPtr[i];
+ delete[] pDataPtr;
+ return _pMidiData;
+}
+
+void MidiParser_SCI::setVolume(byte bVolume) {
+ if (bVolume > 0x7F)
+ bVolume = 0x7F;
+ if (_volume != bVolume) {
+ _volume = bVolume;
+ // sending volume change to all active channels
+ for (int i = 0; i < _pTrack->nChannels; i++)
+ if (_pTrack->aChannels[i].number <= 0xF)
+ _driver->send(0xB0 + _pTrack->aChannels[i].number, 7, _volume);
+ }
+}
+
+//---------------------------
+
+SoundRes::tagTrack* SoundRes::getTrackByNumber(uint16 number) {
+ if (/*number >= 0 &&*/number < nTracks)
+ return &aTracks[number];
+ return NULL;
+}
+
+SoundRes::tagTrack* SoundRes::getTrackByType(kTrackType type) {
+ for (int i = 0; i < nTracks; i++)
+ if (aTracks[i].type == type)
+ return &aTracks[i];
+ return NULL;
+}
+
+} // end of namespace SCI
diff --git a/engines/sci/sfx/music.h b/engines/sci/sfx/music.h
new file mode 100644
index 0000000000..0e78418f32
--- /dev/null
+++ b/engines/sci/sfx/music.h
@@ -0,0 +1,207 @@
+/* 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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#ifndef SCI_MUSIC_H
+#define SCI_MUSIC_H
+
+#include "sound/mixer.h"
+#include "sound/audiostream.h"
+#include "sound/mididrv.h"
+#include "sound/midiparser.h"
+#include "common/mutex.h"
+#include "common/savefile.h"
+
+#include "sci/sci.h"
+
+/*
+ Sound drivers info: (from driver cmd0)
+ Adlib/SB : track 0 , voices 9 , patch 3 ah=1
+ ProAudioSp: track 0 , voices 9 , patch 3 ah=17
+ GenerlMIDI: track 7 , voices 32, patch 4 ah=1 SCI1.1
+ Game Blast: track 9 , voices 12, patch 101 ah=1
+ MT-32 : track 12, voices 32, patch 1 ah=1
+ PC Speaker: track 18, voices 1 , patch 0xFF ah=1
+ Tandy : track 19, voices 3 , patch 101 ah=1
+ IBM PS/1 : track 19, voices 3 , patch 101 ah=1
+
+ */
+
+namespace Sci {
+
+typedef uint16 SCIHANDLE;
+typedef uint16 HEAPHANDLE;
+
+class SoundRes {
+public:
+ enum kTrackType {
+ kTrackAdlib = 0,
+ kTrackGameBlaster = 9,
+ kTrackMT32 = 12,
+ kTrackSpeaker = 18,
+ kTrackTandy = 19
+ };
+
+ struct tagChannel {
+ byte number;
+ byte poly;
+ uint16 unk;
+ uint16 size;
+ byte *ptr;
+ long time;
+ byte prev;
+ };
+
+ struct tagTrack {
+ kTrackType type;
+ byte nDigital;
+ byte nChannels;
+ tagChannel *aChannels;
+ uint sz;
+ };
+public:
+ SoundRes(SCIHANDLE handle, uint32 ResId);
+ ~SoundRes();
+ tagTrack *getTrackByNumber(uint16 number);
+ tagTrack *getTrackByType(kTrackType type);
+
+protected:
+ byte nTracks;
+ tagTrack *aTracks;
+};
+
+enum kSndStatus {
+ kStopped = 0, kPaused, kPlaying
+};
+
+// script-used struct to manipulate sound (358 bytes)
+class MidiParser_SCI;
+
+struct sciSound {
+ //sciNode node; // [0-5] // we use a Common::List
+ uint16 resnum;// [6-7]
+ // byte * pMidiData;
+ Audio::AudioStream* pStreamAud;
+ MidiParser_SCI *pMidiParser;
+ Audio::SoundHandle hCurrentAud;
+ kSndStatus sndStatus;
+
+ uint16 dataInc; //[338-339]
+ uint16 ticker;
+ uint16 signal; //[344]
+ byte prio; // 348
+ byte loop; // 349
+ byte volume; // 350
+
+ byte FadeTo;
+ short FadeStep;
+ uint32 FadeTicker;
+ uint32 FadeTickerStep;
+
+ // byte unk7[7];
+};
+
+class SciMusic {
+public:
+ SciMusic();
+ ~SciMusic();
+
+ void init();
+ void loadPatch();
+ void onTimer();
+ bool saveState(Common::OutSaveFile *pFile);
+ bool restoreState(Common::InSaveFile *pFile);
+ void stopAll();
+ void clearPlaylist() {
+ _playList.clear();
+ }
+ // sound and midi functions
+ void soundInitSnd(SoundRes *res, sciSound *pSnd);
+ void soundPlay(sciSound *pSnd);
+ void soundStop(sciSound *pSnd);
+ void soundKill(sciSound *pSnd);
+ void soundPause(sciSound *pSnd);
+ void soundSetVolume(sciSound *pSnd, byte volume);
+ void soundSetPriority(sciSound *pSnd, byte prio);
+ uint16 soundGetMasterVolume();
+ void soundSetMasterVolume(uint16 vol);
+ uint16 soundGetVoices();
+ uint32 soundGetTempo() {
+ return _dwTempo;
+ }
+
+ uint16 _savelen;
+protected:
+ byte findAudEntry(uint16 nAud, byte&oVolume, uint32& oOffset, uint32&oSize);
+ void sortPlayList();
+ void loadPatchMT32();
+ void patchSysEx(byte * addr, byte *pdata, int len);
+ void patchUpdateAddr(byte *addr, int len);
+ void doFade(sciSound *pSnd);
+
+ Audio::Mixer *_pMixer;
+ MidiDriver *_pMidiDrv;
+ int _midiType;
+ Common::Mutex _mutex;
+
+ Common::Array<sciSound *> _playList;
+ uint32 _dwTempo;
+ bool _bMultiMidi; // use adlib's digital track if midi track don't have one
+
+private:
+ static void miditimerCallback(void *p);
+
+};
+
+class MidiParser_SCI : public MidiParser {
+public:
+ MidiParser_SCI();
+ ~MidiParser_SCI();
+ bool loadMusic(SoundRes::tagTrack *ptrack, sciSound *psnd);
+ bool loadMusic(byte *, uint32) {
+ return false;
+ }
+ void unloadMusic();
+ void setVolume(byte bVolume);
+ void stop() {
+ _abort_parse = true;/*hangAllActiveNotes();*/
+ }
+ void pause() {
+ _abort_parse = true; /*hangAllActiveNotes();*/
+ }
+
+protected:
+ void parseNextEvent(EventInfo &info);
+ byte *midiMixChannels();
+ byte midiGetNextChannel(long ticker);
+ byte *_pMidiData;
+ SoundRes::tagTrack *_pTrack;
+ sciSound *_pSnd;
+ uint32 _loopTick;
+ byte _volume;
+};
+
+} // end of namespace
+
+#endif
diff --git a/engines/sci/sfx/soundcmd.cpp b/engines/sci/sfx/soundcmd.cpp
index 1af9da57ba..7b713ecec7 100644
--- a/engines/sci/sfx/soundcmd.cpp
+++ b/engines/sci/sfx/soundcmd.cpp
@@ -24,10 +24,13 @@
*/
#include "sci/sfx/iterator.h" // for SongIteratorStatus
+#include "sci/sfx/music.h"
#include "sci/sfx/soundcmd.h"
namespace Sci {
+#define USE_OLD_MUSIC_FUNCTIONS
+
#define SCI1_SOUND_FLAG_MAY_PAUSE 1 /* Only here for completeness; The interpreter doesn't touch this bit */
#define SCI1_SOUND_FLAG_SCRIPTED_PRI 2 /* but does touch this */
@@ -126,6 +129,13 @@ void process_sound_events(EngineState *s) { /* Get all sound events, apply their
SoundCommandParser::SoundCommandParser(ResourceManager *resMan, SegManager *segMan, SfxState *state, AudioPlayer *audio, SciVersion doSoundVersion) :
_resMan(resMan), _segMan(segMan), _state(state), _audio(audio), _doSoundVersion(doSoundVersion) {
+ _hasNodePtr = (_doSoundVersion != SCI_VERSION_0_EARLY);
+
+#ifndef USE_OLD_MUSIC_FUNCTIONS
+ _music = new SciMusic();
+ _music->init();
+#endif
+
switch (doSoundVersion) {
case SCI_VERSION_0_EARLY:
SOUNDCOMMAND(cmdInitHandle);
@@ -220,10 +230,11 @@ void SoundCommandParser::cmdInitHandle(reg_t obj, SongHandle handle, int value)
if (!obj.segment)
return;
+#ifdef USE_OLD_MUSIC_FUNCTIONS
SongIteratorType type = (_doSoundVersion == SCI_VERSION_0_EARLY) ? SCI_SONG_ITERATOR_TYPE_SCI0 : SCI_SONG_ITERATOR_TYPE_SCI1;
int number = GET_SEL32V(_segMan, obj, number);
- if (_doSoundVersion != SCI_VERSION_0_EARLY) {
+ if (_hasNodePtr) {
if (GET_SEL32V(_segMan, obj, nodePtr)) {
_state->sfx_song_set_status(handle, SOUND_STATUS_STOPPED);
_state->sfx_remove_song(handle);
@@ -238,13 +249,36 @@ void SoundCommandParser::cmdInitHandle(reg_t obj, SongHandle handle, int value)
_state->sfx_add_song(build_iterator(_resMan, number, type, handle), 0, handle, number);
- if (_doSoundVersion == SCI_VERSION_0_EARLY) {
+ if (!_hasNodePtr)
PUT_SEL32V(_segMan, obj, state, _K_SOUND_STATUS_INITIALIZED);
- } else {
+ else
PUT_SEL32(_segMan, obj, nodePtr, obj);
- }
PUT_SEL32(_segMan, obj, handle, obj);
+#else
+
+ uint16 resnum = GET_SEL32V(_segMan, obj, number);
+ Resource *res = resnum ? _resMan->findResource(ResourceId(kResourceTypeSound, resnum), true) : NULL;
+
+ if (!GET_SEL32V(_segMan, obj, nodePtr)) {
+ PUT_SEL32(_segMan, obj, nodePtr, obj);
+ _soundList.push_back(obj.toUint16());
+ }
+
+ // TODO
+ /*
+ sciSound *pSnd = (sciSound *)heap2Ptr(hptr);
+ pSnd->resnum = resnum;
+ pSnd->loop = (GET_SEL32V(_segMan, obj, loop) == 0xFFFF ? 1 : 0);
+ pSnd->prio = GET_SEL32V(_segMan, obj, pri) & 0xFF; // priority
+ pSnd->volume = GET_SEL32V(_segMan, obj, vol) & 0xFF; // volume
+ pSnd->signal = pSnd->dataInc = 0;
+
+ _music->soundKill(pSnd);
+ if (res)
+ _music->soundInitSnd(res, pSnd);
+ */
+#endif
}
void SoundCommandParser::cmdPlayHandle(reg_t obj, SongHandle handle, int value) {
@@ -332,7 +366,7 @@ void SoundCommandParser::cmdDisposeHandle(reg_t obj, SongHandle handle, int valu
if (obj.segment) {
_state->sfx_remove_song(handle);
- if (_doSoundVersion == SCI_VERSION_0_EARLY)
+ if (!_hasNodePtr)
PUT_SEL32V(_segMan, obj, handle, 0x0000);
}
}
@@ -340,12 +374,12 @@ void SoundCommandParser::cmdDisposeHandle(reg_t obj, SongHandle handle, int valu
void SoundCommandParser::cmdStopHandle(reg_t obj, SongHandle handle, int value) {
changeHandleStatus(obj, handle, SOUND_STATUS_STOPPED);
- if (_doSoundVersion != SCI_VERSION_0_EARLY)
+ if (_hasNodePtr)
PUT_SEL32V(_segMan, obj, signal, SIGNAL_OFFSET);
}
void SoundCommandParser::cmdSuspendHandle(reg_t obj, SongHandle handle, int value) {
- if (_doSoundVersion == SCI_VERSION_0_EARLY)
+ if (!_hasNodePtr)
changeHandleStatus(obj, handle, SOUND_STATUS_SUSPENDED);
else
changeHandleStatus(obj, handle, value ? SOUND_STATUS_SUSPENDED : SOUND_STATUS_PLAYING);
@@ -387,7 +421,7 @@ void SoundCommandParser::cmdFadeHandle(reg_t obj, SongHandle handle, int value)
** than fading it! */
if (obj.segment) {
_state->sfx_song_set_status(handle, SOUND_STATUS_STOPPED);
- if (_doSoundVersion == SCI_VERSION_0_EARLY)
+ if (!_hasNodePtr)
PUT_SEL32V(_segMan, obj, state, SOUND_STATUS_STOPPED);
PUT_SEL32V(_segMan, obj, signal, SIGNAL_OFFSET);
}
diff --git a/engines/sci/sfx/soundcmd.h b/engines/sci/sfx/soundcmd.h
index 60efa3b1dc..4061b10ea1 100644
--- a/engines/sci/sfx/soundcmd.h
+++ b/engines/sci/sfx/soundcmd.h
@@ -26,10 +26,12 @@
#ifndef SCI_SOUNDCMD_H
#define SCI_SOUNDCMD_H
+#include "common/list.h"
#include "sci/engine/state.h"
namespace Sci {
+class SciMusic;
class SoundCommandParser;
typedef void (SoundCommandParser::*SoundCommand)(reg_t obj, SongHandle handle, int value);
@@ -47,15 +49,19 @@ public:
reg_t parseCommand(int argc, reg_t *argv, reg_t acc);
private:
+ SciMusic *_music;
Common::Array<SciSoundCommand*> _soundCommands;
ResourceManager *_resMan;
SegManager *_segMan;
SfxState *_state;
AudioPlayer *_audio;
+ bool _hasNodePtr;
SciVersion _doSoundVersion;
reg_t _acc;
int _midiCmd, _controller, _param;
+ Common::List<uint16> _soundList;
+
void cmdInitHandle(reg_t obj, SongHandle handle, int value);
void cmdPlayHandle(reg_t obj, SongHandle handle, int value);
void cmdDummy(reg_t obj, SongHandle handle, int value);