126
|
1 /*
|
|
2 ** - loaders and replayer handlers -
|
|
3 */
|
|
4
|
|
5 #define DEFAULT_AMP 4
|
|
6 #define DEFAULT_MASTER_VOL 256
|
|
7
|
|
8 #include <stdio.h>
|
|
9 #include <stdlib.h>
|
|
10 #include <string.h>
|
|
11 #include <stdint.h>
|
|
12 #include <stdbool.h>
|
|
13 #include <math.h>
|
|
14 #include <assert.h>
|
|
15 #include "pmplay.h"
|
|
16 #include "pmp_mix.h"
|
|
17 #include "snd_masm.h"
|
|
18 #include "tables.h"
|
|
19
|
|
20 #define INSTR_HEADER_SIZE 263
|
|
21
|
|
22 #define SWAP16(value) \
|
|
23 ( \
|
|
24 (((uint16_t)((value) & 0x00FF)) << 8) | \
|
|
25 (((uint16_t)((value) & 0xFF00)) >> 8) \
|
|
26 )
|
|
27
|
|
28 #ifdef _MSC_VER
|
|
29 #pragma pack(push)
|
|
30 #pragma pack(1)
|
|
31 #endif
|
|
32 typedef struct songHeaderTyp_t
|
|
33 {
|
|
34 char sig[17], name[21], progName[20];
|
|
35 uint16_t ver;
|
|
36 int32_t headerSize;
|
|
37 uint16_t len, repS, antChn, antPtn, antInstrs, flags, defTempo, defSpeed;
|
|
38 uint8_t songTab[256];
|
|
39 }
|
|
40 #ifdef __GNUC__
|
|
41 __attribute__ ((packed))
|
|
42 #endif
|
|
43 songHeaderTyp;
|
|
44
|
|
45 typedef struct modSampleTyp
|
|
46 {
|
|
47 char name[22];
|
|
48 uint16_t len;
|
|
49 uint8_t fine, vol;
|
|
50 uint16_t repS, repL;
|
|
51 }
|
|
52 #ifdef __GNUC__
|
|
53 __attribute__ ((packed))
|
|
54 #endif
|
|
55 modSampleTyp;
|
|
56
|
|
57 typedef struct songMOD31HeaderTyp
|
|
58 {
|
|
59 char name[20];
|
|
60 modSampleTyp sample[31];
|
|
61 uint8_t len, repS, songTab[128];
|
|
62 char Sig[4];
|
|
63 }
|
|
64 #ifdef __GNUC__
|
|
65 __attribute__ ((packed))
|
|
66 #endif
|
|
67 songMOD31HeaderTyp;
|
|
68
|
|
69 typedef struct songMOD15HeaderTyp
|
|
70 {
|
|
71 char name[20];
|
|
72 modSampleTyp sample[15];
|
|
73 uint8_t len, repS, songTab[128];
|
|
74 }
|
|
75 #ifdef __GNUC__
|
|
76 __attribute__ ((packed))
|
|
77 #endif
|
|
78 songMOD15HeaderTyp;
|
|
79
|
|
80 typedef struct sampleHeaderTyp_t
|
|
81 {
|
|
82 int32_t len, repS, repL;
|
|
83 uint8_t vol;
|
|
84 int8_t fine;
|
|
85 uint8_t typ, pan;
|
|
86 int8_t relTon;
|
|
87 uint8_t skrap;
|
|
88 char name[22];
|
|
89 }
|
|
90 #ifdef __GNUC__
|
|
91 __attribute__ ((packed))
|
|
92 #endif
|
|
93 sampleHeaderTyp;
|
|
94
|
|
95 typedef struct instrHeaderTyp_t
|
|
96 {
|
|
97 int32_t instrSize;
|
|
98 char name[22];
|
|
99 uint8_t typ;
|
|
100 uint16_t antSamp;
|
|
101 int32_t sampleSize;
|
|
102 uint8_t ta[96];
|
|
103 int16_t envVP[12][2], envPP[12][2];
|
|
104 uint8_t envVPAnt, envPPAnt, envVSust, envVRepS, envVRepE, envPSust, envPRepS;
|
|
105 uint8_t envPRepE, envVTyp, envPTyp, vibTyp, vibSweep, vibDepth, vibRate;
|
|
106 uint16_t fadeOut;
|
|
107 uint8_t midiOn, midiChannel;
|
|
108 int16_t midiProgram, midiBend;
|
|
109 int8_t mute;
|
|
110 uint8_t reserved[15];
|
|
111 sampleHeaderTyp samp[32];
|
|
112 }
|
|
113 #ifdef __GNUC__
|
|
114 __attribute__ ((packed))
|
|
115 #endif
|
|
116 instrHeaderTyp;
|
|
117
|
|
118 typedef struct patternHeaderTyp_t
|
|
119 {
|
|
120 int32_t patternHeaderSize;
|
|
121 uint8_t typ;
|
|
122 uint16_t pattLen, dataLen;
|
|
123 }
|
|
124 #ifdef __GNUC__
|
|
125 __attribute__ ((packed))
|
|
126 #endif
|
|
127 patternHeaderTyp;
|
|
128 #ifdef _MSC_VER
|
|
129 #pragma pack(pop)
|
|
130 #endif
|
|
131
|
|
132 static int32_t soundBufferSize;
|
|
133
|
|
134 // globalized
|
|
135 volatile bool interpolationFlag, volumeRampingFlag, moduleLoaded, musicPaused, WAVDump_Flag;
|
|
136 bool linearFrqTab;
|
|
137 volatile const uint16_t *note2Period;
|
|
138 uint16_t pattLens[256];
|
|
139 int16_t PMPTmpActiveChannel, boostLevel = DEFAULT_AMP;
|
|
140 int32_t masterVol = DEFAULT_MASTER_VOL, PMPLeft = 0;
|
|
141 int32_t realReplayRate, quickVolSizeVal, speedVal;
|
|
142 uint32_t frequenceDivFactor, frequenceMulFactor, CDA_Amp = 8*DEFAULT_AMP;
|
|
143 tonTyp *patt[256];
|
|
144 instrTyp *instr[1+128];
|
|
145 songTyp song;
|
|
146 stmTyp stm[32];
|
|
147 // ------------------
|
|
148
|
|
149 // 8bb: added these for loader
|
|
150 typedef struct
|
|
151 {
|
|
152 uint8_t *_ptr, *_base;
|
|
153 bool _eof;
|
|
154 size_t _cnt, _bufsiz;
|
|
155 } MEMFILE;
|
|
156
|
|
157 static MEMFILE *mopen(const uint8_t *src, uint32_t length);
|
|
158 static void mclose(MEMFILE **buf);
|
|
159 static size_t mread(void *buffer, size_t size, size_t count, MEMFILE *buf);
|
|
160 static bool meof(MEMFILE *buf);
|
|
161 static void mseek(MEMFILE *buf, int32_t offset, int32_t whence);
|
|
162 static void mrewind(MEMFILE *buf);
|
|
163 // --------------------------
|
|
164
|
|
165 static void resetMusic(void);
|
|
166 static void freeAllPatterns(void);
|
|
167 static void setFrqTab(bool linear);
|
|
168
|
|
169 static CIType *getVoice(int32_t ch) // 8bb: added this
|
|
170 {
|
|
171 if (ch < 0 || ch > 31)
|
|
172 return NULL;
|
|
173
|
|
174 return &CI[chnReloc[ch]];
|
|
175 }
|
|
176
|
|
177 /***************************************************************************
|
|
178 * ROUTINES FOR SAMPLE HANDLING ETC. *
|
|
179 ***************************************************************************/
|
|
180
|
|
181 // 8bb: modifies wrapped sample after loop/end (for branchless mixer interpolation)
|
|
182 static void fixSample(sampleTyp *s)
|
|
183 {
|
|
184 if (s->pek == NULL)
|
|
185 return; // empty sample
|
|
186
|
|
187 const bool sample16Bit = !!(s->typ & SAMPLE_16BIT);
|
|
188 uint8_t loopType = s->typ & 3;
|
|
189 int16_t *ptr16 = (int16_t *)s->pek;
|
|
190 int32_t len = s->len;
|
|
191 int32_t loopStart = s->repS;
|
|
192 int32_t loopEnd = s->repS + s->repL;
|
|
193
|
|
194 if (sample16Bit)
|
|
195 {
|
|
196 len >>= 1;
|
|
197 loopStart >>= 1;
|
|
198 loopEnd >>= 1;
|
|
199 }
|
|
200
|
|
201 if (len < 1)
|
|
202 return;
|
|
203
|
|
204 /* 8bb:
|
|
205 ** This is the exact bit test order of which FT2 handles
|
|
206 ** the sample tap fix.
|
|
207 **
|
|
208 ** This order is important for rare cases where both the
|
|
209 ** "forward" and "pingpong" loop bits are set at once.
|
|
210 **
|
|
211 ** This means that if both flags are set, the mixer will
|
|
212 ** play the sample with pingpong looping, but the sample fix
|
|
213 ** is handled as if it was a forward loop. This results in
|
|
214 ** the wrong interpolation tap sample being written after the
|
|
215 ** loop end point.
|
|
216 */
|
|
217
|
|
218 if (loopType & LOOP_FORWARD)
|
|
219 {
|
|
220 if (sample16Bit)
|
|
221 ptr16[loopEnd] = ptr16[loopStart];
|
|
222 else
|
|
223 s->pek[loopEnd] = s->pek[loopStart];
|
|
224
|
|
225 return;
|
|
226 }
|
|
227 else if (loopType & LOOP_PINGPONG)
|
|
228 {
|
|
229 if (sample16Bit)
|
|
230 ptr16[loopEnd] = ptr16[loopEnd-1];
|
|
231 else
|
|
232 s->pek[loopEnd] = s->pek[loopEnd-1];
|
|
233 }
|
|
234 else // no loop
|
|
235 {
|
|
236 if (sample16Bit)
|
|
237 ptr16[len] = 0;
|
|
238 else
|
|
239 s->pek[len] = 0;
|
|
240 }
|
|
241 }
|
|
242
|
|
243 static void checkSampleRepeat(int32_t nr, int32_t nr2)
|
|
244 {
|
|
245 instrTyp *i = instr[nr];
|
|
246 if (i == NULL)
|
|
247 return;
|
|
248
|
|
249 sampleTyp *s = &i->samp[nr2];
|
|
250
|
|
251 if (s->repS < 0) s->repS = 0;
|
|
252 if (s->repL < 0) s->repL = 0;
|
|
253 if (s->repS > s->len) s->repS = s->len;
|
|
254 if (s->repS+s->repL > s->len) s->repL = s->len - s->repS;
|
|
255 }
|
|
256
|
|
257 static void upDateInstrs(void)
|
|
258 {
|
|
259 for (int32_t i = 0; i <= 128; i++)
|
|
260 {
|
|
261 instrTyp *ins = instr[i];
|
|
262 if (ins == NULL)
|
|
263 continue;
|
|
264
|
|
265 sampleTyp *s = ins->samp;
|
|
266 for (int32_t j = 0; j < 16; j++, s++)
|
|
267 {
|
|
268 checkSampleRepeat(i, j);
|
|
269 fixSample(s);
|
|
270
|
|
271 if (s->pek == NULL)
|
|
272 {
|
|
273 s->len = 0;
|
|
274 s->repS = 0;
|
|
275 s->repL = 0;
|
|
276 }
|
|
277 }
|
|
278 }
|
|
279 }
|
|
280
|
|
281 static bool patternEmpty(uint16_t nr)
|
|
282 {
|
|
283 if (patt[nr] == NULL)
|
|
284 return true;
|
|
285
|
|
286 const uint8_t *scanPtr = (const uint8_t *)patt[nr];
|
|
287 const int32_t scanLen = pattLens[nr] * song.antChn * sizeof (tonTyp);
|
|
288
|
|
289 for (int32_t i = 0; i < scanLen; i++)
|
|
290 {
|
|
291 if (scanPtr[i] != 0)
|
|
292 return false;
|
|
293 }
|
|
294
|
|
295 return true;
|
|
296 }
|
|
297
|
|
298 static bool allocateInstr(uint16_t i)
|
|
299 {
|
|
300 if (instr[i] != NULL)
|
|
301 return true;
|
|
302
|
|
303 instrTyp *p = (instrTyp *)calloc(1, sizeof (instrTyp));
|
|
304 if (p == NULL)
|
|
305 return false;
|
|
306
|
|
307 sampleTyp *s = p->samp;
|
|
308 for (int32_t j = 0; j < 16; j++, s++)
|
|
309 {
|
|
310 s->pan = 128;
|
|
311 s->vol = 64;
|
|
312 }
|
|
313
|
|
314 instr[i] = p;
|
|
315 return true;
|
|
316 }
|
|
317
|
|
318 static void freeInstr(uint16_t nr)
|
|
319 {
|
|
320 if (nr > 128)
|
|
321 return;
|
|
322
|
|
323 instrTyp *ins = instr[nr];
|
|
324 if (ins == NULL)
|
|
325 return;
|
|
326
|
|
327 sampleTyp *s = ins->samp;
|
|
328 for (uint8_t i = 0; i < 16; i++, s++)
|
|
329 {
|
|
330 if (s->pek != NULL)
|
|
331 free(s->pek);
|
|
332 }
|
|
333
|
|
334 free(ins);
|
|
335 instr[nr] = NULL;
|
|
336 }
|
|
337
|
|
338 static void freeAllInstr(void)
|
|
339 {
|
|
340 for (uint16_t i = 0; i <= 128; i++)
|
|
341 freeInstr(i);
|
|
342 }
|
|
343
|
|
344 static void freeAllPatterns(void) // 8bb: added this one, since it's handy
|
|
345 {
|
|
346 for (int32_t i = 0; i < 256; i++)
|
|
347 {
|
|
348 if (patt[i] != NULL)
|
|
349 {
|
|
350 free(patt[i]);
|
|
351 patt[i] = NULL;
|
|
352 }
|
|
353
|
|
354 pattLens[i] = 64;
|
|
355 }
|
|
356 }
|
|
357
|
|
358 static void delta2Samp(int8_t *p, uint32_t len, bool sample16Bit)
|
|
359 {
|
|
360 if (sample16Bit)
|
|
361 {
|
|
362 len >>= 1;
|
|
363
|
|
364 int16_t *p16 = (int16_t *)p;
|
|
365
|
|
366 int16_t olds16 = 0;
|
|
367 for (uint32_t i = 0; i < len; i++)
|
|
368 {
|
|
369 const int16_t news16 = p16[i] + olds16;
|
|
370 p16[i] = news16;
|
|
371 olds16 = news16;
|
|
372 }
|
|
373 }
|
|
374 else
|
|
375 {
|
|
376 int8_t *p8 = (int8_t *)p;
|
|
377
|
|
378 int8_t olds8 = 0;
|
|
379 for (uint32_t i = 0; i < len; i++)
|
|
380 {
|
|
381 const int8_t news8 = p8[i] + olds8;
|
|
382 p8[i] = news8;
|
|
383 olds8 = news8;
|
|
384 }
|
|
385 }
|
|
386 }
|
|
387
|
|
388 static void unpackPatt(uint8_t *dst, uint16_t inn, uint16_t len, uint8_t antChn)
|
|
389 {
|
|
390 if (dst == NULL)
|
|
391 return;
|
|
392
|
|
393 const uint8_t *src = dst + inn;
|
|
394 const int32_t srcEnd = len * (sizeof (tonTyp) * antChn);
|
|
395
|
|
396 int32_t srcIdx = 0;
|
|
397 for (int32_t i = 0; i < len; i++)
|
|
398 {
|
|
399 for (int32_t j = 0; j < antChn; j++)
|
|
400 {
|
|
401 if (srcIdx >= srcEnd)
|
|
402 return; // error!
|
|
403
|
|
404 const uint8_t note = *src++;
|
|
405 if (note & 0x80)
|
|
406 {
|
|
407 *dst++ = (note & 0x01) ? *src++ : 0;
|
|
408 *dst++ = (note & 0x02) ? *src++ : 0;
|
|
409 *dst++ = (note & 0x04) ? *src++ : 0;
|
|
410 *dst++ = (note & 0x08) ? *src++ : 0;
|
|
411 *dst++ = (note & 0x10) ? *src++ : 0;
|
|
412 }
|
|
413 else
|
|
414 {
|
|
415 *dst++ = note;
|
|
416 *dst++ = *src++;
|
|
417 *dst++ = *src++;
|
|
418 *dst++ = *src++;
|
|
419 *dst++ = *src++;
|
|
420 }
|
|
421
|
|
422 // 8bb: added this. If note >97, remove it (prevents out-of-range read in note->sample LUT)
|
|
423 if (*(dst-5) > 97)
|
|
424 *(dst-5) = 0;
|
|
425
|
|
426 srcIdx += sizeof (tonTyp);
|
|
427 }
|
|
428 }
|
|
429 }
|
|
430
|
|
431 void freeMusic(void)
|
|
432 {
|
|
433 stopMusic();
|
|
434 freeAllInstr();
|
|
435 freeAllPatterns();
|
|
436
|
|
437 song.tempo = 6;
|
|
438 song.speed = 125;
|
|
439 song.timer = 1;
|
|
440
|
|
441 setFrqTab(true);
|
|
442 resetMusic();
|
|
443 }
|
|
444
|
|
445 void stopVoices(void)
|
|
446 {
|
|
447 lockMixer();
|
|
448
|
|
449 stmTyp *ch = stm;
|
|
450 for (uint8_t i = 0; i < 32; i++, ch++)
|
|
451 {
|
|
452 ch->tonTyp = 0;
|
|
453 ch->relTonNr = 0;
|
|
454 ch->instrNr = 0;
|
|
455 ch->instrSeg = instr[0]; // 8bb: placeholder instrument
|
|
456 ch->status = IS_Vol;
|
|
457
|
|
458 ch->realVol = 0;
|
|
459 ch->outVol = 0;
|
|
460 ch->oldVol = 0;
|
|
461 ch->finalVol = 0;
|
|
462 ch->oldPan = 128;
|
|
463 ch->outPan = 128;
|
|
464 ch->finalPan = 128;
|
|
465 ch->vibDepth = 0;
|
|
466 }
|
|
467
|
|
468 unlockMixer();
|
|
469 }
|
|
470
|
|
471 static void resetMusic(void)
|
|
472 {
|
|
473 song.timer = 1;
|
|
474 stopVoices();
|
|
475 setPos(0, 0);
|
|
476 }
|
|
477
|
|
478 void setPos(int32_t pos, int32_t row) // -1 = don't change
|
|
479 {
|
|
480 if (pos != -1)
|
|
481 {
|
|
482 song.songPos = (int16_t)pos;
|
|
483 if (song.len > 0 && song.songPos >= song.len)
|
|
484 song.songPos = song.len - 1;
|
|
485
|
|
486 song.pattNr = song.songTab[song.songPos];
|
|
487 song.pattLen = pattLens[song.pattNr];
|
|
488 }
|
|
489
|
|
490 if (row != -1)
|
|
491 {
|
|
492 song.pattPos = (int16_t)row;
|
|
493 if (song.pattPos >= song.pattLen)
|
|
494 song.pattPos = song.pattLen - 1;
|
|
495 }
|
|
496
|
|
497 song.timer = 1;
|
|
498 }
|
|
499
|
|
500 /***************************************************************************
|
|
501 * MODULE LOADING ROUTINES *
|
|
502 ***************************************************************************/
|
|
503
|
|
504 static bool loadInstrHeader(MEMFILE *f, uint16_t i)
|
|
505 {
|
|
506 instrHeaderTyp ih;
|
|
507
|
|
508 memset(&ih, 0, INSTR_HEADER_SIZE);
|
|
509 mread(&ih.instrSize, 4, 1, f);
|
|
510 if (ih.instrSize > INSTR_HEADER_SIZE) ih.instrSize = INSTR_HEADER_SIZE;
|
|
511
|
|
512 if (ih.instrSize < 4) // 8bb: added protection
|
|
513 return false;
|
|
514
|
|
515 mread(ih.name, ih.instrSize-4, 1, f);
|
|
516
|
|
517 if (ih.antSamp > 16)
|
|
518 return false;
|
|
519
|
|
520 if (ih.antSamp > 0)
|
|
521 {
|
|
522 if (!allocateInstr(i))
|
|
523 return false;
|
|
524
|
|
525 instrTyp *ins = instr[i];
|
|
526
|
|
527 memcpy(ins->name, ih.name, 22);
|
|
528 ins->name[22] = '\0';
|
|
529
|
|
530 // 8bb: copy instrument header elements to our instrument struct
|
|
531 memcpy(ins->ta, ih.ta, 96);
|
|
532 memcpy(ins->envVP, ih.envVP, 12*2*sizeof(int16_t));
|
|
533 memcpy(ins->envPP, ih.envPP, 12*2*sizeof(int16_t));
|
|
534 ins->envVPAnt = ih.envVPAnt;
|
|
535 ins->envPPAnt = ih.envPPAnt;
|
|
536 ins->envVSust = ih.envVSust;
|
|
537 ins->envVRepS = ih.envVRepS;
|
|
538 ins->envVRepE = ih.envVRepE;
|
|
539 ins->envPSust = ih.envPSust;
|
|
540 ins->envPRepS = ih.envPRepS;
|
|
541 ins->envPRepE = ih.envPRepE;
|
|
542 ins->envVTyp = ih.envVTyp;
|
|
543 ins->envPTyp = ih.envPTyp;
|
|
544 ins->vibTyp = ih.vibTyp;
|
|
545 ins->vibSweep = ih.vibSweep;
|
|
546 ins->vibDepth = ih.vibDepth;
|
|
547 ins->vibRate = ih.vibRate;
|
|
548 ins->fadeOut = ih.fadeOut;
|
|
549 ins->mute = (ih.mute == 1) ? true : false; // 8bb: correct logic!
|
|
550 ins->antSamp = ih.antSamp;
|
|
551
|
|
552 if (mread(ih.samp, ih.antSamp * sizeof (sampleHeaderTyp), 1, f) != 1)
|
|
553 return false;
|
|
554
|
|
555 sampleTyp *s = instr[i]->samp;
|
|
556 sampleHeaderTyp *src = ih.samp;
|
|
557 for (int32_t j = 0; j < ih.antSamp; j++, s++, src++)
|
|
558 {
|
|
559 memcpy(s->name, src->name, 22);
|
|
560 s->name[22] = '\0';
|
|
561
|
|
562 s->len = src->len;
|
|
563 s->repS = src->repS;
|
|
564 s->repL = src->repL;
|
|
565 s->vol = src->vol;
|
|
566 s->fine = src->fine;
|
|
567 s->typ = src->typ;
|
|
568 s->pan = src->pan;
|
|
569 s->relTon = src->relTon;
|
|
570 }
|
|
571 }
|
|
572
|
|
573 return true;
|
|
574 }
|
|
575
|
|
576 static bool loadInstrSample(MEMFILE *f, uint16_t i)
|
|
577 {
|
|
578 if (instr[i] == NULL)
|
|
579 return true; // empty instrument
|
|
580
|
|
581 sampleTyp *s = instr[i]->samp;
|
|
582 for (uint16_t j = 0; j < instr[i]->antSamp; j++, s++)
|
|
583 {
|
|
584 if (s->len > 0)
|
|
585 {
|
|
586 bool sample16Bit = !!(s->typ & SAMPLE_16BIT);
|
|
587
|
|
588 s->pek = (int8_t *)malloc(s->len+2); // 8bb: +2 for fixed interpolation tap sample
|
|
589 if (s->pek == NULL)
|
|
590 return false;
|
|
591
|
|
592 mread(s->pek, 1, s->len, f);
|
|
593 delta2Samp(s->pek, s->len, sample16Bit);
|
|
594 }
|
|
595
|
|
596 checkSampleRepeat(i, j);
|
|
597 }
|
|
598
|
|
599 return true;
|
|
600 }
|
|
601
|
|
602 static bool loadPatterns(MEMFILE *f, uint16_t antPtn)
|
|
603 {
|
|
604 uint8_t tmpLen;
|
|
605 patternHeaderTyp ph;
|
|
606
|
|
607 for (uint16_t i = 0; i < antPtn; i++)
|
|
608 {
|
|
609 mread(&ph.patternHeaderSize, 4, 1, f);
|
|
610 mread(&ph.typ, 1, 1, f);
|
|
611
|
|
612 ph.pattLen = 0;
|
|
613 if (song.ver == 0x0102)
|
|
614 {
|
|
615 mread(&tmpLen, 1, 1, f);
|
|
616 mread(&ph.dataLen, 2, 1, f);
|
|
617 ph.pattLen = (uint16_t)tmpLen + 1; // 8bb: +1 in v1.02
|
|
618
|
|
619 if (ph.patternHeaderSize > 8)
|
|
620 mseek(f, ph.patternHeaderSize - 8, SEEK_CUR);
|
|
621 }
|
|
622 else
|
|
623 {
|
|
624 mread(&ph.pattLen, 2, 1, f);
|
|
625 mread(&ph.dataLen, 2, 1, f);
|
|
626
|
|
627 if (ph.patternHeaderSize > 9)
|
|
628 mseek(f, ph.patternHeaderSize - 9, SEEK_CUR);
|
|
629 }
|
|
630
|
|
631 if (meof(f))
|
|
632 {
|
|
633 mclose(&f);
|
|
634 return false;
|
|
635 }
|
|
636
|
|
637 pattLens[i] = ph.pattLen;
|
|
638 if (ph.dataLen)
|
|
639 {
|
|
640 const uint16_t a = ph.pattLen * song.antChn * sizeof (tonTyp);
|
|
641
|
|
642 patt[i] = (tonTyp *)malloc(a);
|
|
643 if (patt[i] == NULL)
|
|
644 return false;
|
|
645
|
|
646 uint8_t *pattPtr = (uint8_t *)patt[i];
|
|
647
|
|
648 memset(pattPtr, 0, a);
|
|
649 mread(&pattPtr[a - ph.dataLen], 1, ph.dataLen, f);
|
|
650 unpackPatt(pattPtr, a - ph.dataLen, ph.pattLen, song.antChn);
|
|
651 }
|
|
652
|
|
653 if (patternEmpty(i))
|
|
654 {
|
|
655 if (patt[i] != NULL)
|
|
656 {
|
|
657 free(patt[i]);
|
|
658 patt[i] = NULL;
|
|
659 }
|
|
660
|
|
661 pattLens[i] = 64;
|
|
662 }
|
|
663 }
|
|
664
|
|
665 return true;
|
|
666 }
|
|
667
|
|
668 static bool loadMusicMOD(MEMFILE *f)
|
|
669 {
|
|
670 uint8_t ha[sizeof (songMOD31HeaderTyp)];
|
|
671 songMOD31HeaderTyp *h_MOD31 = (songMOD31HeaderTyp *)ha;
|
|
672 songMOD15HeaderTyp *h_MOD15 = (songMOD15HeaderTyp *)ha;
|
|
673
|
|
674 mread(ha, sizeof (ha), 1, f);
|
|
675 if (meof(f))
|
|
676 goto loadError2;
|
|
677
|
|
678 memcpy(song.name, h_MOD31->name, 20);
|
|
679 song.name[20] = '\0';
|
|
680
|
|
681 uint8_t j = 0;
|
|
682 for (uint8_t i = 1; i <= 16; i++)
|
|
683 {
|
|
684 if (memcmp(h_MOD31->Sig, MODSig[i-1], 4) == 0)
|
|
685 j = i + i;
|
|
686 }
|
|
687
|
|
688 if (memcmp(h_MOD31->Sig, "M!K!", 4) == 0 || memcmp(h_MOD31->Sig, "FLT4", 4) == 0)
|
|
689 j = 4;
|
|
690
|
|
691 if (memcmp(h_MOD31->Sig, "OCTA", 4) == 0)
|
|
692 j = 8;
|
|
693
|
|
694 uint8_t typ;
|
|
695 if (j > 0)
|
|
696 {
|
|
697 typ = 1;
|
|
698 song.antChn = j;
|
|
699 }
|
|
700 else
|
|
701 {
|
|
702 typ = 2;
|
|
703 song.antChn = 4;
|
|
704 }
|
|
705
|
|
706 int16_t ai;
|
|
707 if (typ == 1)
|
|
708 {
|
|
709 mseek(f, sizeof (songMOD31HeaderTyp), SEEK_SET);
|
|
710 song.len = h_MOD31->len;
|
|
711 song.repS = h_MOD31->repS;
|
|
712 memcpy(song.songTab, h_MOD31->songTab, 128);
|
|
713 ai = 31;
|
|
714 }
|
|
715 else
|
|
716 {
|
|
717 mseek(f, sizeof (songMOD15HeaderTyp), SEEK_SET);
|
|
718 song.len = h_MOD15->len;
|
|
719 song.repS = h_MOD15->repS;
|
|
720 memcpy(song.songTab, h_MOD15->songTab, 128);
|
|
721 ai = 15;
|
|
722 }
|
|
723
|
|
724 song.antInstrs = ai; // 8bb: added this
|
|
725
|
|
726 if (meof(f))
|
|
727 goto loadError2;
|
|
728
|
|
729 int32_t b = 0;
|
|
730 for (int32_t a = 0; a < 128; a++)
|
|
731 {
|
|
732 if (song.songTab[a] > b)
|
|
733 b = song.songTab[a];
|
|
734 }
|
|
735
|
|
736 uint8_t pattBuf[32 * 4 * 64]; // 8bb: max pattern size (32 channels, 64 rows)
|
|
737 for (uint16_t a = 0; a <= b; a++)
|
|
738 {
|
|
739 patt[a] = (tonTyp *)calloc(song.antChn * 64, sizeof (tonTyp));
|
|
740 if (patt[a] == NULL)
|
|
741 goto loadError;
|
|
742
|
|
743 pattLens[a] = 64;
|
|
744
|
|
745 mread(pattBuf, 1, song.antChn * 4 * 64, f);
|
|
746 if (meof(f))
|
|
747 goto loadError;
|
|
748
|
|
749 // convert pattern
|
|
750 uint8_t *bytes = pattBuf;
|
|
751 tonTyp *ton = patt[a];
|
|
752 for (int32_t i = 0; i < 64 * song.antChn; i++, bytes += 4, ton++)
|
|
753 {
|
|
754 const uint16_t period = ((bytes[0] & 0x0F) << 8) | bytes[1];
|
|
755 for (uint8_t k = 0; k < 96; k++)
|
|
756 {
|
|
757 if (period >= amigaPeriod[k])
|
|
758 {
|
|
759 ton->ton = k+1;
|
|
760 break;
|
|
761 }
|
|
762 }
|
|
763
|
|
764 ton->instr = (bytes[0] & 0xF0) | (bytes[2] >> 4);
|
|
765 ton->effTyp = bytes[2] & 0x0F;
|
|
766 ton->eff = bytes[3];
|
|
767
|
|
768 switch (ton->effTyp)
|
|
769 {
|
|
770 case 0xC:
|
|
771 {
|
|
772 if (ton->eff > 64)
|
|
773 ton->eff = 64;
|
|
774 }
|
|
775 break;
|
|
776
|
|
777 case 0x1:
|
|
778 case 0x2:
|
|
779 {
|
|
780 if (ton->eff == 0)
|
|
781 ton->effTyp = 0;
|
|
782 }
|
|
783 break;
|
|
784
|
|
785 case 0x5:
|
|
786 {
|
|
787 if (ton->eff == 0)
|
|
788 ton->effTyp = 3;
|
|
789 }
|
|
790 break;
|
|
791
|
|
792 case 0x6:
|
|
793 {
|
|
794 if (ton->eff == 0)
|
|
795 ton->effTyp = 4;
|
|
796 }
|
|
797 break;
|
|
798
|
|
799 case 0xA:
|
|
800 {
|
|
801 if (ton->eff == 0)
|
|
802 ton->effTyp = 0;
|
|
803 }
|
|
804 break;
|
|
805
|
|
806 case 0xE:
|
|
807 {
|
|
808 const uint8_t effTyp = ton->effTyp >> 4;
|
|
809 const uint8_t eff = ton->effTyp & 15;
|
|
810
|
|
811 if (eff == 0 && (effTyp == 0x1 || effTyp == 0x2 || effTyp == 0xA || effTyp == 0xB))
|
|
812 {
|
|
813 ton->eff = 0;
|
|
814 ton->effTyp = 0;
|
|
815 }
|
|
816 }
|
|
817 break;
|
|
818
|
|
819 default: break;
|
|
820 }
|
|
821 }
|
|
822
|
|
823 if (patternEmpty(a))
|
|
824 {
|
|
825 free(patt[a]);
|
|
826 patt[a] = NULL;
|
|
827 pattLens[a] = 64;
|
|
828 }
|
|
829 }
|
|
830
|
|
831 for (uint16_t a = 1; a <= ai; a++)
|
|
832 {
|
|
833 modSampleTyp *modSmp = &h_MOD31->sample[a-1];
|
|
834
|
|
835 uint32_t len = 2 * SWAP16(modSmp->len);
|
|
836 if (len == 0)
|
|
837 continue;
|
|
838
|
|
839 if (!allocateInstr(a))
|
|
840 goto loadError;
|
|
841
|
|
842 sampleTyp *xmSmp = &instr[a]->samp[0];
|
|
843
|
|
844 memcpy(xmSmp->name, modSmp->name, 22);
|
|
845 xmSmp->name[22] = '\0';
|
|
846
|
|
847 uint32_t repS = 2 * SWAP16(modSmp->repS);
|
|
848 uint32_t repL = 2 * SWAP16(modSmp->repL);
|
|
849
|
|
850 if (repL <= 2)
|
|
851 {
|
|
852 repS = 0;
|
|
853 repL = 0;
|
|
854 }
|
|
855
|
|
856 if (repS+repL > len)
|
|
857 {
|
|
858 if (repS >= len)
|
|
859 {
|
|
860 repS = 0;
|
|
861 repL = 0;
|
|
862 }
|
|
863 else
|
|
864 {
|
|
865 repL = len-repS;
|
|
866 }
|
|
867 }
|
|
868
|
|
869 xmSmp->typ = (repL > 2) ? 1 : 0;
|
|
870 xmSmp->len = len;
|
|
871 xmSmp->vol = (modSmp->vol <= 64) ? modSmp->vol : 64;
|
|
872 xmSmp->fine = 8 * ((2 * ((modSmp->fine & 15) ^ 8)) - 16);
|
|
873 xmSmp->repL = repL;
|
|
874 xmSmp->repS = repS;
|
|
875
|
|
876 xmSmp->pek = (int8_t *)malloc(len + 2);
|
|
877 if (xmSmp->pek == NULL)
|
|
878 goto loadError;
|
|
879
|
|
880 mread(xmSmp->pek, 1, len, f);
|
|
881 }
|
|
882
|
|
883 mclose(&f);
|
|
884
|
|
885 if (song.repS > song.len)
|
|
886 song.repS = 0;
|
|
887
|
|
888 resetMusic();
|
|
889 upDateInstrs();
|
|
890
|
|
891 moduleLoaded = true;
|
|
892 return true;
|
|
893 loadError:
|
|
894 freeAllInstr();
|
|
895 freeAllPatterns();
|
|
896 loadError2:
|
|
897 mclose(&f);
|
|
898 return false;
|
|
899 }
|
|
900
|
|
901 bool loadMusicFromData(const uint8_t *data, uint32_t dataLength) // .XM/.MOD/.FT
|
|
902 {
|
|
903 uint16_t i;
|
|
904 songHeaderTyp h;
|
|
905
|
|
906 freeMusic();
|
|
907 setFrqTab(false);
|
|
908
|
|
909 moduleLoaded = false;
|
|
910
|
|
911 MEMFILE *f = mopen(data, dataLength);
|
|
912 if (f == NULL)
|
|
913 return false;
|
|
914
|
|
915 // 8bb: instr 0 is a placeholder for empty instruments
|
|
916 allocateInstr(0);
|
|
917 instr[0]->samp[0].vol = 0;
|
|
918
|
|
919 mread(&h, sizeof (h), 1, f);
|
|
920 if (meof(f))
|
|
921 goto loadError2;
|
|
922
|
|
923 if (memcmp(h.sig, "Extended Module: ", 17) != 0)
|
|
924 {
|
|
925 mrewind(f);
|
|
926 return loadMusicMOD(f);
|
|
927 }
|
|
928
|
|
929 if (h.ver < 0x0102 || h.ver > 0x104 || h.antChn < 2 || h.antChn > 32 || (h.antChn & 1) != 0 ||
|
|
930 h.antPtn > 256 || h.antInstrs > 128)
|
|
931 {
|
|
932 goto loadError2;
|
|
933 }
|
|
934
|
|
935 mseek(f, 60+h.headerSize, SEEK_SET);
|
|
936 if (meof(f))
|
|
937 goto loadError2;
|
|
938
|
|
939 memcpy(song.name, h.name, 20);
|
|
940 song.name[20] = '\0';
|
|
941
|
|
942 song.len = h.len;
|
|
943 song.repS = h.repS;
|
|
944 song.antChn = (uint8_t)h.antChn;
|
|
945 bool linearFrequencies = !!(h.flags & LINEAR_FREQUENCIES);
|
|
946 setFrqTab(linearFrequencies);
|
|
947 memcpy(song.songTab, h.songTab, 256);
|
|
948
|
|
949 song.antInstrs = h.antInstrs; // 8bb: added this
|
|
950 if (h.defSpeed == 0) h.defSpeed = 125; // 8bb: (BPM) FT2 doesn't do this, but we do it for safety
|
|
951 song.speed = h.defSpeed;
|
|
952 song.tempo = h.defTempo;
|
|
953 song.ver = h.ver;
|
|
954
|
|
955 // 8bb: bugfixes...
|
|
956 if (song.speed < 1) song.speed = 1;
|
|
957 if (song.tempo < 1) song.tempo = 1;
|
|
958 // ----------------
|
|
959
|
|
960 if (song.ver < 0x0104) // old FT2 XM format
|
|
961 {
|
|
962 for (i = 1; i <= h.antInstrs; i++)
|
|
963 {
|
|
964 if (!loadInstrHeader(f, i))
|
|
965 goto loadError;
|
|
966 }
|
|
967
|
|
968 if (!loadPatterns(f, h.antPtn))
|
|
969 goto loadError;
|
|
970
|
|
971 for (i = 1; i <= h.antInstrs; i++)
|
|
972 {
|
|
973 if (!loadInstrSample(f, i))
|
|
974 goto loadError;
|
|
975 }
|
|
976 }
|
|
977 else // latest FT2 XM format
|
|
978 {
|
|
979 if (!loadPatterns(f, h.antPtn))
|
|
980 goto loadError;
|
|
981
|
|
982 for (i = 1; i <= h.antInstrs; i++)
|
|
983 {
|
|
984 if (!loadInstrHeader(f, i))
|
|
985 goto loadError;
|
|
986
|
|
987 if (!loadInstrSample(f, i))
|
|
988 goto loadError;
|
|
989 }
|
|
990 }
|
|
991
|
|
992 mclose(&f);
|
|
993
|
|
994 if (song.repS > song.len)
|
|
995 song.repS = 0;
|
|
996
|
|
997 resetMusic();
|
|
998 upDateInstrs();
|
|
999
|
|
1000 moduleLoaded = true;
|
|
1001 return true;
|
|
1002
|
|
1003 loadError:
|
|
1004 freeAllInstr();
|
|
1005 freeAllPatterns();
|
|
1006 loadError2:
|
|
1007 mclose(&f);
|
|
1008 return false;
|
|
1009 }
|
|
1010
|
|
1011 bool loadMusic(const char *fileName) // .XM/.MOD/.FT
|
|
1012 {
|
|
1013 FILE *f = fopen(fileName, "rb");
|
|
1014 if (f == NULL)
|
|
1015 return false;
|
|
1016
|
|
1017 fseek(f, 0, SEEK_END);
|
|
1018 const uint32_t fileSize = (uint32_t)ftell(f);
|
|
1019 rewind(f);
|
|
1020
|
|
1021 uint8_t *fileBuffer = (uint8_t *)malloc(fileSize);
|
|
1022 if (fileBuffer == NULL)
|
|
1023 {
|
|
1024 fclose(f);
|
|
1025 return false;
|
|
1026 }
|
|
1027
|
|
1028 if (fread(fileBuffer, 1, fileSize, f) != fileSize)
|
|
1029 {
|
|
1030 free(fileBuffer);
|
|
1031 fclose(f);
|
|
1032 return false;
|
|
1033 }
|
|
1034
|
|
1035 fclose(f);
|
|
1036
|
|
1037 if (!loadMusicFromData((const uint8_t *)fileBuffer, fileSize))
|
|
1038 {
|
|
1039 free(fileBuffer);
|
|
1040 return false;
|
|
1041 }
|
|
1042
|
|
1043 free(fileBuffer);
|
|
1044 return true;
|
|
1045 }
|
|
1046
|
|
1047 /***************************************************************************
|
|
1048 * PROCESS HANDLING *
|
|
1049 ***************************************************************************/
|
|
1050
|
|
1051 bool startMusic(void)
|
|
1052 {
|
|
1053 if (!moduleLoaded || song.speed == 0)
|
|
1054 return false;
|
|
1055
|
|
1056 mix_ClearChannels();
|
|
1057 stopVoices();
|
|
1058 song.globVol = 64;
|
|
1059
|
|
1060 speedVal = ((realReplayRate * 5) / 2) / song.speed;
|
|
1061 quickVolSizeVal = realReplayRate / 200;
|
|
1062
|
|
1063 if (!mix_Init(soundBufferSize))
|
|
1064 return false;
|
|
1065
|
|
1066 if (openMixer(realReplayRate, soundBufferSize))
|
|
1067 {
|
|
1068 musicPaused = false;
|
|
1069 return true;
|
|
1070 }
|
|
1071
|
|
1072 return false;
|
|
1073 }
|
|
1074
|
|
1075 void stopMusic(void)
|
|
1076 {
|
|
1077 pauseMusic();
|
|
1078
|
|
1079 closeMixer();
|
|
1080 mix_Free();
|
|
1081 song.globVol = 64;
|
|
1082
|
|
1083 resumeMusic();
|
|
1084 }
|
|
1085
|
|
1086 void startPlaying(void)
|
|
1087 {
|
|
1088 stopMusic();
|
|
1089 song.pattDelTime = song.pattDelTime2 = 0; // 8bb: added these
|
|
1090 setPos(0, 0);
|
|
1091 startMusic();
|
|
1092 }
|
|
1093
|
|
1094 void stopPlaying(void)
|
|
1095 {
|
|
1096 stopMusic();
|
|
1097 stopVoices();
|
|
1098 }
|
|
1099
|
|
1100 void pauseMusic(void)
|
|
1101 {
|
|
1102 musicPaused = true;
|
|
1103 }
|
|
1104
|
|
1105 void resumeMusic(void)
|
|
1106 {
|
|
1107 musicPaused = false;
|
|
1108 }
|
|
1109
|
|
1110 // 8bb: added these three, handy
|
|
1111 void toggleMusic(void)
|
|
1112 {
|
|
1113 musicPaused ^= 1;
|
|
1114 }
|
|
1115
|
|
1116 void setInterpolation(bool on)
|
|
1117 {
|
|
1118 interpolationFlag = on;
|
|
1119 mix_ClearChannels();
|
|
1120 }
|
|
1121
|
|
1122 void setVolumeRamping(bool on)
|
|
1123 {
|
|
1124 volumeRampingFlag = on;
|
|
1125 mix_ClearChannels();
|
|
1126 }
|
|
1127
|
|
1128 /***************************************************************************
|
|
1129 * CONFIGURATION ROUTINES *
|
|
1130 ***************************************************************************/
|
|
1131
|
|
1132 void setMasterVol(int32_t v) // 0..256
|
|
1133 {
|
|
1134 masterVol = CLAMP(v, 0, 256);
|
|
1135
|
|
1136 stmTyp *ch = stm;
|
|
1137 for (int32_t i = 0; i < 32; i++, ch++)
|
|
1138 ch->status |= IS_Vol;
|
|
1139 }
|
|
1140
|
|
1141 void setAmp(int32_t level) // 1..32
|
|
1142 {
|
|
1143 boostLevel = (int16_t)CLAMP(level, 1, 32);
|
|
1144 CDA_Amp = boostLevel * 8;
|
|
1145 }
|
|
1146
|
|
1147 int32_t getMasterVol(void) // 8bb: added this
|
|
1148 {
|
|
1149 return masterVol;
|
|
1150 }
|
|
1151
|
|
1152 int32_t getAmp(void) // 8bb: added this
|
|
1153 {
|
|
1154 return boostLevel;
|
|
1155 }
|
|
1156
|
|
1157 uint8_t getNumActiveVoices(void) // 8bb: added this
|
|
1158 {
|
|
1159 uint8_t activeVoices = 0;
|
|
1160 for (int32_t i = 0; i < song.antChn; i++)
|
|
1161 {
|
|
1162 CIType *v = getVoice(i);
|
|
1163 if (!(v->SType & SType_Off) && v->SVol > 0)
|
|
1164 activeVoices++;
|
|
1165 }
|
|
1166
|
|
1167 return activeVoices;
|
|
1168 }
|
|
1169
|
|
1170 static void setFrqTab(bool linear)
|
|
1171 {
|
|
1172 linearFrqTab = linear;
|
|
1173 note2Period = linear ? linearPeriods : amigaPeriods;
|
|
1174 }
|
|
1175
|
|
1176 void updateReplayRate(void)
|
|
1177 {
|
|
1178 lockMixer();
|
|
1179
|
|
1180 // 8bb: bit-exact to FT2
|
|
1181 frequenceDivFactor = (uint32_t)round(65536.0*1712.0/realReplayRate*8363.0);
|
|
1182 frequenceMulFactor = (uint32_t)round(256.0*65536.0/realReplayRate*8363.0);
|
|
1183
|
|
1184 unlockMixer();
|
|
1185 }
|
|
1186
|
|
1187 /***************************************************************************
|
|
1188 * INITIALIZATION ROUTINES *
|
|
1189 ***************************************************************************/
|
|
1190
|
|
1191 bool initMusic(int32_t audioFrequency, int32_t audioBufferSize, bool interpolation, bool volumeRamping)
|
|
1192 {
|
|
1193 closeMixer();
|
|
1194 freeMusic();
|
|
1195 memset(stm, 0, sizeof (stm));
|
|
1196
|
|
1197 realReplayRate = CLAMP(audioFrequency, 8000, 96000);
|
|
1198 updateReplayRate();
|
|
1199
|
|
1200 soundBufferSize = audioBufferSize;
|
|
1201 interpolationFlag = interpolation;
|
|
1202 volumeRampingFlag = volumeRamping;
|
|
1203
|
|
1204 song.tempo = 6;
|
|
1205 song.speed = 125;
|
|
1206 setFrqTab(true);
|
|
1207 resetMusic();
|
|
1208
|
|
1209 return true;
|
|
1210 }
|
|
1211
|
|
1212 /***************************************************************************
|
|
1213 * WAV DUMPING ROUTINES *
|
|
1214 ***************************************************************************/
|
|
1215
|
|
1216 static void WAV_WriteHeader(FILE *f, int32_t frq)
|
|
1217 {
|
|
1218 uint16_t w;
|
|
1219 uint32_t l;
|
|
1220
|
|
1221 // 12 bytes
|
|
1222
|
|
1223 const uint32_t RIFF = 0x46464952;
|
|
1224 fwrite(&RIFF, 4, 1, f);
|
|
1225 fseek(f, 4, SEEK_CUR);
|
|
1226 const uint32_t WAVE = 0x45564157;
|
|
1227 fwrite(&WAVE, 4, 1, f);
|
|
1228
|
|
1229 // 24 bytes
|
|
1230
|
|
1231 const uint32_t fmt = 0x20746D66;
|
|
1232 fwrite(&fmt, 4, 1, f);
|
|
1233 l = 16; fwrite(&l, 4, 1, f);
|
|
1234 w = 1; fwrite(&w, 2, 1, f);
|
|
1235 w = 2; fwrite(&w, 2, 1, f);
|
|
1236 l = frq; fwrite(&l, 4, 1, f);
|
|
1237 l = frq*2*2; fwrite(&l, 4, 1, f);
|
|
1238 w = 2*2; fwrite(&w, 2, 1, f);
|
|
1239 w = 8*2; fwrite(&w, 2, 1, f);
|
|
1240
|
|
1241 // 8 bytes
|
|
1242
|
|
1243 const uint32_t DATA = 0x61746164;
|
|
1244 fwrite(&DATA, 4, 1, f);
|
|
1245 fseek(f, 4, SEEK_CUR);
|
|
1246 }
|
|
1247
|
|
1248 static void WAV_WriteEnd(FILE *f, uint32_t size)
|
|
1249 {
|
|
1250 fseek(f, 4, SEEK_SET);
|
|
1251 uint32_t l = size+4+24+8;
|
|
1252 fwrite(&l, 4, 1, f);
|
|
1253 fseek(f, 12+24+4, SEEK_SET);
|
|
1254 fwrite(&size, 4, 1, f);
|
|
1255 }
|
|
1256
|
|
1257 void WAVDump_Abort(void) // 8bb: added this
|
|
1258 {
|
|
1259 WAVDump_Flag = false;
|
|
1260 }
|
|
1261
|
|
1262 bool WAVDump_Record(const char *filenameOut)
|
|
1263 {
|
|
1264 FILE *fil = fopen(filenameOut, "wb");
|
|
1265 if (fil == NULL)
|
|
1266 {
|
|
1267 WAVDump_Flag = false;
|
|
1268 return false;
|
|
1269 }
|
|
1270
|
|
1271 const int32_t WDFrequency = realReplayRate;
|
|
1272 const int32_t WDAmp = boostLevel;
|
|
1273
|
|
1274 const uint32_t maxSamplesPerTick = (WDFrequency*5 / 2) / 1; // 8bb: added this (min. BPM = 1, through hex editing)
|
|
1275 int16_t *pBlock = (int16_t *)malloc(maxSamplesPerTick * (2 * sizeof (int16_t)));
|
|
1276 if (pBlock == NULL)
|
|
1277 {
|
|
1278 fclose(fil);
|
|
1279 WAVDump_Flag = false;
|
|
1280 return false;
|
|
1281 }
|
|
1282
|
|
1283 WAV_WriteHeader(fil, WDFrequency);
|
|
1284
|
|
1285 stopMusic();
|
|
1286 mix_Init(maxSamplesPerTick);
|
|
1287
|
|
1288 uint16_t WDStartPos = 0;
|
|
1289 uint16_t WDStopPos = song.len-1;
|
|
1290
|
|
1291 dump_Init(WDFrequency, WDAmp, WDStartPos);
|
|
1292
|
|
1293 uint32_t totSize = 0;
|
|
1294
|
|
1295 WAVDump_Flag = true;
|
|
1296 while (!dump_EndOfTune(WDStopPos))
|
|
1297 {
|
|
1298 if (!WAVDump_Flag) // extra check so that external threads can force-abort render
|
|
1299 break;
|
|
1300
|
|
1301 const uint32_t size = dump_GetFrame(pBlock);
|
|
1302 fwrite(pBlock, 1, size, fil);
|
|
1303 totSize += size;
|
|
1304 }
|
|
1305 WAVDump_Flag = false;
|
|
1306
|
|
1307 mix_Free();
|
|
1308
|
|
1309 WAV_WriteEnd(fil, totSize);
|
|
1310 dump_Close();
|
|
1311
|
|
1312 stopMusic();
|
|
1313 fclose(fil);
|
|
1314
|
|
1315 free(pBlock);
|
|
1316
|
|
1317 WAVDump_Flag = false;
|
|
1318 return true;
|
|
1319 }
|
|
1320
|
|
1321 /***************************************************************************
|
|
1322 * MEMORY READ ROUTINES (8bb: added these) *
|
|
1323 ***************************************************************************/
|
|
1324
|
|
1325 static MEMFILE *mopen(const uint8_t *src, uint32_t length)
|
|
1326 {
|
|
1327 if (src == NULL || length == 0)
|
|
1328 return NULL;
|
|
1329
|
|
1330 MEMFILE *b = (MEMFILE *)malloc(sizeof (MEMFILE));
|
|
1331 if (b == NULL)
|
|
1332 return NULL;
|
|
1333
|
|
1334 b->_base = (uint8_t *)src;
|
|
1335 b->_ptr = (uint8_t *)src;
|
|
1336 b->_cnt = length;
|
|
1337 b->_bufsiz = length;
|
|
1338 b->_eof = false;
|
|
1339
|
|
1340 return b;
|
|
1341 }
|
|
1342
|
|
1343 static void mclose(MEMFILE **buf)
|
|
1344 {
|
|
1345 if (*buf != NULL)
|
|
1346 {
|
|
1347 free(*buf);
|
|
1348 *buf = NULL;
|
|
1349 }
|
|
1350 }
|
|
1351
|
|
1352 static size_t mread(void *buffer, size_t size, size_t count, MEMFILE *buf)
|
|
1353 {
|
|
1354 if (buf == NULL || buf->_ptr == NULL)
|
|
1355 return 0;
|
|
1356
|
|
1357 size_t wrcnt = size * count;
|
|
1358 if (size == 0 || buf->_eof)
|
|
1359 return 0;
|
|
1360
|
|
1361 int32_t pcnt = (buf->_cnt > wrcnt) ? (int32_t)wrcnt : (int32_t)buf->_cnt;
|
|
1362 memcpy(buffer, buf->_ptr, pcnt);
|
|
1363
|
|
1364 buf->_cnt -= pcnt;
|
|
1365 buf->_ptr += pcnt;
|
|
1366
|
|
1367 if (buf->_cnt <= 0)
|
|
1368 {
|
|
1369 buf->_ptr = buf->_base + buf->_bufsiz;
|
|
1370 buf->_cnt = 0;
|
|
1371 buf->_eof = true;
|
|
1372 }
|
|
1373
|
|
1374 return pcnt / size;
|
|
1375 }
|
|
1376
|
|
1377 static bool meof(MEMFILE *buf)
|
|
1378 {
|
|
1379 if (buf == NULL)
|
|
1380 return true;
|
|
1381
|
|
1382 return buf->_eof;
|
|
1383 }
|
|
1384
|
|
1385 static void mseek(MEMFILE *buf, int32_t offset, int32_t whence)
|
|
1386 {
|
|
1387 if (buf == NULL)
|
|
1388 return;
|
|
1389
|
|
1390 if (buf->_base)
|
|
1391 {
|
|
1392 switch (whence)
|
|
1393 {
|
|
1394 case SEEK_SET: buf->_ptr = buf->_base + offset; break;
|
|
1395 case SEEK_CUR: buf->_ptr += offset; break;
|
|
1396 case SEEK_END: buf->_ptr = buf->_base + buf->_bufsiz + offset; break;
|
|
1397 default: break;
|
|
1398 }
|
|
1399
|
|
1400 buf->_eof = false;
|
|
1401 if (buf->_ptr >= buf->_base+buf->_bufsiz)
|
|
1402 {
|
|
1403 buf->_ptr = buf->_base + buf->_bufsiz;
|
|
1404 buf->_eof = true;
|
|
1405 }
|
|
1406
|
|
1407 buf->_cnt = (buf->_base + buf->_bufsiz) - buf->_ptr;
|
|
1408 }
|
|
1409 }
|
|
1410
|
|
1411 static void mrewind(MEMFILE *buf)
|
|
1412 {
|
|
1413 mseek(buf, 0, SEEK_SET);
|
|
1414 }
|