1 #include "burnint.h"
2 #include "burn_ymf271.h"
3 
4 static INT32 (*BurnYMF271StreamCallback)(INT32 nSoundRate);
5 
6 static INT16* pBuffer;
7 static INT16* pYMF271Buffer[4];
8 
9 static INT32 bYMF271AddSignal;
10 
11 static UINT32 nSampleSize;
12 static INT32 nYMF271Position;
13 static INT32 nFractionalPosition;
14 
15 static INT32 nBurnYMF271SoundRate;
16 
17 static double YMF271Volumes[4];
18 static INT32 YMF271RouteDirs[4];
19 
20 // ----------------------------------------------------------------------------
21 // Dummy functions
22 
YMF271StreamCallbackDummy(INT32)23 static INT32 YMF271StreamCallbackDummy(INT32 /* nSoundRate */)
24 {
25 	return 0;
26 }
27 
28 
29 // ----------------------------------------------------------------------------
30 // Execute YMF271 for part of a frame
31 
YMF271Render(INT32 nSegmentLength)32 static void YMF271Render(INT32 nSegmentLength)
33 {
34 #if defined FBNEO_DEBUG
35 	if (!DebugSnd_YMF271Initted) bprintf(PRINT_ERROR, _T("YMF271Render called without init\n"));
36 #endif
37 
38 	if (nYMF271Position >= nSegmentLength || !pBurnSoundOut) {
39 		return;
40 	}
41 
42 //	bprintf(PRINT_NORMAL, _T("    YMF271 render %6i -> %6i\n"), nYMF271Position, nSegmentLength);
43 
44 	nSegmentLength -= nYMF271Position;
45 
46 	pYMF271Buffer[0] = pBuffer + 0 * 4096 + 4 + nYMF271Position;
47 	pYMF271Buffer[1] = pBuffer + 1 * 4096 + 4 + nYMF271Position;
48 	pYMF271Buffer[2] = pBuffer + 2 * 4096 + 4 + nYMF271Position;
49 	pYMF271Buffer[3] = pBuffer + 3 * 4096 + 4 + nYMF271Position;
50 
51 	ymf271_update(pYMF271Buffer, nSegmentLength);
52 
53 	nYMF271Position += nSegmentLength;
54 }
55 
56 // ----------------------------------------------------------------------------
57 // Update the sound buffer
58 
59 #define INTERPOLATE_ADD_SOUND_LEFT(route, buffer)																	\
60 	if ((YMF271RouteDirs[route] & BURN_SND_ROUTE_LEFT) == BURN_SND_ROUTE_LEFT) {									\
61 		nLeftSample[0] += (INT32)(pYMF271Buffer[buffer][(nFractionalPosition >> 16) - 3] * YMF271Volumes[route]);	\
62 		nLeftSample[1] += (INT32)(pYMF271Buffer[buffer][(nFractionalPosition >> 16) - 2] * YMF271Volumes[route]);	\
63 		nLeftSample[2] += (INT32)(pYMF271Buffer[buffer][(nFractionalPosition >> 16) - 1] * YMF271Volumes[route]);	\
64 		nLeftSample[3] += (INT32)(pYMF271Buffer[buffer][(nFractionalPosition >> 16) - 0] * YMF271Volumes[route]);	\
65 	}
66 
67 #define INTERPOLATE_ADD_SOUND_RIGHT(route, buffer)																	\
68 	if ((YMF271RouteDirs[route] & BURN_SND_ROUTE_RIGHT) == BURN_SND_ROUTE_RIGHT) {									\
69 		nRightSample[0] += (INT32)(pYMF271Buffer[buffer][(nFractionalPosition >> 16) - 3] * YMF271Volumes[route]);	\
70 		nRightSample[1] += (INT32)(pYMF271Buffer[buffer][(nFractionalPosition >> 16) - 2] * YMF271Volumes[route]);	\
71 		nRightSample[2] += (INT32)(pYMF271Buffer[buffer][(nFractionalPosition >> 16) - 1] * YMF271Volumes[route]);	\
72 		nRightSample[3] += (INT32)(pYMF271Buffer[buffer][(nFractionalPosition >> 16) - 0] * YMF271Volumes[route]);	\
73 	}
74 
BurnYMF271Update(INT32 nSegmentEnd)75 void BurnYMF271Update(INT32 nSegmentEnd)
76 {
77 #if defined FBNEO_DEBUG
78 	if (!DebugSnd_YMF271Initted) bprintf(PRINT_ERROR, _T("BurnYMF271Update called without init\n"));
79 #endif
80 
81 	INT16* pSoundBuf = pBurnSoundOut;
82 
83 	if (nBurnSoundRate == 0 || pBurnSoundOut == NULL) {
84 		return;
85 	}
86 
87 //	bprintf(PRINT_NORMAL, _T("    YMF271 render %6i -> %6i\n"), nYMF271Position, nSegmentEnd);
88 
89 	INT32 nSegmentLength = nSegmentEnd;
90 	INT32 nSamplesNeeded = nSegmentEnd * nBurnYMF271SoundRate / nBurnSoundRate + 1;
91 
92 	if (nSamplesNeeded < nYMF271Position) {
93 		nSamplesNeeded = nYMF271Position;
94 	}
95 
96 	if (nSegmentLength > nBurnSoundLen) {
97 		nSegmentLength = nBurnSoundLen;
98 	}
99 	nSegmentLength <<= 1;
100 
101 	YMF271Render(nSamplesNeeded);
102 
103 	pYMF271Buffer[0] = pBuffer + 0 * 4096 + 4;
104 	pYMF271Buffer[1] = pBuffer + 1 * 4096 + 4;
105 	pYMF271Buffer[2] = pBuffer + 2 * 4096 + 4;
106 	pYMF271Buffer[3] = pBuffer + 3 * 4096 + 4;
107 
108 	for (INT32 i = (nFractionalPosition & 0xFFFF0000) >> 15; i < nSegmentLength; i += 2, nFractionalPosition += nSampleSize) {
109 		INT32 nLeftSample[4] = {0, 0, 0, 0};
110 		INT32 nRightSample[4] = {0, 0, 0, 0};
111 		INT32 nTotalLeftSample, nTotalRightSample;
112 
113 		INTERPOLATE_ADD_SOUND_LEFT  (BURN_SND_YMF271_YMF271_ROUTE_1, 0)
114 		INTERPOLATE_ADD_SOUND_RIGHT (BURN_SND_YMF271_YMF271_ROUTE_1, 0)
115 		INTERPOLATE_ADD_SOUND_LEFT  (BURN_SND_YMF271_YMF271_ROUTE_2, 1)
116 		INTERPOLATE_ADD_SOUND_RIGHT (BURN_SND_YMF271_YMF271_ROUTE_2, 1)
117 		INTERPOLATE_ADD_SOUND_LEFT  (BURN_SND_YMF271_YMF271_ROUTE_3, 2)
118 		INTERPOLATE_ADD_SOUND_RIGHT (BURN_SND_YMF271_YMF271_ROUTE_3, 2)
119 		INTERPOLATE_ADD_SOUND_LEFT  (BURN_SND_YMF271_YMF271_ROUTE_4, 3)
120 		INTERPOLATE_ADD_SOUND_RIGHT (BURN_SND_YMF271_YMF271_ROUTE_4, 3)
121 
122 		nTotalLeftSample  = INTERPOLATE4PS_16BIT((nFractionalPosition >> 4) & 0x0fff, nLeftSample[0], nLeftSample[1], nLeftSample[2], nLeftSample[3]);
123 		nTotalRightSample = INTERPOLATE4PS_16BIT((nFractionalPosition >> 4) & 0x0fff, nRightSample[0], nRightSample[1], nRightSample[2], nRightSample[3]);
124 
125 		nTotalLeftSample  = BURN_SND_CLIP(nTotalLeftSample);
126 		nTotalRightSample = BURN_SND_CLIP(nTotalRightSample);
127 
128 		if (bYMF271AddSignal) {
129 			pSoundBuf[i + 0] = BURN_SND_CLIP(pSoundBuf[i + 0] + nTotalLeftSample);
130 			pSoundBuf[i + 1] = BURN_SND_CLIP(pSoundBuf[i + 1] + nTotalRightSample);
131 		} else {
132 			pSoundBuf[i + 0] = nTotalLeftSample;
133 			pSoundBuf[i + 1] = nTotalRightSample;
134 		}
135 	}
136 
137 	if (nSegmentEnd >= nBurnSoundLen) {
138 		INT32 nExtraSamples = nSamplesNeeded - (nFractionalPosition >> 16);
139 
140 		for (INT32 i = -4; i < nExtraSamples; i++) {
141 			pYMF271Buffer[0][i] = pYMF271Buffer[0][(nFractionalPosition >> 16) + i];
142 			pYMF271Buffer[1][i] = pYMF271Buffer[1][(nFractionalPosition >> 16) + i];
143 			pYMF271Buffer[2][i] = pYMF271Buffer[2][(nFractionalPosition >> 16) + i];
144 			pYMF271Buffer[3][i] = pYMF271Buffer[3][(nFractionalPosition >> 16) + i];
145 		}
146 
147 		nFractionalPosition &= 0xFFFF;
148 
149 		nYMF271Position = nExtraSamples;
150 	}
151 }
152 
153 // ----------------------------------------------------------------------------
154 
BurnYMF271UpdateStream()155 void BurnYMF271UpdateStream()
156 {
157 	YMF271Render(BurnYMF271StreamCallback(nBurnYMF271SoundRate));
158 }
159 
BurnYMF271Write(INT32 nAddress,UINT8 nValue)160 void BurnYMF271Write(INT32 nAddress, UINT8 nValue)
161 {
162 #if defined FBNEO_DEBUG
163 	if (!DebugSnd_YMF271Initted) bprintf(PRINT_ERROR, _T("BurnYMF271Write called without init\n"));
164 #endif
165 
166 	YMF271Render(BurnYMF271StreamCallback(nBurnYMF271SoundRate));
167 	ymf271_write(nAddress, nValue);
168 }
169 
BurnYMF271Read(INT32 nAddress)170 UINT8 BurnYMF271Read(INT32 nAddress)
171 {
172 #if defined FBNEO_DEBUG
173 	if (!DebugSnd_YMF271Initted) bprintf(PRINT_ERROR, _T("BurnYMF271Read called without init\n"));
174 #endif
175 
176 	YMF271Render(BurnYMF271StreamCallback(nBurnYMF271SoundRate));
177 	return ymf271_read(nAddress);
178 }
179 
180 
181 // ----------------------------------------------------------------------------
ymf271_timerover(int,int c)182 static int ymf271_timerover(int /*num*/, int c)
183 {
184 	return ymf271_timer_over(c);
185 }
186 
187 
BurnYMF271Reset()188 void BurnYMF271Reset()
189 {
190 #if defined FBNEO_DEBUG
191 	if (!DebugSnd_YMF271Initted) bprintf(PRINT_ERROR, _T("BurnYMF271Reset called without init\n"));
192 #endif
193 
194 	BurnTimerReset();
195 	ymf271_reset();
196 }
197 
BurnYMF271Exit()198 void BurnYMF271Exit()
199 {
200 #if defined FBNEO_DEBUG
201 	if (!DebugSnd_YMF271Initted) bprintf(PRINT_ERROR, _T("BurnYMF271Exit called without init\n"));
202 #endif
203 
204 	if (!DebugSnd_YMF271Initted) return;
205 
206 	ymf271_exit();
207 
208 	BurnTimerExit();
209 
210 	BurnFree(pBuffer);
211 
212 	DebugSnd_YMF271Initted = 0;
213 }
214 
BurnYMF271Init(INT32 nClockFrequency,UINT8 * rom,INT32 romsize,void (* IRQCallback)(INT32,INT32),INT32 nAdd)215 INT32 BurnYMF271Init(INT32 nClockFrequency, UINT8 *rom, INT32 romsize, void (*IRQCallback)(INT32, INT32), INT32 nAdd)
216 {
217 	return BurnYMF271Init(nClockFrequency, rom, romsize, IRQCallback, BurnSynchroniseStream, nAdd);
218 }
219 
BurnYMF271Init(INT32 nClockFrequency,UINT8 * rom,INT32 romsize,void (* IRQCallback)(INT32,INT32),INT32 (* StreamCallback)(INT32),INT32 nAdd)220 INT32 BurnYMF271Init(INT32 nClockFrequency, UINT8 *rom, INT32 romsize, void (*IRQCallback)(INT32, INT32), INT32 (*StreamCallback)(INT32), INT32 nAdd)
221 {
222 	DebugSnd_YMF271Initted = 1;
223 
224 	BurnYMF271StreamCallback = YMF271StreamCallbackDummy;
225 	if (StreamCallback) {
226 		BurnYMF271StreamCallback = StreamCallback;
227 	}
228 
229 	nBurnYMF271SoundRate = nClockFrequency / 384; // hw rate based on input clock
230 
231 	nSampleSize = (UINT32)nBurnYMF271SoundRate * (1 << 16) / nBurnSoundRate;
232 	bYMF271AddSignal = nAdd;
233 
234 	BurnTimerInit(&ymf271_timerover, NULL);
235 	ymf271_init(nClockFrequency, rom, romsize, IRQCallback, BurnYMF262TimerCallback);
236 
237 	pBuffer = (INT16*)BurnMalloc((4096 + 4) * 4 * sizeof(INT16));
238 	memset(pBuffer, 0, (4096 + 4) * 4 * sizeof(INT16));
239 
240 	nYMF271Position = 0;
241 	nFractionalPosition = 0;
242 
243 	// default routes
244 	YMF271Volumes[BURN_SND_YMF271_YMF271_ROUTE_1] = 1.00;
245 	YMF271Volumes[BURN_SND_YMF271_YMF271_ROUTE_2] = 1.00;
246 	YMF271RouteDirs[BURN_SND_YMF271_YMF271_ROUTE_1] = BURN_SND_ROUTE_LEFT;
247 	YMF271RouteDirs[BURN_SND_YMF271_YMF271_ROUTE_2] = BURN_SND_ROUTE_RIGHT;
248 
249 	YMF271Volumes[BURN_SND_YMF271_YMF271_ROUTE_3] = 1.00;
250 	YMF271Volumes[BURN_SND_YMF271_YMF271_ROUTE_4] = 1.00;
251 	YMF271RouteDirs[BURN_SND_YMF271_YMF271_ROUTE_3] = BURN_SND_ROUTE_LEFT;
252 	YMF271RouteDirs[BURN_SND_YMF271_YMF271_ROUTE_4] = BURN_SND_ROUTE_RIGHT;
253 
254 	return 0;
255 }
256 
BurnYMF271SetRoute(INT32 nIndex,double nVolume,INT32 nRouteDir)257 void BurnYMF271SetRoute(INT32 nIndex, double nVolume, INT32 nRouteDir)
258 {
259 #if defined FBNEO_DEBUG
260 	if (!DebugSnd_YMF271Initted) bprintf(PRINT_ERROR, _T("BurnYMF271SetRoute called without init\n"));
261 	if (nIndex < 0 || nIndex > 3) bprintf(PRINT_ERROR, _T("BurnYMF271SetRoute called with invalid index %i\n"), nIndex);
262 #endif
263 
264 	YMF271Volumes[nIndex] = nVolume;
265 	YMF271RouteDirs[nIndex] = nRouteDir;
266 }
267 
BurnYMF271Scan(INT32 nAction,INT32 * pnMin)268 void BurnYMF271Scan(INT32 nAction, INT32* pnMin)
269 {
270 #if defined FBNEO_DEBUG
271 	if (!DebugSnd_YMF271Initted) bprintf(PRINT_ERROR, _T("BurnYMF271Scan called without init\n"));
272 #endif
273 
274 	BurnTimerScan(nAction, pnMin);
275 	ymf271_scan(nAction);
276 
277 	if (nAction & ACB_WRITE && ~nAction & ACB_RUNAHEAD) {
278 		nYMF271Position = 0;
279 		nFractionalPosition = 0;
280 		memset(pBuffer, 0, 4096 * 2 * sizeof(INT16));
281 	}
282 }
283