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