mirror of
https://github.com/scummvm/scummvm.git
synced 2026-06-20 05:45:29 +00:00
- Started importing the music code from Greg's SCI engine
- Simplified some sound version checks svn-id: r45923
This commit is contained in:
@@ -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 \
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user