- Started importing the music code from Greg's SCI engine

- Simplified some sound version checks

svn-id: r45923
This commit is contained in:
Filippos Karapetis
2009-11-15 14:14:49 +00:00
parent 3e34b1bbab
commit 544daa5c31
5 changed files with 961 additions and 8 deletions
+1
View File
@@ -70,6 +70,7 @@ MODULE_OBJS := \
sfx/audio.o \
sfx/core.o \
sfx/iterator.o \
sfx/music.o \
sfx/songlib.o \
sfx/soundcmd.o \
sfx/seq/gm.o \
+705
View File
@@ -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
+207
View File
@@ -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
+42 -8
View File
@@ -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);
}
+6
View File
@@ -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);