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