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