diff options
-rw-r--r-- | sound/mods/tfmx.cpp | 894 | ||||
-rw-r--r-- | sound/mods/tfmx.h | 13 | ||||
-rw-r--r-- | tfmx/tfmxdebug.cpp | 2 |
3 files changed, 463 insertions, 446 deletions
diff --git a/sound/mods/tfmx.cpp b/sound/mods/tfmx.cpp index d3c8774a05..f813212a82 100644 --- a/sound/mods/tfmx.cpp +++ b/sound/mods/tfmx.cpp @@ -99,9 +99,10 @@ void Tfmx::interrupt() { // see if we have to run the macro-program if (channel.macroRun) { if (!channel.macroWait) { - while (macroStep(channel)) - ; - } else + macroRun(channel); + assert( !channel.deferWait ); // we can remove this variable as it should be never true after macroRun? + } + else --channel.macroWait; } @@ -119,6 +120,14 @@ void Tfmx::interrupt() { void Tfmx::effects(ChannelContext &channel) { // addBegin + if (channel.addBeginLength) { + channel.sampleStart += channel.addBeginDelta; + Paula::setChannelSampleStart(channel.paulaChannel, _resource.getSamplePtr(channel.sampleStart)); + if (!(--channel.addBeginCount)) { + channel.addBeginCount = channel.addBeginLength; + channel.addBeginDelta = -channel.addBeginDelta; + } + } // vibrato if (channel.vibLength) { @@ -130,7 +139,6 @@ void Tfmx::effects(ChannelContext &channel) { if (!channel.portaDelta) { // 16x16 bit multiplication, casts needed for the right results channel.period = (uint16)(((uint32)channel.refPeriod * (uint16)((1 << 11) + channel.vibValue)) >> 11); - //Paula::setChannelPeriod(channel.paulaChannel, channel.period); } } @@ -139,7 +147,7 @@ void Tfmx::effects(ChannelContext &channel) { channel.portaCount = channel.portaSkip; bool resetPorta = true; - uint16 period = channel.refPeriod; + const uint16 period = channel.refPeriod; uint16 portaVal = channel.portaValue; if (period > portaVal) { @@ -153,11 +161,9 @@ void Tfmx::effects(ChannelContext &channel) { if (resetPorta) { channel.portaDelta = 0; - channel.portaValue = channel.refPeriod & 0x7FF; - } else { + channel.portaValue = period & 0x7FF; + } else channel.period = channel.portaValue = portaVal & 0x7FF; - //Paula::setChannelPeriod(channel.paulaChannel, channel.period); - } } // envelope @@ -209,263 +215,271 @@ static void warnMacroUnimplemented(const byte *macroPtr, int level) { #endif } -inline bool Tfmx::macroStep(ChannelContext &channel) { - const byte *const macroPtr = (byte *)(_resource.getMacroPtr(channel.macroOffset) + channel.macroStep); - ++channel.macroStep; - - switch (macroPtr[0]) { - case 0x00: // Reset + DMA Off. Parameters: deferWait, addset, vol - clearEffects(channel); - // FT - case 0x13: // DMA Off. Parameters: deferWait, addset, vol - // TODO: implement PArameters - Paula::disableChannel(channel.paulaChannel); - channel.deferWait = (macroPtr[1] != 0); - if (channel.deferWait) { - // if set, then we expect a DMA On in the same tick. - channel.period = 4; - //Paula::setChannelPeriod(channel.paulaChannel, channel.period); - Paula::setChannelSampleLen(channel.paulaChannel, 1); - // in this state we then need to allow some commands that normally - // would halt the macroprogamm to continue instead. - // those commands are: Wait, WaitDMA, AddPrevNote, AddNote, SetNote, <unknown Cmd> - // DMA On is affected aswell - // TODO remember time disabled, remember pending dmaoff?. - } else { - //TODO ? - } +void Tfmx::macroRun(ChannelContext &channel) { + bool deferWait = false; + for (;;) { + const byte *const macroPtr = (byte *)(_resource.getMacroPtr(channel.macroOffset) + channel.macroStep); + ++channel.macroStep; - if (macroPtr[2]) - channel.volume = macroPtr[3]; - else if (macroPtr[3]) - channel.volume = channel.relVol * 3 + macroPtr[3]; - else - return true; - Paula::setChannelVolume(channel.paulaChannel, channel.volume); - return true; - - case 0x01: // DMA On - channel.dmaIntCount = 0; - if (channel.deferWait) { - // TODO - // there is actually a small delay in the player, but I think that - // only allows to clear DMA-State on real Hardware - } - Paula::setChannelPeriod(channel.paulaChannel, channel.period); - Paula::enableChannel(channel.paulaChannel); - channel.deferWait = false; - return true; + switch (macroPtr[0]) { + case 0x00: // Reset + DMA Off. Parameters: deferWait, addset, vol + clearEffects(channel); + // FT + case 0x13: // DMA Off. Parameters: deferWait, addset, vol + // TODO: implement PArameters + Paula::disableChannel(channel.paulaChannel); + channel.deferWait = deferWait = (macroPtr[1] != 0); + if (deferWait) { + // if set, then we expect a DMA On in the same tick. + channel.period = 4; + //Paula::setChannelPeriod(channel.paulaChannel, channel.period); + Paula::setChannelSampleLen(channel.paulaChannel, 1); + // in this state we then need to allow some commands that normally + // would halt the macroprogamm to continue instead. + // those commands are: Wait, WaitDMA, AddPrevNote, AddNote, SetNote, <unknown Cmd> + // DMA On is affected aswell + // TODO remember time disabled, remember pending dmaoff?. + } else { + //TODO ? + } - case 0x02: // SetBeginn. Parameters: SampleOffset(L) - channel.sampleStart = READ_BE_UINT32(macroPtr) & 0xFFFFFF; - Paula::setChannelSampleStart(channel.paulaChannel, _resource.getSamplePtr(channel.sampleStart)); - return true; + if (macroPtr[2]) + channel.volume = macroPtr[3]; + else if (macroPtr[3]) + channel.volume = channel.relVol * 3 + macroPtr[3]; + else + continue; + Paula::setChannelVolume(channel.paulaChannel, channel.volume); + continue; + + case 0x01: // DMA On + channel.dmaIntCount = 0; + if (deferWait) { + // TODO + // there is actually a small delay in the player, but I think that + // only allows to clear DMA-State on real Hardware + } + Paula::setChannelPeriod(channel.paulaChannel, channel.period); + Paula::enableChannel(channel.paulaChannel); + channel.deferWait = deferWait = false; + continue; + + case 0x02: // SetBeginn. Parameters: SampleOffset(L) + channel.addBeginLength = 0; + channel.sampleStart = READ_BE_UINT32(macroPtr) & 0xFFFFFF; + Paula::setChannelSampleStart(channel.paulaChannel, _resource.getSamplePtr(channel.sampleStart)); + continue; + + case 0x03: // SetLength. Parameters: SampleLength(W) + channel.sampleLen = READ_BE_UINT16(¯oPtr[2]); + Paula::setChannelSampleLen(channel.paulaChannel, channel.sampleLen); + continue; + + case 0x04: // Wait. Parameters: Ticks to wait(W). + // TODO: some unkown Parameter? (macroPtr[1] & 1) + channel.macroWait = READ_BE_UINT16(¯oPtr[2]); + return; + + case 0x10: // Loop Key Up. Parameters: Loopcount, MacroStep(W) + if (channel.keyUp) + continue; + // FT + case 0x05: // Loop. Parameters: Loopcount, MacroStep(W) + if (channel.macroLoopCount != 0) { + if (channel.macroLoopCount == 0xFF) + channel.macroLoopCount = macroPtr[1]; + channel.macroStep = READ_BE_UINT16(¯oPtr[2]); + } + --channel.macroLoopCount; + continue; - case 0x03: // SetLength. Parameters: SampleLength(W) - channel.sampleLen = READ_BE_UINT16(¯oPtr[2]); - Paula::setChannelSampleLen(channel.paulaChannel, channel.sampleLen); - return true; + case 0x06: // Jump. Parameters: MacroIndex, MacroStep(W) + channel.macroIndex = macroPtr[1] & (kMaxMacroOffsets - 1); + channel.macroOffset = _macroOffset[macroPtr[1] & (kMaxMacroOffsets - 1)]; + channel.macroStep = READ_BE_UINT16(¯oPtr[2]); + channel.macroLoopCount = 0xFF; + continue; - case 0x04: // Wait. Parameters: Ticks to wait(W). - // TODO: some unkown Parameter? (macroPtr[1] & 1) - channel.macroWait = READ_BE_UINT16(¯oPtr[2]); - return false; + case 0x07: // Stop Macro + channel.macroRun = false; + --channel.macroStep; + return; - case 0x10: // Loop Key Up. Parameters: Loopcount, MacroStep(W) - if (!channel.keyUp) - return true; - // FT - case 0x05: // Loop. Parameters: Loopcount, MacroStep(W) - if (channel.macroLoopCount != 0) { - if (channel.macroLoopCount == 0xFF) - channel.macroLoopCount = macroPtr[1]; + case 0x08: // AddNote. Parameters: Note, Finetune(W) + setNoteMacro(channel, channel.note + macroPtr[1], READ_BE_UINT16(¯oPtr[2])); + break; + + case 0x09: // SetNote. Parameters: Note, Finetune(W) + setNoteMacro(channel, macroPtr[1], READ_BE_UINT16(¯oPtr[2])); + break; + + case 0x0A: // Clear Effects + clearEffects(channel); + continue; + + case 0x0B: // Portamento. Parameters: count, speed + channel.portaSkip = macroPtr[1]; + channel.portaCount = 1; + // if porta is already running, then keep using old value + if (!channel.portaDelta) + channel.portaValue = channel.refPeriod; + channel.portaDelta = READ_BE_UINT16(¯oPtr[2]); + continue; + + case 0x0C: // Vibrato. Parameters: Speed, intensity + channel.vibLength = macroPtr[1]; + channel.vibCount = macroPtr[1] / 2; + channel.vibDelta = macroPtr[3]; + if (!channel.portaDelta) { + channel.period = channel.refPeriod; + //Paula::setChannelPeriod(channel.paulaChannel, channel.period); + channel.vibValue = 0; + } + continue; + + case 0x0D: // Add Volume. Parameters: note, addNoteFlag, volume + if (macroPtr[2] == 0xFE) + setNoteMacro(channel, channel.note + macroPtr[1], 0); + channel.volume = channel.relVol * 3 + macroPtr[3]; + continue; + + case 0x0E: // Set Volume. Parameters: note, addNoteFlag, volume + if (macroPtr[2] == 0xFE) + setNoteMacro(channel, channel.note + macroPtr[1], 0); + channel.volume = macroPtr[3]; + continue; + + case 0x0F: // Envelope. Parameters: speed, count, endvol + channel.envDelta = macroPtr[1]; + channel.envCount = channel.envSkip = macroPtr[2]; + channel.envEndVolume = macroPtr[3]; + continue; + + case 0x11: // AddBegin. Parameters: times, Offset(W) + channel.addBeginLength = channel.addBeginCount = macroPtr[1]; + channel.addBeginDelta = (int16)READ_BE_UINT16(¯oPtr[2]); + channel.sampleStart += channel.addBeginDelta; + Paula::setChannelSampleStart(channel.paulaChannel, _resource.getSamplePtr(channel.sampleStart)); + warnMacroUnimplemented(macroPtr, 1); + continue; + + case 0x12: // AddLen. Parameters: added Length(W) + channel.sampleLen += (int16)READ_BE_UINT16(¯oPtr[2]); + Paula::setChannelSampleLen(channel.paulaChannel, channel.sampleLen); + continue; + + case 0x14: // Wait key up. Parameters: wait cycles + if (channel.keyUp || channel.macroLoopCount == 0) { + channel.macroLoopCount = 0xFF; + continue; + } else if (channel.macroLoopCount == 0xFF) + channel.macroLoopCount = macroPtr[3]; + --channel.macroLoopCount; + --channel.macroStep; + return; + + case 0x15: // Subroutine. Parameters: MacroIndex, Macrostep(W) + channel.macroReturnOffset = channel.macroOffset; + channel.macroReturnStep = channel.macroStep; + + channel.macroOffset = _macroOffset[macroPtr[1] & (kMaxMacroOffsets - 1)]; channel.macroStep = READ_BE_UINT16(¯oPtr[2]); + // TODO: MI does some weird stuff there. Figure out which varioables need to be set + continue; + + case 0x16: // Return from Sub. + channel.macroOffset = channel.macroReturnOffset; + channel.macroStep = channel.macroReturnStep; + continue; + + case 0x17: // set Period. Parameters: Period(W) + channel.refPeriod = READ_BE_UINT16(¯oPtr[2]); + if (!channel.portaDelta) { + channel.period = channel.refPeriod; + //Paula::setChannelPeriod(channel.paulaChannel, channel.period); + } + continue; + + case 0x18: { // Sampleloop. Parameters: Offset from Samplestart(W) + // TODO: MI loads 24 bit, but thats useless? + const uint16 temp = /* ((int8)macroPtr[1] << 16) | */ READ_BE_UINT16(¯oPtr[2]); + if (macroPtr[1] || (temp & 1)) + warning("Tfmx: Problematic value for sampleloop: %i", (macroPtr[1] << 16) | temp); + channel.sampleStart += temp & 0xFFFE; + channel.sampleLen -= (temp / 2) /* & 0x7FFF */; + Paula::setChannelSampleStart(channel.paulaChannel, _resource.getSamplePtr(channel.sampleStart)); + Paula::setChannelSampleLen(channel.paulaChannel, channel.sampleLen); + continue; } - --channel.macroLoopCount; - return true; - - case 0x06: // Jump. Parameters: MacroIndex, MacroStep(W) - channel.macroIndex = macroPtr[1] & (kMaxMacroOffsets - 1); - channel.macroOffset = _macroOffset[macroPtr[1] & (kMaxMacroOffsets - 1)]; - channel.macroStep = READ_BE_UINT16(¯oPtr[2]); - channel.macroLoopCount = 0xFF; - return true; - - case 0x07: // Stop Macro - channel.macroRun = false; - --channel.macroStep; - return false; - - case 0x08: // AddNote. Parameters: Note, Finetune(W) - setNoteMacro(channel, channel.note + macroPtr[1], READ_BE_UINT16(¯oPtr[2])); - return channel.deferWait; - - case 0x09: // SetNote. Parameters: Note, Finetune(W) - setNoteMacro(channel, macroPtr[1], READ_BE_UINT16(¯oPtr[2])); - return channel.deferWait; - - case 0x0A: // Clear Effects - clearEffects(channel); - return true; - - case 0x0B: // Portamento. Parameters: count, speed - channel.portaSkip = macroPtr[1]; - channel.portaCount = 1; - // if porta is already running, then keep using old value - if (!channel.portaDelta) - channel.portaValue = channel.refPeriod; - channel.portaDelta = READ_BE_UINT16(¯oPtr[2]); - return true; + case 0x19: // set one-shot Sample + channel.addBeginLength = 0; + channel.sampleStart = 0; + channel.sampleLen = 1; + Paula::setChannelSampleStart(channel.paulaChannel, _resource.getSamplePtr(0)); + Paula::setChannelSampleLen(channel.paulaChannel, 1); + continue; - case 0x0C: // Vibrato. Parameters: Speed, intensity - channel.vibLength = macroPtr[1]; - channel.vibCount = macroPtr[1] / 2; - channel.vibDelta = macroPtr[3]; - if (!channel.portaDelta) { - channel.period = channel.refPeriod; - //Paula::setChannelPeriod(channel.paulaChannel, channel.period); - channel.vibValue = 0; - } - return true; - - case 0x0D: // Add Volume. Parameters: note, addNoteFlag, volume - if (macroPtr[2] == 0xFE) - setNoteMacro(channel, channel.note + macroPtr[1], 0); - channel.volume = channel.relVol * 3 + macroPtr[3]; - return true; - - case 0x0E: // Set Volume. Parameters: note, addNoteFlag, volume - if (macroPtr[2] == 0xFE) - setNoteMacro(channel, channel.note + macroPtr[1], 0); - channel.volume = macroPtr[3]; - return true; - - case 0x0F: // Envelope. Parameters: speed, count, endvol - channel.envDelta = macroPtr[1]; - channel.envCount = channel.envSkip = macroPtr[2]; - channel.envEndVolume = macroPtr[3]; - return true; - - case 0x11: // AddBegin. Parameters: times, Offset(W) - // TODO: implement Parameter - macroPtr[1]; - channel.sampleStart += (int16)READ_BE_UINT16(¯oPtr[2]); - Paula::setChannelSampleStart(channel.paulaChannel, _resource.getSamplePtr(channel.sampleStart)); - warnMacroUnimplemented(macroPtr, 1); - return true; + case 0x1A: // Wait on DMA. Parameters: Cycles-1(W) to wait + channel.dmaIntCount = READ_BE_UINT16(¯oPtr[2]) + 1; + channel.macroRun = false; + Paula::setChannelDmaCount(channel.paulaChannel); + break; - case 0x12: // AddLen. Parameters: added Length(W) - channel.sampleLen += (int16)READ_BE_UINT16(¯oPtr[2]); - Paula::setChannelSampleLen(channel.paulaChannel, channel.sampleLen); - return true; + case 0x1B: // Random play. Parameters: macro/speed/mode + warnMacroUnimplemented(macroPtr, 0); + continue; - case 0x14: // Wait key up. Parameters: wait cycles - if (!channel.keyUp || channel.macroLoopCount == 0) { - channel.macroLoopCount = 0xFF; - return true; - } else if (channel.macroLoopCount == 0xFF) - channel.macroLoopCount = macroPtr[3]; - --channel.macroLoopCount; - --channel.macroStep; - return false; - - case 0x15: // Subroutine. Parameters: MacroIndex, Macrostep(W) - channel.macroReturnOffset = channel.macroOffset; - channel.macroReturnStep = channel.macroStep; - - channel.macroOffset = _macroOffset[macroPtr[1] & (kMaxMacroOffsets - 1)]; - channel.macroStep = READ_BE_UINT16(¯oPtr[2]); - // TODO: MI does some weird stuff there. Figure out which varioables need to be set - return true; - - case 0x16: // Return from Sub. - channel.macroOffset = channel.macroReturnOffset; - channel.macroStep = channel.macroReturnStep; - return true; - - case 0x17: // set Period. Parameters: Period(W) - channel.refPeriod = READ_BE_UINT16(¯oPtr[2]); - if (!channel.portaDelta) { - channel.period = channel.refPeriod; - //Paula::setChannelPeriod(channel.paulaChannel, channel.period); - } - return true; + case 0x1C: // Branch on Note. Parameters: note/macrostep(W) + if (channel.note > macroPtr[1]) + channel.macroStep = READ_BE_UINT16(¯oPtr[2]); + continue; - case 0x18: { // Sampleloop. Parameters: Offset from Samplestart(W) - // TODO: MI loads 24 bit, but thats useless? - uint16 temp = READ_BE_UINT16(¯oPtr[2]); - channel.sampleStart += temp & 0xFFFE; - channel.sampleLen -= (uint16)(temp / 2); - Paula::setChannelSampleStart(channel.paulaChannel, _resource.getSamplePtr(channel.sampleStart)); - Paula::setChannelSampleLen(channel.paulaChannel, channel.sampleLen); - return true; - } - case 0x19: // set one-shot Sample - channel.sampleStart = 0; - channel.sampleLen = 1; - Paula::setChannelSampleStart(channel.paulaChannel, _resource.getSamplePtr(0)); - Paula::setChannelSampleLen(channel.paulaChannel, 1); - return true; - - case 0x1A: // Wait on DMA. Parameters: Cycles-1(W) to wait - channel.dmaIntCount = READ_BE_UINT16(¯oPtr[2]) + 1; - channel.macroRun = false; - Paula::setChannelDmaCount(channel.paulaChannel); - return channel.deferWait; - - case 0x1B: // Random play. Parameters: macro/speed/mode - warnMacroUnimplemented(macroPtr, 0); - return true; - - case 0x1C: // Branch on Note. Parameters: note/macrostep(W) - if (channel.note > macroPtr[1]) - channel.macroStep = READ_BE_UINT16(¯oPtr[2]); - return true; + case 0x1D: // Branch on Volume. Parameters: volume/macrostep(W) + if (channel.volume > macroPtr[1]) + channel.macroStep = READ_BE_UINT16(¯oPtr[2]); + continue; - case 0x1D: // Branch on Volume. Parameters: volume/macrostep(W) - if (channel.volume > macroPtr[1]) - channel.macroStep = READ_BE_UINT16(¯oPtr[2]); - return true; - - case 0x1E: // Addvol+note. Parameters: note/CONST./volume - warnMacroUnimplemented(macroPtr, 0); - return true; - - case 0x1F: // AddPrevNote. Parameters: Note, Finetune(W) - setNoteMacro(channel, channel.prevNote + macroPtr[1], READ_BE_UINT16(¯oPtr[2])); - return channel.deferWait; - - case 0x20: // Signal. Parameters: signalnumber/value - if (_playerCtx.signal) - _playerCtx.signal[macroPtr[1]] = READ_BE_UINT16(¯oPtr[2]); - return true; - - case 0x21: // Play macro. Parameters: macro/chan/detune - noteCommand(channel.note, (channel.relVol << 4) | macroPtr[1], macroPtr[2], macroPtr[3]); - return true; -#if defined(TFMX_NOT_IMPLEMENTED) - // used by Gem`X according to the docs - case 0x22: // SID setbeg. Parameters: sample-startadress - return true; - case 0x23: // SID setlen. Parameters: buflen/sourcelen - return true; - case 0x24: // SID op3 ofs. Parameters: offset - return true; - case 0x25: // SID op3 frq. Parameters: speed/amplitude - return true; - case 0x26: // SID op2 ofs. Parameters: offset - return true; - case 0x27: // SID op2 frq. Parameters: speed/amplitude - return true; - case 0x28: // ID op1. Parameters: speed/amplitude/TC - return true; - case 0x29: // SID stop. Parameters: flag (1=clear all) - return true; - // 30-34 used by Carribean Disaster -#endif - default: - warnMacroUnimplemented(macroPtr, 0); - return channel.deferWait; + case 0x1E: // Addvol+note. Parameters: note/CONST./volume + warnMacroUnimplemented(macroPtr, 0); + continue; + + case 0x1F: // AddPrevNote. Parameters: Note, Finetune(W) + setNoteMacro(channel, channel.prevNote + macroPtr[1], READ_BE_UINT16(¯oPtr[2])); + break; + + case 0x20: // Signal. Parameters: signalnumber/value + if (_playerCtx.signal) + _playerCtx.signal[macroPtr[1]] = READ_BE_UINT16(¯oPtr[2]); + continue; + + case 0x21: // Play macro. Parameters: macro/chan/detune + noteCommand(channel.note, (channel.relVol << 4) | macroPtr[1], macroPtr[2], macroPtr[3]); + continue; + #if defined(TFMX_NOT_IMPLEMENTED) + // used by Gem`X according to the docs + case 0x22: // SID setbeg. Parameters: sample-startadress + return true; + case 0x23: // SID setlen. Parameters: buflen/sourcelen + return true; + case 0x24: // SID op3 ofs. Parameters: offset + return true; + case 0x25: // SID op3 frq. Parameters: speed/amplitude + return true; + case 0x26: // SID op2 ofs. Parameters: offset + return true; + case 0x27: // SID op2 frq. Parameters: speed/amplitude + return true; + case 0x28: // ID op1. Parameters: speed/amplitude/TC + return true; + case 0x29: // SID stop. Parameters: flag (1=clear all) + return true; + // 30-34 used by Carribean Disaster + #endif + default: + warnMacroUnimplemented(macroPtr, 0); + } + if (!deferWait) + return; } } @@ -478,19 +492,12 @@ startPatterns: if (pattCmd < 0x90) { // execute Patternstep ++runningPatterns; if (_patternCtx[i].wait == 0) { - bool pendingTrackstep = false; // issue all Steps for this tick - while (patternStep(_patternCtx[i], pendingTrackstep)) - ; + const bool pendingTrackstep = patternRun(_patternCtx[i]); + if (pendingTrackstep) { // we load the next Trackstep Command and then process all Channels again - // TODO Optionally disable looping - if (_trackCtx.posInd == _trackCtx.stopInd) - _trackCtx.posInd = _trackCtx.startInd; - else - ++_trackCtx.posInd; - while (trackStep()) - ; + trackRun(true); goto startPatterns; } @@ -523,195 +530,203 @@ static void warnPatternUnimplemented(const byte *patternPtr, int level) { #endif } +bool Tfmx::patternRun(PatternContext &pattern) { + for (;;) { + const byte *const patternPtr = (byte *)(_resource.getPatternPtr(pattern.offset) + pattern.step); + ++pattern.step; + const byte pattCmd = patternPtr[0]; + + if (pattCmd < 0xF0) { // Playnote + bool doWait = false; + byte noteCmd = pattCmd + pattern.expose; + byte param3 = patternPtr[3]; + if (pattCmd < 0xC0) { // Note + if (pattCmd >= 0x80) { // Wait + pattern.wait = param3; + param3 = 0; + doWait = true; + } + noteCmd &= 0x3F; + } // else Portamento + noteCommand(noteCmd, patternPtr[1], patternPtr[2], param3); + if (doWait) + return false; + + } else { // Patterncommand + switch (pattCmd & 0xF) { + case 0: // End Pattern + Next Trackstep + pattern.command = 0xFF; + --pattern.step; + return true; + + case 1: // Loop Pattern. Parameters: Loopcount, PatternStep(W) + if (pattern.loopCount != 0) { + if (pattern.loopCount == 0xFF) + pattern.loopCount = patternPtr[1]; + pattern.step = READ_BE_UINT16(&patternPtr[2]); + } + --pattern.loopCount; + continue; -inline bool Tfmx::patternStep(PatternContext &pattern, bool &pendingTrackstep) { - const byte *const patternPtr = (byte *)(_resource.getPatternPtr(pattern.offset) + pattern.step); - ++pattern.step; - - /*debug("Pattern %04X +%d", pattern.offset, pattern.step-1); - displayPatternstep(patternPtr);*/ - - const byte pattCmd = patternPtr[0]; - - if (pattCmd < 0xF0) { // Playnote - bool nextStep = true; - byte noteCmd = pattCmd + pattern.expose; - byte param3 = patternPtr[3]; - if (pattCmd < 0xC0) { // Note - if (pattCmd >= 0x80) { // Wait - pattern.wait = param3; - param3 = 0; - nextStep = false; - } - noteCmd &= 0x3F; - } // else Portamento - noteCommand(noteCmd, patternPtr[1], patternPtr[2], param3); - return nextStep; - - } else { // Patterncommand - switch (pattCmd & 0xF) { - case 0: // End Pattern + Next Trackstep - pattern.command = 0xFF; - --pattern.step; - pendingTrackstep = true; - return false; - - case 1: // Loop Pattern. Parameters: Loopcount, PatternStep(W) - if (pattern.loopCount != 0) { - if (pattern.loopCount == 0xFF) - pattern.loopCount = patternPtr[1]; + case 2: // Jump. Parameters: PatternIndex, PatternStep(W) + pattern.offset = _patternOffset[patternPtr[1]]; pattern.step = READ_BE_UINT16(&patternPtr[2]); - } - --pattern.loopCount; - return true; - - case 2: // Jump. Parameters: PatternIndex, PatternStep(W) - pattern.offset = _patternOffset[patternPtr[1]]; - pattern.step = READ_BE_UINT16(&patternPtr[2]); - return true; - - case 3: // Wait. Paramters: ticks to wait - pattern.wait = patternPtr[1]; - return false; - - case 14: // Stop custompattern - // TODO ? - warnPatternUnimplemented(patternPtr, 1); - // FT - case 4: // Stop this pattern - pattern.command = 0xFF; - --pattern.step; - // TODO: try figuring out if this was the last Channel? - return false; - - case 5: // Kup^-Set key up - if (!_channelCtx[patternPtr[2] & (kNumVoices - 1)].sfxLocked) - _channelCtx[patternPtr[2] & (kNumVoices - 1)].keyUp = false; - return true; - - case 6: // Vibrato - case 7: // Envelope - noteCommand(pattCmd, patternPtr[1], patternPtr[2], patternPtr[3]); - return true; - - case 8: // Subroutine - warnPatternUnimplemented(patternPtr, 0); - return true; - case 9: // Return from Subroutine - warnPatternUnimplemented(patternPtr, 0); - return true; + continue; + + case 3: // Wait. Paramters: ticks to wait + pattern.wait = patternPtr[1]; + return false; + + case 14: // Stop custompattern + // TODO ? + warnPatternUnimplemented(patternPtr, 1); + // FT + case 4: // Stop this pattern + pattern.command = 0xFF; + --pattern.step; + // TODO: try figuring out if this was the last Channel? + return false; + + case 5: // Key Up Signal + if (!_channelCtx[patternPtr[2] & (kNumVoices - 1)].sfxLocked) + _channelCtx[patternPtr[2] & (kNumVoices - 1)].keyUp = true; + continue; + + case 6: // Vibrato + case 7: // Envelope + noteCommand(pattCmd, patternPtr[1], patternPtr[2], patternPtr[3]); + continue; + + case 8: // Subroutine + warnPatternUnimplemented(patternPtr, 0); + continue; + + case 9: // Return from Subroutine + warnPatternUnimplemented(patternPtr, 0); + continue; + + case 10: // fade master volume + _playerCtx.fadeCount = _playerCtx.fadeSkip = patternPtr[1]; + _playerCtx.fadeEndVolume = (int8)patternPtr[3]; + if (_playerCtx.fadeSkip) { + const int diff = _playerCtx.fadeEndVolume - _playerCtx.volume; + _playerCtx.fadeDelta = (diff != 0) ? ((diff > 0) ? 1 : -1) : 0; + } else { + _playerCtx.volume = _playerCtx.fadeEndVolume; + _playerCtx.fadeDelta = 0; + } + ++_trackCtx.posInd; + continue; + + case 11: { // play pattern. Parameters: patternCmd, channel, expose + PatternContext &target = _patternCtx[patternPtr[2] & (kNumChannels - 1)]; + + target.command = patternPtr[1]; + target.offset = _patternOffset[patternPtr[1] & (kMaxPatternOffsets - 1)]; + target.expose = patternPtr[3]; + target.step = 0; + target.wait = 0; + target.loopCount = 0xFF; + } + continue; - case 10: // fade master volume - _playerCtx.fadeCount = _playerCtx.fadeSkip = patternPtr[1]; - _playerCtx.fadeEndVolume = (int8)patternPtr[3]; - if (_playerCtx.fadeSkip) { - const int diff = _playerCtx.fadeEndVolume - _playerCtx.volume; - _playerCtx.fadeDelta = (diff != 0) ? ((diff > 0) ? 1 : -1) : 0; - } else { - _playerCtx.volume = _playerCtx.fadeEndVolume; - _playerCtx.fadeDelta = 0; - } - ++_trackCtx.posInd; - return true; + case 12: // Lock + _channelCtx[patternPtr[2] & (kNumVoices - 1)].sfxLocked = (patternPtr[1] != 0); + _channelCtx[patternPtr[2] & (kNumVoices - 1)].sfxLockTime = patternPtr[3]; + continue; - case 11: { // play pattern. Parameters: patternCmd, channel, expose - PatternContext &target = _patternCtx[patternPtr[2] & (kNumChannels - 1)]; + case 13: // Cue + if (_playerCtx.signal) + _playerCtx.signal[patternPtr[1]] = READ_BE_UINT16(&patternPtr[2]); + continue; - target.command = patternPtr[1]; - target.offset = _patternOffset[patternPtr[1] & (kMaxPatternOffsets - 1)]; - target.expose = patternPtr[3]; - target.step = 0; - target.wait = 0; - target.loopCount = 0xFF; + case 15: // NOP + continue; } - return true; - - case 12: // Lock - _channelCtx[patternPtr[2] & (kNumVoices - 1)].sfxLocked = (patternPtr[1] != 0); - _channelCtx[patternPtr[2] & (kNumVoices - 1)].sfxLockTime = patternPtr[3]; - return true; - - case 13: // Cue - if (_playerCtx.signal) - _playerCtx.signal[patternPtr[1]] = READ_BE_UINT16(&patternPtr[2]); - return true; - - case 15: // NOP - return true; } } - - return true; } -bool Tfmx::trackStep() { - const uint16 *const trackData = _resource.getTrackPtr(_trackCtx.posInd); - - if (trackData[0] != FROM_BE_16(0xEFFE)) { - // 8 commands for Patterns - for (int i = 0; i < 8; ++i) { - const uint patCmd = READ_BE_UINT16(&trackData[i]); - // First byte is pattern number - const uint patNum = (patCmd >> 8); - // if highest bit is set then keep previous pattern - if (patNum < 0x80) { - _patternCtx[i].step = 0; - _patternCtx[i].wait = 0; - _patternCtx[i].loopCount = 0xFF; - _patternCtx[i].offset = _patternOffset[patNum]; - } - _patternCtx[i].command = (uint8)patNum; - _patternCtx[i].expose = patCmd & 0xFF; - } - return false; - - } else { - // 16 byte Trackstep Command - switch (READ_BE_UINT16(&trackData[1])) { - case 0: // Stop Player. No Parameters - stopPaula(); - return false; - - case 1: // Branch/Loop section of tracksteps. Parameters: branch target, loopcount +bool Tfmx::trackRun(const bool incStep) { + assert(_playerCtx.song >= 0); + if (incStep) { + // TODO Optionally disable looping + if (_trackCtx.posInd == _trackCtx.stopInd) + _trackCtx.posInd = _trackCtx.startInd; + else ++_trackCtx.posInd; - if (_trackCtx.loopCount != 0) { - if (_trackCtx.loopCount < 0) - _trackCtx.loopCount = READ_BE_UINT16(&trackData[3]); - _trackCtx.posInd = READ_BE_UINT16(&trackData[2]); + } + for (;;) { + const uint16 *const trackData = _resource.getTrackPtr(_trackCtx.posInd); + + if (trackData[0] != FROM_BE_16(0xEFFE)) { + // 8 commands for Patterns + for (int i = 0; i < 8; ++i) { + const uint patCmd = READ_BE_UINT16(&trackData[i]); + // First byte is pattern number + const uint patNum = (patCmd >> 8); + // if highest bit is set then keep previous pattern + if (patNum < 0x80) { + _patternCtx[i].step = 0; + _patternCtx[i].wait = 0; + _patternCtx[i].loopCount = 0xFF; + _patternCtx[i].offset = _patternOffset[patNum]; + } + _patternCtx[i].command = (uint8)patNum; + _patternCtx[i].expose = patCmd & 0xFF; } - --_trackCtx.loopCount; return true; - case 2: { // Set Tempo. Parameters: tempo, divisor - _playerCtx.patternCount = _playerCtx.patternSkip = READ_BE_UINT16(&trackData[2]); // tempo - const uint16 temp = READ_BE_UINT16(&trackData[3]); // divisor + } else { + // 16 byte Trackstep Command + switch (READ_BE_UINT16(&trackData[1])) { + case 0: // Stop Player. No Parameters + stopPaula(); + return false; + + case 1: // Branch/Loop section of tracksteps. Parameters: branch target, loopcount + if (_trackCtx.loopCount != 0) { + if (_trackCtx.loopCount < 0) + _trackCtx.loopCount = READ_BE_UINT16(&trackData[3]); + _trackCtx.posInd = READ_BE_UINT16(&trackData[2]); + continue; + } + --_trackCtx.loopCount; + break; - if (!(temp & 0x8000) && (temp & 0x1FF)) - setInterruptFreqUnscaled(temp & 0x1FF); - ++_trackCtx.posInd; - return true; - } - case 4: // Fade - _playerCtx.fadeCount = _playerCtx.fadeSkip = (uint8)READ_BE_UINT16(&trackData[2]); - _playerCtx.fadeEndVolume = (int8)READ_BE_UINT16(&trackData[3]); + case 2: { // Set Tempo. Parameters: tempo, divisor + _playerCtx.patternCount = _playerCtx.patternSkip = READ_BE_UINT16(&trackData[2]); // tempo + const uint16 temp = READ_BE_UINT16(&trackData[3]); // divisor - if (_playerCtx.fadeSkip) { - const int diff = _playerCtx.fadeEndVolume - _playerCtx.volume; - _playerCtx.fadeDelta = (diff != 0) ? ((diff > 0) ? 1 : -1) : 0; - } else { - _playerCtx.volume = _playerCtx.fadeEndVolume; - _playerCtx.fadeDelta = 0; + if (!(temp & 0x8000) && (temp & 0x1FF)) + setInterruptFreqUnscaled(temp & 0x1FF); + break; } - ++_trackCtx.posInd; - return true; + case 4: // Fade + _playerCtx.fadeCount = _playerCtx.fadeSkip = (uint8)READ_BE_UINT16(&trackData[2]); + _playerCtx.fadeEndVolume = (int8)READ_BE_UINT16(&trackData[3]); + + if (_playerCtx.fadeSkip) { + const int diff = _playerCtx.fadeEndVolume - _playerCtx.volume; + _playerCtx.fadeDelta = (diff != 0) ? ((diff > 0) ? 1 : -1) : 0; + } else { + _playerCtx.volume = _playerCtx.fadeEndVolume; + _playerCtx.fadeDelta = 0; + } + break; - case 3: // Unknown, stops player aswell - default: - debug("Unknown Command: %02X", READ_BE_UINT16(&trackData[1])); - // MI-Player handles this by stopping the player, we just continue - ++_trackCtx.posInd; - return true; + case 3: // Unknown, stops player aswell + default: + debug("Unknown Command: %02X", READ_BE_UINT16(&trackData[1])); + // MI-Player handles this by stopping the player, we just continue + } } + + if (_trackCtx.posInd == _trackCtx.stopInd) { + warning("Tfmx: Reached invalid Song-Position"); + return false; + } + ++_trackCtx.posInd; } } @@ -735,7 +750,7 @@ void Tfmx::noteCommand(const uint8 note, const uint8 param1, const uint8 param2, channel.fineTune = (int8)param3; initMacroProgramm(channel); - channel.keyUp = true; + channel.keyUp = false; // key down = playing a Note } else if (note < 0xF0) { // Portamento channel.portaSkip = param1; @@ -747,8 +762,8 @@ void Tfmx::noteCommand(const uint8 note, const uint8 param1, const uint8 param2, channel.note = note & 0x3F; channel.refPeriod = noteIntervalls[channel.note]; } else switch (note & 0xF) { // Command - case 5: // Key Up Release - channel.keyUp = false; + case 5: // Key Up Signal + channel.keyUp = true; break; case 6: // Vibratio channel.vibLength = param1 & 0xFE; @@ -936,9 +951,8 @@ void Tfmx::doSong(int songPos, bool stopAudio) { setInterruptFreqUnscaled(ciaIntervall); _playerCtx.patternCount = 0; - while (trackStep()) - ; - startPaula(); + if (trackRun()) + startPaula(); } int Tfmx::doSfx(uint16 sfxIndex, bool unlockChannel) { diff --git a/sound/mods/tfmx.h b/sound/mods/tfmx.h index 36e4151ce9..695062762c 100644 --- a/sound/mods/tfmx.h +++ b/sound/mods/tfmx.h @@ -184,9 +184,9 @@ public: int16 vibValue; int8 vibDelta; - uint8 addBeginTime; - uint8 addBeginReset; - int16 addBeginDelta; + uint8 addBeginLength; + uint8 addBeginCount; + int32 addBeginDelta; } _channelCtx[kNumVoices]; struct PatternContext { @@ -237,6 +237,7 @@ private: } static void clearEffects(ChannelContext &channel) { + channel.addBeginLength = 0; channel.envSkip = 0; channel.vibLength = 0; channel.portaDelta = 0; @@ -280,10 +281,10 @@ private: } void effects(ChannelContext &channel); - inline bool macroStep(ChannelContext &channel); + void macroRun(ChannelContext &channel); void advancePatterns(); - inline bool patternStep(PatternContext &pattern, bool &pendingTrackstep); - bool trackStep(); + bool patternRun(PatternContext &pattern); + bool trackRun(bool incStep = false); void noteCommand(uint8 note, uint8 param1, uint8 param2, uint8 param3); }; diff --git a/tfmx/tfmxdebug.cpp b/tfmx/tfmxdebug.cpp index cf2595a220..95bad15a96 100644 --- a/tfmx/tfmxdebug.cpp +++ b/tfmx/tfmxdebug.cpp @@ -144,7 +144,9 @@ void dumpTracksteps(Audio::Tfmx &player, uint16 first, uint16 last) { } void dumpTrackstepsBySong(Audio::Tfmx &player, int song) { + debug("Song %02X: Pos %02X - %02X. Tempo: %02X", song, player._subsong[song].songstart, player._subsong[song].songend, player._subsong[song].tempo); dumpTracksteps(player, player._subsong[song].songstart, player._subsong[song].songend); + debug(""); } void dumpMacro(Audio::Tfmx &player, uint16 macroIndex, uint16 len, uint16 start) { |