|
1 /* |
|
2 * QEMU Proxy for OPL2/3 emulation by MAME team |
|
3 * |
|
4 * Copyright (c) 2004-2005 Vassili Karpov (malc) |
|
5 * |
|
6 * Permission is hereby granted, free of charge, to any person obtaining a copy |
|
7 * of this software and associated documentation files (the "Software"), to deal |
|
8 * in the Software without restriction, including without limitation the rights |
|
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|
10 * copies of the Software, and to permit persons to whom the Software is |
|
11 * furnished to do so, subject to the following conditions: |
|
12 * |
|
13 * The above copyright notice and this permission notice shall be included in |
|
14 * all copies or substantial portions of the Software. |
|
15 * |
|
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
|
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
|
22 * THE SOFTWARE. |
|
23 */ |
|
24 |
|
25 #include <assert.h> |
|
26 #include "hw.h" |
|
27 #include "audiodev.h" |
|
28 #include "audio/audio.h" |
|
29 #include "isa.h" |
|
30 |
|
31 //#define DEBUG |
|
32 |
|
33 #define ADLIB_KILL_TIMERS 1 |
|
34 |
|
35 #ifdef DEBUG |
|
36 #include "qemu-timer.h" |
|
37 #endif |
|
38 |
|
39 #define dolog(...) AUD_log ("adlib", __VA_ARGS__) |
|
40 #ifdef DEBUG |
|
41 #define ldebug(...) dolog (__VA_ARGS__) |
|
42 #else |
|
43 #define ldebug(...) |
|
44 #endif |
|
45 |
|
46 #ifdef HAS_YMF262 |
|
47 #include "ymf262.h" |
|
48 void YMF262UpdateOneQEMU (int which, INT16 *dst, int length); |
|
49 #define SHIFT 2 |
|
50 #else |
|
51 #include "fmopl.h" |
|
52 #define SHIFT 1 |
|
53 #endif |
|
54 |
|
55 #define IO_READ_PROTO(name) \ |
|
56 uint32_t name (void *opaque, uint32_t nport) |
|
57 #define IO_WRITE_PROTO(name) \ |
|
58 void name (void *opaque, uint32_t nport, uint32_t val) |
|
59 |
|
60 static struct { |
|
61 int port; |
|
62 int freq; |
|
63 } conf = {0x220, 44100}; |
|
64 |
|
65 typedef struct { |
|
66 QEMUSoundCard card; |
|
67 int ticking[2]; |
|
68 int enabled; |
|
69 int active; |
|
70 int bufpos; |
|
71 #ifdef DEBUG |
|
72 int64_t exp[2]; |
|
73 #endif |
|
74 int16_t *mixbuf; |
|
75 uint64_t dexp[2]; |
|
76 SWVoiceOut *voice; |
|
77 int left, pos, samples; |
|
78 QEMUAudioTimeStamp ats; |
|
79 #ifndef HAS_YMF262 |
|
80 FM_OPL *opl; |
|
81 #endif |
|
82 } AdlibState; |
|
83 |
|
84 static AdlibState glob_adlib; |
|
85 |
|
86 static void adlib_stop_opl_timer (AdlibState *s, size_t n) |
|
87 { |
|
88 #ifdef HAS_YMF262 |
|
89 YMF262TimerOver (0, n); |
|
90 #else |
|
91 OPLTimerOver (s->opl, n); |
|
92 #endif |
|
93 s->ticking[n] = 0; |
|
94 } |
|
95 |
|
96 static void adlib_kill_timers (AdlibState *s) |
|
97 { |
|
98 size_t i; |
|
99 |
|
100 for (i = 0; i < 2; ++i) { |
|
101 if (s->ticking[i]) { |
|
102 uint64_t delta; |
|
103 |
|
104 delta = AUD_get_elapsed_usec_out (s->voice, &s->ats); |
|
105 ldebug ( |
|
106 "delta = %f dexp = %f expired => %d\n", |
|
107 delta / 1000000.0, |
|
108 s->dexp[i] / 1000000.0, |
|
109 delta >= s->dexp[i] |
|
110 ); |
|
111 if (ADLIB_KILL_TIMERS || delta >= s->dexp[i]) { |
|
112 adlib_stop_opl_timer (s, i); |
|
113 AUD_init_time_stamp_out (s->voice, &s->ats); |
|
114 } |
|
115 } |
|
116 } |
|
117 } |
|
118 |
|
119 static IO_WRITE_PROTO(adlib_write) |
|
120 { |
|
121 AdlibState *s = opaque; |
|
122 int a = nport & 3; |
|
123 int status; |
|
124 |
|
125 s->active = 1; |
|
126 AUD_set_active_out (s->voice, 1); |
|
127 |
|
128 adlib_kill_timers (s); |
|
129 |
|
130 #ifdef HAS_YMF262 |
|
131 status = YMF262Write (0, a, val); |
|
132 #else |
|
133 status = OPLWrite (s->opl, a, val); |
|
134 #endif |
|
135 } |
|
136 |
|
137 static IO_READ_PROTO(adlib_read) |
|
138 { |
|
139 AdlibState *s = opaque; |
|
140 uint8_t data; |
|
141 int a = nport & 3; |
|
142 |
|
143 adlib_kill_timers (s); |
|
144 |
|
145 #ifdef HAS_YMF262 |
|
146 data = YMF262Read (0, a); |
|
147 #else |
|
148 data = OPLRead (s->opl, a); |
|
149 #endif |
|
150 return data; |
|
151 } |
|
152 |
|
153 static void timer_handler (int c, double interval_Sec) |
|
154 { |
|
155 AdlibState *s = &glob_adlib; |
|
156 unsigned n = c & 1; |
|
157 #ifdef DEBUG |
|
158 double interval; |
|
159 int64_t exp; |
|
160 #endif |
|
161 |
|
162 if (interval_Sec == 0.0) { |
|
163 s->ticking[n] = 0; |
|
164 return; |
|
165 } |
|
166 |
|
167 s->ticking[n] = 1; |
|
168 #ifdef DEBUG |
|
169 interval = ticks_per_sec * interval_Sec; |
|
170 exp = qemu_get_clock (vm_clock) + interval; |
|
171 s->exp[n] = exp; |
|
172 #endif |
|
173 |
|
174 s->dexp[n] = interval_Sec * 1000000.0; |
|
175 AUD_init_time_stamp_out (s->voice, &s->ats); |
|
176 } |
|
177 |
|
178 static int write_audio (AdlibState *s, int samples) |
|
179 { |
|
180 int net = 0; |
|
181 int pos = s->pos; |
|
182 |
|
183 while (samples) { |
|
184 int nbytes, wbytes, wsampl; |
|
185 |
|
186 nbytes = samples << SHIFT; |
|
187 wbytes = AUD_write ( |
|
188 s->voice, |
|
189 s->mixbuf + (pos << (SHIFT - 1)), |
|
190 nbytes |
|
191 ); |
|
192 |
|
193 if (wbytes) { |
|
194 wsampl = wbytes >> SHIFT; |
|
195 |
|
196 samples -= wsampl; |
|
197 pos = (pos + wsampl) % s->samples; |
|
198 |
|
199 net += wsampl; |
|
200 } |
|
201 else { |
|
202 break; |
|
203 } |
|
204 } |
|
205 |
|
206 return net; |
|
207 } |
|
208 |
|
209 static void adlib_callback (void *opaque, int free) |
|
210 { |
|
211 AdlibState *s = opaque; |
|
212 int samples, net = 0, to_play, written; |
|
213 |
|
214 samples = free >> SHIFT; |
|
215 if (!(s->active && s->enabled) || !samples) { |
|
216 return; |
|
217 } |
|
218 |
|
219 to_play = audio_MIN (s->left, samples); |
|
220 while (to_play) { |
|
221 written = write_audio (s, to_play); |
|
222 |
|
223 if (written) { |
|
224 s->left -= written; |
|
225 samples -= written; |
|
226 to_play -= written; |
|
227 s->pos = (s->pos + written) % s->samples; |
|
228 } |
|
229 else { |
|
230 return; |
|
231 } |
|
232 } |
|
233 |
|
234 samples = audio_MIN (samples, s->samples - s->pos); |
|
235 if (!samples) { |
|
236 return; |
|
237 } |
|
238 |
|
239 #ifdef HAS_YMF262 |
|
240 YMF262UpdateOneQEMU (0, s->mixbuf + s->pos * 2, samples); |
|
241 #else |
|
242 YM3812UpdateOne (s->opl, s->mixbuf + s->pos, samples); |
|
243 #endif |
|
244 |
|
245 while (samples) { |
|
246 written = write_audio (s, samples); |
|
247 |
|
248 if (written) { |
|
249 net += written; |
|
250 samples -= written; |
|
251 s->pos = (s->pos + written) % s->samples; |
|
252 } |
|
253 else { |
|
254 s->left = samples; |
|
255 return; |
|
256 } |
|
257 } |
|
258 } |
|
259 |
|
260 static void Adlib_fini (AdlibState *s) |
|
261 { |
|
262 #ifdef HAS_YMF262 |
|
263 YMF262Shutdown (); |
|
264 #else |
|
265 if (s->opl) { |
|
266 OPLDestroy (s->opl); |
|
267 s->opl = NULL; |
|
268 } |
|
269 #endif |
|
270 |
|
271 if (s->mixbuf) { |
|
272 qemu_free (s->mixbuf); |
|
273 } |
|
274 |
|
275 s->active = 0; |
|
276 s->enabled = 0; |
|
277 AUD_remove_card (&s->card); |
|
278 } |
|
279 |
|
280 int Adlib_init (AudioState *audio, qemu_irq *pic) |
|
281 { |
|
282 AdlibState *s = &glob_adlib; |
|
283 struct audsettings as; |
|
284 |
|
285 if (!audio) { |
|
286 dolog ("No audio state\n"); |
|
287 return -1; |
|
288 } |
|
289 |
|
290 #ifdef HAS_YMF262 |
|
291 if (YMF262Init (1, 14318180, conf.freq)) { |
|
292 dolog ("YMF262Init %d failed\n", conf.freq); |
|
293 return -1; |
|
294 } |
|
295 else { |
|
296 YMF262SetTimerHandler (0, timer_handler, 0); |
|
297 s->enabled = 1; |
|
298 } |
|
299 #else |
|
300 s->opl = OPLCreate (OPL_TYPE_YM3812, 3579545, conf.freq); |
|
301 if (!s->opl) { |
|
302 dolog ("OPLCreate %d failed\n", conf.freq); |
|
303 return -1; |
|
304 } |
|
305 else { |
|
306 OPLSetTimerHandler (s->opl, timer_handler, 0); |
|
307 s->enabled = 1; |
|
308 } |
|
309 #endif |
|
310 |
|
311 as.freq = conf.freq; |
|
312 as.nchannels = SHIFT; |
|
313 as.fmt = AUD_FMT_S16; |
|
314 as.endianness = AUDIO_HOST_ENDIANNESS; |
|
315 |
|
316 AUD_register_card (audio, "adlib", &s->card); |
|
317 |
|
318 s->voice = AUD_open_out ( |
|
319 &s->card, |
|
320 s->voice, |
|
321 "adlib", |
|
322 s, |
|
323 adlib_callback, |
|
324 &as |
|
325 ); |
|
326 if (!s->voice) { |
|
327 Adlib_fini (s); |
|
328 return -1; |
|
329 } |
|
330 |
|
331 s->samples = AUD_get_buffer_size_out (s->voice) >> SHIFT; |
|
332 s->mixbuf = qemu_mallocz (s->samples << SHIFT); |
|
333 |
|
334 if (!s->mixbuf) { |
|
335 dolog ("Could not allocate mixing buffer, %d samples (each %d bytes)\n", |
|
336 s->samples, 1 << SHIFT); |
|
337 Adlib_fini (s); |
|
338 return -1; |
|
339 } |
|
340 |
|
341 register_ioport_read (0x388, 4, 1, adlib_read, s); |
|
342 register_ioport_write (0x388, 4, 1, adlib_write, s); |
|
343 |
|
344 register_ioport_read (conf.port, 4, 1, adlib_read, s); |
|
345 register_ioport_write (conf.port, 4, 1, adlib_write, s); |
|
346 |
|
347 register_ioport_read (conf.port + 8, 2, 1, adlib_read, s); |
|
348 register_ioport_write (conf.port + 8, 2, 1, adlib_write, s); |
|
349 |
|
350 return 0; |
|
351 } |