diff options
Diffstat (limited to 'sound/mods')
-rw-r--r-- | sound/mods/maxtrax.cpp | 67 | ||||
-rw-r--r-- | sound/mods/maxtrax.h | 16 |
2 files changed, 64 insertions, 19 deletions
diff --git a/sound/mods/maxtrax.cpp b/sound/mods/maxtrax.cpp index 60bf715795..f9e2a509cf 100644 --- a/sound/mods/maxtrax.cpp +++ b/sound/mods/maxtrax.cpp @@ -42,6 +42,9 @@ int32 calcVolumeDelta(int32 delta, uint16 time, uint16 vBlankFreq) { // div <= 1000 means time to small (or even 0) return (div <= 1000) ? delta : (1000 * delta) / div; } +int32 calcTempo(const uint16 tempo, uint16 vBlankFreq) { + return (int32)(((uint32)(tempo & 0xFFF0) << 8) / (uint16)(5 * vBlankFreq)); +} } namespace Audio { @@ -61,6 +64,7 @@ MaxTrax::MaxTrax(int rate, bool stereo) _playerCtx.tempo = 120; _playerCtx.tempoTime = 0; + _playerCtx.syncCallBack = 0; for (int i = 0; i < ARRAYSIZE(_channelCtx); ++i) resetChannel(_channelCtx[i], (i & 1) != 0); @@ -99,44 +103,41 @@ void MaxTrax::interrupt() { int32 eventDelta = _playerCtx.nextEventTime - millis; for (; eventDelta <= 0; eventDelta += (++curEvent)->startTime) { const byte cmd = curEvent->command; - const byte data = curEvent->parameter; - const uint16 stopTime = curEvent->stopTime; - ChannelContext &channel = _channelCtx[data & 0x0F]; + ChannelContext &channel = _channelCtx[curEvent->parameter & 0x0F]; // outPutEvent(*curEvent); // debug("CurTime, EventDelta, NextDelta: %d, %d, %d", millis, eventDelta, eventDelta + curEvent[1].startTime ); if (cmd < 0x80) { // Note - const uint16 vol = (data & 0xF0) >> 1; - const int8 voiceIndex = noteOn(channel, cmd, vol, kPriorityScore); + const int8 voiceIndex = noteOn(channel, cmd, (curEvent->parameter & 0xF0) >> 1, kPriorityScore); if (voiceIndex >= 0) { VoiceContext &voice = _voiceCtx[voiceIndex]; voice.stopEventCommand = cmd; - voice.stopEventParameter = data & 0x0F; - voice.stopEventTime = (eventDelta + stopTime) << 8; + voice.stopEventParameter = curEvent->parameter & 0x0F; + voice.stopEventTime = (eventDelta + curEvent->stopTime) << 8; } } else { switch (cmd) { case 0x80: // TEMPO - if ((_playerCtx.tickUnit >> 8) > stopTime) { - setTempo(data << 4); + if ((_playerCtx.tickUnit >> 8) > curEvent->stopTime) { + _playerCtx.tickUnit = calcTempo(curEvent->parameter << 4, _playerCtx.vBlankFreq); _playerCtx.tempoTime = 0; } else { _playerCtx.tempoStart = _playerCtx.tempo; - _playerCtx.tempoDelta = (data << 4) - _playerCtx.tempoStart; - _playerCtx.tempoTime = (stopTime << 8); + _playerCtx.tempoDelta = (curEvent->parameter << 4) - _playerCtx.tempoStart; + _playerCtx.tempoTime = (curEvent->stopTime << 8); _playerCtx.tempoTicks = 0; } break; case 0xC0: // PROGRAM - channel.patch = &_patch[stopTime & (kNumPatches - 1)]; + channel.patch = &_patch[curEvent->stopTime & (kNumPatches - 1)]; break; case 0xE0: // BEND - channel.pitchBend = ((stopTime & 0x7F00) >> 1) | (stopTime & 0x7f); + channel.pitchBend = ((curEvent->stopTime & 0x7F00) >> 1) | (curEvent->stopTime & 0x7f); channel.pitchReal = (((int32)channel.pitchBendRange * channel.pitchBend) >> 5) - (channel.pitchBendRange << 8); channel.isAltered = true; break; @@ -151,7 +152,36 @@ void MaxTrax::interrupt() { // stop processing for this tick goto endOfEventLoop; - case 0xA0: // SPECIAL + case 0xA0: // SPECIAL + switch (curEvent->stopTime >> 8){ + case 0x01: // SPECIAL_SYNC + if (_playerCtx.syncCallBack) + _playerCtx.syncCallBack(curEvent->stopTime & 0xFF); + break; + case 0x02: // SPECIAL_BEGINREP + // we allow a depth of 4 loops + for (int i = 0; i < ARRAYSIZE(_playerCtx.repeatPoint); ++i) { + if (!_playerCtx.repeatPoint[i]) { + _playerCtx.repeatPoint[i] = curEvent; + _playerCtx.repeatCount[i] = curEvent->stopTime & 0xFF; + break; + } + } + break; + case 0x03: // SPECIAL_ENDREP + for (int i = ARRAYSIZE(_playerCtx.repeatPoint) - 1; i >= 0; --i) { + if (_playerCtx.repeatPoint[i]) { + if (_playerCtx.repeatCount[i]--) + curEvent = _playerCtx.repeatPoint[i]; // gets incremented by 1 at end of loop + else + _playerCtx.repeatPoint[i] = 0; + break; + } + } + break; + } + break; + case 0xB0: // CONTROL // TODO: controlChange((byte)stopTime, (byte)(stopTime >> 8)) default: @@ -174,7 +204,7 @@ endOfEventLoop: _playerCtx.tempoTime = 0; newTempo += _playerCtx.tempoDelta; } - setTempo(_playerCtx.tempoStart + newTempo); + _playerCtx.tickUnit = calcTempo(_playerCtx.tempoStart + newTempo, _playerCtx.vBlankFreq); } } @@ -307,6 +337,11 @@ endOfEventLoop: } } +void MaxTrax::setTempo(const uint16 tempo) { + Common::StackLock lock(_mutex); + _playerCtx.tickUnit = calcTempo(tempo, _playerCtx.vBlankFreq); +} + void MaxTrax::stopMusic() { Common::StackLock lock(_mutex); _playerCtx.musicPlaying = false; @@ -327,6 +362,8 @@ bool MaxTrax::playSong(int songIndex, bool loop) { _playerCtx.scoreIndex = songIndex; _playerCtx.ticks = 0; + for (int i = 0; i < ARRAYSIZE(_playerCtx.repeatPoint); ++i) + _playerCtx.repeatPoint[i] = 0; for (int i = 0; i < ARRAYSIZE(_voiceCtx); ++i) killVoice((byte)i); for (int i = 0; i < kNumChannels; ++i) diff --git a/sound/mods/maxtrax.h b/sound/mods/maxtrax.h index a0e0d04d34..1ec696b6f6 100644 --- a/sound/mods/maxtrax.h +++ b/sound/mods/maxtrax.h @@ -43,11 +43,15 @@ public: bool playSong(int songIndex, bool loop = false); void advanceSong(int advance = 1); int playNote(byte note, byte patch, uint16 duration, uint16 volume, bool rightSide); - void setVolume(const byte volume) { _playerCtx.volume = volume; } - void setTempo(const uint16 tempo) { - _playerCtx.tickUnit = (int32)(((uint32)(tempo & 0xFFF0) << 8) / (uint16)(5 * _playerCtx.vBlankFreq)); - } + void setVolume(const byte volume) { Common::StackLock lock(_mutex); _playerCtx.volume = volume; } + void setTempo(const uint16 tempo); void stopMusic(); + /** + * Set a callback function for sync-events. + * @param callback Callback function, will be called synchronously, so DONT modify the player + * directly in response + */ + void setSignalCallback(void (*callback) (int)) { Common::StackLock lock(_mutex); _playerCtx.syncCallBack = callback; } protected: void interrupt(); @@ -95,6 +99,10 @@ private: int scoreIndex; const Event *nextEvent; int32 nextEventTime; + + void (*syncCallBack) (int); + const Event *repeatPoint[4]; + byte repeatCount[4]; } _playerCtx; struct Envelope { |