| 126 | 1 /* - main XM replayer - | 
|  | 2 ** | 
|  | 3 ** NOTE: Effect handling is slightly different because | 
|  | 4 ** I've removed the channel muting logic. | 
|  | 5 ** Muted channels would only process *some* effects, but | 
|  | 6 ** since we can't mute channels, we don't care about this. | 
|  | 7 ** | 
|  | 8 ** In FT2, the only way to mute a channel is through the | 
|  | 9 ** tracker itself, so this is not really needed in a replayer. | 
|  | 10 */ | 
|  | 11 | 
|  | 12 #include <stdio.h> | 
|  | 13 #include <stdint.h> | 
|  | 14 #include <stdbool.h> | 
|  | 15 #include "pmplay.h" | 
|  | 16 #include "pmp_mix.h" | 
|  | 17 #include "snd_masm.h" | 
|  | 18 #include "tables.h" | 
|  | 19 | 
|  | 20 #define MAX_FRQ 32000 | 
|  | 21 #define MAX_NOTES (10*12*16+16) | 
|  | 22 | 
|  | 23 static tonTyp nilPatternLine[32]; // 8bb: used for non-allocated (empty) patterns | 
|  | 24 | 
|  | 25 typedef void (*volKolEfxRoutine)(stmTyp *ch); | 
|  | 26 typedef void (*volKolEfxRoutine2)(stmTyp *ch, uint8_t *volKol); | 
|  | 27 typedef void (*efxRoutine)(stmTyp *ch, uint8_t param); | 
|  | 28 | 
|  | 29 static void retrigVolume(stmTyp *ch) | 
|  | 30 { | 
|  | 31 	ch->realVol = ch->oldVol; | 
|  | 32 	ch->outVol = ch->oldVol; | 
|  | 33 	ch->outPan = ch->oldPan; | 
|  | 34 	ch->status |= IS_Vol + IS_Pan + IS_QuickVol; | 
|  | 35 } | 
|  | 36 | 
|  | 37 static void retrigEnvelopeVibrato(stmTyp *ch) | 
|  | 38 { | 
|  | 39 	// 8bb: reset vibrato position | 
|  | 40 	if (!(ch->waveCtrl & 0x04)) | 
|  | 41 		ch->vibPos = 0; | 
|  | 42 | 
|  | 43 	/* | 
|  | 44 	** 8bb: | 
|  | 45 	** In FT2.00 .. FT2.09, if the sixth bit of "ch->waveCtrl" is set | 
|  | 46 	** (from effect E7x where x is $4..$7 or $C..$F) and you trigger a note, | 
|  | 47 	** the replayer interrupt will freeze / lock up. This is because of a | 
|  | 48 	** label bug in the original code, causing it to jump back to itself | 
|  | 49 	** indefinitely. | 
|  | 50 	*/ | 
|  | 51 | 
|  | 52 	// 8bb: safely reset tremolo position | 
|  | 53 	if (!(ch->waveCtrl & 0x40)) | 
|  | 54 		ch->tremPos = 0; | 
|  | 55 | 
|  | 56 	ch->retrigCnt = 0; | 
|  | 57 	ch->tremorPos = 0; | 
|  | 58 | 
|  | 59 	ch->envSustainActive = true; | 
|  | 60 | 
|  | 61 	instrTyp *ins = ch->instrSeg; | 
|  | 62 | 
|  | 63 	if (ins->envVTyp & ENV_ENABLED) | 
|  | 64 	{ | 
|  | 65 		ch->envVCnt = 65535; | 
|  | 66 		ch->envVPos = 0; | 
|  | 67 	} | 
|  | 68 | 
|  | 69 	if (ins->envPTyp & ENV_ENABLED) | 
|  | 70 	{ | 
|  | 71 		ch->envPCnt = 65535; | 
|  | 72 		ch->envPPos = 0; | 
|  | 73 	} | 
|  | 74 | 
|  | 75 	ch->fadeOutSpeed = ins->fadeOut; // 8bb: ranges 0..4095 (FT2 doesn't check if it's higher than 4095!) | 
|  | 76 | 
|  | 77 	// 8bb: final fadeout range is in fact 0..32768, and not 0..65536 like the XM format doc says | 
|  | 78 	ch->fadeOutAmp = 32768; | 
|  | 79 | 
|  | 80 	if (ins->vibDepth > 0) | 
|  | 81 	{ | 
|  | 82 		ch->eVibPos = 0; | 
|  | 83 | 
|  | 84 		if (ins->vibSweep > 0) | 
|  | 85 		{ | 
|  | 86 			ch->eVibAmp = 0; | 
|  | 87 			ch->eVibSweep = (ins->vibDepth << 8) / ins->vibSweep; | 
|  | 88 		} | 
|  | 89 		else | 
|  | 90 		{ | 
|  | 91 			ch->eVibAmp = ins->vibDepth << 8; | 
|  | 92 			ch->eVibSweep = 0; | 
|  | 93 		} | 
|  | 94 	} | 
|  | 95 } | 
|  | 96 | 
|  | 97 static void keyOff(stmTyp *ch) | 
|  | 98 { | 
|  | 99 	instrTyp *ins = ch->instrSeg; | 
|  | 100 | 
|  | 101 	if (!(ins->envPTyp & ENV_ENABLED)) // 8bb: probably an FT2 bug | 
|  | 102 	{ | 
|  | 103 		if (ch->envPCnt >= (uint16_t)ins->envPP[ch->envPPos][0]) | 
|  | 104 			ch->envPCnt = ins->envPP[ch->envPPos][0]-1; | 
|  | 105 	} | 
|  | 106 | 
|  | 107 	if (ins->envVTyp & ENV_ENABLED) | 
|  | 108 	{ | 
|  | 109 		if (ch->envVCnt >= (uint16_t)ins->envVP[ch->envVPos][0]) | 
|  | 110 			ch->envVCnt = ins->envVP[ch->envVPos][0]-1; | 
|  | 111 	} | 
|  | 112 	else | 
|  | 113 	{ | 
|  | 114 		ch->realVol = 0; | 
|  | 115 		ch->outVol = 0; | 
|  | 116 		ch->status |= IS_Vol + IS_QuickVol; | 
|  | 117 	} | 
|  | 118 | 
|  | 119 	ch->envSustainActive = false; | 
|  | 120 } | 
|  | 121 | 
|  | 122 uint32_t getFrequenceValue(uint16_t period) // 8bb: converts period to 16.16fp resampling delta | 
|  | 123 { | 
|  | 124 	uint32_t delta; | 
|  | 125 | 
|  | 126 	if (period == 0) | 
|  | 127 		return 0; | 
|  | 128 | 
|  | 129 	if (linearFrqTab) | 
|  | 130 	{ | 
|  | 131 		const uint16_t invPeriod = (12 * 192 * 4) - period; // 8bb: this intentionally underflows uint16_t to be accurate to FT2 | 
|  | 132 | 
|  | 133 		const uint32_t quotient = invPeriod / 768; | 
|  | 134 		const uint32_t remainder = invPeriod % 768; | 
|  | 135 | 
|  | 136 		const int32_t octShift = 14 - quotient; | 
|  | 137 | 
|  | 138 		delta = (uint32_t)(((int64_t)logTab[remainder] * (int32_t)frequenceMulFactor) >> 24); | 
|  | 139 		delta >>= (octShift & 31); // 8bb: added needed 32-bit bitshift mask | 
|  | 140 	} | 
|  | 141 	else | 
|  | 142 	{ | 
|  | 143 		delta = frequenceDivFactor / (uint32_t)period; | 
|  | 144 	} | 
|  | 145 | 
|  | 146 	return delta; | 
|  | 147 } | 
|  | 148 | 
|  | 149 static void startTone(uint8_t ton, uint8_t effTyp, uint8_t eff, stmTyp *ch) | 
|  | 150 { | 
|  | 151 	if (ton == NOTE_KEYOFF) | 
|  | 152 	{ | 
|  | 153 		keyOff(ch); | 
|  | 154 		return; | 
|  | 155 	} | 
|  | 156 | 
|  | 157 	// 8bb: if we came from Rxy (retrig), we didn't check note (Ton) yet | 
|  | 158 	if (ton == 0) | 
|  | 159 	{ | 
|  | 160 		ton = ch->tonNr; | 
|  | 161 		if (ton == 0) | 
|  | 162 			return; // 8bb: if still no note, return | 
|  | 163 	} | 
|  | 164 | 
|  | 165 	ch->tonNr = ton; | 
|  | 166 | 
|  | 167 	instrTyp *ins = instr[ch->instrNr]; | 
|  | 168 	if (ins == NULL) | 
|  | 169 		ins = instr[0]; | 
|  | 170 | 
|  | 171 	ch->instrSeg = ins; | 
|  | 172 	ch->mute = ins->mute; | 
|  | 173 | 
|  | 174 	uint8_t smp = ins->ta[ton-1] & 0xF; // 8bb: added for safety | 
|  | 175 	ch->sampleNr = smp; | 
|  | 176 | 
|  | 177 	sampleTyp *s = &ins->samp[smp]; | 
|  | 178 	ch->relTonNr = s->relTon; | 
|  | 179 | 
|  | 180 	ton += ch->relTonNr; | 
|  | 181 	if (ton >= 10*12) | 
|  | 182 		return; | 
|  | 183 | 
|  | 184 	ch->oldVol = s->vol; | 
|  | 185 	ch->oldPan = s->pan; | 
|  | 186 | 
|  | 187 	if (effTyp == 0x0E && (eff & 0xF0) == 0x50) // 8bb: EFx - Set Finetune | 
|  | 188 		ch->fineTune = ((eff & 0x0F) << 4) - 128; | 
|  | 189 	else | 
|  | 190 		ch->fineTune = s->fine; | 
|  | 191 | 
|  | 192 	if (ton != 0) | 
|  | 193 	{ | 
|  | 194 		const uint16_t tmpTon = ((ton-1) << 4) + (((int8_t)ch->fineTune >> 3) + 16); // 8bb: 0..1935 | 
|  | 195 		if (tmpTon < MAX_NOTES) // 8bb: tmpTon is *always* below MAX_NOTES here, this check is not needed | 
|  | 196 			ch->outPeriod = ch->realPeriod = note2Period[tmpTon]; | 
|  | 197 	} | 
|  | 198 | 
|  | 199 	ch->status |= IS_Period + IS_Vol + IS_Pan + IS_NyTon + IS_QuickVol; | 
|  | 200 | 
|  | 201 	if (effTyp == 9) // 8bb: 9xx - Set Sample Offset | 
|  | 202 	{ | 
|  | 203 		if (eff) | 
|  | 204 			ch->smpOffset = ch->eff; | 
|  | 205 | 
|  | 206 		ch->smpStartPos = ch->smpOffset << 8; | 
|  | 207 	} | 
|  | 208 	else | 
|  | 209 	{ | 
|  | 210 		ch->smpStartPos = 0; | 
|  | 211 	} | 
|  | 212 | 
|  | 213 	P_StartTone(s, ch->smpStartPos); | 
|  | 214 } | 
|  | 215 | 
|  | 216 static void volume(stmTyp *ch, uint8_t param); // 8bb: volume slide | 
|  | 217 static void vibrato2(stmTyp *ch); | 
|  | 218 static void tonePorta(stmTyp *ch, uint8_t param); | 
|  | 219 | 
|  | 220 static void dummy(stmTyp *ch, uint8_t param) | 
|  | 221 { | 
|  | 222 	return; | 
|  | 223 | 
|  | 224 	(void)ch; | 
|  | 225 	(void)param; | 
|  | 226 } | 
|  | 227 | 
|  | 228 static void finePortaUp(stmTyp *ch, uint8_t param) | 
|  | 229 { | 
|  | 230 	if (param == 0) | 
|  | 231 		param = ch->fPortaUpSpeed; | 
|  | 232 | 
|  | 233 	ch->fPortaUpSpeed = param; | 
|  | 234 | 
|  | 235 	ch->realPeriod -= param << 2; | 
|  | 236 	if ((int16_t)ch->realPeriod < 1) | 
|  | 237 		ch->realPeriod = 1; | 
|  | 238 | 
|  | 239 	ch->outPeriod = ch->realPeriod; | 
|  | 240 	ch->status |= IS_Period; | 
|  | 241 } | 
|  | 242 | 
|  | 243 static void finePortaDown(stmTyp *ch, uint8_t param) | 
|  | 244 { | 
|  | 245 	if (param == 0) | 
|  | 246 		param = ch->fPortaDownSpeed; | 
|  | 247 | 
|  | 248 	ch->fPortaDownSpeed = param; | 
|  | 249 | 
|  | 250 	ch->realPeriod += param << 2; | 
|  | 251 	if ((int16_t)ch->realPeriod > MAX_FRQ-1) // 8bb: FT2 bug, should've been unsigned comparison! | 
|  | 252 		ch->realPeriod = MAX_FRQ-1; | 
|  | 253 | 
|  | 254 	ch->outPeriod = ch->realPeriod; | 
|  | 255 	ch->status |= IS_Period; | 
|  | 256 } | 
|  | 257 | 
|  | 258 static void setGlissCtrl(stmTyp *ch, uint8_t param) | 
|  | 259 { | 
|  | 260 	ch->glissFunk = param; | 
|  | 261 } | 
|  | 262 | 
|  | 263 static void setVibratoCtrl(stmTyp *ch, uint8_t param) | 
|  | 264 { | 
|  | 265 	ch->waveCtrl = (ch->waveCtrl & 0xF0) | param; | 
|  | 266 } | 
|  | 267 | 
|  | 268 static void jumpLoop(stmTyp *ch, uint8_t param) | 
|  | 269 { | 
|  | 270 	if (param == 0) | 
|  | 271 	{ | 
|  | 272 		ch->pattPos = song.pattPos & 0xFF; | 
|  | 273 	} | 
|  | 274 	else if (ch->loopCnt == 0) | 
|  | 275 	{ | 
|  | 276 		ch->loopCnt = param; | 
|  | 277 | 
|  | 278 		song.pBreakPos = ch->pattPos; | 
|  | 279 		song.pBreakFlag = true; | 
|  | 280 	} | 
|  | 281 	else if (--ch->loopCnt > 0) | 
|  | 282 	{ | 
|  | 283 		song.pBreakPos = ch->pattPos; | 
|  | 284 		song.pBreakFlag = true; | 
|  | 285 	} | 
|  | 286 } | 
|  | 287 | 
|  | 288 static void setTremoloCtrl(stmTyp *ch, uint8_t param) | 
|  | 289 { | 
|  | 290 	ch->waveCtrl = (param << 4) | (ch->waveCtrl & 0x0F); | 
|  | 291 } | 
|  | 292 | 
|  | 293 static void volFineUp(stmTyp *ch, uint8_t param) | 
|  | 294 { | 
|  | 295 	if (param == 0) | 
|  | 296 		param = ch->fVolSlideUpSpeed; | 
|  | 297 | 
|  | 298 	ch->fVolSlideUpSpeed = param; | 
|  | 299 | 
|  | 300 	ch->realVol += param; | 
|  | 301 	if (ch->realVol > 64) | 
|  | 302 		ch->realVol = 64; | 
|  | 303 | 
|  | 304 	ch->outVol = ch->realVol; | 
|  | 305 	ch->status |= IS_Vol; | 
|  | 306 } | 
|  | 307 | 
|  | 308 static void volFineDown(stmTyp *ch, uint8_t param) | 
|  | 309 { | 
|  | 310 	if (param == 0) | 
|  | 311 		param = ch->fVolSlideDownSpeed; | 
|  | 312 | 
|  | 313 	ch->fVolSlideDownSpeed = param; | 
|  | 314 | 
|  | 315 	ch->realVol -= param; | 
|  | 316 	if ((int8_t)ch->realVol < 0) | 
|  | 317 		ch->realVol = 0; | 
|  | 318 | 
|  | 319 	ch->outVol = ch->realVol; | 
|  | 320 	ch->status |= IS_Vol; | 
|  | 321 } | 
|  | 322 | 
|  | 323 static void noteCut0(stmTyp *ch, uint8_t param) | 
|  | 324 { | 
|  | 325 	if (param == 0) // 8bb: only a parameter of zero is handled here | 
|  | 326 	{ | 
|  | 327 		ch->realVol = 0; | 
|  | 328 		ch->outVol = 0; | 
|  | 329 		ch->status |= IS_Vol + IS_QuickVol; | 
|  | 330 	} | 
|  | 331 } | 
|  | 332 | 
|  | 333 static void pattDelay(stmTyp *ch, uint8_t param) | 
|  | 334 { | 
|  | 335 	if (song.pattDelTime2 == 0) | 
|  | 336 		song.pattDelTime = param + 1; | 
|  | 337 | 
|  | 338 	(void)ch; | 
|  | 339 } | 
|  | 340 | 
|  | 341 static const efxRoutine EJumpTab_TickZero[16] = | 
|  | 342 { | 
|  | 343 	dummy, // 0 | 
|  | 344 	finePortaUp, // 1 | 
|  | 345 	finePortaDown, // 2 | 
|  | 346 	setGlissCtrl, // 3 | 
|  | 347 	setVibratoCtrl, // 4 | 
|  | 348 	dummy, // 5 | 
|  | 349 	jumpLoop, // 6 | 
|  | 350 	setTremoloCtrl, // 7 | 
|  | 351 	dummy, // 8 | 
|  | 352 	dummy, // 9 | 
|  | 353 	volFineUp, // A | 
|  | 354 	volFineDown, // B | 
|  | 355 	noteCut0, // C | 
|  | 356 	dummy, // D | 
|  | 357 	pattDelay, // E | 
|  | 358 	dummy // F | 
|  | 359 }; | 
|  | 360 | 
|  | 361 static void E_Effects_TickZero(stmTyp *ch, uint8_t param) | 
|  | 362 { | 
|  | 363 	EJumpTab_TickZero[param >> 4](ch, param & 0x0F); | 
|  | 364 } | 
|  | 365 | 
|  | 366 static void posJump(stmTyp *ch, uint8_t param) | 
|  | 367 { | 
|  | 368 	song.songPos = (int16_t)param - 1; | 
|  | 369 	song.pBreakPos = 0; | 
|  | 370 	song.posJumpFlag = true; | 
|  | 371 | 
|  | 372 	(void)ch; | 
|  | 373 } | 
|  | 374 | 
|  | 375 static void pattBreak(stmTyp *ch, uint8_t param) | 
|  | 376 { | 
|  | 377 	song.posJumpFlag = true; | 
|  | 378 | 
|  | 379 	param = ((param >> 4) * 10) + (param & 0x0F); | 
|  | 380 	if (param <= 63) | 
|  | 381 		song.pBreakPos = param; | 
|  | 382 	else | 
|  | 383 		song.pBreakPos = 0; | 
|  | 384 | 
|  | 385 	(void)ch; | 
|  | 386 } | 
|  | 387 | 
|  | 388 static void setSpeed(stmTyp *ch, uint8_t param) | 
|  | 389 { | 
|  | 390 	if (param >= 32) | 
|  | 391 	{ | 
|  | 392 		song.speed = param; | 
|  | 393 		P_SetSpeed(song.speed); | 
|  | 394 	} | 
|  | 395 	else | 
|  | 396 	{ | 
|  | 397 		song.timer = song.tempo = param; | 
|  | 398 	} | 
|  | 399 | 
|  | 400 	(void)ch; | 
|  | 401 } | 
|  | 402 | 
|  | 403 static void setGlobaVol(stmTyp *ch, uint8_t param) | 
|  | 404 { | 
|  | 405 	if (param > 64) | 
|  | 406 		param = 64; | 
|  | 407 | 
|  | 408 	song.globVol = param; | 
|  | 409 | 
|  | 410 	stmTyp *c = stm; | 
|  | 411 	for (int32_t i = 0; i < song.antChn; i++, c++) // 8bb: this updates the volume for all voices | 
|  | 412 		c->status |= IS_Vol; | 
|  | 413 | 
|  | 414 	(void)ch; | 
|  | 415 } | 
|  | 416 | 
|  | 417 static void setEnvelopePos(stmTyp *ch, uint8_t param) | 
|  | 418 { | 
|  | 419 	int8_t envPos; | 
|  | 420 	bool envUpdate; | 
|  | 421 	int16_t newEnvPos; | 
|  | 422 | 
|  | 423 	instrTyp *ins = ch->instrSeg; | 
|  | 424 | 
|  | 425 	// *** VOLUME ENVELOPE *** | 
|  | 426 	if (ins->envVTyp & ENV_ENABLED) | 
|  | 427 	{ | 
|  | 428 		ch->envVCnt = param - 1; | 
|  | 429 | 
|  | 430 		envPos = 0; | 
|  | 431 		envUpdate = true; | 
|  | 432 		newEnvPos = param; | 
|  | 433 | 
|  | 434 		if (ins->envVPAnt > 1) | 
|  | 435 		{ | 
|  | 436 			envPos++; | 
|  | 437 			for (int32_t i = 0; i < ins->envVPAnt-1; i++) | 
|  | 438 			{ | 
|  | 439 				if (newEnvPos < ins->envVP[envPos][0]) | 
|  | 440 				{ | 
|  | 441 					envPos--; | 
|  | 442 | 
|  | 443 					newEnvPos -= ins->envVP[envPos][0]; | 
|  | 444 					if (newEnvPos == 0) | 
|  | 445 					{ | 
|  | 446 						envUpdate = false; | 
|  | 447 						break; | 
|  | 448 					} | 
|  | 449 | 
|  | 450 					if (ins->envVP[envPos+1][0] <= ins->envVP[envPos][0]) | 
|  | 451 					{ | 
|  | 452 						envUpdate = true; | 
|  | 453 						break; | 
|  | 454 					} | 
|  | 455 | 
|  | 456 					ch->envVIPValue = ((ins->envVP[envPos+1][1] - ins->envVP[envPos][1]) & 0xFF) << 8; | 
|  | 457 					ch->envVIPValue /= (ins->envVP[envPos+1][0] - ins->envVP[envPos][0]); | 
|  | 458 | 
|  | 459 					ch->envVAmp = (ch->envVIPValue * (newEnvPos - 1)) + ((ins->envVP[envPos][1] & 0xFF) << 8); | 
|  | 460 | 
|  | 461 					envPos++; | 
|  | 462 | 
|  | 463 					envUpdate = false; | 
|  | 464 					break; | 
|  | 465 				} | 
|  | 466 | 
|  | 467 				envPos++; | 
|  | 468 			} | 
|  | 469 | 
|  | 470 			if (envUpdate) | 
|  | 471 				envPos--; | 
|  | 472 		} | 
|  | 473 | 
|  | 474 		if (envUpdate) | 
|  | 475 		{ | 
|  | 476 			ch->envVIPValue = 0; | 
|  | 477 			ch->envVAmp = (ins->envVP[envPos][1] & 0xFF) << 8; | 
|  | 478 		} | 
|  | 479 | 
|  | 480 		if (envPos >= ins->envVPAnt) | 
|  | 481 		{ | 
|  | 482 			envPos = ins->envVPAnt - 1; | 
|  | 483 			if (envPos < 0) | 
|  | 484 				envPos = 0; | 
|  | 485 		} | 
|  | 486 | 
|  | 487 		ch->envVPos = envPos; | 
|  | 488 	} | 
|  | 489 | 
|  | 490 	// *** PANNING ENVELOPE *** | 
|  | 491 	if (ins->envVTyp & ENV_SUSTAIN) // 8bb: FT2 bug? (should probably have been "ins->envPTyp & ENV_ENABLED") | 
|  | 492 	{ | 
|  | 493 		ch->envPCnt = param - 1; | 
|  | 494 | 
|  | 495 		envPos = 0; | 
|  | 496 		envUpdate = true; | 
|  | 497 		newEnvPos = param; | 
|  | 498 | 
|  | 499 		if (ins->envPPAnt > 1) | 
|  | 500 		{ | 
|  | 501 			envPos++; | 
|  | 502 			for (int32_t i = 0; i < ins->envPPAnt-1; i++) | 
|  | 503 			{ | 
|  | 504 				if (newEnvPos < ins->envPP[envPos][0]) | 
|  | 505 				{ | 
|  | 506 					envPos--; | 
|  | 507 | 
|  | 508 					newEnvPos -= ins->envPP[envPos][0]; | 
|  | 509 					if (newEnvPos == 0) | 
|  | 510 					{ | 
|  | 511 						envUpdate = false; | 
|  | 512 						break; | 
|  | 513 					} | 
|  | 514 | 
|  | 515 					if (ins->envPP[envPos + 1][0] <= ins->envPP[envPos][0]) | 
|  | 516 					{ | 
|  | 517 						envUpdate = true; | 
|  | 518 						break; | 
|  | 519 					} | 
|  | 520 | 
|  | 521 					ch->envPIPValue = ((ins->envPP[envPos+1][1] - ins->envPP[envPos][1]) & 0xFF) << 8; | 
|  | 522 					ch->envPIPValue /= (ins->envPP[envPos+1][0] - ins->envPP[envPos][0]); | 
|  | 523 | 
|  | 524 					ch->envPAmp = (ch->envPIPValue * (newEnvPos - 1)) + ((ins->envPP[envPos][1] & 0xFF) << 8); | 
|  | 525 | 
|  | 526 					envPos++; | 
|  | 527 | 
|  | 528 					envUpdate = false; | 
|  | 529 					break; | 
|  | 530 				} | 
|  | 531 | 
|  | 532 				envPos++; | 
|  | 533 			} | 
|  | 534 | 
|  | 535 			if (envUpdate) | 
|  | 536 				envPos--; | 
|  | 537 		} | 
|  | 538 | 
|  | 539 		if (envUpdate) | 
|  | 540 		{ | 
|  | 541 			ch->envPIPValue = 0; | 
|  | 542 			ch->envPAmp = (ins->envPP[envPos][1] & 0xFF) << 8; | 
|  | 543 		} | 
|  | 544 | 
|  | 545 		if (envPos >= ins->envPPAnt) | 
|  | 546 		{ | 
|  | 547 			envPos = ins->envPPAnt - 1; | 
|  | 548 			if (envPos < 0) | 
|  | 549 				envPos = 0; | 
|  | 550 		} | 
|  | 551 | 
|  | 552 		ch->envPPos = envPos; | 
|  | 553 	} | 
|  | 554 } | 
|  | 555 | 
|  | 556 /* -- tick-zero volume column effects -- | 
|  | 557 ** 2nd parameter is used for a volume column quirk with the Rxy command (multiretrig) | 
|  | 558 */ | 
|  | 559 | 
|  | 560 static void v_SetVibSpeed(stmTyp *ch, uint8_t *volKol) | 
|  | 561 { | 
|  | 562 	*volKol = (ch->volKolVol & 0x0F) << 2; | 
|  | 563 	if (*volKol != 0) | 
|  | 564 		ch->vibSpeed = *volKol; | 
|  | 565 } | 
|  | 566 | 
|  | 567 static void v_Volume(stmTyp *ch, uint8_t *volKol) | 
|  | 568 { | 
|  | 569 	*volKol -= 16; | 
|  | 570 	if (*volKol > 64) // 8bb: no idea why FT2 has this check, this can't happen... | 
|  | 571 		*volKol = 64; | 
|  | 572 | 
|  | 573 	ch->outVol = ch->realVol = *volKol; | 
|  | 574 	ch->status |= IS_Vol + IS_QuickVol; | 
|  | 575 } | 
|  | 576 | 
|  | 577 static void v_FineSlideDown(stmTyp *ch, uint8_t *volKol) | 
|  | 578 { | 
|  | 579 	*volKol = (uint8_t)(0 - (ch->volKolVol & 0x0F)) + ch->realVol; | 
|  | 580 	if ((int8_t)*volKol < 0) | 
|  | 581 		*volKol = 0; | 
|  | 582 | 
|  | 583 	ch->outVol = ch->realVol = *volKol; | 
|  | 584 	ch->status |= IS_Vol; | 
|  | 585 } | 
|  | 586 | 
|  | 587 static void v_FineSlideUp(stmTyp *ch, uint8_t *volKol) | 
|  | 588 { | 
|  | 589 	*volKol = (ch->volKolVol & 0x0F) + ch->realVol; | 
|  | 590 	if (*volKol > 64) | 
|  | 591 		*volKol = 64; | 
|  | 592 | 
|  | 593 	ch->outVol = ch->realVol = *volKol; | 
|  | 594 	ch->status |= IS_Vol; | 
|  | 595 } | 
|  | 596 | 
|  | 597 static void v_SetPan(stmTyp *ch, uint8_t *volKol) | 
|  | 598 { | 
|  | 599 	*volKol <<= 4; | 
|  | 600 | 
|  | 601 	ch->outPan = *volKol; | 
|  | 602 	ch->status |= IS_Pan; | 
|  | 603 } | 
|  | 604 | 
|  | 605 // -- non-tick-zero volume column effects -- | 
|  | 606 | 
|  | 607 static void v_SlideDown(stmTyp *ch) | 
|  | 608 { | 
|  | 609 	uint8_t newVol = (uint8_t)(0 - (ch->volKolVol & 0x0F)) + ch->realVol; | 
|  | 610 	if ((int8_t)newVol < 0) | 
|  | 611 		newVol = 0; | 
|  | 612 | 
|  | 613 	ch->outVol = ch->realVol = newVol; | 
|  | 614 	ch->status |= IS_Vol; | 
|  | 615 } | 
|  | 616 | 
|  | 617 static void v_SlideUp(stmTyp *ch) | 
|  | 618 { | 
|  | 619 	uint8_t newVol = (ch->volKolVol & 0x0F) + ch->realVol; | 
|  | 620 	if (newVol > 64) | 
|  | 621 		newVol = 64; | 
|  | 622 | 
|  | 623 	ch->outVol = ch->realVol = newVol; | 
|  | 624 	ch->status |= IS_Vol; | 
|  | 625 } | 
|  | 626 | 
|  | 627 static void v_Vibrato(stmTyp *ch) | 
|  | 628 { | 
|  | 629 	const uint8_t param = ch->volKolVol & 0xF; | 
|  | 630 	if (param > 0) | 
|  | 631 		ch->vibDepth = param; | 
|  | 632 | 
|  | 633 	vibrato2(ch); | 
|  | 634 } | 
|  | 635 | 
|  | 636 static void v_PanSlideLeft(stmTyp *ch) | 
|  | 637 { | 
|  | 638 	uint16_t tmp16 = (uint8_t)(0 - (ch->volKolVol & 0x0F)) + ch->outPan; | 
|  | 639 	if (tmp16 < 256) // 8bb: includes an FT2 bug: pan-slide-left of 0 = set pan to 0 | 
|  | 640 		tmp16 = 0; | 
|  | 641 | 
|  | 642 	ch->outPan = (uint8_t)tmp16; | 
|  | 643 	ch->status |= IS_Pan; | 
|  | 644 } | 
|  | 645 | 
|  | 646 static void v_PanSlideRight(stmTyp *ch) | 
|  | 647 { | 
|  | 648 	uint16_t tmp16 = (ch->volKolVol & 0x0F) + ch->outPan; | 
|  | 649 	if (tmp16 > 255) | 
|  | 650 		tmp16 = 255; | 
|  | 651 | 
|  | 652 	ch->outPan = (uint8_t)tmp16; | 
|  | 653 	ch->status |= IS_Pan; | 
|  | 654 } | 
|  | 655 | 
|  | 656 static void v_TonePorta(stmTyp *ch) | 
|  | 657 { | 
|  | 658 	tonePorta(ch, 0); // 8bb: the last parameter is actually not used in tonePorta() | 
|  | 659 } | 
|  | 660 | 
|  | 661 static void v_dummy(stmTyp *ch) | 
|  | 662 { | 
|  | 663 	(void)ch; | 
|  | 664 	return; | 
|  | 665 } | 
|  | 666 | 
|  | 667 static void v_dummy2(stmTyp *ch, uint8_t *volKol) | 
|  | 668 { | 
|  | 669 	(void)ch; | 
|  | 670 	(void)volKol; | 
|  | 671 	return; | 
|  | 672 } | 
|  | 673 | 
|  | 674 static const volKolEfxRoutine VJumpTab_TickNonZero[16] = | 
|  | 675 { | 
|  | 676 	v_dummy,        v_dummy,         v_dummy,  v_dummy, | 
|  | 677 	v_dummy,        v_dummy,     v_SlideDown, v_SlideUp, | 
|  | 678 	v_dummy,        v_dummy,         v_dummy, v_Vibrato, | 
|  | 679 	v_dummy, v_PanSlideLeft, v_PanSlideRight, v_TonePorta | 
|  | 680 }; | 
|  | 681 | 
|  | 682 static const volKolEfxRoutine2 VJumpTab_TickZero[16] = | 
|  | 683 { | 
|  | 684 	       v_dummy2,      v_Volume,      v_Volume, v_Volume, | 
|  | 685 	       v_Volume,      v_Volume,      v_dummy2, v_dummy2, | 
|  | 686 	v_FineSlideDown, v_FineSlideUp, v_SetVibSpeed, v_dummy2, | 
|  | 687 	       v_SetPan,      v_dummy2,      v_dummy2, v_dummy2 | 
|  | 688 }; | 
|  | 689 | 
|  | 690 static void setPan(stmTyp *ch, uint8_t param) | 
|  | 691 { | 
|  | 692 	ch->outPan = param; | 
|  | 693 	ch->status |= IS_Pan; | 
|  | 694 } | 
|  | 695 | 
|  | 696 static void setVol(stmTyp *ch, uint8_t param) | 
|  | 697 { | 
|  | 698 	if (param > 64) | 
|  | 699 		param = 64; | 
|  | 700 | 
|  | 701 	ch->outVol = ch->realVol = param; | 
|  | 702 	ch->status |= IS_Vol + IS_QuickVol; | 
|  | 703 } | 
|  | 704 | 
|  | 705 static void xFinePorta(stmTyp *ch, uint8_t param) | 
|  | 706 { | 
|  | 707 	const uint8_t type = param >> 4; | 
|  | 708 	param &= 0x0F; | 
|  | 709 | 
|  | 710 	if (type == 0x1) // extra fine porta up | 
|  | 711 	{ | 
|  | 712 		if (param == 0) | 
|  | 713 			param = ch->ePortaUpSpeed; | 
|  | 714 | 
|  | 715 		ch->ePortaUpSpeed = param; | 
|  | 716 | 
|  | 717 		uint16_t newPeriod = ch->realPeriod; | 
|  | 718 | 
|  | 719 		newPeriod -= param; | 
|  | 720 		if ((int16_t)newPeriod < 1) | 
|  | 721 			newPeriod = 1; | 
|  | 722 | 
|  | 723 		ch->outPeriod = ch->realPeriod = newPeriod; | 
|  | 724 		ch->status |= IS_Period; | 
|  | 725 	} | 
|  | 726 	else if (type == 0x2) // extra fine porta down | 
|  | 727 	{ | 
|  | 728 		if (param == 0) | 
|  | 729 			param = ch->ePortaDownSpeed; | 
|  | 730 | 
|  | 731 		ch->ePortaDownSpeed = param; | 
|  | 732 | 
|  | 733 		uint16_t newPeriod = ch->realPeriod; | 
|  | 734 | 
|  | 735 		newPeriod += param; | 
|  | 736 		if ((int16_t)newPeriod > MAX_FRQ-1) // 8bb: FT2 bug, should've been unsigned comparison! | 
|  | 737 			newPeriod = MAX_FRQ-1; | 
|  | 738 | 
|  | 739 		ch->outPeriod = ch->realPeriod = newPeriod; | 
|  | 740 		ch->status |= IS_Period; | 
|  | 741 	} | 
|  | 742 } | 
|  | 743 | 
|  | 744 static void doMultiRetrig(stmTyp *ch, uint8_t param) // 8bb: "param" is never used (needed for efx jumptable structure) | 
|  | 745 { | 
|  | 746 	uint8_t cnt = ch->retrigCnt + 1; | 
|  | 747 	if (cnt < ch->retrigSpeed) | 
|  | 748 	{ | 
|  | 749 		ch->retrigCnt = cnt; | 
|  | 750 		return; | 
|  | 751 	} | 
|  | 752 | 
|  | 753 	ch->retrigCnt = 0; | 
|  | 754 | 
|  | 755 	int16_t vol = ch->realVol; | 
|  | 756 	switch (ch->retrigVol) | 
|  | 757 	{ | 
|  | 758 		case 0x1: vol -= 1; break; | 
|  | 759 		case 0x2: vol -= 2; break; | 
|  | 760 		case 0x3: vol -= 4; break; | 
|  | 761 		case 0x4: vol -= 8; break; | 
|  | 762 		case 0x5: vol -= 16; break; | 
|  | 763 		case 0x6: vol = (vol >> 1) + (vol >> 3) + (vol >> 4); break; | 
|  | 764 		case 0x7: vol >>= 1; break; | 
|  | 765 		case 0x8: break; // 8bb: does not change the volume | 
|  | 766 		case 0x9: vol += 1; break; | 
|  | 767 		case 0xA: vol += 2; break; | 
|  | 768 		case 0xB: vol += 4; break; | 
|  | 769 		case 0xC: vol += 8; break; | 
|  | 770 		case 0xD: vol += 16; break; | 
|  | 771 		case 0xE: vol = (vol >> 1) + vol; break; | 
|  | 772 		case 0xF: vol += vol; break; | 
|  | 773 		default: break; | 
|  | 774 	} | 
|  | 775 	vol = CLAMP(vol, 0, 64); | 
|  | 776 | 
|  | 777 	ch->realVol = (uint8_t)vol; | 
|  | 778 	ch->outVol = ch->realVol; | 
|  | 779 | 
|  | 780 	if (ch->volKolVol >= 0x10 && ch->volKolVol <= 0x50) // 8bb: Set Volume (volume column) | 
|  | 781 	{ | 
|  | 782 		ch->outVol = ch->volKolVol - 0x10; | 
|  | 783 		ch->realVol = ch->outVol; | 
|  | 784 	} | 
|  | 785 	else if (ch->volKolVol >= 0xC0 && ch->volKolVol <= 0xCF) // 8bb: Set Panning (volume column) | 
|  | 786 	{ | 
|  | 787 		ch->outPan = (ch->volKolVol & 0x0F) << 4; | 
|  | 788 	} | 
|  | 789 | 
|  | 790 	startTone(0, 0, 0, ch); | 
|  | 791 | 
|  | 792 	(void)param; | 
|  | 793 } | 
|  | 794 | 
|  | 795 static void multiRetrig(stmTyp *ch, uint8_t param, uint8_t volumeColumnData) | 
|  | 796 { | 
|  | 797 	uint8_t tmpParam; | 
|  | 798 | 
|  | 799 	tmpParam = param & 0x0F; | 
|  | 800 	if (tmpParam == 0) | 
|  | 801 		tmpParam = ch->retrigSpeed; | 
|  | 802 | 
|  | 803 	ch->retrigSpeed = tmpParam; | 
|  | 804 | 
|  | 805 	tmpParam = param >> 4; | 
|  | 806 	if (tmpParam == 0) | 
|  | 807 		tmpParam = ch->retrigVol; | 
|  | 808 | 
|  | 809 	ch->retrigVol = tmpParam; | 
|  | 810 | 
|  | 811 	if (volumeColumnData == 0) | 
|  | 812 		doMultiRetrig(ch, 0); // 8bb: the second parameter is never used (needed for efx jumptable structure) | 
|  | 813 } | 
|  | 814 | 
|  | 815 static const efxRoutine JumpTab_TickZero[36] = | 
|  | 816 { | 
|  | 817 	dummy, // 0 | 
|  | 818 	dummy, // 1 | 
|  | 819 	dummy, // 2 | 
|  | 820 	dummy, // 3 | 
|  | 821 	dummy, // 4 | 
|  | 822 	dummy, // 5 | 
|  | 823 	dummy, // 6 | 
|  | 824 	dummy, // 7 | 
|  | 825 	setPan, // 8 | 
|  | 826 	dummy, // 9 | 
|  | 827 	dummy, // A | 
|  | 828 	posJump, // B | 
|  | 829 	setVol, // C | 
|  | 830 	pattBreak, // D | 
|  | 831 	E_Effects_TickZero, // E | 
|  | 832 	setSpeed, // F | 
|  | 833 	setGlobaVol, // G | 
|  | 834 	dummy, // H | 
|  | 835 	dummy, // I | 
|  | 836 	dummy, // J | 
|  | 837 	dummy, // K | 
|  | 838 	setEnvelopePos, // L | 
|  | 839 	dummy, // M | 
|  | 840 	dummy, // N | 
|  | 841 	dummy, // O | 
|  | 842 	dummy, // P | 
|  | 843 	dummy, // Q | 
|  | 844 	dummy, // R | 
|  | 845 	dummy, // S | 
|  | 846 	dummy, // T | 
|  | 847 	dummy, // U | 
|  | 848 	dummy, // V | 
|  | 849 	dummy, // W | 
|  | 850 	xFinePorta, // X | 
|  | 851 	dummy, // Y | 
|  | 852 	dummy  // Z | 
|  | 853 }; | 
|  | 854 | 
|  | 855 static void checkEffects(stmTyp *ch) // tick0 effect handling | 
|  | 856 { | 
|  | 857 	// volume column effects | 
|  | 858 	uint8_t newVolKol = ch->volKolVol; // 8bb: manipulated by vol. column effects, then used for multiretrig check (FT2 quirk) | 
|  | 859 	VJumpTab_TickZero[ch->volKolVol >> 4](ch, &newVolKol); | 
|  | 860 | 
|  | 861 	// normal effects | 
|  | 862 	const uint8_t param = ch->eff; | 
|  | 863 | 
|  | 864 	if ((ch->effTyp == 0 && param == 0) || ch->effTyp > 35) | 
|  | 865 		return; | 
|  | 866 | 
|  | 867 	// 8bb: this one has to be done here instead of in the jumptable, as it needs the "newVolKol" parameter (FT2 quirk) | 
|  | 868 	if (ch->effTyp == 27) // 8bb: Rxy - Multi Retrig | 
|  | 869 	{ | 
|  | 870 		multiRetrig(ch, param, newVolKol); | 
|  | 871 		return; | 
|  | 872 	} | 
|  | 873 | 
|  | 874 	JumpTab_TickZero[ch->effTyp](ch, ch->eff); | 
|  | 875 } | 
|  | 876 | 
|  | 877 static void fixTonePorta(stmTyp *ch, const tonTyp *p, uint8_t inst) | 
|  | 878 { | 
|  | 879 	if (p->ton > 0) | 
|  | 880 	{ | 
|  | 881 		if (p->ton == NOTE_KEYOFF) | 
|  | 882 		{ | 
|  | 883 			keyOff(ch); | 
|  | 884 		} | 
|  | 885 		else | 
|  | 886 		{ | 
|  | 887 			const uint16_t portaTmp = (((p->ton-1) + ch->relTonNr) << 4) + (((int8_t)ch->fineTune >> 3) + 16); | 
|  | 888 			if (portaTmp < MAX_NOTES) | 
|  | 889 			{ | 
|  | 890 				ch->wantPeriod = note2Period[portaTmp]; | 
|  | 891 | 
|  | 892 				if (ch->wantPeriod == ch->realPeriod) | 
|  | 893 					ch->portaDir = 0; | 
|  | 894 				else if (ch->wantPeriod > ch->realPeriod) | 
|  | 895 					ch->portaDir = 1; | 
|  | 896 				else | 
|  | 897 					ch->portaDir = 2; | 
|  | 898 			} | 
|  | 899 		} | 
|  | 900 	} | 
|  | 901 | 
|  | 902 	if (inst > 0) | 
|  | 903 	{ | 
|  | 904 		retrigVolume(ch); | 
|  | 905 | 
|  | 906 		if (p->ton != NOTE_KEYOFF) | 
|  | 907 			retrigEnvelopeVibrato(ch); | 
|  | 908 	} | 
|  | 909 } | 
|  | 910 | 
|  | 911 static void getNewNote(stmTyp *ch, const tonTyp *p) | 
|  | 912 { | 
|  | 913 	ch->volKolVol = p->vol; | 
|  | 914 | 
|  | 915 	if (ch->effTyp == 0) | 
|  | 916 	{ | 
|  | 917 		if (ch->eff != 0) // 8bb: we have an arpeggio (0xy) running, set period back | 
|  | 918 		{ | 
|  | 919 			ch->outPeriod = ch->realPeriod; | 
|  | 920 			ch->status |= IS_Period; | 
|  | 921 		} | 
|  | 922 	} | 
|  | 923 	else | 
|  | 924 	{ | 
|  | 925 		// 8bb: if we have a vibrato (4xy/6xy) on previous row (ch) that ends at current row (p), set period back | 
|  | 926 		if ((ch->effTyp == 4 || ch->effTyp == 6) && (p->effTyp != 4 && p->effTyp != 6)) | 
|  | 927 		{ | 
|  | 928 			ch->outPeriod = ch->realPeriod; | 
|  | 929 			ch->status |= IS_Period; | 
|  | 930 		} | 
|  | 931 	} | 
|  | 932 | 
|  | 933 	ch->effTyp = p->effTyp; | 
|  | 934 	ch->eff = p->eff; | 
|  | 935 	ch->tonTyp = (p->instr << 8) | p->ton; | 
|  | 936 | 
|  | 937 	// 8bb: 'inst' var is used for later if-checks | 
|  | 938 	uint8_t inst = p->instr; | 
|  | 939 	if (inst > 0) | 
|  | 940 	{ | 
|  | 941 		if (inst <= 128) | 
|  | 942 			ch->instrNr = inst; | 
|  | 943 		else | 
|  | 944 			inst = 0; | 
|  | 945 	} | 
|  | 946 | 
|  | 947 	bool checkEfx = true; | 
|  | 948 	if (p->effTyp == 0x0E) // 8bb: check for EDx (Note Delay) and E90 (Retrigger Note) | 
|  | 949 	{ | 
|  | 950 		if (p->eff >= 0xD1 && p->eff <= 0xDF) // 8bb: ED1..EDF (Note Delay) | 
|  | 951 			return; | 
|  | 952 		else if (p->eff == 0x90) // 8bb: E90 (Retrigger Note) | 
|  | 953 			checkEfx = false; | 
|  | 954 	} | 
|  | 955 | 
|  | 956 	if (checkEfx) | 
|  | 957 	{ | 
|  | 958 		if ((ch->volKolVol & 0xF0) == 0xF0) // 8bb: Portamento (volume column) | 
|  | 959 		{ | 
|  | 960 			const uint8_t volKolParam = ch->volKolVol & 0x0F; | 
|  | 961 			if (volKolParam > 0) | 
|  | 962 				ch->portaSpeed = volKolParam << 6; | 
|  | 963 | 
|  | 964 			fixTonePorta(ch, p, inst); | 
|  | 965 			checkEffects(ch); | 
|  | 966 			return; | 
|  | 967 		} | 
|  | 968 | 
|  | 969 		if (p->effTyp == 3 || p->effTyp == 5) // 8bb: Portamento (3xx/5xx) | 
|  | 970 		{ | 
|  | 971 			if (p->effTyp != 5 && p->eff != 0) | 
|  | 972 				ch->portaSpeed = p->eff << 2; | 
|  | 973 | 
|  | 974 			fixTonePorta(ch, p, inst); | 
|  | 975 			checkEffects(ch); | 
|  | 976 			return; | 
|  | 977 		} | 
|  | 978 | 
|  | 979 		if (p->effTyp == 0x14 && p->eff == 0) // 8bb: K00 (Key Off - only handle tick 0 here) | 
|  | 980 		{ | 
|  | 981 			keyOff(ch); | 
|  | 982 | 
|  | 983 			if (inst) | 
|  | 984 				retrigVolume(ch); | 
|  | 985 | 
|  | 986 			checkEffects(ch); | 
|  | 987 			return; | 
|  | 988 		} | 
|  | 989 | 
|  | 990 		if (p->ton == 0) | 
|  | 991 		{ | 
|  | 992 			if (inst > 0) | 
|  | 993 			{ | 
|  | 994 				retrigVolume(ch); | 
|  | 995 				retrigEnvelopeVibrato(ch); | 
|  | 996 			} | 
|  | 997 | 
|  | 998 			checkEffects(ch); | 
|  | 999 			return; | 
|  | 1000 		} | 
|  | 1001 	} | 
|  | 1002 | 
|  | 1003 	if (p->ton == NOTE_KEYOFF) | 
|  | 1004 		keyOff(ch); | 
|  | 1005 	else | 
|  | 1006 		startTone(p->ton, p->effTyp, p->eff, ch); | 
|  | 1007 | 
|  | 1008 	if (inst > 0) | 
|  | 1009 	{ | 
|  | 1010 		retrigVolume(ch); | 
|  | 1011 		if (p->ton != NOTE_KEYOFF) | 
|  | 1012 			retrigEnvelopeVibrato(ch); | 
|  | 1013 	} | 
|  | 1014 | 
|  | 1015 	checkEffects(ch); | 
|  | 1016 } | 
|  | 1017 | 
|  | 1018 static void fixaEnvelopeVibrato(stmTyp *ch) | 
|  | 1019 { | 
|  | 1020 	bool envInterpolateFlag, envDidInterpolate; | 
|  | 1021 	uint8_t envPos; | 
|  | 1022 	int16_t autoVibVal; | 
|  | 1023 	uint16_t autoVibAmp, envVal; | 
|  | 1024 	uint32_t vol; | 
|  | 1025 | 
|  | 1026 	instrTyp *ins = ch->instrSeg; | 
|  | 1027 | 
|  | 1028 	// *** FADEOUT *** | 
|  | 1029 	if (!ch->envSustainActive) | 
|  | 1030 	{ | 
|  | 1031 		ch->status |= IS_Vol; | 
|  | 1032 | 
|  | 1033 		if (ch->fadeOutAmp >= ch->fadeOutSpeed) | 
|  | 1034 		{ | 
|  | 1035 			ch->fadeOutAmp -= ch->fadeOutSpeed; | 
|  | 1036 		} | 
|  | 1037 		else | 
|  | 1038 		{ | 
|  | 1039 			ch->fadeOutAmp = 0; | 
|  | 1040 			ch->fadeOutSpeed = 0; | 
|  | 1041 		} | 
|  | 1042 	} | 
|  | 1043 | 
|  | 1044 	if (!ch->mute) | 
|  | 1045 	{ | 
|  | 1046 		// *** VOLUME ENVELOPE *** | 
|  | 1047 		envVal = 0; | 
|  | 1048 		if (ins->envVTyp & ENV_ENABLED) | 
|  | 1049 		{ | 
|  | 1050 			envDidInterpolate = false; | 
|  | 1051 			envPos = ch->envVPos; | 
|  | 1052 | 
|  | 1053 			if (++ch->envVCnt == ins->envVP[envPos][0]) | 
|  | 1054 			{ | 
|  | 1055 				ch->envVAmp = ins->envVP[envPos][1] << 8; | 
|  | 1056 | 
|  | 1057 				envPos++; | 
|  | 1058 				if (ins->envVTyp & ENV_LOOP) | 
|  | 1059 				{ | 
|  | 1060 					envPos--; | 
|  | 1061 | 
|  | 1062 					if (envPos == ins->envVRepE) | 
|  | 1063 					{ | 
|  | 1064 						if (!(ins->envVTyp & ENV_SUSTAIN) || envPos != ins->envVSust || ch->envSustainActive) | 
|  | 1065 						{ | 
|  | 1066 							envPos = ins->envVRepS; | 
|  | 1067 | 
|  | 1068 							ch->envVCnt = ins->envVP[envPos][0]; | 
|  | 1069 							ch->envVAmp = ins->envVP[envPos][1] << 8; | 
|  | 1070 						} | 
|  | 1071 					} | 
|  | 1072 | 
|  | 1073 					envPos++; | 
|  | 1074 				} | 
|  | 1075 | 
|  | 1076 				if (envPos < ins->envVPAnt) | 
|  | 1077 				{ | 
|  | 1078 					envInterpolateFlag = true; | 
|  | 1079 					if ((ins->envVTyp & ENV_SUSTAIN) && ch->envSustainActive) | 
|  | 1080 					{ | 
|  | 1081 						if (envPos-1 == ins->envVSust) | 
|  | 1082 						{ | 
|  | 1083 							envPos--; | 
|  | 1084 							ch->envVIPValue = 0; | 
|  | 1085 							envInterpolateFlag = false; | 
|  | 1086 						} | 
|  | 1087 					} | 
|  | 1088 | 
|  | 1089 					if (envInterpolateFlag) | 
|  | 1090 					{ | 
|  | 1091 						ch->envVPos = envPos; | 
|  | 1092 | 
|  | 1093 						ch->envVIPValue = 0; | 
|  | 1094 						if (ins->envVP[envPos][0] > ins->envVP[envPos-1][0]) | 
|  | 1095 						{ | 
|  | 1096 							ch->envVIPValue = (ins->envVP[envPos][1] - ins->envVP[envPos-1][1]) << 8; | 
|  | 1097 							ch->envVIPValue /= (ins->envVP[envPos][0] - ins->envVP[envPos-1][0]); | 
|  | 1098 | 
|  | 1099 							envVal = ch->envVAmp; | 
|  | 1100 							envDidInterpolate = true; | 
|  | 1101 						} | 
|  | 1102 					} | 
|  | 1103 				} | 
|  | 1104 				else | 
|  | 1105 				{ | 
|  | 1106 					ch->envVIPValue = 0; | 
|  | 1107 				} | 
|  | 1108 			} | 
|  | 1109 | 
|  | 1110 			if (!envDidInterpolate) | 
|  | 1111 			{ | 
|  | 1112 				ch->envVAmp += ch->envVIPValue; | 
|  | 1113 | 
|  | 1114 				envVal = ch->envVAmp; | 
|  | 1115 				if (envVal > 64*256) | 
|  | 1116 				{ | 
|  | 1117 					if (envVal > 128*256) | 
|  | 1118 						envVal = 64*256; | 
|  | 1119 					else | 
|  | 1120 						envVal = 0; | 
|  | 1121 | 
|  | 1122 					ch->envVIPValue = 0; | 
|  | 1123 				} | 
|  | 1124 			} | 
|  | 1125 | 
|  | 1126 			envVal >>= 8; | 
|  | 1127 | 
|  | 1128 			vol = (envVal * ch->outVol * ch->fadeOutAmp) >> (16+2); | 
|  | 1129 			vol = (vol * song.globVol) >> 7; | 
|  | 1130 | 
|  | 1131 			ch->status |= IS_Vol; // 8bb: this updates vol on every tick (because vol envelope is enabled) | 
|  | 1132 		} | 
|  | 1133 		else | 
|  | 1134 		{ | 
|  | 1135 			vol = ((ch->outVol << 4) * ch->fadeOutAmp) >> 16; | 
|  | 1136 			vol = (vol * song.globVol) >> 7; | 
|  | 1137 		} | 
|  | 1138 | 
|  | 1139 		ch->finalVol = (uint16_t)vol; // 0..256 | 
|  | 1140 	} | 
|  | 1141 	else | 
|  | 1142 	{ | 
|  | 1143 		ch->finalVol = 0; | 
|  | 1144 	} | 
|  | 1145 | 
|  | 1146 	// *** PANNING ENVELOPE *** | 
|  | 1147 | 
|  | 1148 	envVal = 0; | 
|  | 1149 	if (ins->envPTyp & ENV_ENABLED) | 
|  | 1150 	{ | 
|  | 1151 		envDidInterpolate = false; | 
|  | 1152 		envPos = ch->envPPos; | 
|  | 1153 | 
|  | 1154 		if (++ch->envPCnt == ins->envPP[envPos][0]) | 
|  | 1155 		{ | 
|  | 1156 			ch->envPAmp = ins->envPP[envPos][1] << 8; | 
|  | 1157 | 
|  | 1158 			envPos++; | 
|  | 1159 			if (ins->envPTyp & ENV_LOOP) | 
|  | 1160 			{ | 
|  | 1161 				envPos--; | 
|  | 1162 | 
|  | 1163 				if (envPos == ins->envPRepE) | 
|  | 1164 				{ | 
|  | 1165 					if (!(ins->envPTyp & ENV_SUSTAIN) || envPos != ins->envPSust || ch->envSustainActive) | 
|  | 1166 					{ | 
|  | 1167 						envPos = ins->envPRepS; | 
|  | 1168 | 
|  | 1169 						ch->envPCnt = ins->envPP[envPos][0]; | 
|  | 1170 						ch->envPAmp = ins->envPP[envPos][1] << 8; | 
|  | 1171 					} | 
|  | 1172 				} | 
|  | 1173 | 
|  | 1174 				envPos++; | 
|  | 1175 			} | 
|  | 1176 | 
|  | 1177 			if (envPos < ins->envPPAnt) | 
|  | 1178 			{ | 
|  | 1179 				envInterpolateFlag = true; | 
|  | 1180 				if ((ins->envPTyp & ENV_SUSTAIN) && ch->envSustainActive) | 
|  | 1181 				{ | 
|  | 1182 					if (envPos-1 == ins->envPSust) | 
|  | 1183 					{ | 
|  | 1184 						envPos--; | 
|  | 1185 						ch->envPIPValue = 0; | 
|  | 1186 						envInterpolateFlag = false; | 
|  | 1187 					} | 
|  | 1188 				} | 
|  | 1189 | 
|  | 1190 				if (envInterpolateFlag) | 
|  | 1191 				{ | 
|  | 1192 					ch->envPPos = envPos; | 
|  | 1193 | 
|  | 1194 					ch->envPIPValue = 0; | 
|  | 1195 					if (ins->envPP[envPos][0] > ins->envPP[envPos-1][0]) | 
|  | 1196 					{ | 
|  | 1197 						ch->envPIPValue = (ins->envPP[envPos][1] - ins->envPP[envPos-1][1]) << 8; | 
|  | 1198 						ch->envPIPValue /= (ins->envPP[envPos][0] - ins->envPP[envPos-1][0]); | 
|  | 1199 | 
|  | 1200 						envVal = ch->envPAmp; | 
|  | 1201 						envDidInterpolate = true; | 
|  | 1202 					} | 
|  | 1203 				} | 
|  | 1204 			} | 
|  | 1205 			else | 
|  | 1206 			{ | 
|  | 1207 				ch->envPIPValue = 0; | 
|  | 1208 			} | 
|  | 1209 		} | 
|  | 1210 | 
|  | 1211 		if (!envDidInterpolate) | 
|  | 1212 		{ | 
|  | 1213 			ch->envPAmp += ch->envPIPValue; | 
|  | 1214 | 
|  | 1215 			envVal = ch->envPAmp; | 
|  | 1216 			if (envVal > 64*256) | 
|  | 1217 			{ | 
|  | 1218 				if (envVal > 128*256) | 
|  | 1219 					envVal = 64*256; | 
|  | 1220 				else | 
|  | 1221 					envVal = 0; | 
|  | 1222 | 
|  | 1223 				ch->envPIPValue = 0; | 
|  | 1224 			} | 
|  | 1225 		} | 
|  | 1226 | 
|  | 1227 		int16_t panTmp = ch->outPan - 128; | 
|  | 1228 		if (panTmp > 0) | 
|  | 1229 			panTmp = 0 - panTmp; | 
|  | 1230 		panTmp += 128; | 
|  | 1231 | 
|  | 1232 		panTmp <<= 3; | 
|  | 1233 		envVal -= 32*256; | 
|  | 1234 | 
|  | 1235 		ch->finalPan = ch->outPan + (uint8_t)(((int16_t)envVal * panTmp) >> 16); | 
|  | 1236 		ch->status |= IS_Pan; | 
|  | 1237 	} | 
|  | 1238 	else | 
|  | 1239 	{ | 
|  | 1240 		ch->finalPan = ch->outPan; | 
|  | 1241 	} | 
|  | 1242 | 
|  | 1243 	// *** AUTO VIBRATO *** | 
|  | 1244 	if (ins->vibDepth > 0) | 
|  | 1245 	{ | 
|  | 1246 		if (ch->eVibSweep > 0) | 
|  | 1247 		{ | 
|  | 1248 			autoVibAmp = ch->eVibSweep; | 
|  | 1249 			if (ch->envSustainActive) | 
|  | 1250 			{ | 
|  | 1251 				autoVibAmp += ch->eVibAmp; | 
|  | 1252 				if ((autoVibAmp >> 8) > ins->vibDepth) | 
|  | 1253 				{ | 
|  | 1254 					autoVibAmp = ins->vibDepth << 8; | 
|  | 1255 					ch->eVibSweep = 0; | 
|  | 1256 				} | 
|  | 1257 | 
|  | 1258 				ch->eVibAmp = autoVibAmp; | 
|  | 1259 			} | 
|  | 1260 		} | 
|  | 1261 		else | 
|  | 1262 		{ | 
|  | 1263 			autoVibAmp = ch->eVibAmp; | 
|  | 1264 		} | 
|  | 1265 | 
|  | 1266 		ch->eVibPos += ins->vibRate; | 
|  | 1267 | 
|  | 1268 		     if (ins->vibTyp == 1) autoVibVal = (ch->eVibPos > 127) ? 64 : -64; // square | 
|  | 1269 		else if (ins->vibTyp == 2) autoVibVal = (((ch->eVibPos >> 1) + 64) & 127) - 64; // ramp up | 
|  | 1270 		else if (ins->vibTyp == 3) autoVibVal = ((-(ch->eVibPos >> 1) + 64) & 127) - 64; // ramp down | 
|  | 1271 		else autoVibVal = vibSineTab[ch->eVibPos]; // sine | 
|  | 1272 | 
|  | 1273 		autoVibVal <<= 2; | 
|  | 1274 		uint16_t tmpPeriod = (autoVibVal * (int16_t)autoVibAmp) >> 16; | 
|  | 1275 | 
|  | 1276 		tmpPeriod += ch->outPeriod; | 
|  | 1277 		if (tmpPeriod >= MAX_FRQ) | 
|  | 1278 			tmpPeriod = 0; // 8bb: yes, FT2 does this (!) | 
|  | 1279 | 
|  | 1280 		ch->finalPeriod = tmpPeriod; | 
|  | 1281 		ch->status |= IS_Period; | 
|  | 1282 	} | 
|  | 1283 	else | 
|  | 1284 	{ | 
|  | 1285 		ch->finalPeriod = ch->outPeriod; | 
|  | 1286 	} | 
|  | 1287 } | 
|  | 1288 | 
|  | 1289 // 8bb: converts period to note number, for arpeggio and portamento (in semitone-slide mode) | 
|  | 1290 static uint16_t relocateTon(uint16_t period, uint8_t arpNote, stmTyp *ch) | 
|  | 1291 { | 
|  | 1292 	int32_t tmpPeriod; | 
|  | 1293 | 
|  | 1294 	const int32_t fineTune = ((int8_t)ch->fineTune >> 3) + 16; | 
|  | 1295 | 
|  | 1296 	/* 8bb: FT2 bug, should've been 10*12*16. Notes above B-7 (95) will have issues. | 
|  | 1297 	** You can only achieve such high notes by having a high relative note value | 
|  | 1298 	** in the sample. | 
|  | 1299 	*/ | 
|  | 1300 	int32_t hiPeriod = 8*12*16; | 
|  | 1301 | 
|  | 1302 	int32_t loPeriod = 0; | 
|  | 1303 | 
|  | 1304 	for (int32_t i = 0; i < 8; i++) | 
|  | 1305 	{ | 
|  | 1306 		tmpPeriod = (((loPeriod + hiPeriod) >> 1) & ~15) + fineTune; | 
|  | 1307 | 
|  | 1308 		int32_t lookUp = tmpPeriod - 8; | 
|  | 1309 		if (lookUp < 0) | 
|  | 1310 			lookUp = 0; // 8bb: safety fix (C-0 w/ ftune <= -65). This buggy read seems to return 0 in FT2 (TODO: verify) | 
|  | 1311 | 
|  | 1312 		if (period >= note2Period[lookUp]) | 
|  | 1313 			hiPeriod = (tmpPeriod - fineTune) & ~15; | 
|  | 1314 		else | 
|  | 1315 			loPeriod = (tmpPeriod - fineTune) & ~15; | 
|  | 1316 	} | 
|  | 1317 | 
|  | 1318 	tmpPeriod = loPeriod + fineTune + (arpNote << 4); | 
|  | 1319 	if (tmpPeriod >= (8*12*16+15)-1) // 8bb: FT2 bug, should've been 10*12*16+16 (also notice the +2 difference) | 
|  | 1320 		tmpPeriod = (8*12*16+16)-1; | 
|  | 1321 | 
|  | 1322 	return note2Period[tmpPeriod]; | 
|  | 1323 } | 
|  | 1324 | 
|  | 1325 static void vibrato2(stmTyp *ch) | 
|  | 1326 { | 
|  | 1327 	uint8_t tmpVib = (ch->vibPos >> 2) & 0x1F; | 
|  | 1328 | 
|  | 1329 	switch (ch->waveCtrl & 3) | 
|  | 1330 	{ | 
|  | 1331 		// 0: sine | 
|  | 1332 		case 0: tmpVib = vibTab[tmpVib]; break; | 
|  | 1333 | 
|  | 1334 		// 1: ramp | 
|  | 1335 		case 1: | 
|  | 1336 		{ | 
|  | 1337 			tmpVib <<= 3; | 
|  | 1338 			if ((int8_t)ch->vibPos < 0) | 
|  | 1339 				tmpVib = ~tmpVib; | 
|  | 1340 		} | 
|  | 1341 		break; | 
|  | 1342 | 
|  | 1343 		// 2/3: square | 
|  | 1344 		default: tmpVib = 255; break; | 
|  | 1345 	} | 
|  | 1346 | 
|  | 1347 	tmpVib = (tmpVib * ch->vibDepth) >> 5; | 
|  | 1348 | 
|  | 1349 	if ((int8_t)ch->vibPos < 0) | 
|  | 1350 		ch->outPeriod = ch->realPeriod - tmpVib; | 
|  | 1351 	else | 
|  | 1352 		ch->outPeriod = ch->realPeriod + tmpVib; | 
|  | 1353 | 
|  | 1354 	ch->status |= IS_Period; | 
|  | 1355 	ch->vibPos += ch->vibSpeed; | 
|  | 1356 } | 
|  | 1357 | 
|  | 1358 static void arp(stmTyp *ch, uint8_t param) | 
|  | 1359 { | 
|  | 1360 	/* 8bb: The original arpTab table only supports 16 ticks, so it can and will overflow. | 
|  | 1361 	** I have added overflown values to the table so that we can handle up to 256 ticks. | 
|  | 1362 	** The added overflow entries are accurate to the overflow-read in FT2.08/FT2.09. | 
|  | 1363 	*/ | 
|  | 1364 	const uint8_t tick = arpTab[song.timer & 0xFF]; | 
|  | 1365 | 
|  | 1366 	if (tick == 0) | 
|  | 1367 	{ | 
|  | 1368 		ch->outPeriod = ch->realPeriod; | 
|  | 1369 	} | 
|  | 1370 	else | 
|  | 1371 	{ | 
|  | 1372 		const uint8_t note = (tick == 1) ? (param >> 4) : (param & 0x0F); | 
|  | 1373 		ch->outPeriod = relocateTon(ch->realPeriod, note, ch); | 
|  | 1374 	} | 
|  | 1375 | 
|  | 1376 	ch->status |= IS_Period; | 
|  | 1377 } | 
|  | 1378 | 
|  | 1379 static void portaUp(stmTyp *ch, uint8_t param) | 
|  | 1380 { | 
|  | 1381 	if (param == 0) | 
|  | 1382 		param = ch->portaUpSpeed; | 
|  | 1383 | 
|  | 1384 	ch->portaUpSpeed = param; | 
|  | 1385 | 
|  | 1386 	ch->realPeriod -= param << 2; | 
|  | 1387 	if ((int16_t)ch->realPeriod < 1) | 
|  | 1388 		ch->realPeriod = 1; | 
|  | 1389 | 
|  | 1390 	ch->outPeriod = ch->realPeriod; | 
|  | 1391 	ch->status |= IS_Period; | 
|  | 1392 } | 
|  | 1393 | 
|  | 1394 static void portaDown(stmTyp *ch, uint8_t param) | 
|  | 1395 { | 
|  | 1396 	if (param == 0) | 
|  | 1397 		param = ch->portaDownSpeed; | 
|  | 1398 | 
|  | 1399 	ch->portaDownSpeed = param; | 
|  | 1400 | 
|  | 1401 	ch->realPeriod += param << 2; | 
|  | 1402 	if ((int16_t)ch->realPeriod > MAX_FRQ-1) // 8bb: FT2 bug, should've been unsigned comparison! | 
|  | 1403 		ch->realPeriod = MAX_FRQ-1; | 
|  | 1404 | 
|  | 1405 	ch->outPeriod = ch->realPeriod; | 
|  | 1406 	ch->status |= IS_Period; | 
|  | 1407 } | 
|  | 1408 | 
|  | 1409 static void tonePorta(stmTyp *ch, uint8_t param) // 8bb: param is a placeholder (not used) | 
|  | 1410 { | 
|  | 1411 	if (ch->portaDir == 0) | 
|  | 1412 		return; | 
|  | 1413 | 
|  | 1414 	if (ch->portaDir > 1) | 
|  | 1415 	{ | 
|  | 1416 		ch->realPeriod -= ch->portaSpeed; | 
|  | 1417 		if ((int16_t)ch->realPeriod <= (int16_t)ch->wantPeriod) | 
|  | 1418 		{ | 
|  | 1419 			ch->portaDir = 1; | 
|  | 1420 			ch->realPeriod = ch->wantPeriod; | 
|  | 1421 		} | 
|  | 1422 	} | 
|  | 1423 	else | 
|  | 1424 	{ | 
|  | 1425 		ch->realPeriod += ch->portaSpeed; | 
|  | 1426 		if (ch->realPeriod >= ch->wantPeriod) | 
|  | 1427 		{ | 
|  | 1428 			ch->portaDir = 1; | 
|  | 1429 			ch->realPeriod = ch->wantPeriod; | 
|  | 1430 		} | 
|  | 1431 	} | 
|  | 1432 | 
|  | 1433 	if (ch->glissFunk) // 8bb: semitone-slide flag | 
|  | 1434 		ch->outPeriod = relocateTon(ch->realPeriod, 0, ch); | 
|  | 1435 	else | 
|  | 1436 		ch->outPeriod = ch->realPeriod; | 
|  | 1437 | 
|  | 1438 	ch->status |= IS_Period; | 
|  | 1439 | 
|  | 1440 	(void)param; | 
|  | 1441 } | 
|  | 1442 | 
|  | 1443 static void vibrato(stmTyp *ch, uint8_t param) | 
|  | 1444 { | 
|  | 1445 	uint8_t tmp8; | 
|  | 1446 | 
|  | 1447 	if (ch->eff > 0) | 
|  | 1448 	{ | 
|  | 1449 		tmp8 = param & 0x0F; | 
|  | 1450 		if (tmp8 > 0) | 
|  | 1451 			ch->vibDepth = tmp8; | 
|  | 1452 | 
|  | 1453 		tmp8 = (param & 0xF0) >> 2; | 
|  | 1454 		if (tmp8 > 0) | 
|  | 1455 			ch->vibSpeed = tmp8; | 
|  | 1456 	} | 
|  | 1457 | 
|  | 1458 	vibrato2(ch); | 
|  | 1459 } | 
|  | 1460 | 
|  | 1461 static void tonePlusVol(stmTyp *ch, uint8_t param) | 
|  | 1462 { | 
|  | 1463 	tonePorta(ch, 0); // 8bb: the last parameter is not used in tonePorta() | 
|  | 1464 	volume(ch, param); | 
|  | 1465 | 
|  | 1466 	(void)param; | 
|  | 1467 } | 
|  | 1468 | 
|  | 1469 static void vibratoPlusVol(stmTyp *ch, uint8_t param) | 
|  | 1470 { | 
|  | 1471 	vibrato2(ch); | 
|  | 1472 	volume(ch, param); | 
|  | 1473 | 
|  | 1474 	(void)param; | 
|  | 1475 } | 
|  | 1476 | 
|  | 1477 static void tremolo(stmTyp *ch, uint8_t param) | 
|  | 1478 { | 
|  | 1479 	uint8_t tmp8; | 
|  | 1480 	int16_t tremVol; | 
|  | 1481 | 
|  | 1482 	const uint8_t tmpEff = param; | 
|  | 1483 	if (tmpEff > 0) | 
|  | 1484 	{ | 
|  | 1485 		tmp8 = tmpEff & 0x0F; | 
|  | 1486 		if (tmp8 > 0) | 
|  | 1487 			ch->tremDepth = tmp8; | 
|  | 1488 | 
|  | 1489 		tmp8 = (tmpEff & 0xF0) >> 2; | 
|  | 1490 		if (tmp8 > 0) | 
|  | 1491 			ch->tremSpeed = tmp8; | 
|  | 1492 	} | 
|  | 1493 | 
|  | 1494 	uint8_t tmpTrem = (ch->tremPos >> 2) & 0x1F; | 
|  | 1495 	switch ((ch->waveCtrl >> 4) & 3) | 
|  | 1496 	{ | 
|  | 1497 		// 0: sine | 
|  | 1498 		case 0: tmpTrem = vibTab[tmpTrem]; break; | 
|  | 1499 | 
|  | 1500 		// 1: ramp | 
|  | 1501 		case 1: | 
|  | 1502 		{ | 
|  | 1503 			tmpTrem <<= 3; | 
|  | 1504 			if ((int8_t)ch->vibPos < 0) // 8bb: FT2 bug, should've been ch->tremPos | 
|  | 1505 				tmpTrem = ~tmpTrem; | 
|  | 1506 		} | 
|  | 1507 		break; | 
|  | 1508 | 
|  | 1509 		// 2/3: square | 
|  | 1510 		default: tmpTrem = 255; break; | 
|  | 1511 	} | 
|  | 1512 	tmpTrem = (tmpTrem * ch->tremDepth) >> 6; | 
|  | 1513 | 
|  | 1514 	if ((int8_t)ch->tremPos < 0) | 
|  | 1515 	{ | 
|  | 1516 		tremVol = ch->realVol - tmpTrem; | 
|  | 1517 		if (tremVol < 0) | 
|  | 1518 			tremVol = 0; | 
|  | 1519 	} | 
|  | 1520 	else | 
|  | 1521 	{ | 
|  | 1522 		tremVol = ch->realVol + tmpTrem; | 
|  | 1523 		if (tremVol > 64) | 
|  | 1524 			tremVol = 64; | 
|  | 1525 	} | 
|  | 1526 | 
|  | 1527 	ch->outVol = (uint8_t)tremVol; | 
|  | 1528 	ch->status |= IS_Vol; | 
|  | 1529 	ch->tremPos += ch->tremSpeed; | 
|  | 1530 } | 
|  | 1531 | 
|  | 1532 static void volume(stmTyp *ch, uint8_t param) // 8bb: volume slide | 
|  | 1533 { | 
|  | 1534 	if (param == 0) | 
|  | 1535 		param = ch->volSlideSpeed; | 
|  | 1536 | 
|  | 1537 	ch->volSlideSpeed = param; | 
|  | 1538 | 
|  | 1539 	uint8_t newVol = ch->realVol; | 
|  | 1540 	if ((param & 0xF0) == 0) | 
|  | 1541 	{ | 
|  | 1542 		newVol -= param; | 
|  | 1543 		if ((int8_t)newVol < 0) | 
|  | 1544 			newVol = 0; | 
|  | 1545 	} | 
|  | 1546 	else | 
|  | 1547 	{ | 
|  | 1548 		param >>= 4; | 
|  | 1549 | 
|  | 1550 		newVol += param; | 
|  | 1551 		if (newVol > 64) | 
|  | 1552 			newVol = 64; | 
|  | 1553 	} | 
|  | 1554 | 
|  | 1555 	ch->outVol = ch->realVol = newVol; | 
|  | 1556 	ch->status |= IS_Vol; | 
|  | 1557 } | 
|  | 1558 | 
|  | 1559 static void globalVolSlide(stmTyp *ch, uint8_t param) | 
|  | 1560 { | 
|  | 1561 	if (param == 0) | 
|  | 1562 		param = ch->globVolSlideSpeed; | 
|  | 1563 | 
|  | 1564 	ch->globVolSlideSpeed = param; | 
|  | 1565 | 
|  | 1566 	uint8_t newVol = (uint8_t)song.globVol; | 
|  | 1567 	if ((param & 0xF0) == 0) | 
|  | 1568 	{ | 
|  | 1569 		newVol -= param; | 
|  | 1570 		if ((int8_t)newVol < 0) | 
|  | 1571 			newVol = 0; | 
|  | 1572 	} | 
|  | 1573 	else | 
|  | 1574 	{ | 
|  | 1575 		param >>= 4; | 
|  | 1576 | 
|  | 1577 		newVol += param; | 
|  | 1578 		if (newVol > 64) | 
|  | 1579 			newVol = 64; | 
|  | 1580 	} | 
|  | 1581 | 
|  | 1582 	song.globVol = newVol; | 
|  | 1583 | 
|  | 1584 	stmTyp *c = stm; | 
|  | 1585 	for (int32_t i = 0; i < song.antChn; i++, c++) // 8bb: this updates the volume for all voices | 
|  | 1586 		c->status |= IS_Vol; | 
|  | 1587 } | 
|  | 1588 | 
|  | 1589 static void keyOffCmd(stmTyp *ch, uint8_t param) | 
|  | 1590 { | 
|  | 1591 	if ((uint8_t)(song.tempo-song.timer) == (param & 31)) | 
|  | 1592 		keyOff(ch); | 
|  | 1593 } | 
|  | 1594 | 
|  | 1595 static void panningSlide(stmTyp *ch, uint8_t param) | 
|  | 1596 { | 
|  | 1597 	if (param == 0) | 
|  | 1598 		param = ch->panningSlideSpeed; | 
|  | 1599 | 
|  | 1600 	ch->panningSlideSpeed = param; | 
|  | 1601 | 
|  | 1602 	int16_t newPan = (int16_t)ch->outPan; | 
|  | 1603 	if ((param & 0xF0) == 0) | 
|  | 1604 	{ | 
|  | 1605 		newPan -= param; | 
|  | 1606 		if (newPan < 0) | 
|  | 1607 			newPan = 0; | 
|  | 1608 	} | 
|  | 1609 	else | 
|  | 1610 	{ | 
|  | 1611 		param >>= 4; | 
|  | 1612 | 
|  | 1613 		newPan += param; | 
|  | 1614 		if (newPan > 255) | 
|  | 1615 			newPan = 255; | 
|  | 1616 	} | 
|  | 1617 | 
|  | 1618 	ch->outPan = (uint8_t)newPan; | 
|  | 1619 	ch->status |= IS_Pan; | 
|  | 1620 } | 
|  | 1621 | 
|  | 1622 static void tremor(stmTyp *ch, uint8_t param) | 
|  | 1623 { | 
|  | 1624 	if (param == 0) | 
|  | 1625 		param = ch->tremorSave; | 
|  | 1626 | 
|  | 1627 	ch->tremorSave = param; | 
|  | 1628 | 
|  | 1629 	uint8_t tremorSign = ch->tremorPos & 0x80; | 
|  | 1630 	uint8_t tremorData = ch->tremorPos & 0x7F; | 
|  | 1631 | 
|  | 1632 	tremorData--; | 
|  | 1633 	if ((int8_t)tremorData < 0) | 
|  | 1634 	{ | 
|  | 1635 		if (tremorSign == 0x80) | 
|  | 1636 		{ | 
|  | 1637 			tremorSign = 0x00; | 
|  | 1638 			tremorData = param & 0x0F; | 
|  | 1639 		} | 
|  | 1640 		else | 
|  | 1641 		{ | 
|  | 1642 			tremorSign = 0x80; | 
|  | 1643 			tremorData = param >> 4; | 
|  | 1644 		} | 
|  | 1645 	} | 
|  | 1646 | 
|  | 1647 	ch->tremorPos = tremorSign | tremorData; | 
|  | 1648 	ch->outVol = (tremorSign == 0x80) ? ch->realVol : 0; | 
|  | 1649 	ch->status |= IS_Vol + IS_QuickVol; | 
|  | 1650 } | 
|  | 1651 | 
|  | 1652 static void retrigNote(stmTyp *ch, uint8_t param) | 
|  | 1653 { | 
|  | 1654 	if (param == 0) // 8bb: E9x with a param of zero is handled in getNewNote() | 
|  | 1655 		return; | 
|  | 1656 | 
|  | 1657 	if ((song.tempo-song.timer) % param == 0) | 
|  | 1658 	{ | 
|  | 1659 		startTone(0, 0, 0, ch); | 
|  | 1660 		retrigEnvelopeVibrato(ch); | 
|  | 1661 	} | 
|  | 1662 } | 
|  | 1663 | 
|  | 1664 static void noteCut(stmTyp *ch, uint8_t param) | 
|  | 1665 { | 
|  | 1666 	if ((uint8_t)(song.tempo-song.timer) == param) | 
|  | 1667 	{ | 
|  | 1668 		ch->outVol = ch->realVol = 0; | 
|  | 1669 		ch->status |= IS_Vol + IS_QuickVol; | 
|  | 1670 	} | 
|  | 1671 } | 
|  | 1672 | 
|  | 1673 static void noteDelay(stmTyp *ch, uint8_t param) | 
|  | 1674 { | 
|  | 1675 	if ((uint8_t)(song.tempo-song.timer) == param) | 
|  | 1676 	{ | 
|  | 1677 		startTone(ch->tonTyp & 0xFF, 0, 0, ch); | 
|  | 1678 | 
|  | 1679 		if ((ch->tonTyp & 0xFF00) > 0) // 8bb: do we have an instrument number? | 
|  | 1680 			retrigVolume(ch); | 
|  | 1681 | 
|  | 1682 		retrigEnvelopeVibrato(ch); | 
|  | 1683 | 
|  | 1684 		if (ch->volKolVol >= 0x10 && ch->volKolVol <= 0x50) // 8bb: Set Volume (volume column) | 
|  | 1685 		{ | 
|  | 1686 			ch->outVol = ch->volKolVol - 16; | 
|  | 1687 			ch->realVol = ch->outVol; | 
|  | 1688 		} | 
|  | 1689 		else if (ch->volKolVol >= 0xC0 && ch->volKolVol <= 0xCF) // 8bb: Set Panning (volume column) | 
|  | 1690 		{ | 
|  | 1691 			ch->outPan = (ch->volKolVol & 0x0F) << 4; | 
|  | 1692 		} | 
|  | 1693 	} | 
|  | 1694 } | 
|  | 1695 | 
|  | 1696 static const efxRoutine EJumpTab_TickNonZero[16] = | 
|  | 1697 { | 
|  | 1698 	dummy, // 0 | 
|  | 1699 	dummy, // 1 | 
|  | 1700 	dummy, // 2 | 
|  | 1701 	dummy, // 3 | 
|  | 1702 	dummy, // 4 | 
|  | 1703 	dummy, // 5 | 
|  | 1704 	dummy, // 6 | 
|  | 1705 	dummy, // 7 | 
|  | 1706 	dummy, // 8 | 
|  | 1707 	retrigNote, // 9 | 
|  | 1708 	dummy, // A | 
|  | 1709 	dummy, // B | 
|  | 1710 	noteCut, // C | 
|  | 1711 	noteDelay, // D | 
|  | 1712 	dummy, // E | 
|  | 1713 	dummy // F | 
|  | 1714 }; | 
|  | 1715 | 
|  | 1716 static void E_Effects_TickNonZero(stmTyp *ch, uint8_t param) | 
|  | 1717 { | 
|  | 1718 	EJumpTab_TickNonZero[param >> 4](ch, param & 0xF); | 
|  | 1719 } | 
|  | 1720 | 
|  | 1721 static const efxRoutine JumpTab_TickNonZero[36] = | 
|  | 1722 { | 
|  | 1723 	arp, // 0 | 
|  | 1724 	portaUp, // 1 | 
|  | 1725 	portaDown, // 2 | 
|  | 1726 	tonePorta, // 3 | 
|  | 1727 	vibrato, // 4 | 
|  | 1728 	tonePlusVol, // 5 | 
|  | 1729 	vibratoPlusVol, // 6 | 
|  | 1730 	tremolo, // 7 | 
|  | 1731 	dummy, // 8 | 
|  | 1732 	dummy, // 9 | 
|  | 1733 	volume, // A | 
|  | 1734 	dummy, // B | 
|  | 1735 	dummy, // C | 
|  | 1736 	dummy, // D | 
|  | 1737 	E_Effects_TickNonZero, // E | 
|  | 1738 	dummy, // F | 
|  | 1739 	dummy, // G | 
|  | 1740 	globalVolSlide, // H | 
|  | 1741 	dummy, // I | 
|  | 1742 	dummy, // J | 
|  | 1743 	keyOffCmd, // K | 
|  | 1744 	dummy, // L | 
|  | 1745 	dummy, // M | 
|  | 1746 	dummy, // N | 
|  | 1747 	dummy, // O | 
|  | 1748 	panningSlide, // P | 
|  | 1749 	dummy, // Q | 
|  | 1750 	doMultiRetrig, // R | 
|  | 1751 	dummy, // S | 
|  | 1752 	tremor, // T | 
|  | 1753 	dummy, // U | 
|  | 1754 	dummy, // V | 
|  | 1755 	dummy, // W | 
|  | 1756 	dummy, // X | 
|  | 1757 	dummy, // Y | 
|  | 1758 	dummy  // Z | 
|  | 1759 }; | 
|  | 1760 | 
|  | 1761 static void doEffects(stmTyp *ch) // tick>0 effect handling | 
|  | 1762 { | 
|  | 1763 	const uint8_t volKolEfx = ch->volKolVol >> 4; | 
|  | 1764 	if (volKolEfx > 0) | 
|  | 1765 		VJumpTab_TickNonZero[volKolEfx](ch); | 
|  | 1766 | 
|  | 1767 	if ((ch->eff == 0 && ch->effTyp == 0) || ch->effTyp > 35) | 
|  | 1768 		return; | 
|  | 1769 | 
|  | 1770 	JumpTab_TickNonZero[ch->effTyp](ch, ch->eff); | 
|  | 1771 } | 
|  | 1772 | 
|  | 1773 static void getNextPos(void) | 
|  | 1774 { | 
|  | 1775 	song.pattPos++; | 
|  | 1776 | 
|  | 1777 	if (song.pattDelTime > 0) | 
|  | 1778 	{ | 
|  | 1779 		song.pattDelTime2 = song.pattDelTime; | 
|  | 1780 		song.pattDelTime = 0; | 
|  | 1781 	} | 
|  | 1782 | 
|  | 1783 	if (song.pattDelTime2 > 0) | 
|  | 1784 	{ | 
|  | 1785 		song.pattDelTime2--; | 
|  | 1786 		if (song.pattDelTime2 > 0) | 
|  | 1787 			song.pattPos--; | 
|  | 1788 	} | 
|  | 1789 | 
|  | 1790 	if (song.pBreakFlag) | 
|  | 1791 	{ | 
|  | 1792 		song.pBreakFlag = false; | 
|  | 1793 		song.pattPos = song.pBreakPos; | 
|  | 1794 	} | 
|  | 1795 | 
|  | 1796 	if (song.pattPos >= song.pattLen || song.posJumpFlag) | 
|  | 1797 	{ | 
|  | 1798 		song.pattPos = song.pBreakPos; | 
|  | 1799 		song.pBreakPos = 0; | 
|  | 1800 		song.posJumpFlag = false; | 
|  | 1801 | 
|  | 1802 		song.songPos++; | 
|  | 1803 		if (song.songPos >= song.len) | 
|  | 1804 			song.songPos = song.repS; | 
|  | 1805 | 
|  | 1806 		song.pattNr = song.songTab[(uint8_t)song.songPos]; | 
|  | 1807 		song.pattLen = pattLens[(uint8_t)song.pattNr]; | 
|  | 1808 	} | 
|  | 1809 } | 
|  | 1810 | 
|  | 1811 void mainPlayer(void) | 
|  | 1812 { | 
|  | 1813 	if (musicPaused) | 
|  | 1814 		return; | 
|  | 1815 | 
|  | 1816 	bool tickZero = false; | 
|  | 1817 | 
|  | 1818 	song.timer--; | 
|  | 1819 	if (song.timer == 0) | 
|  | 1820 	{ | 
|  | 1821 		song.timer = song.tempo; | 
|  | 1822 		tickZero = true; | 
|  | 1823 	} | 
|  | 1824 | 
|  | 1825 	const bool readNewNote = tickZero && (song.pattDelTime2 == 0); | 
|  | 1826 	if (readNewNote) | 
|  | 1827 	{ | 
|  | 1828 		const tonTyp *pattPtr = nilPatternLine; | 
|  | 1829 		if (patt[song.pattNr] != NULL) | 
|  | 1830 			pattPtr = &patt[song.pattNr][song.pattPos * song.antChn]; | 
|  | 1831 | 
|  | 1832 		stmTyp *c = stm; | 
|  | 1833 		for (uint8_t i = 0; i < song.antChn; i++, c++, pattPtr++) | 
|  | 1834 		{ | 
|  | 1835 			PMPTmpActiveChannel = i; // 8bb: for P_StartTone() | 
|  | 1836 			getNewNote(c, pattPtr); | 
|  | 1837 			fixaEnvelopeVibrato(c); | 
|  | 1838 		} | 
|  | 1839 	} | 
|  | 1840 	else | 
|  | 1841 	{ | 
|  | 1842 		stmTyp *c = stm; | 
|  | 1843 		for (uint8_t i = 0; i < song.antChn; i++, c++) | 
|  | 1844 		{ | 
|  | 1845 			PMPTmpActiveChannel = i; // 8bb: for P_StartTone() | 
|  | 1846 			doEffects(c); | 
|  | 1847 			fixaEnvelopeVibrato(c); | 
|  | 1848 		} | 
|  | 1849 	} | 
|  | 1850 | 
|  | 1851 	if (song.timer == 1) | 
|  | 1852 		getNextPos(); | 
|  | 1853 } |