| 126 | 1 #include <stdint.h> | 
|  | 2 #include <stdbool.h> | 
|  | 3 #include <stdlib.h> | 
|  | 4 #include <string.h> | 
|  | 5 #include <math.h> | 
|  | 6 #include "pmplay.h" | 
|  | 7 #include "pmp_main.h" | 
|  | 8 #include "snd_masm.h" | 
|  | 9 #include "tables.h" | 
|  | 10 | 
|  | 11 // fast 32-bit -> 16-bit clamp | 
|  | 12 #define CLAMP16(i) if ((int16_t)(i) != i) i = 0x7FFF ^ (i >> 31) | 
|  | 13 | 
|  | 14 static bool dump_Flag; | 
|  | 15 static int32_t oldReplayRate; | 
|  | 16 | 
|  | 17 // globalized | 
|  | 18 int16_t chnReloc[32]; | 
|  | 19 int32_t *CDA_MixBuffer = NULL; | 
|  | 20 CIType CI[32 * 2]; | 
|  | 21 // ------------ | 
|  | 22 | 
|  | 23 static void mix_UpdateChannel(int32_t nr, WaveChannelInfoType *WCI); | 
|  | 24 | 
|  | 25 void P_SetSpeed(uint16_t bpm) | 
|  | 26 { | 
|  | 27 	// 8bb: added this | 
|  | 28 	if (bpm == 0) | 
|  | 29 		bpm = 125; | 
|  | 30 | 
|  | 31 	speedVal = ((realReplayRate + realReplayRate) + (realReplayRate >> 1)) / bpm; // 8bb: same as doing "((realReplayRate * 5) / 2) / bpm" | 
|  | 32 } | 
|  | 33 | 
|  | 34 void P_StartTone(sampleTyp *s, int32_t smpStartPos) | 
|  | 35 { | 
|  | 36 	WaveChannelInfoType WCI; | 
|  | 37 | 
|  | 38 	WCI.SStartPos = smpStartPos; | 
|  | 39 	WCI.SBase = s->pek; | 
|  | 40 	WCI.SLen = s->len; | 
|  | 41 	WCI.SRepS = s->repS; | 
|  | 42 	WCI.SRepL = s->repL; | 
|  | 43 	WCI.SType = s->typ; | 
|  | 44 	WCI.Status = Status_StartTone+Status_StopTone; | 
|  | 45 | 
|  | 46 	mix_UpdateChannel(PMPTmpActiveChannel, &WCI); | 
|  | 47 } | 
|  | 48 | 
|  | 49 // 8bb: added these two | 
|  | 50 bool mix_Init(int32_t audioBufferSize) | 
|  | 51 { | 
|  | 52 	CDA_MixBuffer = (int32_t *)malloc(audioBufferSize * 2 * sizeof (int32_t)); | 
|  | 53 	if (CDA_MixBuffer == NULL) | 
|  | 54 		return false; | 
|  | 55 | 
|  | 56 	PMPLeft = 0; | 
|  | 57 	return true; | 
|  | 58 } | 
|  | 59 | 
|  | 60 void mix_Free(void) | 
|  | 61 { | 
|  | 62 	if (CDA_MixBuffer != NULL) | 
|  | 63 	{ | 
|  | 64 		free(CDA_MixBuffer); | 
|  | 65 		CDA_MixBuffer = NULL; | 
|  | 66 	} | 
|  | 67 } | 
|  | 68 // -------------------- | 
|  | 69 | 
|  | 70 static void updateVolume(CIType *v, int32_t volIPLen) | 
|  | 71 { | 
|  | 72 	const uint32_t vol = v->SVol * CDA_Amp; | 
|  | 73 | 
|  | 74 	v->SLVol1 = (vol * panningTab[256-v->SPan]) >> (32-28); | 
|  | 75 	v->SRVol1 = (vol * panningTab[    v->SPan]) >> (32-28); | 
|  | 76 | 
|  | 77 	if (volumeRampingFlag) | 
|  | 78 	{ | 
|  | 79 		v->SLVolIP = (v->SLVol1 - v->SLVol2) / volIPLen; | 
|  | 80 		v->SRVolIP = (v->SRVol1 - v->SRVol2) / volIPLen; | 
|  | 81 		v->SVolIPLen = volIPLen; | 
|  | 82 	} | 
|  | 83 } | 
|  | 84 | 
|  | 85 static void mix_UpdateChannel(int32_t nr, WaveChannelInfoType *WCI) | 
|  | 86 { | 
|  | 87 	CIType *v = &CI[chnReloc[nr]]; | 
|  | 88 	const uint8_t status = WCI->Status; | 
|  | 89 | 
|  | 90 	if (status & Status_StopTone) | 
|  | 91 	{ | 
|  | 92 		if (volumeRampingFlag) | 
|  | 93 		{ | 
|  | 94 			// 8bb: fade out current voice | 
|  | 95 			v->SType |= SType_Fadeout; | 
|  | 96 			v->SVol = 0; | 
|  | 97 			updateVolume(v, quickVolSizeVal); | 
|  | 98 | 
|  | 99 			// 8bb: swap current voice with neighbor | 
|  | 100 			chnReloc[nr] ^= 1; | 
|  | 101 			v = &CI[chnReloc[nr]]; | 
|  | 102 		} | 
|  | 103 | 
|  | 104 		v->SType = SType_Off; | 
|  | 105 	} | 
|  | 106 | 
|  | 107 	if (status & Status_SetPan) | 
|  | 108 		v->SPan = (uint8_t)WCI->SPan; | 
|  | 109 | 
|  | 110 	if (status & Status_SetVol) | 
|  | 111 	{ | 
|  | 112 		uint16_t vol = WCI->SVol; | 
|  | 113 		if (vol > 0) vol--; // 8bb: 0..256 -> 0..255 ( FT2 does this to prevent mul overflow in updateVolume() ) | 
|  | 114 		v->SVol = (uint8_t)vol; | 
|  | 115 	} | 
|  | 116 | 
|  | 117 	if (status & (Status_SetVol+Status_SetPan)) | 
|  | 118 		updateVolume(v, (status & Status_QuickVol) ? quickVolSizeVal : speedVal); | 
|  | 119 | 
|  | 120 	if (status & Status_SetFrq) | 
|  | 121 		v->SFrq = WCI->SFrq; | 
|  | 122 | 
|  | 123 	if (status & Status_StartTone) | 
|  | 124 	{ | 
|  | 125 		int32_t len; | 
|  | 126 | 
|  | 127 		uint8_t type = WCI->SType; | 
|  | 128 		const bool sample16Bit = (type >> 4) & 1; | 
|  | 129 | 
|  | 130 		if (type & (SType_Fwd+SType_Rev)) | 
|  | 131 		{ | 
|  | 132 			int32_t repL = WCI->SRepL; | 
|  | 133 			int32_t repS = WCI->SRepS; | 
|  | 134 | 
|  | 135 			if (sample16Bit) | 
|  | 136 			{ | 
|  | 137 				repL >>= 1; | 
|  | 138 				repS >>= 1; | 
|  | 139 | 
|  | 140 				v->SRevBase = (int16_t *)WCI->SBase + (repS+repS+repL); | 
|  | 141 			} | 
|  | 142 			else | 
|  | 143 			{ | 
|  | 144 				v->SRevBase = (int8_t *)WCI->SBase + (repS+repS+repL); | 
|  | 145 			} | 
|  | 146 | 
|  | 147 			v->SRepL = repL; | 
|  | 148 			v->SRepS = repS; | 
|  | 149 | 
|  | 150 			len = repS + repL; | 
|  | 151 		} | 
|  | 152 		else | 
|  | 153 		{ | 
|  | 154 			type &= ~(SType_Fwd+SType_Rev); // 8bb: keep loop flags only | 
|  | 155 | 
|  | 156 			len = WCI->SLen; | 
|  | 157 			if (sample16Bit) | 
|  | 158 				len >>= 1; | 
|  | 159 | 
|  | 160 			if (len == 0) | 
|  | 161 				return; | 
|  | 162 		} | 
|  | 163 | 
|  | 164 		// 8bb: overflown 9xx (set sample offset), cut voice (voice got ended earlier in "if (status & Status_StopTone)") | 
|  | 165 		if (WCI->SStartPos >= len) | 
|  | 166 			return; | 
|  | 167 | 
|  | 168 		v->SLen = len; | 
|  | 169 		v->SPos = WCI->SStartPos; | 
|  | 170 		v->SPosDec = 0; | 
|  | 171 		v->SBase = WCI->SBase; | 
|  | 172 		v->SMixType = (sample16Bit * 4) + (volumeRampingFlag * 2) + interpolationFlag; | 
|  | 173 		v->SType = type; | 
|  | 174 	} | 
|  | 175 } | 
|  | 176 | 
|  | 177 static void mix_UpdateChannelVolPanFrq(void) | 
|  | 178 { | 
|  | 179 	WaveChannelInfoType WCI; | 
|  | 180 | 
|  | 181 	stmTyp *ch = stm; | 
|  | 182 	for (int32_t i = 0; i < song.antChn; i++, ch++) | 
|  | 183 	{ | 
|  | 184 		uint8_t newStatus = 0; | 
|  | 185 | 
|  | 186 		const uint8_t status = ch->status; | 
|  | 187 		ch->status = 0; | 
|  | 188 | 
|  | 189 		if (status == 0) | 
|  | 190 			continue; | 
|  | 191 | 
|  | 192 		if (status & IS_Vol) | 
|  | 193 		{ | 
|  | 194 			WCI.SVol = ch->finalVol; | 
|  | 195 			newStatus |= Status_SetVol; | 
|  | 196 		} | 
|  | 197 | 
|  | 198 		if (status & IS_QuickVol) | 
|  | 199 			newStatus |= Status_QuickVol; | 
|  | 200 | 
|  | 201 		if (status & IS_Pan) | 
|  | 202 		{ | 
|  | 203 			WCI.SPan = ch->finalPan; | 
|  | 204 			newStatus |= Status_SetPan; | 
|  | 205 		} | 
|  | 206 | 
|  | 207 		if (status & IS_Period) | 
|  | 208 		{ | 
|  | 209 			WCI.SFrq = getFrequenceValue(ch->finalPeriod); | 
|  | 210 			newStatus |= Status_SetFrq; | 
|  | 211 		} | 
|  | 212 | 
|  | 213 		WCI.Status = newStatus; | 
|  | 214 		mix_UpdateChannel(i, &WCI); | 
|  | 215 	} | 
|  | 216 } | 
|  | 217 | 
|  | 218 void mix_ClearChannels(void) // 8bb: rewritten to handle all voices instead of song.antChn | 
|  | 219 { | 
|  | 220 	lockMixer(); | 
|  | 221 | 
|  | 222 	memset(CI, 0, sizeof (CI)); | 
|  | 223 | 
|  | 224 	CIType *v = CI; | 
|  | 225 	for (int16_t i = 0; i < 32*2; i++, v++) | 
|  | 226 	{ | 
|  | 227 		v->SPan = 128; | 
|  | 228 		v->SType = SType_Off; | 
|  | 229 	} | 
|  | 230 | 
|  | 231 	for (int16_t i = 0; i < 32; i++) | 
|  | 232 		chnReloc[i] = i+i; | 
|  | 233 | 
|  | 234 	unlockMixer(); | 
|  | 235 } | 
|  | 236 | 
|  | 237 static void mix_SaveIPVolumes(void) | 
|  | 238 { | 
|  | 239 	CIType *v = CI; | 
|  | 240 	for (int32_t i = 0; i < song.antChn*2; i++, v++) | 
|  | 241 	{ | 
|  | 242 		// 8bb: this cuts any active fade-out voices (volume ramping) | 
|  | 243 		if (v->SType & SType_Fadeout) | 
|  | 244 			v->SType = SType_Off; | 
|  | 245 | 
|  | 246 		v->SLVol2 = v->SLVol1; | 
|  | 247 		v->SRVol2 = v->SRVol1; | 
|  | 248 		v->SVolIPLen = 0; | 
|  | 249 	} | 
|  | 250 } | 
|  | 251 | 
|  | 252 void mix_UpdateBuffer(int16_t *buffer, int32_t numSamples) | 
|  | 253 { | 
|  | 254 	if (numSamples <= 0) | 
|  | 255 		return; | 
|  | 256 | 
|  | 257 	if (musicPaused || WAVDump_Flag) // silence output | 
|  | 258 	{ | 
|  | 259 		memset(buffer, 0, numSamples * (2 * sizeof (int16_t))); | 
|  | 260 		return; | 
|  | 261 	} | 
|  | 262 | 
|  | 263 	memset(CDA_MixBuffer, 0, numSamples * (2 * sizeof (int32_t))); | 
|  | 264 | 
|  | 265 	int32_t c = 0; | 
|  | 266 	int32_t a = numSamples; | 
|  | 267 | 
|  | 268 	while (a > 0) | 
|  | 269 	{ | 
|  | 270 		if (PMPLeft == 0) | 
|  | 271 		{ | 
|  | 272 			mix_SaveIPVolumes(); | 
|  | 273 			mainPlayer(); | 
|  | 274 			mix_UpdateChannelVolPanFrq(); | 
|  | 275 			PMPLeft = speedVal; | 
|  | 276 		} | 
|  | 277 | 
|  | 278 		int32_t b = a; | 
|  | 279 		if (b > PMPLeft) | 
|  | 280 			b = PMPLeft; | 
|  | 281 | 
|  | 282 		CIType *v = CI; | 
|  | 283 		for (int32_t i = 0; i < song.antChn*2; i++, v++) | 
|  | 284 			PMPMix32Proc(v, b, c); | 
|  | 285 | 
|  | 286 		c += b; | 
|  | 287 		a -= b; | 
|  | 288 		PMPLeft -= b; | 
|  | 289 	} | 
|  | 290 | 
|  | 291 	numSamples *= 2; // 8bb: stereo | 
|  | 292 | 
|  | 293 	/* 8bb: Done a bit differently since we don't use a | 
|  | 294 	** Sound Blaster with its master volume setting. | 
|  | 295 	** Instead we change the amplitude here. | 
|  | 296 	*/ | 
|  | 297 | 
|  | 298 	if (masterVol == 256) // 8bb: max master volume, no need to change amp | 
|  | 299 	{ | 
|  | 300 		for (int32_t i = 0; i < numSamples; i++) | 
|  | 301 		{ | 
|  | 302 			int32_t out32 = CDA_MixBuffer[i] >> 8; | 
|  | 303 			CLAMP16(out32); | 
|  | 304 			buffer[i] = (int16_t)out32; | 
|  | 305 		} | 
|  | 306 	} | 
|  | 307 	else | 
|  | 308 	{ | 
|  | 309 		for (int32_t i = 0; i < numSamples; i++) | 
|  | 310 		{ | 
|  | 311 			int32_t out32 = CDA_MixBuffer[i] >> 8; | 
|  | 312 			CLAMP16(out32); | 
|  | 313 			out32 = (out32 * masterVol) >> 8; | 
|  | 314 			buffer[i] = (int16_t)out32; | 
|  | 315 		} | 
|  | 316 	} | 
|  | 317 } | 
|  | 318 | 
|  | 319 bool dump_Init(int32_t frq, int32_t amp, int16_t songPos) | 
|  | 320 { | 
|  | 321 	setPos(songPos, 0); | 
|  | 322 | 
|  | 323 	oldReplayRate = realReplayRate; | 
|  | 324 | 
|  | 325 	realReplayRate = frq; | 
|  | 326 	updateReplayRate(); | 
|  | 327 	CDA_Amp = 8 * amp; | 
|  | 328 | 
|  | 329 	mix_ClearChannels(); | 
|  | 330 	stopVoices(); | 
|  | 331 	song.globVol = 64; | 
|  | 332 	speedVal = (frq*5 / 2) / song.speed; | 
|  | 333 	quickVolSizeVal = frq / 200; | 
|  | 334 | 
|  | 335 	dump_Flag = false; | 
|  | 336 	return true; | 
|  | 337 } | 
|  | 338 | 
|  | 339 void dump_Close(void) | 
|  | 340 { | 
|  | 341 	stopVoices(); | 
|  | 342 	realReplayRate = oldReplayRate; | 
|  | 343 	updateReplayRate(); | 
|  | 344 } | 
|  | 345 | 
|  | 346 bool dump_EndOfTune(int32_t endSongPos) | 
|  | 347 { | 
|  | 348 	bool returnValue = (dump_Flag && song.pattPos == 0 && song.timer == 1) || (song.tempo == 0); | 
|  | 349 | 
|  | 350 	// 8bb: FT2 bugfix for EEx (pattern delay) on first row of a pattern | 
|  | 351 	if (song.pattDelTime2 > 0) | 
|  | 352 		returnValue = false; | 
|  | 353 | 
|  | 354 	if (song.songPos == endSongPos && song.pattPos == 0 && song.timer == 1) | 
|  | 355 		dump_Flag = true; | 
|  | 356 | 
|  | 357 	return returnValue; | 
|  | 358 } | 
|  | 359 | 
|  | 360 int32_t dump_GetFrame(int16_t *p) // 8bb: returns bytes mixed to 16-bit stereo buffer | 
|  | 361 { | 
|  | 362 	mix_SaveIPVolumes(); | 
|  | 363 	mainPlayer(); | 
|  | 364 	mix_UpdateChannelVolPanFrq(); | 
|  | 365 | 
|  | 366 	memset(CDA_MixBuffer, 0, speedVal * (2 * sizeof (int32_t))); | 
|  | 367 | 
|  | 368 	CIType *v = CI; | 
|  | 369 	for (int32_t i = 0; i < song.antChn*2; i++, v++) | 
|  | 370 		PMPMix32Proc(v, speedVal, 0); | 
|  | 371 | 
|  | 372 	const int32_t numSamples = speedVal * 2; // 8bb: *2 for stereo | 
|  | 373 	for (int32_t i = 0; i < numSamples; i++) | 
|  | 374 	{ | 
|  | 375 		int32_t out32 = CDA_MixBuffer[i] >> 8; | 
|  | 376 		CLAMP16(out32); | 
|  | 377 		p[i] = (int16_t)out32; | 
|  | 378 	} | 
|  | 379 | 
|  | 380 	return speedVal * (2 * sizeof (int16_t)); | 
|  | 381 } |