Mercurial > codedump
comparison win95kggui/dep/ft2play/pmplay.c @ 126:8e4ee43d3b81
remove submodules
| author | Paper <mrpapersonic@gmail.com> |
|---|---|
| date | Sun, 01 Oct 2023 03:48:43 -0400 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 125:5cc85ef3a675 | 126:8e4ee43d3b81 |
|---|---|
| 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 } |
