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) {  | 
