1 #include "burnint.h"
2 #include "burn_ym3812.h"
3 
4 #define MAX_YM3812	2
5 
6 // Timer Related
7 
8 #define MAX_TIMER_VALUE ((1 << 30) - 65536)
9 
10 static double dTimeYM3812;									// Time elapsed since the emulated machine was started
11 
12 static INT32 nTimerCount[2], nTimerStart[2];
13 
14 // Callbacks
15 static INT32 (*pTimerOverCallback)(INT32, INT32);
16 static double (*pTimerTimeCallback)();
17 
18 static INT32 nCPUClockspeed = 0;
19 static INT32 (*pCPUTotalCycles)() = NULL;
20 static INT32 (*pCPURun)(INT32) = NULL;
21 static void (*pCPURunEnd)() = NULL;
22 
23 // ---------------------------------------------------------------------------
24 // Running time
25 
BurnTimerTimeCallbackDummy()26 static double BurnTimerTimeCallbackDummy()
27 {
28 	return 0.0;
29 }
30 
BurnTimerGetTimeYM3812()31 extern "C" double BurnTimerGetTimeYM3812()
32 {
33 	return dTimeYM3812 + pTimerTimeCallback();
34 }
35 
36 // ---------------------------------------------------------------------------
37 // Update timers
38 
39 static INT32 nTicksTotal, nTicksDone, nTicksExtra;
40 
BurnTimerUpdateYM3812(INT32 nCycles)41 INT32 BurnTimerUpdateYM3812(INT32 nCycles)
42 {
43 	INT32 nIRQStatus = 0;
44 
45 	nTicksTotal = MAKE_TIMER_TICKS(nCycles, nCPUClockspeed);
46 
47 	while (nTicksDone < nTicksTotal) {
48 		INT32 nTimer, nCyclesSegment, nTicksSegment;
49 
50 		// Determine which timer fires first
51 		if (nTimerCount[0] <= nTimerCount[1]) {
52 			nTicksSegment = nTimerCount[0];
53 		} else {
54 			nTicksSegment = nTimerCount[1];
55 		}
56 		if (nTicksSegment > nTicksTotal) {
57 			nTicksSegment = nTicksTotal;
58 		}
59 
60 		nCyclesSegment = MAKE_CPU_CYLES(nTicksSegment + nTicksExtra, nCPUClockspeed);
61 
62 		pCPURun(nCyclesSegment - pCPUTotalCycles());
63 
64 		nTicksDone = MAKE_TIMER_TICKS(pCPUTotalCycles() + 1, nCPUClockspeed) - 1;
65 
66 		nTimer = 0;
67 		if (nTicksDone >= nTimerCount[0]) {
68 			if (nTimerStart[0] == MAX_TIMER_VALUE) {
69 				nTimerCount[0] = MAX_TIMER_VALUE;
70 			} else {
71 				nTimerCount[0] += nTimerStart[0];
72 			}
73 			nTimer |= 1;
74 		}
75 		if (nTicksDone >= nTimerCount[1]) {
76 			if (nTimerStart[1] == MAX_TIMER_VALUE) {
77 				nTimerCount[1] = MAX_TIMER_VALUE;
78 			} else {
79 				nTimerCount[1] += nTimerStart[1];
80 			}
81 			nTimer |= 2;
82 		}
83 		if (nTimer & 1) {
84 			nIRQStatus |= pTimerOverCallback(0, 0);
85 		}
86 		if (nTimer & 2) {
87 			nIRQStatus |= pTimerOverCallback(0, 1);
88 		}
89 	}
90 
91 	return nIRQStatus;
92 }
93 
BurnTimerEndFrameYM3812(INT32 nCycles)94 void BurnTimerEndFrameYM3812(INT32 nCycles)
95 {
96 	INT32 nTicks = MAKE_TIMER_TICKS(nCycles, nCPUClockspeed);
97 
98 	BurnTimerUpdateYM3812(nCycles);
99 
100 	if (nTimerCount[0] < MAX_TIMER_VALUE) {
101 		nTimerCount[0] -= nTicks;
102 	}
103 	if (nTimerCount[1] < MAX_TIMER_VALUE) {
104 		nTimerCount[1] -= nTicks;
105 	}
106 
107 	nTicksDone -= nTicks;
108 	if (nTicksDone < 0) {
109 		nTicksDone = 0;
110 	}
111 }
112 
BurnTimerUpdateEndYM3812()113 void BurnTimerUpdateEndYM3812()
114 {
115 	pCPURunEnd();
116 
117 	nTicksTotal = 0;
118 }
119 
BurnOPLTimerCallbackYM3812(INT32 c,double period)120 void BurnOPLTimerCallbackYM3812(INT32 c, double period)
121 {
122 	pCPURunEnd();
123 
124 	if (period == 0.0) {
125 		nTimerCount[c] = MAX_TIMER_VALUE;
126 		return;
127 	}
128 
129 	nTimerCount[c]  = (INT32)(period * (double)TIMER_TICKS_PER_SECOND);
130 	nTimerCount[c] += MAKE_TIMER_TICKS(pCPUTotalCycles(), nCPUClockspeed);
131 }
132 
BurnTimerScanYM3812(INT32 nAction,INT32 * pnMin)133 void BurnTimerScanYM3812(INT32 nAction, INT32* pnMin)
134 {
135 	if (pnMin && *pnMin < 0x029521) {
136 		*pnMin = 0x029521;
137 	}
138 
139 	if (nAction & ACB_DRIVER_DATA) {
140 		SCAN_VAR(nTimerCount);
141 		SCAN_VAR(nTimerStart);
142 		SCAN_VAR(dTimeYM3812);
143 
144 		SCAN_VAR(nTicksDone);
145 	}
146 }
147 
BurnTimerExitYM3812()148 void BurnTimerExitYM3812()
149 {
150 	nCPUClockspeed = 0;
151 	pCPUTotalCycles = NULL;
152 	pCPURun = NULL;
153 	pCPURunEnd = NULL;
154 
155 	return;
156 }
157 
BurnTimerResetYM3812()158 void BurnTimerResetYM3812()
159 {
160 	nTimerCount[0] = nTimerCount[1] = MAX_TIMER_VALUE;
161 	nTimerStart[0] = nTimerStart[1] = MAX_TIMER_VALUE;
162 
163 	dTimeYM3812 = 0.0;
164 
165 	nTicksDone = 0;
166 }
167 
BurnTimerInitYM3812(INT32 (* pOverCallback)(INT32,INT32),double (* pTimeCallback)())168 INT32 BurnTimerInitYM3812(INT32 (*pOverCallback)(INT32, INT32), double (*pTimeCallback)())
169 {
170 	BurnTimerExitYM3812();
171 
172 	pTimerOverCallback = pOverCallback;
173 	pTimerTimeCallback = pTimeCallback ? pTimeCallback : BurnTimerTimeCallbackDummy;
174 
175 	BurnTimerResetYM3812();
176 
177 	return 0;
178 }
179 
BurnTimerAttachYM3812(cpu_core_config * ptr,INT32 nClockspeed)180 INT32 BurnTimerAttachYM3812(cpu_core_config *ptr, INT32 nClockspeed)
181 {
182 	nCPUClockspeed = nClockspeed;
183 	pCPUTotalCycles = ptr->totalcycles;
184 	pCPURun = ptr->run;
185 	pCPURunEnd = ptr->runend;
186 
187 	nTicksExtra = MAKE_TIMER_TICKS(1, nCPUClockspeed) - 1;
188 
189 	return 0;
190 }
191 
YM3812SynchroniseStream(INT32 nSoundRate)192 static INT32 YM3812SynchroniseStream(INT32 nSoundRate)
193 {
194 	return (INT64)(pCPUTotalCycles() * nSoundRate / nCPUClockspeed);
195 }
196 
197 // Sound Related
198 
199 void (*BurnYM3812Update)(INT16* pSoundBuf, INT32 nSegmentEnd);
200 
201 static INT32 (*BurnYM3812StreamCallback)(INT32 nSoundRate);
202 
203 static INT32 nBurnYM3812SoundRate;
204 
205 static INT16* pBuffer;
206 static INT16* pYM3812Buffer[2 * MAX_YM3812];
207 
208 static INT32 nYM3812Position;
209 
210 static UINT32 nSampleSize;
211 static INT32 nFractionalPosition;
212 
213 static INT32 nNumChips = 0;
214 static INT32 bYM3812AddSignal;
215 
216 static double YM3812Volumes[1 * MAX_YM3812];
217 static INT32 YM3812RouteDirs[1 * MAX_YM3812];
218 
219 // ----------------------------------------------------------------------------
220 // Dummy functions
221 
YM3812UpdateDummy(INT16 *,INT32)222 static void YM3812UpdateDummy(INT16* , INT32)
223 {
224 	return;
225 }
226 
YM3812StreamCallbackDummy(INT32)227 static INT32 YM3812StreamCallbackDummy(INT32)
228 {
229 	return 0;
230 }
231 
232 // ----------------------------------------------------------------------------
233 // Execute YM3812 for part of a frame
234 
YM3812Render(INT32 nSegmentLength)235 static void YM3812Render(INT32 nSegmentLength)
236 {
237 #if defined FBA_DEBUG
238 	if (!DebugSnd_YM3812Initted) bprintf(PRINT_ERROR, _T("YM3812Render called without init\n"));
239 #endif
240 
241 	if (nYM3812Position >= nSegmentLength) {
242 		return;
243 	}
244 
245 	nSegmentLength -= nYM3812Position;
246 
247 	YM3812UpdateOne(0, pBuffer + 0 * 4096 + 4 + nYM3812Position, nSegmentLength);
248 
249 	if (nNumChips > 1) {
250 		YM3812UpdateOne(1, pBuffer + 1 * 4096 + 4 + nYM3812Position, nSegmentLength);
251 	}
252 
253 	nYM3812Position += nSegmentLength;
254 }
255 
256 // ----------------------------------------------------------------------------
257 // Update the sound buffer
258 
YM3812UpdateResample(INT16 * pSoundBuf,INT32 nSegmentEnd)259 static void YM3812UpdateResample(INT16* pSoundBuf, INT32 nSegmentEnd)
260 {
261 #if defined FBA_DEBUG
262 	if (!DebugSnd_YM3812Initted) bprintf(PRINT_ERROR, _T("YM3812UpdateResample called without init\n"));
263 #endif
264 
265 	INT32 nSegmentLength = nSegmentEnd;
266 	INT32 nSamplesNeeded = nSegmentEnd * nBurnYM3812SoundRate / nBurnSoundRate + 1;
267 
268 	if (nSamplesNeeded < nYM3812Position) {
269 		nSamplesNeeded = nYM3812Position;
270 	}
271 
272 	if (nSegmentLength > nBurnSoundLen) {
273 		nSegmentLength = nBurnSoundLen;
274 	}
275 	nSegmentLength <<= 1;
276 
277 	YM3812Render(nSamplesNeeded);
278 
279 	pYM3812Buffer[0] = pBuffer + 0 * 4096 + 4;
280 	if (nNumChips > 1) {
281 		pYM3812Buffer[1] = pBuffer + 1 * 4096 + 4;
282 	}
283 
284 	for (INT32 i = (nFractionalPosition & 0xFFFF0000) >> 15; i < nSegmentLength; i += 2, nFractionalPosition += nSampleSize) {
285 		INT32 nLeftSample[4] = {0, 0, 0, 0};
286 		INT32 nRightSample[4] = {0, 0, 0, 0};
287 		INT32 nTotalLeftSample, nTotalRightSample;
288 
289 		if ((YM3812RouteDirs[0 + BURN_SND_YM3812_ROUTE] & BURN_SND_ROUTE_LEFT) == BURN_SND_ROUTE_LEFT) {
290 			nLeftSample[0] += (INT32)(pYM3812Buffer[0][(nFractionalPosition >> 16) - 3] * YM3812Volumes[0 + BURN_SND_YM3812_ROUTE]);
291 			nLeftSample[1] += (INT32)(pYM3812Buffer[0][(nFractionalPosition >> 16) - 2] * YM3812Volumes[0 + BURN_SND_YM3812_ROUTE]);
292 			nLeftSample[2] += (INT32)(pYM3812Buffer[0][(nFractionalPosition >> 16) - 1] * YM3812Volumes[0 + BURN_SND_YM3812_ROUTE]);
293 			nLeftSample[3] += (INT32)(pYM3812Buffer[0][(nFractionalPosition >> 16) - 0] * YM3812Volumes[0 + BURN_SND_YM3812_ROUTE]);
294 		}
295 		if ((YM3812RouteDirs[0 + BURN_SND_YM3812_ROUTE] & BURN_SND_ROUTE_RIGHT) == BURN_SND_ROUTE_RIGHT) {
296 			nRightSample[0] += (INT32)(pYM3812Buffer[0][(nFractionalPosition >> 16) - 3] * YM3812Volumes[0 + BURN_SND_YM3812_ROUTE]);
297 			nRightSample[1] += (INT32)(pYM3812Buffer[0][(nFractionalPosition >> 16) - 2] * YM3812Volumes[0 + BURN_SND_YM3812_ROUTE]);
298 			nRightSample[2] += (INT32)(pYM3812Buffer[0][(nFractionalPosition >> 16) - 1] * YM3812Volumes[0 + BURN_SND_YM3812_ROUTE]);
299 			nRightSample[3] += (INT32)(pYM3812Buffer[0][(nFractionalPosition >> 16) - 0] * YM3812Volumes[0 + BURN_SND_YM3812_ROUTE]);
300 		}
301 
302 		if (nNumChips > 1) {
303 			if ((YM3812RouteDirs[1 + BURN_SND_YM3812_ROUTE] & BURN_SND_ROUTE_LEFT) == BURN_SND_ROUTE_LEFT) {
304 				nLeftSample[0] += (INT32)(pYM3812Buffer[1][(nFractionalPosition >> 16) - 3] * YM3812Volumes[1 + BURN_SND_YM3812_ROUTE]);
305 				nLeftSample[1] += (INT32)(pYM3812Buffer[1][(nFractionalPosition >> 16) - 2] * YM3812Volumes[1 + BURN_SND_YM3812_ROUTE]);
306 				nLeftSample[2] += (INT32)(pYM3812Buffer[1][(nFractionalPosition >> 16) - 1] * YM3812Volumes[1 + BURN_SND_YM3812_ROUTE]);
307 				nLeftSample[3] += (INT32)(pYM3812Buffer[1][(nFractionalPosition >> 16) - 0] * YM3812Volumes[1 + BURN_SND_YM3812_ROUTE]);
308 			}
309 			if ((YM3812RouteDirs[1 + BURN_SND_YM3812_ROUTE] & BURN_SND_ROUTE_RIGHT) == BURN_SND_ROUTE_RIGHT) {
310 				nRightSample[0] += (INT32)(pYM3812Buffer[1][(nFractionalPosition >> 16) - 3] * YM3812Volumes[1 + BURN_SND_YM3812_ROUTE]);
311 				nRightSample[1] += (INT32)(pYM3812Buffer[1][(nFractionalPosition >> 16) - 2] * YM3812Volumes[1 + BURN_SND_YM3812_ROUTE]);
312 				nRightSample[2] += (INT32)(pYM3812Buffer[1][(nFractionalPosition >> 16) - 1] * YM3812Volumes[1 + BURN_SND_YM3812_ROUTE]);
313 				nRightSample[3] += (INT32)(pYM3812Buffer[1][(nFractionalPosition >> 16) - 0] * YM3812Volumes[1 + BURN_SND_YM3812_ROUTE]);
314 			}
315 		}
316 
317 		nTotalLeftSample = INTERPOLATE4PS_16BIT((nFractionalPosition >> 4) & 0x0fff, nLeftSample[0], nLeftSample[1], nLeftSample[2], nLeftSample[3]);
318 		nTotalRightSample = INTERPOLATE4PS_16BIT((nFractionalPosition >> 4) & 0x0fff, nRightSample[0], nRightSample[1], nRightSample[2], nRightSample[3]);
319 
320 		nTotalLeftSample = BURN_SND_CLIP(nTotalLeftSample);
321 		nTotalRightSample = BURN_SND_CLIP(nTotalRightSample);
322 
323 		if (bYM3812AddSignal) {
324 			pSoundBuf[i + 0] = BURN_SND_CLIP(pSoundBuf[i + 0] + nTotalLeftSample);
325 			pSoundBuf[i + 1] = BURN_SND_CLIP(pSoundBuf[i + 1] + nTotalRightSample);
326 		} else {
327 			pSoundBuf[i + 0] = nTotalLeftSample;
328 			pSoundBuf[i + 1] = nTotalRightSample;
329 		}
330 	}
331 
332 	if (nSegmentEnd >= nBurnSoundLen) {
333 		INT32 nExtraSamples = nSamplesNeeded - (nFractionalPosition >> 16);
334 
335 		for (INT32 i = -4; i < nExtraSamples; i++) {
336 			pYM3812Buffer[0][i] = pYM3812Buffer[0][(nFractionalPosition >> 16) + i];
337 			if (nNumChips > 1) {
338 				pYM3812Buffer[1][i] = pYM3812Buffer[1][(nFractionalPosition >> 16) + i];
339 			}
340 		}
341 
342 		nFractionalPosition &= 0xFFFF;
343 
344 		nYM3812Position = nExtraSamples;
345 	}
346 }
347 
YM3812UpdateNormal(INT16 * pSoundBuf,INT32 nSegmentEnd)348 static void YM3812UpdateNormal(INT16* pSoundBuf, INT32 nSegmentEnd)
349 {
350 #if defined FBA_DEBUG
351 	if (!DebugSnd_YM3812Initted) bprintf(PRINT_ERROR, _T("YM3812UpdateNormal called without init\n"));
352 #endif
353 
354 	INT32 nSegmentLength = nSegmentEnd;
355 
356 	if (nSegmentEnd < nYM3812Position) {
357 		nSegmentEnd = nYM3812Position;
358 	}
359 
360 	if (nSegmentLength > nBurnSoundLen) {
361 		nSegmentLength = nBurnSoundLen;
362 	}
363 
364 	YM3812Render(nSegmentEnd);
365 
366 	pYM3812Buffer[0] = pBuffer + 4 + 0 * 4096;
367 	pYM3812Buffer[1] = pBuffer + 4 + 1 * 4096;
368 
369 	for (INT32 n = nFractionalPosition; n < nSegmentLength; n++) {
370 		INT32 nLeftSample = 0, nRightSample = 0;
371 
372 		if ((YM3812RouteDirs[0 + BURN_SND_YM3812_ROUTE] & BURN_SND_ROUTE_LEFT) == BURN_SND_ROUTE_LEFT) {
373 			nLeftSample += (INT32)(pYM3812Buffer[0][n] * YM3812Volumes[0 + BURN_SND_YM3812_ROUTE]);
374 		}
375 		if ((YM3812RouteDirs[0 + BURN_SND_YM3812_ROUTE] & BURN_SND_ROUTE_RIGHT) == BURN_SND_ROUTE_RIGHT) {
376 			nRightSample += (INT32)(pYM3812Buffer[0][n] * YM3812Volumes[0 + BURN_SND_YM3812_ROUTE]);
377 		}
378 
379 		if (nNumChips > 1) {
380 			if ((YM3812RouteDirs[1 + BURN_SND_YM3812_ROUTE] & BURN_SND_ROUTE_LEFT) == BURN_SND_ROUTE_LEFT) {
381 				nLeftSample += (INT32)(pYM3812Buffer[1][n] * YM3812Volumes[1 + BURN_SND_YM3812_ROUTE]);
382 			}
383 			if ((YM3812RouteDirs[1 + BURN_SND_YM3812_ROUTE] & BURN_SND_ROUTE_RIGHT) == BURN_SND_ROUTE_RIGHT) {
384 				nRightSample += (INT32)(pYM3812Buffer[1][n] * YM3812Volumes[1 + BURN_SND_YM3812_ROUTE]);
385 			}
386 		}
387 
388 		nLeftSample = BURN_SND_CLIP(nLeftSample);
389 		nRightSample = BURN_SND_CLIP(nRightSample);
390 
391 		if (bYM3812AddSignal) {
392 			pSoundBuf[(n << 1) + 0] = BURN_SND_CLIP(pSoundBuf[(n << 1) + 0] + nLeftSample);
393 			pSoundBuf[(n << 1) + 1] = BURN_SND_CLIP(pSoundBuf[(n << 1) + 1] + nRightSample);
394 		} else {
395 			pSoundBuf[(n << 1) + 0] = nLeftSample;
396 			pSoundBuf[(n << 1) + 1] = nRightSample;
397 		}
398 	}
399 
400 	nFractionalPosition = nSegmentLength;
401 
402 	if (nSegmentEnd >= nBurnSoundLen) {
403 		INT32 nExtraSamples = nSegmentEnd - nBurnSoundLen;
404 
405 		for (INT32 i = 0; i < nExtraSamples; i++) {
406 			pYM3812Buffer[0][i] = pYM3812Buffer[0][nBurnSoundLen + i];
407 			if (nNumChips > 1) {
408 				pYM3812Buffer[1][i] = pYM3812Buffer[1][nBurnSoundLen + i];
409 			}
410 		}
411 
412 		nFractionalPosition = 0;
413 
414 		nYM3812Position = nExtraSamples;
415 
416 	}
417 }
418 
419 // ----------------------------------------------------------------------------
420 // Callbacks for YM3812 core
421 
BurnYM3812UpdateRequest(INT32,INT32)422 void BurnYM3812UpdateRequest(INT32, INT32)
423 {
424 #if defined FBA_DEBUG
425 	if (!DebugSnd_YM3812Initted) bprintf(PRINT_ERROR, _T("BurnYM3812UpdateRequest called without init\n"));
426 #endif
427 
428 	YM3812Render(BurnYM3812StreamCallback(nBurnYM3812SoundRate));
429 }
430 
431 // ----------------------------------------------------------------------------
432 // Initialisation, etc.
433 
BurnYM3812Reset()434 void BurnYM3812Reset()
435 {
436 #if defined FBA_DEBUG
437 	if (!DebugSnd_YM3812Initted) bprintf(PRINT_ERROR, _T("BurnYM3812Reset called without init\n"));
438 #endif
439 
440 	BurnTimerResetYM3812();
441 
442 	for (INT32 i = 0; i < nNumChips; i++) {
443 		YM3812ResetChip(i);
444 	}
445 }
446 
BurnYM3812Exit()447 void BurnYM3812Exit()
448 {
449 #if defined FBA_DEBUG
450 	if (!DebugSnd_YM3812Initted) bprintf(PRINT_ERROR, _T("BurnYM3812Exit called without init\n"));
451 #endif
452 
453 	if (!DebugSnd_YM3812Initted) return;
454 
455 	YM3812Shutdown();
456 
457 	BurnTimerExitYM3812();
458 
459 	BurnFree(pBuffer);
460 
461 	nNumChips = 0;
462 	bYM3812AddSignal = 0;
463 
464 	DebugSnd_YM3812Initted = 0;
465 }
466 
BurnYM3812Init(INT32 num,INT32 nClockFrequency,OPL_IRQHANDLER IRQCallback,INT32 bAddSignal)467 INT32 BurnYM3812Init(INT32 num, INT32 nClockFrequency, OPL_IRQHANDLER IRQCallback, INT32 bAddSignal)
468 {
469 	return BurnYM3812Init(num, nClockFrequency, IRQCallback, YM3812SynchroniseStream, bAddSignal);
470 }
471 
BurnYM3812Init(INT32 num,INT32 nClockFrequency,OPL_IRQHANDLER IRQCallback,INT32 (* StreamCallback)(INT32),INT32 bAddSignal)472 INT32 BurnYM3812Init(INT32 num, INT32 nClockFrequency, OPL_IRQHANDLER IRQCallback, INT32 (*StreamCallback)(INT32), INT32 bAddSignal)
473 {
474 	DebugSnd_YM3812Initted = 1;
475 
476 	if (num > MAX_YM3812) num = MAX_YM3812;
477 
478 	BurnTimerInitYM3812(&YM3812TimerOver, NULL);
479 
480 	if (nBurnSoundRate <= 0) {
481 		BurnYM3812StreamCallback = YM3812StreamCallbackDummy;
482 
483 		BurnYM3812Update = YM3812UpdateDummy;
484 
485 		YM3812Init(num, nClockFrequency, 11025);
486 		return 0;
487 	}
488 
489 	BurnYM3812StreamCallback = StreamCallback;
490 
491 	if (nFMInterpolation == 3) {
492 		// Set YM3812 core samplerate to match the hardware
493 		nBurnYM3812SoundRate = nClockFrequency / 72;
494 		// Bring YM3812 core samplerate within usable range
495 		while (nBurnYM3812SoundRate > nBurnSoundRate * 3) {
496 			nBurnYM3812SoundRate >>= 1;
497 		}
498 
499 		BurnYM3812Update = YM3812UpdateResample;
500 
501 		nSampleSize = (UINT32)nBurnYM3812SoundRate * (1 << 16) / nBurnSoundRate;
502 		nFractionalPosition = 0;
503 	} else {
504 		nBurnYM3812SoundRate = nBurnSoundRate;
505 
506 		BurnYM3812Update = YM3812UpdateNormal;
507 	}
508 
509 	YM3812Init(num, nClockFrequency, nBurnYM3812SoundRate);
510 	YM3812SetIRQHandler(0, IRQCallback, 0);
511 	YM3812SetTimerHandler(0, &BurnOPLTimerCallbackYM3812, 0);
512 	YM3812SetUpdateHandler(0, &BurnYM3812UpdateRequest, 0);
513 
514 	pBuffer = (INT16*)BurnMalloc(4096 * num * sizeof(INT16));
515 	memset(pBuffer, 0, 4096 * num * sizeof(INT16));
516 
517 	nYM3812Position = 0;
518 
519 	nFractionalPosition = 0;
520 
521 	nNumChips = num;
522 	bYM3812AddSignal = bAddSignal;
523 
524 	// default routes
525 	YM3812Volumes[BURN_SND_YM3812_ROUTE] = 1.00;
526 	YM3812RouteDirs[BURN_SND_YM3812_ROUTE] = BURN_SND_ROUTE_BOTH;
527 
528 	if (nNumChips > 0) {
529 		YM3812Volumes[1 + BURN_SND_YM3812_ROUTE] = 1.00;
530 		YM3812RouteDirs[1 + BURN_SND_YM3812_ROUTE] = BURN_SND_ROUTE_BOTH;
531 	}
532 
533 	return 0;
534 }
535 
BurnYM3812SetRoute(INT32 nChip,INT32 nIndex,double nVolume,INT32 nRouteDir)536 void BurnYM3812SetRoute(INT32 nChip, INT32 nIndex, double nVolume, INT32 nRouteDir)
537 {
538 #if defined FBA_DEBUG
539 	if (!DebugSnd_YM3812Initted) bprintf(PRINT_ERROR, _T("BurnYM3812SetRoute called without init\n"));
540 	if (nIndex < 0 || nIndex > 1) bprintf(PRINT_ERROR, _T("BurnYM3812SetRoute called with invalid index %i\n"), nIndex);
541 	if (nChip >= nNumChips) bprintf(PRINT_ERROR, _T("BurnYM3812SetRoute called with invalid chip %i\n"), nChip);
542 #endif
543 
544 	if (nChip == 0) {
545 		YM3812Volumes[nIndex] = nVolume;
546 		YM3812RouteDirs[nIndex] = nRouteDir;
547 	}
548 
549 	if (nChip == 1) {
550 		YM3812Volumes[1 + nIndex] = nVolume;
551 		YM3812RouteDirs[1 + nIndex] = nRouteDir;
552 	}
553 }
554 
BurnYM3812Scan(INT32 nAction,INT32 * pnMin)555 void BurnYM3812Scan(INT32 nAction, INT32* pnMin)
556 {
557 #if defined FBA_DEBUG
558 	if (!DebugSnd_YM3812Initted) bprintf(PRINT_ERROR, _T("BurnYM3812Scan called without init\n"));
559 #endif
560 
561 	BurnTimerScanYM3812(nAction, pnMin);
562 	FMOPLScan(FM_OPL_SAVESTATE_YM3812, 0, nAction, pnMin);
563 
564 	if (nAction & ACB_DRIVER_DATA) {
565 		SCAN_VAR(nYM3812Position);
566 	}
567 }
568