]> git.proxmox.com Git - mirror_qemu.git/blame - hw/audio/adlib.c
Include qemu/module.h where needed, drop it from qemu-common.h
[mirror_qemu.git] / hw / audio / adlib.c
CommitLineData
85571bc7 1/*
1d14ffa9
FB
2 * QEMU Proxy for OPL2/3 emulation by MAME team
3 *
4 * Copyright (c) 2004-2005 Vassili Karpov (malc)
5 *
85571bc7
FB
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 */
9f0683d9 24
6086a565 25#include "qemu/osdep.h"
da34e65c 26#include "qapi/error.h"
0b8fa32f 27#include "qemu/module.h"
83c9f4ca 28#include "hw/hw.h"
8a824e4d 29#include "hw/audio/soundhw.h"
e140e05c 30#include "audio/audio.h"
0d09e41a 31#include "hw/isa/isa.h"
85571bc7 32
9f0683d9
TS
33//#define DEBUG
34
1d14ffa9
FB
35#define ADLIB_KILL_TIMERS 1
36
8c444a19 37#define ADLIB_DESC "Yamaha YM3812 (OPL2)"
8c444a19 38
9f0683d9 39#ifdef DEBUG
1de7afc9 40#include "qemu/timer.h"
9f0683d9
TS
41#endif
42
fb065187
FB
43#define dolog(...) AUD_log ("adlib", __VA_ARGS__)
44#ifdef DEBUG
45#define ldebug(...) dolog (__VA_ARGS__)
46#else
47#define ldebug(...)
48#endif
85571bc7 49
47b43a1f 50#include "fmopl.h"
85571bc7 51#define SHIFT 1
85571bc7 52
8c444a19
PB
53#define TYPE_ADLIB "adlib"
54#define ADLIB(obj) OBJECT_CHECK(AdlibState, (obj), TYPE_ADLIB)
85571bc7
FB
55
56typedef struct {
8c444a19
PB
57 ISADevice parent_obj;
58
c0fe3827 59 QEMUSoundCard card;
8c444a19
PB
60 uint32_t freq;
61 uint32_t port;
1d14ffa9 62 int ticking[2];
85571bc7
FB
63 int enabled;
64 int active;
85571bc7 65 int bufpos;
1d14ffa9
FB
66#ifdef DEBUG
67 int64_t exp[2];
68#endif
85571bc7 69 int16_t *mixbuf;
1d14ffa9
FB
70 uint64_t dexp[2];
71 SWVoiceOut *voice;
72 int left, pos, samples;
73 QEMUAudioTimeStamp ats;
85571bc7 74 FM_OPL *opl;
848696bf 75 PortioList port_list;
85571bc7
FB
76} AdlibState;
77
1d14ffa9
FB
78static void adlib_stop_opl_timer (AdlibState *s, size_t n)
79{
1d14ffa9 80 OPLTimerOver (s->opl, n);
1d14ffa9
FB
81 s->ticking[n] = 0;
82}
83
84static void adlib_kill_timers (AdlibState *s)
85{
86 size_t i;
87
88 for (i = 0; i < 2; ++i) {
89 if (s->ticking[i]) {
90 uint64_t delta;
91
c0fe3827 92 delta = AUD_get_elapsed_usec_out (s->voice, &s->ats);
1d14ffa9
FB
93 ldebug (
94 "delta = %f dexp = %f expired => %d\n",
95 delta / 1000000.0,
96 s->dexp[i] / 1000000.0,
97 delta >= s->dexp[i]
98 );
99 if (ADLIB_KILL_TIMERS || delta >= s->dexp[i]) {
100 adlib_stop_opl_timer (s, i);
101 AUD_init_time_stamp_out (s->voice, &s->ats);
102 }
103 }
104 }
105}
106
8307c294 107static void adlib_write(void *opaque, uint32_t nport, uint32_t val)
85571bc7
FB
108{
109 AdlibState *s = opaque;
110 int a = nport & 3;
85571bc7 111
85571bc7 112 s->active = 1;
1d14ffa9 113 AUD_set_active_out (s->voice, 1);
85571bc7 114
1d14ffa9
FB
115 adlib_kill_timers (s);
116
e8beeae4 117 OPLWrite (s->opl, a, val);
85571bc7
FB
118}
119
8307c294 120static uint32_t adlib_read(void *opaque, uint32_t nport)
85571bc7
FB
121{
122 AdlibState *s = opaque;
123 uint8_t data;
124 int a = nport & 3;
125
1d14ffa9 126 adlib_kill_timers (s);
85571bc7 127 data = OPLRead (s->opl, a);
68883a40 128
85571bc7
FB
129 return data;
130}
131
c57fbf50 132static void timer_handler (void *opaque, int c, double interval_Sec)
85571bc7 133{
639b49ef 134 AdlibState *s = opaque;
1d14ffa9
FB
135 unsigned n = c & 1;
136#ifdef DEBUG
137 double interval;
c0fe3827 138 int64_t exp;
85571bc7 139#endif
85571bc7 140
85571bc7 141 if (interval_Sec == 0.0) {
1d14ffa9 142 s->ticking[n] = 0;
85571bc7
FB
143 return;
144 }
1d14ffa9
FB
145
146 s->ticking[n] = 1;
147#ifdef DEBUG
73bcb24d 148 interval = NANOSECONDS_PER_SECOND * interval_Sec;
bc72ad67 149 exp = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + interval;
1d14ffa9
FB
150 s->exp[n] = exp;
151#endif
152
153 s->dexp[n] = interval_Sec * 1000000.0;
154 AUD_init_time_stamp_out (s->voice, &s->ats);
85571bc7
FB
155}
156
157static int write_audio (AdlibState *s, int samples)
158{
159 int net = 0;
1d14ffa9
FB
160 int pos = s->pos;
161
85571bc7 162 while (samples) {
1d14ffa9
FB
163 int nbytes, wbytes, wsampl;
164
165 nbytes = samples << SHIFT;
166 wbytes = AUD_write (
167 s->voice,
168 s->mixbuf + (pos << (SHIFT - 1)),
169 nbytes
170 );
171
172 if (wbytes) {
173 wsampl = wbytes >> SHIFT;
174
175 samples -= wsampl;
176 pos = (pos + wsampl) % s->samples;
177
178 net += wsampl;
179 }
180 else {
85571bc7 181 break;
1d14ffa9 182 }
85571bc7 183 }
1d14ffa9 184
85571bc7
FB
185 return net;
186}
187
1d14ffa9 188static void adlib_callback (void *opaque, int free)
85571bc7
FB
189{
190 AdlibState *s = opaque;
1d14ffa9 191 int samples, net = 0, to_play, written;
85571bc7 192
1d14ffa9
FB
193 samples = free >> SHIFT;
194 if (!(s->active && s->enabled) || !samples) {
195 return;
85571bc7 196 }
85571bc7 197
1d14ffa9
FB
198 to_play = audio_MIN (s->left, samples);
199 while (to_play) {
200 written = write_audio (s, to_play);
201
202 if (written) {
203 s->left -= written;
204 samples -= written;
205 to_play -= written;
206 s->pos = (s->pos + written) % s->samples;
207 }
208 else {
209 return;
210 }
211 }
85571bc7
FB
212
213 samples = audio_MIN (samples, s->samples - s->pos);
1d14ffa9
FB
214 if (!samples) {
215 return;
216 }
85571bc7 217
85571bc7 218 YM3812UpdateOne (s->opl, s->mixbuf + s->pos, samples);
85571bc7
FB
219
220 while (samples) {
1d14ffa9
FB
221 written = write_audio (s, samples);
222
223 if (written) {
224 net += written;
225 samples -= written;
226 s->pos = (s->pos + written) % s->samples;
227 }
228 else {
229 s->left = samples;
230 return;
231 }
85571bc7 232 }
85571bc7
FB
233}
234
235static void Adlib_fini (AdlibState *s)
236{
85571bc7
FB
237 if (s->opl) {
238 OPLDestroy (s->opl);
239 s->opl = NULL;
240 }
85571bc7 241
fb7da626 242 g_free(s->mixbuf);
85571bc7
FB
243
244 s->active = 0;
245 s->enabled = 0;
c0fe3827 246 AUD_remove_card (&s->card);
85571bc7
FB
247}
248
a8aec295 249static MemoryRegionPortio adlib_portio_list[] = {
a8aec295
JK
250 { 0, 4, 1, .read = adlib_read, .write = adlib_write, },
251 { 0, 2, 1, .read = adlib_read, .write = adlib_write, },
2b21fb57 252 { 0x388, 4, 1, .read = adlib_read, .write = adlib_write, },
a8aec295
JK
253 PORTIO_END_OF_LIST(),
254};
255
db895a1e 256static void adlib_realizefn (DeviceState *dev, Error **errp)
85571bc7 257{
8c444a19 258 AdlibState *s = ADLIB(dev);
1ea879e5 259 struct audsettings as;
c0fe3827 260
8f7e2c2c 261 s->opl = OPLCreate (3579545, s->freq);
85571bc7 262 if (!s->opl) {
db895a1e
AF
263 error_setg (errp, "OPLCreate %d failed", s->freq);
264 return;
85571bc7
FB
265 }
266 else {
639b49ef 267 OPLSetTimerHandler(s->opl, timer_handler, s);
85571bc7
FB
268 s->enabled = 1;
269 }
85571bc7 270
8c444a19 271 as.freq = s->freq;
c0fe3827 272 as.nchannels = SHIFT;
85bc5852 273 as.fmt = AUDIO_FORMAT_S16;
d929eba5 274 as.endianness = AUDIO_HOST_ENDIANNESS;
c0fe3827 275
1a7dafce 276 AUD_register_card ("adlib", &s->card);
c0fe3827 277
1d14ffa9 278 s->voice = AUD_open_out (
c0fe3827 279 &s->card,
1d14ffa9
FB
280 s->voice,
281 "adlib",
282 s,
283 adlib_callback,
d929eba5 284 &as
1d14ffa9 285 );
85571bc7
FB
286 if (!s->voice) {
287 Adlib_fini (s);
db895a1e
AF
288 error_setg (errp, "Initializing audio voice failed");
289 return;
85571bc7
FB
290 }
291
1d14ffa9 292 s->samples = AUD_get_buffer_size_out (s->voice) >> SHIFT;
7267c094 293 s->mixbuf = g_malloc0 (s->samples << SHIFT);
85571bc7 294
7f0ba7bb
PB
295 adlib_portio_list[0].offset = s->port;
296 adlib_portio_list[1].offset = s->port + 8;
848696bf
KB
297 portio_list_init (&s->port_list, OBJECT(s), adlib_portio_list, s, "adlib");
298 portio_list_add (&s->port_list, isa_address_space_io(&s->parent_obj), 0);
85571bc7 299}
8c444a19
PB
300
301static Property adlib_properties[] = {
c7bcc85d 302 DEFINE_PROP_UINT32 ("iobase", AdlibState, port, 0x220),
8c444a19
PB
303 DEFINE_PROP_UINT32 ("freq", AdlibState, freq, 44100),
304 DEFINE_PROP_END_OF_LIST (),
305};
306
307static void adlib_class_initfn (ObjectClass *klass, void *data)
308{
309 DeviceClass *dc = DEVICE_CLASS (klass);
db895a1e
AF
310
311 dc->realize = adlib_realizefn;
125ee0ed 312 set_bit(DEVICE_CATEGORY_SOUND, dc->categories);
8c444a19
PB
313 dc->desc = ADLIB_DESC;
314 dc->props = adlib_properties;
315}
316
317static const TypeInfo adlib_info = {
318 .name = TYPE_ADLIB,
319 .parent = TYPE_ISA_DEVICE,
320 .instance_size = sizeof (AdlibState),
321 .class_init = adlib_class_initfn,
322};
323
36cd6f6f 324static int Adlib_init (ISABus *bus)
8c444a19
PB
325{
326 isa_create_simple (bus, TYPE_ADLIB);
327 return 0;
328}
329
330static void adlib_register_types (void)
331{
332 type_register_static (&adlib_info);
36cd6f6f 333 isa_register_soundhw("adlib", ADLIB_DESC, Adlib_init);
8c444a19
PB
334}
335
336type_init (adlib_register_types)