diff options
author | Norbert Lange | 2009-07-06 17:37:54 +0000 |
---|---|---|
committer | Norbert Lange | 2009-07-06 17:37:54 +0000 |
commit | e9a480062984189c3ab33212dda34ce3db469acd (patch) | |
tree | 48b9684a39f42cd110745a381ba1fb67ffd54533 | |
parent | c0b1d10ba2eeb75d614d24b984333507cba8e20f (diff) | |
download | scummvm-rg350-e9a480062984189c3ab33212dda34ce3db469acd.tar.gz scummvm-rg350-e9a480062984189c3ab33212dda34ce3db469acd.tar.bz2 scummvm-rg350-e9a480062984189c3ab33212dda34ce3db469acd.zip |
Implemented a few commands
Partially implemented queued Events and some related commands
svn-id: r42186
-rw-r--r-- | sound/mods/maxtrax.cpp | 282 | ||||
-rw-r--r-- | sound/mods/maxtrax.h | 129 | ||||
-rw-r--r-- | tfmx/mxtxplayer.cpp | 8 |
3 files changed, 407 insertions, 12 deletions
diff --git a/sound/mods/maxtrax.cpp b/sound/mods/maxtrax.cpp index 13c7148fe4..f945a5544b 100644 --- a/sound/mods/maxtrax.cpp +++ b/sound/mods/maxtrax.cpp @@ -34,7 +34,23 @@ namespace Audio { MaxTrax::MaxTrax(int rate, bool stereo) - : Paula(stereo, rate, rate/50), _patch(), _scores(), _numScores(), _microtonal() { + : Paula(stereo, rate, rate/50), _playerCtx(), _voiceCtx(), _patch(), _channelCtx(), _scores(), _numScores(), _microtonal() { + _playerCtx.maxScoreNum = 128; + _playerCtx.vBlankFreq = 50; + _playerCtx.frameUnit = (uint16)((1000 * (1<<8)) / _playerCtx.vBlankFreq); + _playerCtx.scoreIndex = -1; + // glob_CurrentScore = _scoreptr; + _playerCtx.volume = 0x64; + + _playerCtx.tempoTime = 0; + + uint32 uinqueId = 0; + byte flags = 0; + + uint32 colorClock = kPalSystemClock / 2; + + // init extraChannel + // extraChannel. chan_Number = 16, chan_Flags = chan_VoicesActive = 0 } MaxTrax::~MaxTrax() { @@ -44,11 +60,263 @@ MaxTrax::~MaxTrax() { } void MaxTrax::interrupt() { + // a5 - maxtraxm a4 . globaldata + + // test for changes in shared struct and make changes + // specifically all used channels get marked altered + + _playerCtx.ticks += _playerCtx.tickUnit; + const int32 millis = _playerCtx.ticks >> 8; // d4 + for (int i = 0; i < ARRAYSIZE(_voiceCtx); ++i) { + VoiceContext &voice = _voiceCtx[i]; // a3 + if (!voice.channel || voice.stopEventCommand >= 0x80) + continue; + const int channelNo = voice.stopEventParameter; + voice.stopEventTime -= (channelNo < kNumChannels) ? _playerCtx.tickUnit : _playerCtx.frameUnit; + if (voice.stopEventTime <= 0) { + noteOff(_channelCtx[channelNo], voice.stopEventCommand); + voice.stopEventCommand = 0xFF; + } + } + + if (_playerCtx.musicPlaying) { + Event *curEvent = _playerCtx.nextEvent; + int32 eventTime = _playerCtx.nextEventTime; + for (; eventTime <= millis; eventTime += (++curEvent)->startTime) { + const byte cmd = curEvent->command; + const byte data = curEvent->parameter; + const uint16 stopTime = curEvent->stopTime; + ChannelContext &channel = _channelCtx[data & 0x0F]; + + outPutEvent(*curEvent); + + if (cmd < 0x80) { + _playerCtx.addedNote = false; + const uint16 vol = (data & 0xF0) >> 1; + + const int8 voiceIndex = noteOn(channel, cmd, vol, kPriorityScore); + if (voiceIndex >= 0) { + VoiceContext &voice = _voiceCtx[voiceIndex]; + voice.stopEventCommand = cmd; + voice.stopEventParameter = data & 0x0F; + voice.stopEventTime = (eventTime + stopTime - millis) << 8; + } + } else { + switch (cmd) { + case 0x80: // TEMPO + if ((_playerCtx.tickUnit >> 8) > stopTime) { + setTempo(data << 4); + _playerCtx.tempoTime = 0; + } else { + _playerCtx.tempoStart = _playerCtx.tempo; + _playerCtx.tempoDelta = (data << 4) - _playerCtx.tempoStart; + _playerCtx.tempoTime = (stopTime << 8); + _playerCtx.tempoTicks = 0; + } + break; +/* case 0xA0: // SPECIAL + break; + case 0xB0: // CONTROL + // TODO: controlChange((byte)stopTime, (byte)(stopTime >> 8)) + break; +*/ case 0xC0: // PROGRAM + channel.patch = &_patch[stopTime & (kNumPatches - 1)]; + break; + case 0xE0: // BEND + channel.pitchBend = ((stopTime & 0x7F00) >> 1) | (stopTime & 0x7f); + channel.pitchReal = ((int32)(channel.pitchBendRange << 8) * (channel.pitchBend - (64 << 7))) / (64 << 7); + channel.flags |= ChannelContext::kFlagAltered; + break; + case 0xFF: // END + if (_playerCtx.musicLoop) { + // event -1 as it gets increased at the end of the loop + curEvent = _scores[_playerCtx.scoreIndex].events - 1; + _playerCtx.ticks = 0; + eventTime = 0; + } else + _playerCtx.musicPlaying = false; + break; + default: + debug("Unhandled Command"); + outPutEvent(*curEvent); + } + } + } + _playerCtx.nextEvent = curEvent; + _playerCtx.nextEventTime = eventTime; + + // tempoEffect + if (_playerCtx.tempoTime) { + _playerCtx.tempoTicks += _playerCtx.tickUnit; + uint16 newTempo; + if (_playerCtx.tempoTicks < _playerCtx.tempoTime) { + const uint16 delta = (_playerCtx.tempoTicks * _playerCtx.tempoDelta) / _playerCtx.tempoTime; + newTempo = delta; + } else { + _playerCtx.tempoTime = 0; + newTempo = _playerCtx.tempoDelta; + } + setTempo(_playerCtx.tempoStart + newTempo); + } + + // envelopes + + } + } void MaxTrax::stopMusic() { } +bool MaxTrax::doSong(int songIndex, int advance) { + if (songIndex < 0 || songIndex >= _numScores) + return false; + Paula::pausePlay(true); + _playerCtx.musicPlaying = false; + _playerCtx.musicLoop = false; + + setTempo(_playerCtx.tempoInitial); + _playerCtx.nextEvent = _scores[songIndex].events; + _playerCtx.scoreIndex = songIndex; + + _playerCtx.musicPlaying = true; + Paula::startPaula(); +} + +void MaxTrax::killVoice(byte num) { + VoiceContext &voice = _voiceCtx[num]; + --(voice.channel->voicesActive); + voice.channel = 0; + voice.status = VoiceContext::kStatusFree; + voice.flags = 0; + voice.priority = 0; + voice.uinqueId = 0; + + // "stop" voice, set period to 1, vol to 0 + Paula::disableChannel(0); + Paula::setChannelPeriod(num, 1); + Paula::setChannelVolume(num, 0); +} + +int8 MaxTrax::noteOn(ChannelContext &channel, const byte note, uint16 volume, uint16 pri) { + if (channel.microtonal >= 0) + _microtonal[note % 127] = channel.microtonal; + if (!volume) + return -1; + + Patch &patch = *channel.patch; + if (!patch.samplePtr) + return -1; + int8 voiceNum = -1; + if ((channel.flags & ChannelContext::kFlagMono) != 0 && channel.voicesActive) { + for (voiceNum = ARRAYSIZE(_voiceCtx) - 1; voiceNum >= 0 && _voiceCtx[voiceNum].channel != &channel; --voiceNum) + ; + if (voiceNum < 0) + return -1; + + VoiceContext &voice = _voiceCtx[voiceNum]; + if (voice.status >= VoiceContext::kStatusSustain && (channel.flags & ChannelContext::kFlagPortamento) != 0) { + // reset previous porta + if ((voice.flags & VoiceContext::kFlagPortamento) != 0) + voice.baseNote = voice.endNote; + voice.portaTicks = 0; + voice.flags |= VoiceContext::kFlagPortamento; + voice.endNote = channel.lastNote = note; + voice.noteVolume = (_playerCtx.handleVolume) ? volume + 1 : 128; + goto endFunction; + } + } else { + // TODO: + // pickvoice based on channel.isRightChannel + // return if no channel found + voiceNum = (channel.flags & ChannelContext::kFlagRightChannel) != 0 ? 0 : 1; + } + assert(voiceNum >= 0 && voiceNum < ARRAYSIZE(_voiceCtx)); + + VoiceContext &voice = _voiceCtx[voiceNum]; + voice.flags = 0; + if (voice.channel) { + killVoice(voiceNum); + voice.flags |= VoiceContext::kFlagStolen; + } + voice.channel = &channel; + voice.patch = &patch; + voice.baseNote = note; + + // calc note period + voice.priority = (byte)pri; + voice.status = VoiceContext::kStatusStart; + + voice.noteVolume = (_playerCtx.handleVolume) ? volume + 1 : 128; + + // ifeq HAS_FULLCHANVOL macro + if (channel.volume < 128) + voice.noteVolume = (voice.noteVolume * channel.volume) >> 7; + + voice.baseVolume = 0; + voice.lastTicks = 0; + + uint16 period = voice.lastPeriod; + if (!period) + period = 1000; + + int useOctave = 0; + // get samplestart for the given octave + const int8 *samplePtr = patch.samplePtr + (patch.sampleTotalLen << useOctave) - patch.sampleTotalLen; + if (patch.sampleAttackLen) { + Paula::setChannelSampleStart(voiceNum, samplePtr); + Paula::setChannelSampleLen(voiceNum, patch.sampleAttackLen << useOctave); + Paula::setChannelPeriod(voiceNum, period); + Paula::setChannelVolume(voiceNum, 0); + + Paula::enableChannel(voiceNum); + // wait for dma-clear + } + + if (patch.sampleTotalLen > patch.sampleAttackLen) { + Paula::setChannelSampleStart(voiceNum, samplePtr + patch.sampleAttackLen); + Paula::setChannelSampleLen(voiceNum, (patch.sampleTotalLen - patch.sampleAttackLen) << useOctave); + if (!patch.sampleAttackLen) { + // need to enable channel + Paula::setChannelPeriod(voiceNum, period); + Paula::setChannelVolume(voiceNum, 0); + + Paula::enableChannel(voiceNum); + } + // another pointless wait for DMA-Clear??? + } + + channel.voicesActive++; + if (&channel < &_channelCtx[kNumChannels]) { + const byte flagsSet = ChannelContext::kFlagMono | ChannelContext::kFlagPortamento; + if ((channel.flags & flagsSet) == flagsSet && channel.lastNote < 0x80 && channel.lastNote != voice.baseNote) { + voice.portaTicks = 0; + voice.endNote = voice.baseNote; + voice.baseNote = channel.lastNote; + voice.flags |= VoiceContext::kFlagPortamento; + } + if ((channel.flags & ChannelContext::kFlagPortamento) != 0) + channel.lastNote = note; + } +endFunction: + _playerCtx.addedNote = true; + _playerCtx.lastVoice = voiceNum; + return voiceNum; +} + +void MaxTrax::noteOff(ChannelContext &channel, const byte note) { + VoiceContext &voice = _voiceCtx[_playerCtx.lastVoice]; + if (channel.voicesActive && voice.channel == &channel && voice.status != VoiceContext::kStatusRelease) { + const byte refNote = ((voice.flags & VoiceContext::kFlagPortamento) != 0) ? voice.endNote : voice.baseNote; + if (refNote == note) { + if ((channel.flags & ChannelContext::kFlagDamper) != 0) + voice.flags |= VoiceContext::kFlagDamper; + else + voice.status = VoiceContext::kStatusRelease; + } + } +} + void MaxTrax::freeScores() { if (_scores) { for (int i = 0; i < _numScores; ++i) @@ -82,7 +350,7 @@ bool MaxTrax::load(Common::SeekableReadStream &musicData, bool loadScores, bool warning("Maxtrax: File is not a Maxtrax Module"); return false; } - _playerCtx.tempo = musicData.readUint16BE(); + _playerCtx.tempoInitial = musicData.readUint16BE(); const uint16 flags = musicData.readUint16BE(); _playerCtx.filterOn = (flags & 1) != 0; _playerCtx.handleVolume = (flags & 2) != 0; @@ -99,8 +367,7 @@ bool MaxTrax::load(Common::SeekableReadStream &musicData, bool loadScores, bool const uint16 scoresInFile = musicData.readUint16BE(); if (loadScores) { - const uint16 scoremax = 128; // some variable which is set upon initialisation of player - const uint16 tempScores = MIN(scoresInFile, scoremax); + const uint16 tempScores = MIN(scoresInFile, _playerCtx.maxScoreNum); debug("#Scores: %d, loading # of scores: %d", scoresInFile, tempScores); Score *curScore =_scores = new Score[tempScores]; @@ -142,10 +409,11 @@ bool MaxTrax::load(Common::SeekableReadStream &musicData, bool loadScores, bool curPatch.tune = musicData.readUint16BE(); curPatch.volume = musicData.readUint16BE(); curPatch.sampleOctaves = musicData.readUint16BE(); - curPatch.sampleAttack = musicData.readUint32BE(); - curPatch.sampleSustain = musicData.readUint32BE(); + curPatch.sampleAttackLen = musicData.readUint32BE(); + const uint32 sustainLen = musicData.readUint32BE(); + curPatch.sampleTotalLen = curPatch.sampleAttackLen + sustainLen; // each octave the number of samples doubles. - const uint32 totalSamples = (curPatch.sampleAttack + curPatch.sampleSustain) * ((1 << curPatch.sampleOctaves) - 1); + const uint32 totalSamples = curPatch.sampleTotalLen * ((1 << curPatch.sampleOctaves) - 1); curPatch.attackLen = musicData.readUint16BE(); curPatch.releaseLen = musicData.readUint16BE(); const uint32 totalEnvs = curPatch.attackLen + curPatch.releaseLen; diff --git a/sound/mods/maxtrax.h b/sound/mods/maxtrax.h index ff759e5996..bb1244d53a 100644 --- a/sound/mods/maxtrax.h +++ b/sound/mods/maxtrax.h @@ -41,12 +41,41 @@ protected: private: public: - uint16 _microtonal[128]; + enum { kNumPatches = 64, kNumVoices = 4, kNumChannels = 16, kNumExtraChannels = 1 }; + enum { kPriorityScore, kPriorityNote, kPrioritySound }; + int16 _microtonal[128]; + struct Event; struct PlayerContext { + uint16 vBlankFreq; + int32 ticks; + int32 tickUnit; + uint16 frameUnit; + + uint16 maxScoreNum; uint16 tempo; + uint16 tempoInitial; + uint16 tempoStart; + int16 tempoDelta; + int32 tempoTime; + int32 tempoTicks; + + byte volume; + bool filterOn; bool handleVolume; + bool musicPlaying; + bool musicLoop; + + int scoreIndex; + Event *nextEvent; + int32 nextEventTime; + + + + bool addedNote; + byte lastVoice; + byte voicesActive; } _playerCtx; @@ -66,10 +95,10 @@ public: // this was the SampleData struct in the assembler source int8 *samplePtr; - uint32 sampleAttack; - uint32 sampleSustain; + uint32 sampleTotalLen; + uint32 sampleAttackLen; uint16 sampleOctaves; - } _patch[64]; + } _patch[kNumPatches]; struct Event { uint16 startTime; @@ -85,14 +114,106 @@ public: int _numScores; + struct ChannelContext { + Patch *patch; + uint16 regParamNumber; + + uint16 modulation; + uint16 modulationTime; + + int16 microtonal; + + uint16 portamento; + + int16 pitchBend; + int16 pitchReal; + int8 pitchBendRange; + uint8 volume; + uint8 voicesActive; +// uint8 number; + + enum { + kFlagRightChannel = 1 << 0, + kFlagPortamento = 1 << 1, + kFlagDamper = 1 << 2, + kFlagMono = 1 << 3, + kFlagMicrotonal = 1 << 4, + kFlagModVolume = 1 << 5, + kFlagAltered = 1 << 6 + }; + byte flags; + + uint8 lastNote; + uint8 program; + + } _channelCtx[kNumChannels + kNumExtraChannels]; + + struct VoiceContext { + ChannelContext *channel; + Patch *patch; + Envelope *envelope; + uint32 uinqueId; + uint32 lastTicks; + uint32 tocksLeft; + uint32 portaTicks; + uint32 incrVolume; + uint32 periodOffset; + /*ifne FASTSOUND + APTR voice_CurFastIOB ; current fast iob playing + APTR voice_NextFastIOB ; next fast iob to play + APTR voice_FastBuffer ; pointer to buffer area + endc*/ + uint16 envelopeLeft; + uint16 noteVolume; + uint16 baseVolume; + uint16 lastPeriod; + byte baseNote; + byte endNote; + byte number; + byte link; + byte priority; + enum { + kStatusFree, + kStatusHalt, + kStatusDecay, + kStatusRelease, + kStatusSustain, + kStatusAttack, + kStatusStart + }; + byte status; + enum { + kFlagStolen = 1 << 0, + kFlagPortamento = 1 << 1, + kFlagDamper = 1 << 2, + kFlagBlocked = 1 << 3, + kFlagRecalc = 1 << 4 + }; + byte flags; + byte lastVolume; + + int32 stopEventTime; + byte stopEventCommand; + byte stopEventParameter; + } _voiceCtx[kNumVoices]; bool load(Common::SeekableReadStream &musicData, bool loadScores = true, bool loadSamples = true); + bool doSong(int songIndex, int advance = 0); void stopMusic(); void freePatches(); void freeScores(); + int8 noteOn(ChannelContext &channel, byte note, uint16 volume, uint16 pri); + void noteOff(ChannelContext &channel, byte note); + void killVoice(byte num); + + void setTempo(const uint16 tempo) { + _playerCtx.tickUnit = (int32)(((uint32)(tempo & 0xFFF0) << 8) / (uint16)(5 * _playerCtx.vBlankFreq)); + // (tempo >> 4) * ((12 * 16) << 8) / (12 * 5 * _playerCtx.vBlankFreq); + } + static void outPutEvent(const Event &ev, int num = -1) { struct { byte cmd; diff --git a/tfmx/mxtxplayer.cpp b/tfmx/mxtxplayer.cpp index f2cc0ce6bc..e5200c6584 100644 --- a/tfmx/mxtxplayer.cpp +++ b/tfmx/mxtxplayer.cpp @@ -30,7 +30,7 @@ Audio::MaxTrax *loadMtmxfile(const char *mdatName, const char *smplName) { Audio::MaxTrax *mxtxPlay = new Audio::MaxTrax(44100, true); - if (!strcmp(mdatName, smplName)) { + if (strcmp(mdatName, smplName)) { SeekableReadStream *sampleIn = sampleNode.createReadStream(); if (0 == sampleIn) { debug("Couldnt load file %s", smplName); @@ -73,6 +73,11 @@ void modcmdmain(const int argc, const char *const argv[]) { if (i + 1 < argc) { param = atoi(argv[++i]); debug( "play Song %02X", param); + + player->doSong(0); + + //player->noteOn(player->_channelCtx[0], 43, 64, 0); + hasCmd = true; } @@ -83,6 +88,7 @@ void modcmdmain(const int argc, const char *const argv[]) { } if (!hasCmd) { + } int maxsecs = 10 * 60; |