1 // FB Alpha sample player module
2 
3 #include "burnint.h"
4 #include "samples.h"
5 
6 #define SAMPLE_DIRECTORY	szAppSamplesPath
7 
8 #define get_long()	((ptr[3] << 24) | (ptr[2] << 16) | (ptr[1] << 8) | (ptr[0] << 0))
9 #define get_short()	((ptr[1] << 8) | (ptr[0] << 0))
10 
get_shorti(const UINT8 * const p)11 static inline UINT16 get_shorti(const UINT8* const p)
12 {
13 	return (p[1] << 8) | p[0];
14 }
15 
16 static INT32 bAddToStream = 0;
17 static INT32 nTotalSamples = 0;
18 INT32 bBurnSampleTrimSampleEnd = 0;
19 
20 struct sample_format
21 {
22 	UINT8 *data;
23 	UINT32 length;
24 	UINT64 position;
25 	UINT8 playing;
26 	UINT8 loop;
27 	UINT8 flags;
28 	INT32 playback_rate; // 100 = 100%, 200 = 200%,
29 	double gain[2];
30 	INT32 output_dir[2];
31 };
32 
33 static struct sample_format *samples		= NULL; // store samples
34 static struct sample_format *sample_ptr		= NULL; // generic pointer for sample
35 
make_raw(UINT8 * src,UINT32 len)36 static void make_raw(UINT8 *src, UINT32 len)
37 {
38 	UINT8 *ptr = src;
39 
40 	if (ptr[0] != 'R' || ptr[1] != 'I' || ptr[2] != 'F' || ptr[3] != 'F') return;
41 	                                    ptr += 4; // skip RIFF
42 	UINT32 length = get_long();		    ptr += 4; // total length of file
43 	if (len < length) length = len - 8;	    	  // first 8 bytes (RIFF + Len)
44 
45 	/* "WAVEfmt " */			        ptr += 8; // WAVEfmt + 1 space
46 	UINT32 length2 = get_long();		ptr += 4; // Wavefmt length
47 /*	UINT16 format = get_short();  */    ptr += 2; // format?
48 	UINT16 channels = get_short();		ptr += 2; // channels
49 	UINT32 sample_rate = get_long();	ptr += 4; // sample rate
50 /*	UINT32 speed = get_long();      */  ptr += 4; // speed - should equal (bits * channels * sample_rate)
51 /*	UINT16 align = get_short();   */    ptr += 2; // block align	should be ((bits / 8) * channels)
52 	UINT16 bits = get_short() / 8;		ptr += 2; // bits per sample	(0010)
53 	ptr += length2 - 16;				          // get past the wave format chunk
54 
55 	// are we in the 'data' chunk? if not, skip this chunk.
56 	if (ptr[0] != 'd' || ptr[1] != 'a' || ptr[2] != 't' || ptr[3] != 'a') {
57 		                                ptr += 4; // skip tag
58 		UINT32 length3 = get_long();    ptr += 4;
59 		ptr += length3;
60 	}
61 
62 	/* "data" */				        ptr += 4; // "data"
63 	UINT32 data_length = get_long();	ptr += 4; // should be up to the data...
64 
65 	if ((len - (ptr - src)) < data_length) data_length = len - (ptr - src);
66 
67 	UINT32 converted_len = (UINT32)((float)(data_length * (nBurnSoundRate * 1.00000 / sample_rate) / (bits * channels)));
68 	if (converted_len == 0) return;
69 
70 	sample_ptr->data = (UINT8*)BurnMalloc(converted_len * 4);
71 
72 	// up/down sample everything and convert to raw 16 bit stereo
73 	INT16 *data = (INT16*)sample_ptr->data;
74 	INT16 *poin = (INT16*)ptr;
75 	UINT8 *poib = ptr;
76 
77 	if ((INT32)sample_rate == nBurnSoundRate)
78 	{
79 		// don't try to interpolate, just copy
80 		bprintf(0, _T("Sample at native rate already..\n"));
81 		for (UINT32 i = 0; i < converted_len; i++)
82 		{
83 			if (bits == 2)											//  signed 16 bit, stereo & mono
84 			{
85 				data[i * 2 + 0] = poin[i * channels + 0             ];
86 				data[i * 2 + 1] = poin[i * channels + (channels / 2)];
87 			}
88 			else if (bits == 1)										// unsigned 8 bit, stereo & mono
89 			{
90 				data[i * 2 + 0] = (poib[i * channels + 0             ] - 128) << 8; data[i * 2 + 0] |= (data[i * 2 + 0] >> 7) & 0xFF;
91 				data[i * 2 + 1] = (poib[i * channels + (channels / 2)] - 128) << 8; data[i * 2 + 1] |= (data[i * 2 + 1] >> 7) & 0xFF;
92 			}
93 		}
94 	}
95 	else
96 	{
97 		// interpolate sample
98 		bprintf(0, _T("Converting %dhz [%d bit, %d channels] to %dhz (native).\n"), sample_rate, bits*8, channels, nBurnSoundRate);
99 		INT32 buffer_l[4];
100 		INT32 buffer_r[4];
101 
102 		memset(buffer_l, 0, sizeof(buffer_l));
103 		memset(buffer_r, 0, sizeof(buffer_r));
104 
105 		if (sample_ptr->flags & SAMPLE_AUTOLOOP)
106 		{
107 			UINT8* end = sample_ptr->data + data_length / (bits * channels);
108 
109 			if (bits == 1)
110 			{
111 				buffer_l[1] = (INT16)((*(end - 3 * channels)) - 0x80) << 8; buffer_l[1] |= (buffer_l[1] >> 7) & 0xFF;
112 				buffer_l[2] = (INT16)((*(end - 2 * channels)) - 0x80) << 8; buffer_l[2] |= (buffer_l[2] >> 7) & 0xFF;
113 				buffer_l[3] = (INT16)((*(end - 1 * channels)) - 0x80) << 8; buffer_l[3] |= (buffer_l[3] >> 7) & 0xFF;
114 
115 				buffer_r[1] = (INT16)((*(end - 3 * channels) + (channels / 2)) - 0x80) << 8; buffer_r[1] |= (buffer_r[1] >> 7) & 0xFF;
116 				buffer_r[2] = (INT16)((*(end - 2 * channels) + (channels / 2)) - 0x80) << 8; buffer_r[2] |= (buffer_r[2] >> 7) & 0xFF;
117 				buffer_r[3] = (INT16)((*(end - 1 * channels) + (channels / 2)) - 0x80) << 8; buffer_r[3] |= (buffer_r[3] >> 7) & 0xFF;
118 			}
119 			else
120 			{
121 				buffer_l[1] = (INT16)(get_shorti(end - 6 * channels));
122 				buffer_l[2] = (INT16)(get_shorti(end - 4 * channels));
123 				buffer_l[3] = (INT16)(get_shorti(end - 2 * channels));
124 
125 				buffer_r[1] = (INT16)(get_shorti(end - 6 * channels) + (channels & 2));
126 				buffer_r[2] = (INT16)(get_shorti(end - 4 * channels) + (channels & 2));
127 				buffer_r[3] = (INT16)(get_shorti(end - 2 * channels) + (channels & 2));
128 			}
129 		}
130 
131 		UINT64 prev_offs = ~0;
132 
133 		for (UINT64 i = 0; i < converted_len; i++)
134 		{
135 			UINT64 pos = (i * sample_rate << 12) / nBurnSoundRate;
136 			UINT64 curr_offs = pos >> 12;
137 
138 			while (prev_offs != curr_offs)
139 			{
140 				prev_offs += 1;
141 				buffer_l[0] = buffer_l[1]; buffer_r[0] = buffer_r[1];
142 				buffer_l[1] = buffer_l[2]; buffer_r[1] = buffer_r[2];
143 				buffer_l[2] = buffer_l[3]; buffer_r[2] = buffer_r[3];
144 
145 				if (bits == 2)										// signed 16 bit, stereo & mono
146 				{
147 					buffer_l[3] = (INT32)(poin[prev_offs * channels + 0             ]);
148 					buffer_r[3] = (INT32)(poin[prev_offs * channels + (channels / 2)]);
149 				}
150 				else if (bits == 1)									// unsigned 8 bit, stereo & mono
151 				{
152 					buffer_l[3] = (INT32)(poib[prev_offs * channels + 0             ] - 128) << 8; buffer_l[3] |= (buffer_l[3] >> 7) & 0xFF;
153 					buffer_r[3] = (INT32)(poib[prev_offs * channels + (channels / 2)] - 128) << 8; buffer_r[3] |= (buffer_r[3] >> 7) & 0xFF;
154 				}
155 			}
156 
157 			data[i * 2 + 0] = BURN_SND_CLIP(INTERPOLATE4PS_16BIT(pos & 0x0FFF, buffer_l[0], buffer_l[1], buffer_l[2], buffer_l[3]));
158 			data[i * 2 + 1] = BURN_SND_CLIP(INTERPOLATE4PS_16BIT(pos & 0x0FFF, buffer_r[0], buffer_r[1], buffer_r[2], buffer_r[3]));
159 		}
160 	}
161 
162 	{ // sample cleanup
163 		if (bBurnSampleTrimSampleEnd) { // trim silence off the end of the sample, bBurnSampleTrimSampleEnd must be set before init!
164 			while (data[converted_len * 2] == 0) converted_len -= 2;
165 		}
166 	}
167 
168 	sample_ptr->length = converted_len;
169 	sample_ptr->playing = 0;
170 	sample_ptr->position = 0;
171 }
172 
173 void BurnSampleInitOne(INT32); // below...
174 
BurnSamplePlay(INT32 sample)175 void BurnSamplePlay(INT32 sample)
176 {
177 #if defined FBA_DEBUG
178 	if (!DebugSnd_SamplesInitted) bprintf(PRINT_ERROR, _T("BurnSamplePlay called without init\n"));
179 #endif
180 
181 	if (sample >= nTotalSamples) return;
182 
183 	sample_ptr = &samples[sample];
184 
185 	if (sample_ptr->flags & SAMPLE_IGNORE) return;
186 
187 	if (sample_ptr->flags & SAMPLE_NOSTORE) {
188 		BurnSampleInitOne(sample);
189 	}
190 
191 	sample_ptr->playing = 1;
192 	sample_ptr->position = 0;
193 }
194 
BurnSamplePause(INT32 sample)195 void BurnSamplePause(INT32 sample)
196 {
197 #if defined FBA_DEBUG
198 	if (!DebugSnd_SamplesInitted) bprintf(PRINT_ERROR, _T("BurnSamplePause called without init\n"));
199 #endif
200 
201 	if (sample >= nTotalSamples) return;
202 
203 	sample_ptr = &samples[sample];
204 	sample_ptr->playing = 0;
205 }
206 
BurnSampleResume(INT32 sample)207 void BurnSampleResume(INT32 sample)
208 {
209 #if defined FBA_DEBUG
210 	if (!DebugSnd_SamplesInitted) bprintf(PRINT_ERROR, _T("BurnSampleResume called without init\n"));
211 #endif
212 
213 	if (sample >= nTotalSamples) return;
214 
215 	sample_ptr = &samples[sample];
216 	sample_ptr->playing = 1;
217 }
218 
BurnSampleStop(INT32 sample)219 void BurnSampleStop(INT32 sample)
220 {
221 #if defined FBA_DEBUG
222 	if (!DebugSnd_SamplesInitted) bprintf(PRINT_ERROR, _T("BurnSampleStop called without init\n"));
223 #endif
224 
225 	if (sample >= nTotalSamples) return;
226 
227 	sample_ptr = &samples[sample];
228 	sample_ptr->playing = 0;
229 	sample_ptr->position = 0;
230 	//sample_ptr->playback_rate = 100; // 100% // on load and reset, only!
231 }
232 
BurnSampleSetLoop(INT32 sample,bool dothis)233 void BurnSampleSetLoop(INT32 sample, bool dothis)
234 {
235 #if defined FBA_DEBUG
236 	if (!DebugSnd_SamplesInitted) bprintf(PRINT_ERROR, _T("BurnSampleSetLoop called without init\n"));
237 #endif
238 
239 	if (sample >= nTotalSamples) return;
240 
241 	sample_ptr = &samples[sample];
242 
243 	sample_ptr->loop = (dothis ? 1 : 0);
244 }
245 
BurnSampleGetStatus(INT32 sample)246 INT32 BurnSampleGetStatus(INT32 sample)
247 {
248 	// this is also used to see if samples initialized and/or the game has samples.
249 
250 	if (sample >= nTotalSamples) return -1;
251 
252 	sample_ptr = &samples[sample];
253 	return (sample_ptr->playing);
254 }
255 
BurnSampleGetPosition(INT32 sample)256 INT32 BurnSampleGetPosition(INT32 sample)
257 {
258 #if defined FBA_DEBUG
259 	if (!DebugSnd_SamplesInitted) bprintf(PRINT_ERROR, _T("BurnSampleGetPosition called without init\n"));
260 #endif
261 
262 	if (sample >= nTotalSamples) return -1;
263 
264 	sample_ptr = &samples[sample];
265 	return (sample_ptr->position / 0x10000);
266 }
267 
BurnSampleSetPosition(INT32 sample,UINT32 position)268 void BurnSampleSetPosition(INT32 sample, UINT32 position)
269 {
270 #if defined FBA_DEBUG
271 	if (!DebugSnd_SamplesInitted) bprintf(PRINT_ERROR, _T("BurnSampleSetPosition called without init\n"));
272 #endif
273 
274 	if (sample >= nTotalSamples) return;
275 
276 	sample_ptr = &samples[sample];
277 	sample_ptr->position = position * 0x10000;
278 }
279 
BurnSampleSetPlaybackRate(INT32 sample,INT32 rate)280 void BurnSampleSetPlaybackRate(INT32 sample, INT32 rate)
281 {
282 #if defined FBA_DEBUG
283 	if (!DebugSnd_SamplesInitted) bprintf(PRINT_ERROR, _T("BurnSampleSetPlaybackRate called without init\n"));
284 	if (rate > 5000 || rate < 0) bprintf (PRINT_ERROR, _T("BurnSampleSetPlaybackRate called with unlikely rate (%d)!\n"), rate);
285 #endif
286 
287 	if (sample >= nTotalSamples) return;
288 
289 	sample_ptr = &samples[sample];
290 	sample_ptr->playback_rate = rate;
291 }
292 
BurnSampleReset()293 void BurnSampleReset()
294 {
295 #if defined FBA_DEBUG
296 	if (!DebugSnd_SamplesInitted) bprintf(PRINT_ERROR, _T("BurnSampleReset called without init\n"));
297 #endif
298 
299 	for (INT32 i = 0; i < nTotalSamples; i++) {
300 		BurnSampleStop(i);
301 		BurnSampleSetPlaybackRate(i, 100);
302 
303 		if (sample_ptr->flags & SAMPLE_AUTOLOOP) {
304 			BurnSampleSetLoop(i, true); // this sets the loop flag, from the driver.
305 		}
306 	}
307 }
308 
309 INT32 __cdecl ZipLoadOneFile(char* arcName, const char* fileName, void** Dest, INT32* pnWrote);
310 char* TCHARToANSI(const TCHAR* pszInString, char* pszOutString, INT32 nOutSize);
311 #define _TtoA(a)	TCHARToANSI(a, NULL, 0)
312 
BurnSampleInit(INT32 bAdd)313 void BurnSampleInit(INT32 bAdd /*add samples to stream?*/)
314 {
315 	bAddToStream = bAdd;
316 	nTotalSamples = 0;
317 
318 	DebugSnd_SamplesInitted = 1;
319 
320 	if (nBurnSoundRate == 0) {
321 		nTotalSamples = 0;
322 		return;
323 	}
324 
325 	INT32 length;
326 	char path[256*2];
327 	char setname[128];
328 	void *destination = NULL;
329 	char szTempPath[MAX_PATH];
330 	sprintf(szTempPath, _TtoA(SAMPLE_DIRECTORY));
331 
332 	// test to see if file exists
333 	INT32 nEnableSamples = 0;
334 
335 	if (BurnDrvGetTextA(DRV_SAMPLENAME) == NULL) { // called with no samples
336 		nTotalSamples = 0;
337 		return;
338 	}
339 
340 	strcpy(setname, BurnDrvGetTextA(DRV_SAMPLENAME));
341 	sprintf(path, "%s%s.zip", szTempPath, setname);
342 
343 	FILE *test = fopen(path, "rb");
344 	if (test)
345 	{
346 		nEnableSamples = 1;
347 		fclose(test);
348 	}
349 
350 #ifdef INCLUDE_7Z_SUPPORT
351 	sprintf(path, "%s%s.7z", szTempPath, setname);
352 
353 	test = fopen(path, "rb");
354 	if (test)
355 	{
356 		nEnableSamples = 1;
357 		fclose(test);
358 	}
359 #endif
360 
361 	if (!nEnableSamples) return;
362 
363 	struct BurnSampleInfo si;
364 	INT32 nSampleOffset = -1;
365 	do {
366 		BurnDrvGetSampleInfo(&si, ++nSampleOffset);
367 		if (si.nFlags) nTotalSamples++;
368 	} while (si.nFlags);
369 
370 	samples = (sample_format*)BurnMalloc(sizeof(sample_format) * nTotalSamples);
371 	memset (samples, 0, sizeof(sample_format) * nTotalSamples);
372 
373 	for (INT32 i = 0; i < nTotalSamples; i++) {
374 		BurnDrvGetSampleInfo(&si, i);
375 		char *szSampleNameTmp = NULL;
376 		BurnDrvGetSampleName(&szSampleNameTmp, i, 0);
377 
378 		sample_ptr = &samples[i];
379 
380 		// append .wav to filename
381 		char szSampleName[1024];
382 		memset(&szSampleName, 0, sizeof(szSampleName));
383 		strncpy(&szSampleName[0], szSampleNameTmp, sizeof(szSampleName) - 5); // leave space for ".wav" + null, just incase!
384 		strcat(&szSampleName[0], ".wav");
385 
386 		if (si.nFlags == 0) break;
387 
388 		if (si.nFlags & SAMPLE_NOSTORE) {
389 			sample_ptr->flags = si.nFlags;
390 			sample_ptr->data = NULL;
391 			continue;
392 		}
393 
394 		sprintf (path, "%s%s", szTempPath, setname);
395 
396 		destination = NULL;
397 		length = 0;
398 		ZipLoadOneFile((char*)path, (const char*)szSampleName, &destination, &length);
399 
400 		if (length) {
401 			sample_ptr->flags = si.nFlags;
402 			bprintf(0, _T("Loading \"%S\": "), szSampleName);
403 			make_raw((UINT8*)destination, length);
404 		} else {
405 			sample_ptr->flags = SAMPLE_IGNORE;
406 		}
407 
408 		sample_ptr->gain[BURN_SND_SAMPLE_ROUTE_1] = 1.00;
409 		sample_ptr->gain[BURN_SND_SAMPLE_ROUTE_2] = 1.00;
410 		sample_ptr->output_dir[BURN_SND_SAMPLE_ROUTE_1] = BURN_SND_ROUTE_BOTH;
411 		sample_ptr->output_dir[BURN_SND_SAMPLE_ROUTE_2] = BURN_SND_ROUTE_BOTH;
412 		sample_ptr->playback_rate = 100;
413 
414 		BurnFree (destination);
415 
416 		BurnSetProgressRange(1.0 / nTotalSamples);
417 		BurnUpdateProgress((double)1.0 / i * nTotalSamples, _T("Loading samples..."), 0);
418 	}
419 }
420 
BurnSampleInitOne(INT32 sample)421 void BurnSampleInitOne(INT32 sample)
422 {
423 	if (sample >= nTotalSamples) {
424 		return;
425 	}
426 
427 	{
428 		struct sample_format *clr_ptr = &samples[0];
429 
430 		int i = 0;
431 		while (i < nTotalSamples) {
432 
433 			if (clr_ptr->data != NULL && i != sample && (clr_ptr->flags & SAMPLE_NOSTORE)) {
434 				BurnFree(clr_ptr->data);
435 				clr_ptr->playing = 0;
436 				clr_ptr->playback_rate = 100;
437 				clr_ptr->data = NULL;
438 			}
439 
440 			clr_ptr++, i++;
441 		}
442 	}
443 
444 	if ((sample_ptr->flags & SAMPLE_NOSTORE) == 0) {
445 		return;
446 	}
447 
448 	INT32 length;
449 	char path[256];
450 	char setname[128];
451 	void *destination = NULL;
452 	char szTempPath[MAX_PATH];
453 	sprintf(szTempPath, _TtoA(SAMPLE_DIRECTORY));
454 
455 	strcpy(setname, BurnDrvGetTextA(DRV_SAMPLENAME));
456 	sprintf(path, "%s%s.zip", szTempPath, setname);
457 
458 	struct BurnSampleInfo si;
459 	BurnDrvGetSampleInfo(&si, sample);
460 	char *szSampleNameTmp = NULL;
461 	BurnDrvGetSampleName(&szSampleNameTmp, sample, 0);
462 
463 	sample_ptr = &samples[sample];
464 
465 	// append .wav to filename
466 	char szSampleName[1024];
467 	memset(&szSampleName, 0, sizeof(szSampleName));
468 	strncpy(&szSampleName[0], szSampleNameTmp, sizeof(szSampleName) - 5); // leave space for ".wav" + null, just incase!
469 	strcat(&szSampleName[0], ".wav");
470 
471 	if (sample_ptr->playing || sample_ptr->data != NULL || sample_ptr->flags == SAMPLE_IGNORE) {
472 		return;
473 	}
474 
475 	sprintf (path, "%s%s", szTempPath, setname);
476 
477 	destination = NULL;
478 	length = 0;
479 	ZipLoadOneFile((char*)path, (const char*)szSampleName, &destination, &length);
480 
481 	if (length) {
482 		make_raw((UINT8*)destination, length);
483 	}
484 
485 	BurnFree (destination);
486 }
487 
BurnSampleSetRoute(INT32 sample,INT32 nIndex,double nVolume,INT32 nRouteDir)488 void BurnSampleSetRoute(INT32 sample, INT32 nIndex, double nVolume, INT32 nRouteDir)
489 {
490 #if defined FBA_DEBUG
491 	if (!DebugSnd_SamplesInitted) bprintf(PRINT_ERROR, _T("BurnSampleSetRoute called without init\n"));
492 	if (nIndex < 0 || nIndex > 1) bprintf(PRINT_ERROR, _T("BurnSampleSetRoute called with invalid index %i\n"), nIndex);
493 #endif
494 
495 	if (sample >= nTotalSamples) return;
496 
497 	sample_ptr = &samples[sample];
498 	sample_ptr->gain[nIndex] = nVolume;
499 	sample_ptr->output_dir[nIndex] = nRouteDir;
500 }
501 
BurnSampleSetRouteAllSamples(INT32 nIndex,double nVolume,INT32 nRouteDir)502 void BurnSampleSetRouteAllSamples(INT32 nIndex, double nVolume, INT32 nRouteDir)
503 {
504 #if defined FBA_DEBUG
505 	if (!DebugSnd_SamplesInitted) bprintf(PRINT_ERROR, _T("BurnSampleSetRouteAllSamples called without init\n"));
506 	if (nIndex < 0 || nIndex > 1) bprintf(PRINT_ERROR, _T("BurnSampleSetRouteAllSamples called with invalid index %i\n"), nIndex);
507 #endif
508 
509 	if (!nTotalSamples) return;
510 
511 	for (INT32 i = 0; i < nTotalSamples; i++) {
512 		sample_ptr = &samples[i];
513 		sample_ptr->gain[nIndex] = nVolume;
514 		sample_ptr->output_dir[nIndex] = nRouteDir;
515 	}
516 }
517 
BurnSampleExit()518 void BurnSampleExit()
519 {
520 #if defined FBA_DEBUG
521 	if (!DebugSnd_SamplesInitted) bprintf(PRINT_ERROR, _T("BurnSampleExit called without init\n"));
522 #endif
523 
524 	if (!DebugSnd_SamplesInitted) return;
525 
526 	for (INT32 i = 0; i < nTotalSamples; i++) {
527 		sample_ptr = &samples[i];
528 		if (sample_ptr)
529 			BurnFree (sample_ptr->data);
530 	}
531 
532 	if (samples)
533 		BurnFree (samples);
534 
535 	sample_ptr = NULL;
536 	nTotalSamples = 0;
537 	bAddToStream = 0;
538 	bBurnSampleTrimSampleEnd = 0;
539 
540 	DebugSnd_SamplesInitted = 0;
541 }
542 
BurnSampleRender(INT16 * pDest,UINT32 pLen)543 void BurnSampleRender(INT16 *pDest, UINT32 pLen)
544 {
545 #if defined FBA_DEBUG
546 	if (!DebugSnd_SamplesInitted) bprintf(PRINT_ERROR, _T("BurnSampleRender called without init\n"));
547 #endif
548 
549 	if (pBurnSoundOut == NULL) {
550 		return;
551 	}
552 
553 	// if the sample player is the only, or the first, sound chip, clear out the sound buffer!
554 	if (bAddToStream == 0) {
555 		memset (pDest, 0, pLen * 2 * sizeof(INT16)); // clear buffer
556 	}
557 
558 	for (INT32 i = 0; i < nTotalSamples; i++)
559 	{
560 		sample_ptr = &samples[i];
561 		if (sample_ptr->playing == 0) continue;
562 
563 		INT32 playlen = pLen;
564 		INT32 length = sample_ptr->length;
565 		UINT64 pos = sample_ptr->position;
566 		INT32 playback_rate = (0x10000 * sample_ptr->playback_rate) / 100;
567 
568 		INT16 *dst = pDest;
569 		INT16 *dat = (INT16*)sample_ptr->data;
570 
571 		if (sample_ptr->loop == 0) // if not looping, check to make sure sample is in bounds
572 		{
573 			INT32 current_pos = (pos / 0x10000);
574 			// if sample position is greater than length, stop playback
575 			if ((length - current_pos) <= 0) {
576 				BurnSampleStop(i);
577 				continue;
578 			}
579 
580 			// if samples remaining are less than playlen, set playlen to samples remaining
581 			if (playlen > (length - current_pos)) playlen = length - current_pos;
582 		}
583 
584 		length *= 2; // (stereo) used to ensure position is within bounds
585 
586 		for (INT32 j = 0; j < playlen; j++, dst+=2, pos+=playback_rate) {
587 			INT32 nLeftSample = 0, nRightSample = 0;
588 			UINT32 current_pos = (pos / 0x10000);
589 			UINT32 position = current_pos * 2; // ~1
590 
591 			if (sample_ptr->loop == 0) // if not looping, check to make sure sample is in bounds
592 			{
593 				// if sample position is greater than length, stop playback
594 				if ((sample_ptr->length - current_pos) <= 0) {
595 					BurnSampleStop(i);
596 					break;
597 				}
598 			}
599 
600 			if ((sample_ptr->output_dir[BURN_SND_SAMPLE_ROUTE_1] & BURN_SND_ROUTE_LEFT) == BURN_SND_ROUTE_LEFT) {
601 				nLeftSample += (INT32)(dat[(position) % length] * sample_ptr->gain[BURN_SND_SAMPLE_ROUTE_1]);
602 			}
603 			if ((sample_ptr->output_dir[BURN_SND_SAMPLE_ROUTE_1] & BURN_SND_ROUTE_RIGHT) == BURN_SND_ROUTE_RIGHT) {
604 				nRightSample += (INT32)(dat[(position) % length] * sample_ptr->gain[BURN_SND_SAMPLE_ROUTE_1]);
605 			}
606 
607 			if ((sample_ptr->output_dir[BURN_SND_SAMPLE_ROUTE_2] & BURN_SND_ROUTE_LEFT) == BURN_SND_ROUTE_LEFT) {
608 				nLeftSample += (INT32)(dat[(position + 1) % length] * sample_ptr->gain[BURN_SND_SAMPLE_ROUTE_2]);
609 			}
610 			if ((sample_ptr->output_dir[BURN_SND_SAMPLE_ROUTE_2] & BURN_SND_ROUTE_RIGHT) == BURN_SND_ROUTE_RIGHT) {
611 				nRightSample += (INT32)(dat[(position + 1) % length] * sample_ptr->gain[BURN_SND_SAMPLE_ROUTE_2]);
612 			}
613 
614 			dst[0] = BURN_SND_CLIP(nLeftSample + dst[0]);
615 			dst[1] = BURN_SND_CLIP(nRightSample + dst[1]);
616 		}
617 
618 		sample_ptr->position = pos; // store the updated position
619 	}
620 }
621 
BurnSampleScan(INT32 nAction,INT32 * pnMin)622 void BurnSampleScan(INT32 nAction, INT32 *pnMin)
623 {
624 #if defined FBA_DEBUG
625 	if (!DebugSnd_SamplesInitted) bprintf(PRINT_ERROR, _T("BurnSampleScan called without init\n"));
626 #endif
627 
628 	if (pnMin != NULL) {
629 		*pnMin = 0x029707;
630 	}
631 
632 	if (nAction & ACB_DRIVER_DATA) {
633 		for (INT32 i = 0; i < nTotalSamples; i++) {
634 			sample_ptr = &samples[i];
635 			SCAN_VAR(sample_ptr->playing);
636 			SCAN_VAR(sample_ptr->loop);
637 			SCAN_VAR(sample_ptr->position);
638 			SCAN_VAR(sample_ptr->playback_rate);
639 		}
640 	}
641 }
642