| 
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 }
 |