]> git.proxmox.com Git - qemu.git/blob - hw/adlib.c
a49b32b53d6720e7a7fb29a56857d39c12ec18fa
[qemu.git] / hw / adlib.c
1 /*
2 * QEMU Adlib emulation
3 *
4 * Copyright (c) 2004 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 #include "vl.h"
25
26 #define AUDIO_CAP "adlib"
27 #include "audio/audio.h"
28
29 #ifdef USE_YMF262
30 #define HAS_YMF262 1
31 #include "ymf262.h"
32 void YMF262UpdateOneQEMU(int which, INT16 *dst, int length);
33 #define SHIFT 2
34 #else
35 #include "fmopl.h"
36 #define SHIFT 1
37 #endif
38
39 #ifdef _WIN32
40 #include <windows.h>
41 #define small_delay() Sleep (1)
42 #else
43 #define small_delay() usleep (1)
44 #endif
45
46 #define IO_READ_PROTO(name) \
47 uint32_t name (void *opaque, uint32_t nport)
48 #define IO_WRITE_PROTO(name) \
49 void name (void *opaque, uint32_t nport, uint32_t val)
50
51 static struct {
52 int port;
53 int freq;
54 } conf = {0x220, 44100};
55
56 typedef struct {
57 int enabled;
58 int active;
59 int cparam;
60 int64_t ticks;
61 int bufpos;
62 int16_t *mixbuf;
63 double interval;
64 QEMUTimer *ts, *opl_ts;
65 SWVoice *voice;
66 int left, pos, samples, bytes_per_second, old_free;
67 int refcount;
68 #ifndef USE_YMF262
69 FM_OPL *opl;
70 #endif
71 } AdlibState;
72
73 static AdlibState adlib;
74
75 static IO_WRITE_PROTO(adlib_write)
76 {
77 AdlibState *s = opaque;
78 int a = nport & 3;
79 int status;
80
81 s->ticks = qemu_get_clock (vm_clock);
82 s->active = 1;
83 AUD_enable (s->voice, 1);
84
85 #ifdef USE_YMF262
86 status = YMF262Write (0, a, val);
87 #else
88 status = OPLWrite (s->opl, a, val);
89 #endif
90 }
91
92 static IO_READ_PROTO(adlib_read)
93 {
94 AdlibState *s = opaque;
95 uint8_t data;
96 int a = nport & 3;
97
98 #ifdef USE_YMF262
99 (void) s;
100 data = YMF262Read (0, a);
101 #else
102 data = OPLRead (s->opl, a);
103 #endif
104 return data;
105 }
106
107 static void OPL_timer (void *opaque)
108 {
109 AdlibState *s = opaque;
110 #ifdef USE_YMF262
111 YMF262TimerOver (s->cparam >> 1, s->cparam & 1);
112 #else
113 OPLTimerOver (s->opl, s->cparam);
114 #endif
115 qemu_mod_timer (s->opl_ts, qemu_get_clock (vm_clock) + s->interval);
116 }
117
118 static void YMF262TimerHandler (int c, double interval_Sec)
119 {
120 AdlibState *s = &adlib;
121 if (interval_Sec == 0.0) {
122 qemu_del_timer (s->opl_ts);
123 return;
124 }
125 s->cparam = c;
126 s->interval = ticks_per_sec * interval_Sec;
127 qemu_mod_timer (s->opl_ts, qemu_get_clock (vm_clock) + s->interval);
128 small_delay ();
129 }
130
131 static int write_audio (AdlibState *s, int samples)
132 {
133 int net = 0;
134 int ss = samples;
135 while (samples) {
136 int nbytes = samples << SHIFT;
137 int wbytes = AUD_write (s->voice,
138 s->mixbuf + (s->pos << (SHIFT - 1)),
139 nbytes);
140 int wsampl = wbytes >> SHIFT;
141 samples -= wsampl;
142 s->pos = (s->pos + wsampl) % s->samples;
143 net += wsampl;
144 if (!wbytes)
145 break;
146 }
147 if (net > ss) {
148 dolog ("WARNING: net > ss\n");
149 }
150 return net;
151 }
152
153 static void timer (void *opaque)
154 {
155 AdlibState *s = opaque;
156 int elapsed, samples, net = 0;
157
158 if (s->refcount)
159 dolog ("refcount=%d\n", s->refcount);
160
161 s->refcount += 1;
162 if (!(s->active && s->enabled))
163 goto reset;
164
165 AUD_run ();
166
167 while (s->left) {
168 int written = write_audio (s, s->left);
169 net += written;
170 if (!written)
171 goto reset2;
172 s->left -= written;
173 }
174 s->pos = 0;
175
176 elapsed = AUD_calc_elapsed (s->voice);
177 if (!elapsed)
178 goto reset2;
179
180 /* elapsed = AUD_get_free (s->voice); */
181 samples = elapsed >> SHIFT;
182 if (!samples)
183 goto reset2;
184
185 samples = audio_MIN (samples, s->samples - s->pos);
186 if (s->left)
187 dolog ("left=%d samples=%d elapsed=%d free=%d\n",
188 s->left, samples, elapsed, AUD_get_free (s->voice));
189
190 if (!samples)
191 goto reset2;
192
193 #ifdef USE_YMF262
194 YMF262UpdateOneQEMU (0, s->mixbuf + s->pos * 2, samples);
195 #else
196 YM3812UpdateOne (s->opl, s->mixbuf + s->pos, samples);
197 #endif
198
199 while (samples) {
200 int written = write_audio (s, samples);
201 net += written;
202 if (!written)
203 break;
204 samples -= written;
205 }
206 if (!samples)
207 s->pos = 0;
208 s->left = samples;
209
210 reset2:
211 AUD_adjust (s->voice, net << SHIFT);
212 reset:
213 qemu_mod_timer (s->ts, qemu_get_clock (vm_clock) + ticks_per_sec / 1024);
214 s->refcount -= 1;
215 }
216
217 static void Adlib_fini (AdlibState *s)
218 {
219 #ifdef USE_YMF262
220 YMF262Shutdown ();
221 #else
222 if (s->opl) {
223 OPLDestroy (s->opl);
224 s->opl = NULL;
225 }
226 #endif
227
228 if (s->opl_ts)
229 qemu_free_timer (s->opl_ts);
230
231 if (s->ts)
232 qemu_free_timer (s->ts);
233
234 #define maybe_free(p) if (p) qemu_free (p)
235 maybe_free (s->mixbuf);
236 #undef maybe_free
237
238 s->active = 0;
239 s->enabled = 0;
240 }
241
242 void Adlib_init (void)
243 {
244 AdlibState *s = &adlib;
245
246 memset (s, 0, sizeof (*s));
247
248 #ifdef USE_YMF262
249 if (YMF262Init (1, 14318180, conf.freq)) {
250 dolog ("YMF262Init %d failed\n", conf.freq);
251 return;
252 }
253 else {
254 YMF262SetTimerHandler (0, YMF262TimerHandler, 0);
255 s->enabled = 1;
256 }
257 #else
258 s->opl = OPLCreate (OPL_TYPE_YM3812, 3579545, conf.freq);
259 if (!s->opl) {
260 dolog ("OPLCreate %d failed\n", conf.freq);
261 return;
262 }
263 else {
264 OPLSetTimerHandler (s->opl, YMF262TimerHandler, 0);
265 s->enabled = 1;
266 }
267 #endif
268
269 s->opl_ts = qemu_new_timer (vm_clock, OPL_timer, s);
270 if (!s->opl_ts) {
271 dolog ("Can not get timer for adlib emulation\n");
272 Adlib_fini (s);
273 return;
274 }
275
276 s->ts = qemu_new_timer (vm_clock, timer, s);
277 if (!s->opl_ts) {
278 dolog ("Can not get timer for adlib emulation\n");
279 Adlib_fini (s);
280 return;
281 }
282
283 s->voice = AUD_open (s->voice, "adlib", conf.freq, SHIFT, AUD_FMT_S16);
284 if (!s->voice) {
285 Adlib_fini (s);
286 return;
287 }
288
289 s->bytes_per_second = conf.freq << SHIFT;
290 s->samples = AUD_get_buffer_size (s->voice) >> SHIFT;
291 s->mixbuf = qemu_mallocz (s->samples << SHIFT);
292
293 if (!s->mixbuf) {
294 dolog ("not enough memory for adlib mixing buffer (%d)\n",
295 s->samples << SHIFT);
296 Adlib_fini (s);
297 return;
298 }
299 register_ioport_read (0x388, 4, 1, adlib_read, s);
300 register_ioport_write (0x388, 4, 1, adlib_write, s);
301
302 register_ioport_read (conf.port, 4, 1, adlib_read, s);
303 register_ioport_write (conf.port, 4, 1, adlib_write, s);
304
305 register_ioport_read (conf.port + 8, 2, 1, adlib_read, s);
306 register_ioport_write (conf.port + 8, 2, 1, adlib_write, s);
307
308 qemu_mod_timer (s->ts, qemu_get_clock (vm_clock) + 1);
309 }