diff options
author | Filippos Karapetis | 2008-12-01 20:35:36 +0000 |
---|---|---|
committer | Filippos Karapetis | 2008-12-01 20:35:36 +0000 |
commit | af945ac7881ae7e414f004bd0e99e8c3b5d76be9 (patch) | |
tree | 72e9c6fd43406e2021973b4f163ab4faa10143fb /engines/tinsel/music.cpp | |
parent | f10f151ff742801e12534bb052bd89419bf906cb (diff) | |
download | scummvm-rg350-af945ac7881ae7e414f004bd0e99e8c3b5d76be9.tar.gz scummvm-rg350-af945ac7881ae7e414f004bd0e99e8c3b5d76be9.tar.bz2 scummvm-rg350-af945ac7881ae7e414f004bd0e99e8c3b5d76be9.zip |
Merged the tinsel 2 engine with tinsel 1. Both Discworld 1 and Discworld 2 should be completable
svn-id: r35196
Diffstat (limited to 'engines/tinsel/music.cpp')
-rw-r--r-- | engines/tinsel/music.cpp | 481 |
1 files changed, 444 insertions, 37 deletions
diff --git a/engines/tinsel/music.cpp b/engines/tinsel/music.cpp index d165ba0a10..979fc61270 100644 --- a/engines/tinsel/music.cpp +++ b/engines/tinsel/music.cpp @@ -31,12 +31,21 @@ #include "sound/mididrv.h" #include "sound/midiparser.h" #include "sound/audiocd.h" +#include "sound/adpcm.h" #include "common/config-manager.h" #include "common/file.h" #include "tinsel/config.h" #include "tinsel/sound.h" #include "tinsel/music.h" +#include "tinsel/handle.h" +#include "tinsel/sysvar.h" + +#define MUSIC_JUMP (-1) +#define MUSIC_END (-2) +#define BLMAGIC (-3458) + +#define DIM_SPEED 8 namespace Tinsel { @@ -153,8 +162,8 @@ bool PlayMidiSequence(uint32 dwFileOffset, bool bLoop) { currentMidi = dwFileOffset; currentLoop = bLoop; - if (volMidi != 0) { - SetMidiVolume(volMidi); + if (volMusic != 0) { + SetMidiVolume(volMusic); // Support for compressed music from the music enhancement project AudioCD.stop(); @@ -191,7 +200,7 @@ bool PlayMidiSequence(uint32 dwFileOffset, bool bLoop) { // open MIDI sequence file in binary mode if (!midiStream.open(MIDI_FILE)) - error("Cannot find file %s", MIDI_FILE); + error(CANNOT_FIND_FILE, MIDI_FILE); // update index of last tune loaded dwLastMidiIndex = dwMidiIndex; @@ -206,22 +215,22 @@ bool PlayMidiSequence(uint32 dwFileOffset, bool bLoop) { assert(dwSeqLen > 0 && dwSeqLen <= midiBuffer.size); // stop any currently playing tune - _vm->_music->stop(); + _vm->_midiMusic->stop(); // read the sequence if (midiStream.read(midiBuffer.pDat, dwSeqLen) != dwSeqLen) - error("File %s is corrupt", MIDI_FILE); + error(FILE_IS_CORRUPT, MIDI_FILE); midiStream.close(); - _vm->_music->playXMIDI(midiBuffer.pDat, dwSeqLen, bLoop); + _vm->_midiMusic->playXMIDI(midiBuffer.pDat, dwSeqLen, bLoop); // Store the length dwLastSeqLen = dwSeqLen; } else { // dwMidiIndex == dwLastMidiIndex - _vm->_music->stop(); - _vm->_music->playXMIDI(midiBuffer.pDat, dwSeqLen, bLoop); + _vm->_midiMusic->stop(); + _vm->_midiMusic->playXMIDI(midiBuffer.pDat, dwSeqLen, bLoop); } // allow another sequence to play @@ -235,7 +244,7 @@ bool PlayMidiSequence(uint32 dwFileOffset, bool bLoop) { */ bool MidiPlaying(void) { if (AudioCD.isPlaying()) return true; - return _vm->_music->isPlaying(); + return _vm->_midiMusic->isPlaying(); } /** @@ -246,7 +255,7 @@ bool StopMidi(void) { currentLoop = false; AudioCD.stop(); - _vm->_music->stop(); + _vm->_midiMusic->stop(); return true; } @@ -255,7 +264,7 @@ bool StopMidi(void) { * Gets the volume of the MIDI music. */ int GetMidiVolume() { - return volMidi; + return volMusic; } /** @@ -265,24 +274,24 @@ int GetMidiVolume() { void SetMidiVolume(int vol) { assert(vol >= 0 && vol <= Audio::Mixer::kMaxChannelVolume); - if (vol == 0 && volMidi == 0) { + if (vol == 0 && volMusic == 0) { // Nothing to do - } else if (vol == 0 && volMidi != 0) { + } else if (vol == 0 && volMusic != 0) { // Stop current midi sequence AudioCD.stop(); StopMidi(); - } else if (vol != 0 && volMidi == 0) { + } else if (vol != 0 && volMusic == 0) { // Perhaps restart last midi sequence if (currentLoop) { PlayMidiSequence(currentMidi, true); - _vm->_music->setVolume(vol); + _vm->_midiMusic->setVolume(vol); } - } else if (vol != 0 && volMidi != 0) { + } else if (vol != 0 && volMusic != 0) { // Alter current volume - _vm->_music->setVolume(vol); + _vm->_midiMusic->setVolume(vol); } - volMidi = vol; + volMusic = vol; } /** @@ -292,7 +301,7 @@ void OpenMidiFiles(void) { Common::File midiStream; // Demo version has no midi file - if (_vm->getFeatures() & GF_DEMO) + if ((_vm->getFeatures() & GF_DEMO) || (TinselVersion == TINSEL_V2)) return; if (midiBuffer.pDat) @@ -301,12 +310,12 @@ void OpenMidiFiles(void) { // open MIDI sequence file in binary mode if (!midiStream.open(MIDI_FILE)) - error("Cannot find file %s", MIDI_FILE); + error(CANNOT_FIND_FILE, MIDI_FILE); // gen length of the largest sequence midiBuffer.size = midiStream.readUint32LE(); if (midiStream.ioFailed()) - error("File %s is corrupt", MIDI_FILE); + error(FILE_IS_CORRUPT, MIDI_FILE); if (midiBuffer.size) { // allocate a buffer big enough for the largest MIDI sequence @@ -327,14 +336,14 @@ void DeleteMidiBuffer() { midiBuffer.pDat = NULL; } -MusicPlayer::MusicPlayer(MidiDriver *driver) : _parser(0), _driver(driver), _looping(false), _isPlaying(false) { +MidiMusicPlayer::MidiMusicPlayer(MidiDriver *driver) : _parser(0), _driver(driver), _looping(false), _isPlaying(false) { memset(_channel, 0, sizeof(_channel)); _masterVolume = 0; this->open(); _xmidiParser = MidiParser::createParser_XMIDI(); } -MusicPlayer::~MusicPlayer() { +MidiMusicPlayer::~MidiMusicPlayer() { _driver->setTimerCallback(NULL, NULL); stop(); this->close(); @@ -342,7 +351,7 @@ MusicPlayer::~MusicPlayer() { delete _xmidiParser; } -void MusicPlayer::setVolume(int volume) { +void MidiMusicPlayer::setVolume(int volume) { _vm->_mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, volume); if (_masterVolume == volume) @@ -359,7 +368,7 @@ void MusicPlayer::setVolume(int volume) { } } -int MusicPlayer::open() { +int MidiMusicPlayer::open() { // Don't ever call open without first setting the output driver! if (!_driver) return 255; @@ -372,14 +381,14 @@ int MusicPlayer::open() { return 0; } -void MusicPlayer::close() { +void MidiMusicPlayer::close() { stop(); if (_driver) _driver->close(); _driver = 0; } -void MusicPlayer::send(uint32 b) { +void MidiMusicPlayer::send(uint32 b) { byte channel = (byte)(b & 0x0F); if ((b & 0xFFF0) == 0x07B0) { // Adjust volume changes by master volume @@ -409,7 +418,7 @@ void MusicPlayer::send(uint32 b) { } } -void MusicPlayer::metaEvent(byte type, byte *data, uint16 length) { +void MidiMusicPlayer::metaEvent(byte type, byte *data, uint16 length) { switch (type) { case 0x2F: // End of Track if (_looping) @@ -423,15 +432,15 @@ void MusicPlayer::metaEvent(byte type, byte *data, uint16 length) { } } -void MusicPlayer::onTimer(void *refCon) { - MusicPlayer *music = (MusicPlayer *)refCon; +void MidiMusicPlayer::onTimer(void *refCon) { + MidiMusicPlayer *music = (MidiMusicPlayer *)refCon; Common::StackLock lock(music->_mutex); if (music->_isPlaying) music->_parser->onTimer(); } -void MusicPlayer::playXMIDI(byte *midiData, uint32 size, bool loop) { +void MidiMusicPlayer::playXMIDI(byte *midiData, uint32 size, bool loop) { if (_isPlaying) return; @@ -463,7 +472,7 @@ void MusicPlayer::playXMIDI(byte *midiData, uint32 size, bool loop) { } } -void MusicPlayer::stop() { +void MidiMusicPlayer::stop() { Common::StackLock lock(_mutex); _isPlaying = false; @@ -473,16 +482,414 @@ void MusicPlayer::stop() { } } -void MusicPlayer::pause() { +void MidiMusicPlayer::pause() { setVolume(-1); _isPlaying = false; } -void MusicPlayer::resume() { +void MidiMusicPlayer::resume() { setVolume(GetMidiVolume()); _isPlaying = true; } +PCMMusicPlayer::PCMMusicPlayer() { + _silenceSamples = 0; + + _curChunk = 0; + _fileName = 0; + _state = S_IDLE; + _mState = S_IDLE; + _scriptNum = -1; + _scriptIndex = 0; + _forcePlay = false; + + _volume = 255; + _dimmed = false; + _dimmedTinsel = false; + _dimIteration = 0; + + _fadeOutVolume = 0; + _fadeOutIteration = 0; + + _end = true; + + _vm->_mixer->playInputStream(Audio::Mixer::kMusicSoundType, + &_handle, this, -1, _volume, 0, false, true); +} + +PCMMusicPlayer::~PCMMusicPlayer() { + _vm->_mixer->stopHandle(_handle); + + delete[] _fileName; +} + +void PCMMusicPlayer::startPlay(int id) { + if (!_fileName) + return; + + debugC(DEBUG_DETAILED, kTinselDebugMusic, "Playing PCM music %s, index %d", _fileName, id); + + Common::StackLock slock(_mutex); + + stop(); + + _scriptNum = id; + _scriptIndex = 1; + _state = S_NEW; + + play(); +} + +void PCMMusicPlayer::stopPlay() { + Common::StackLock slock(_mutex); + + stop(); +} + +int PCMMusicPlayer::readBuffer(int16 *buffer, const int numSamples) { + Common::StackLock slock(_mutex); + + if (!_curChunk && ((_state == S_IDLE) || (_state == S_STOP))) + return 0; + + int samplesLeft = numSamples; + + while (samplesLeft > 0) { + if (_silenceSamples > 0) { + int n = MIN(_silenceSamples, samplesLeft); + + memset(buffer, 0, n); + + buffer += n; + _silenceSamples -= n; + samplesLeft -= n; + + } else if (_curChunk && + ((_state == S_MID) || (_state == S_NEXT) || (_state == S_NEW))) { + int n = _curChunk->readBuffer(buffer, samplesLeft); + + buffer += n; + samplesLeft -= n; + + if (_curChunk->endOfData()) { + _state = S_END1; + + delete _curChunk; + _curChunk = 0; + } + } else { + + if (!getNextChunk()) + break; + } + } + + return (numSamples - samplesLeft); +} + +bool PCMMusicPlayer::isPlaying() const { + return ((_state != S_IDLE) && (_state != S_STOP)); +} + +bool PCMMusicPlayer::isDimmed() const { + return _dimmed; +} + +void PCMMusicPlayer::getTunePlaying(void *voidPtr, int length) { + Common::StackLock lock(_mutex); + + debugC(DEBUG_DETAILED, kTinselDebugMusic, "getTunePlaying"); + + assert(length == (3 * sizeof(int32))); + + int32 *p = (int32 *) voidPtr; + + _mState = _state; + + p[0] = (int32) _mState; + p[1] = _scriptNum; + p[2] = _scriptIndex; +} + +void PCMMusicPlayer::restoreThatTune(void *voidPtr) { + Common::StackLock lock(_mutex); + + debugC(DEBUG_DETAILED, kTinselDebugMusic, "restoreThatTune"); + + int32 *p = (int32 *) voidPtr; + + _mState = (State) p[0]; + _scriptNum = p[1]; + _scriptIndex = p[2]; + + if (_mState != S_IDLE) + _state = S_NEW; + + delete _curChunk; + _curChunk = 0; + + _end = false; +} + +void PCMMusicPlayer::setMusicSceneDetails(SCNHANDLE hScript, + SCNHANDLE hSegment, const char *fileName) { + + Common::StackLock lock(_mutex); + + stop(); + + debugC(DEBUG_INTERMEDIATE, kTinselDebugMusic, "Setting music scene details: %s", fileName); + + _hScript = hScript; + _hSegment = hSegment; + _fileName = new char[strlen(fileName) + 1]; + strcpy(_fileName, fileName); + + // Start scene with music not dimmed + _dimmed = false; + _dimmedTinsel = false; + _dimIteration = 0; + setVol(255); +} + +void PCMMusicPlayer::setVolume(int volume) { + assert((volume >= 0) && (volume <= 100)); + + _dimmed = false; + setVol((volume * 255) / 100); +} + +void PCMMusicPlayer::setVol(uint8 volume) { + _volume = volume; + + _vm->_mixer->setChannelVolume(_handle, _volume); +} + +bool PCMMusicPlayer::getMusicTinselDimmed() const { + return _dimmedTinsel; +} + +void PCMMusicPlayer::dim(bool bTinselDim) { + if (_dimmed || (_volume == 0) || + (_state == S_IDLE) || !_curChunk || (SysVar(SV_MUSICDIMFACTOR) == 0)) + return; + + _dimmed = true; + if (bTinselDim) + _dimmedTinsel = true; + + _dimmedVolume = _volume - (_volume / SysVar(SV_MUSICDIMFACTOR)); + + // Iterate down, negative iteration + if (!_dimIteration) + _dimPosition = _volume; + _dimIteration = (_dimmedVolume - _volume)/DIM_SPEED; + + debugC(DEBUG_DETAILED, kTinselDebugMusic, "Dimming music from %d to %d, steps %d", _dimPosition, _dimmedVolume, _dimIteration); + + // And SFX + if (SysVar(SYS_SceneFxDimFactor)) + _vm->_sound->setSFXVolumes(255 - 255/SysVar(SYS_SceneFxDimFactor)); +} + +void PCMMusicPlayer::unDim(bool bTinselUnDim) { + if (!_dimmed || (_dimmedTinsel && !bTinselUnDim)) + return; // not dimmed + + _dimmed = _dimmedTinsel = false; + + if ((_volume == 0) || (_state == S_IDLE) || !_curChunk) + return; + + // Iterate up, positive iteration + if (!_dimIteration) + _dimPosition = _dimmedVolume; + _dimIteration = (_volume - _dimmedVolume)/DIM_SPEED; + + debugC(DEBUG_DETAILED, kTinselDebugMusic, "UnDimming music from %d to %d, steps %d", _dimPosition, _volume, _dimIteration); + + // And SFX + _vm->_sound->setSFXVolumes(255); +} + +void PCMMusicPlayer::dimIteration() { + if (_dimIteration != 0) + { + _dimPosition += _dimIteration; + if (_dimPosition >= _volume) + { + _dimPosition = _volume; + _dimIteration = 0; + } + else if (_dimPosition <= _dimmedVolume) + { + _dimPosition = _dimmedVolume; + _dimIteration = 0; + } + + _vm->_mixer->setChannelVolume(_handle, _dimPosition); + } +} + +void PCMMusicPlayer::startFadeOut(int ticks) { + if ((_volume == 0) || (_state == S_IDLE) || !_curChunk) + return; + + debugC(DEBUG_INTERMEDIATE, kTinselDebugMusic, "Fading out music..."); + + if (_dimmed) { + // Start from dimmed volume and go from there + _dimmed = false; + _fadeOutVolume = _volume - _volume/SysVar(SV_MUSICDIMFACTOR); + } else + _fadeOutVolume = _volume; + + assert(ticks != 0); + _fadeOutIteration = _fadeOutVolume / ticks; + + fadeOutIteration(); +} + +void PCMMusicPlayer::fadeOutIteration() { + if ((_volume == 0) || (_state == S_IDLE) || !_curChunk) + return; + + _fadeOutVolume = CLIP<int>(_fadeOutVolume -= _fadeOutIteration, 0, 255); + + _vm->_mixer->setChannelVolume(_handle, _fadeOutVolume); +} + +bool PCMMusicPlayer::getNextChunk() { + MusicSegment *musicSegments; + int32 *script, *scriptBuffer; + int id; + int snum; + uint32 sampleOffset, sampleLength, sampleCLength; + Common::File f; + byte *buffer; + Common::MemoryReadStream *sampleStream; + + switch (_state) { + case S_NEW: + case S_NEXT: + _forcePlay = false; + + script = scriptBuffer = (int32 *) LockMem(_hScript); + + // Set parameters for this chunk of music + id = _scriptNum; + while(id--) + script = scriptBuffer + FROM_LE_32(*script); + snum = FROM_LE_32(script[_scriptIndex++]); + + if (snum == MUSIC_JUMP || snum == MUSIC_END) + { + // Let usual code sort it out! + _scriptIndex--; // Undo increment + _forcePlay = true; // Force a Play + _state = S_END1; // 'Goto' S_END1 + break; + } + + musicSegments = (MusicSegment *) LockMem(_hSegment); + + assert(FROM_LE_32(musicSegments[snum].numChannels) == 1); + assert(FROM_LE_32(musicSegments[snum].bitsPerSample) == 16); + + sampleOffset = FROM_LE_32(musicSegments[snum].sampleOffset); + sampleLength = FROM_LE_32(musicSegments[snum].sampleLength); + sampleCLength = (((sampleLength + 63) & ~63)*33)/64; + + if (!f.open(_fileName)) + error(CANNOT_FIND_FILE, _fileName); + + f.seek(sampleOffset); + if (f.ioFailed() || (uint32)f.pos() != sampleOffset) + error(FILE_IS_CORRUPT, _fileName); + + buffer = (byte *) malloc(sampleCLength); + assert(buffer); + + // read all of the sample + if (f.read(buffer, sampleCLength) != sampleCLength) + error(FILE_IS_CORRUPT, _fileName); + + sampleStream = new Common::MemoryReadStream(buffer, sampleCLength, true); + + delete _curChunk; + _curChunk = makeADPCMStream(sampleStream, true, sampleCLength, + Audio::kADPCMTinsel8, 22050, 1, 32); + + _state = S_MID; + return true; + + case S_END1: + script = scriptBuffer = (int32 *) LockMem(_hScript); + + id = _scriptNum; + while(id--) + script = scriptBuffer + FROM_LE_32(*script); + + switch (script[_scriptIndex]) { + + case MUSIC_END: + _state = S_END2; + break; + + case MUSIC_JUMP: + _scriptIndex = script[++_scriptIndex]; + // Fall through + default: + if (_forcePlay) + _state = S_NEW; + else + _state = S_NEXT; + _forcePlay = false; + break; + } + + return true; + + case S_END2: + _silenceSamples = 11025; // Half a second of silence + return true; + + case S_END3: + stop(); + _state = S_IDLE; + return false; + + case S_IDLE: + return false; + + default: + break; + } + + return true; +} + +void PCMMusicPlayer::play() { + if (_curChunk) + return; + if (_scriptNum == -1) + return; + + _end = false; + + getNextChunk(); +} + +void PCMMusicPlayer::stop() { + delete _curChunk; + _curChunk = 0; + _scriptNum = -1; + _state = S_IDLE; + _mState = S_IDLE; + + _end = true; +} + void CurrentMidiFacts(SCNHANDLE *pMidi, bool *pLoop) { *pMidi = currentMidi; *pLoop = currentLoop; @@ -495,9 +902,9 @@ void RestoreMidiFacts(SCNHANDLE Midi, bool Loop) { currentMidi = Midi; currentLoop = Loop; - if (volMidi != 0 && Loop) { + if (volMusic != 0 && Loop) { PlayMidiSequence(currentMidi, true); - SetMidiVolume(volMidi); + SetMidiVolume(volMusic); } } @@ -546,4 +953,4 @@ void dumpMusic() { } #endif -} // End of namespace Made +} // End of namespace Tinsel |