aboutsummaryrefslogtreecommitdiff
path: root/engines/sci/sfx/music.cpp
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/music.cpp
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/music.cpp')
-rw-r--r--engines/sci/sfx/music.cpp705
1 files changed, 705 insertions, 0 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