Statistics
| Branch: | Revision:

root / hw / audio / adlib.c @ a8aec295

History | View | Annotate | Download (8.8 kB)

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 "hw/hw.h"
26
#include "hw/audio/audio.h"
27
#include "audio/audio.h"
28
#include "hw/isa/isa.h"
29

    
30
//#define DEBUG
31

    
32
#define ADLIB_KILL_TIMERS 1
33

    
34
#ifdef HAS_YMF262
35
#define ADLIB_DESC "Yamaha YMF262 (OPL3)"
36
#else
37
#define ADLIB_DESC "Yamaha YM3812 (OPL2)"
38
#endif
39

    
40
#ifdef DEBUG
41
#include "qemu/timer.h"
42
#endif
43

    
44
#define dolog(...) AUD_log ("adlib", __VA_ARGS__)
45
#ifdef DEBUG
46
#define ldebug(...) dolog (__VA_ARGS__)
47
#else
48
#define ldebug(...)
49
#endif
50

    
51
#ifdef HAS_YMF262
52
#include "ymf262.h"
53
void YMF262UpdateOneQEMU (int which, INT16 *dst, int length);
54
#define SHIFT 2
55
#else
56
#include "fmopl.h"
57
#define SHIFT 1
58
#endif
59

    
60
#define IO_READ_PROTO(name) \
61
    uint32_t name (void *opaque, uint32_t nport)
62
#define IO_WRITE_PROTO(name) \
63
    void name (void *opaque, uint32_t nport, uint32_t val)
64

    
65
#define TYPE_ADLIB "adlib"
66
#define ADLIB(obj) OBJECT_CHECK(AdlibState, (obj), TYPE_ADLIB)
67

    
68
typedef struct {
69
    ISADevice parent_obj;
70

    
71
    QEMUSoundCard card;
72
    uint32_t freq;
73
    uint32_t port;
74
    int ticking[2];
75
    int enabled;
76
    int active;
77
    int bufpos;
78
#ifdef DEBUG
79
    int64_t exp[2];
80
#endif
81
    int16_t *mixbuf;
82
    uint64_t dexp[2];
83
    SWVoiceOut *voice;
84
    int left, pos, samples;
85
    QEMUAudioTimeStamp ats;
86
#ifndef HAS_YMF262
87
    FM_OPL *opl;
88
#endif
89
} AdlibState;
90

    
91
static AdlibState *glob_adlib;
92

    
93
static void adlib_stop_opl_timer (AdlibState *s, size_t n)
94
{
95
#ifdef HAS_YMF262
96
    YMF262TimerOver (0, n);
97
#else
98
    OPLTimerOver (s->opl, n);
99
#endif
100
    s->ticking[n] = 0;
101
}
102

    
103
static void adlib_kill_timers (AdlibState *s)
104
{
105
    size_t i;
106

    
107
    for (i = 0; i < 2; ++i) {
108
        if (s->ticking[i]) {
109
            uint64_t delta;
110

    
111
            delta = AUD_get_elapsed_usec_out (s->voice, &s->ats);
112
            ldebug (
113
                "delta = %f dexp = %f expired => %d\n",
114
                delta / 1000000.0,
115
                s->dexp[i] / 1000000.0,
116
                delta >= s->dexp[i]
117
                );
118
            if (ADLIB_KILL_TIMERS || delta >= s->dexp[i]) {
119
                adlib_stop_opl_timer (s, i);
120
                AUD_init_time_stamp_out (s->voice, &s->ats);
121
            }
122
        }
123
    }
124
}
125

    
126
static IO_WRITE_PROTO (adlib_write)
127
{
128
    AdlibState *s = opaque;
129
    int a = nport & 3;
130

    
131
    s->active = 1;
132
    AUD_set_active_out (s->voice, 1);
133

    
134
    adlib_kill_timers (s);
135

    
136
#ifdef HAS_YMF262
137
    YMF262Write (0, a, val);
138
#else
139
    OPLWrite (s->opl, a, val);
140
#endif
141
}
142

    
143
static IO_READ_PROTO (adlib_read)
144
{
145
    AdlibState *s = opaque;
146
    uint8_t data;
147
    int a = nport & 3;
148

    
149
    adlib_kill_timers (s);
150

    
151
#ifdef HAS_YMF262
152
    data = YMF262Read (0, a);
153
#else
154
    data = OPLRead (s->opl, a);
155
#endif
156
    return data;
157
}
158

    
159
static void timer_handler (int c, double interval_Sec)
160
{
161
    AdlibState *s = glob_adlib;
162
    unsigned n = c & 1;
163
#ifdef DEBUG
164
    double interval;
165
    int64_t exp;
166
#endif
167

    
168
    if (interval_Sec == 0.0) {
169
        s->ticking[n] = 0;
170
        return;
171
    }
172

    
173
    s->ticking[n] = 1;
174
#ifdef DEBUG
175
    interval = get_ticks_per_sec () * interval_Sec;
176
    exp = qemu_get_clock_ns (vm_clock) + interval;
177
    s->exp[n] = exp;
178
#endif
179

    
180
    s->dexp[n] = interval_Sec * 1000000.0;
181
    AUD_init_time_stamp_out (s->voice, &s->ats);
182
}
183

    
184
static int write_audio (AdlibState *s, int samples)
185
{
186
    int net = 0;
187
    int pos = s->pos;
188

    
189
    while (samples) {
190
        int nbytes, wbytes, wsampl;
191

    
192
        nbytes = samples << SHIFT;
193
        wbytes = AUD_write (
194
            s->voice,
195
            s->mixbuf + (pos << (SHIFT - 1)),
196
            nbytes
197
            );
198

    
199
        if (wbytes) {
200
            wsampl = wbytes >> SHIFT;
201

    
202
            samples -= wsampl;
203
            pos = (pos + wsampl) % s->samples;
204

    
205
            net += wsampl;
206
        }
207
        else {
208
            break;
209
        }
210
    }
211

    
212
    return net;
213
}
214

    
215
static void adlib_callback (void *opaque, int free)
216
{
217
    AdlibState *s = opaque;
218
    int samples, net = 0, to_play, written;
219

    
220
    samples = free >> SHIFT;
221
    if (!(s->active && s->enabled) || !samples) {
222
        return;
223
    }
224

    
225
    to_play = audio_MIN (s->left, samples);
226
    while (to_play) {
227
        written = write_audio (s, to_play);
228

    
229
        if (written) {
230
            s->left -= written;
231
            samples -= written;
232
            to_play -= written;
233
            s->pos = (s->pos + written) % s->samples;
234
        }
235
        else {
236
            return;
237
        }
238
    }
239

    
240
    samples = audio_MIN (samples, s->samples - s->pos);
241
    if (!samples) {
242
        return;
243
    }
244

    
245
#ifdef HAS_YMF262
246
    YMF262UpdateOneQEMU (0, s->mixbuf + s->pos * 2, samples);
247
#else
248
    YM3812UpdateOne (s->opl, s->mixbuf + s->pos, samples);
249
#endif
250

    
251
    while (samples) {
252
        written = write_audio (s, samples);
253

    
254
        if (written) {
255
            net += written;
256
            samples -= written;
257
            s->pos = (s->pos + written) % s->samples;
258
        }
259
        else {
260
            s->left = samples;
261
            return;
262
        }
263
    }
264
}
265

    
266
static void Adlib_fini (AdlibState *s)
267
{
268
#ifdef HAS_YMF262
269
    YMF262Shutdown ();
270
#else
271
    if (s->opl) {
272
        OPLDestroy (s->opl);
273
        s->opl = NULL;
274
    }
275
#endif
276

    
277
    if (s->mixbuf) {
278
        g_free (s->mixbuf);
279
    }
280

    
281
    s->active = 0;
282
    s->enabled = 0;
283
    AUD_remove_card (&s->card);
284
}
285

    
286
static MemoryRegionPortio adlib_portio_list[] = {
287
    { 0x388, 4, 1, .read = adlib_read, .write = adlib_write, },
288
    { 0, 4, 1, .read = adlib_read, .write = adlib_write, },
289
    { 0, 2, 1, .read = adlib_read, .write = adlib_write, },
290
    PORTIO_END_OF_LIST(),
291
};
292

    
293
static void adlib_realizefn (DeviceState *dev, Error **errp)
294
{
295
    AdlibState *s = ADLIB(dev);
296
    PortioList *port_list = g_new(PortioList, 1);
297
    struct audsettings as;
298

    
299
    if (glob_adlib) {
300
        error_setg (errp, "Cannot create more than 1 adlib device");
301
        return;
302
    }
303
    glob_adlib = s;
304

    
305
#ifdef HAS_YMF262
306
    if (YMF262Init (1, 14318180, s->freq)) {
307
        error_setg (errp, "YMF262Init %d failed", s->freq);
308
        return;
309
    }
310
    else {
311
        YMF262SetTimerHandler (0, timer_handler, 0);
312
        s->enabled = 1;
313
    }
314
#else
315
    s->opl = OPLCreate (OPL_TYPE_YM3812, 3579545, s->freq);
316
    if (!s->opl) {
317
        error_setg (errp, "OPLCreate %d failed", s->freq);
318
        return;
319
    }
320
    else {
321
        OPLSetTimerHandler (s->opl, timer_handler, 0);
322
        s->enabled = 1;
323
    }
324
#endif
325

    
326
    as.freq = s->freq;
327
    as.nchannels = SHIFT;
328
    as.fmt = AUD_FMT_S16;
329
    as.endianness = AUDIO_HOST_ENDIANNESS;
330

    
331
    AUD_register_card ("adlib", &s->card);
332

    
333
    s->voice = AUD_open_out (
334
        &s->card,
335
        s->voice,
336
        "adlib",
337
        s,
338
        adlib_callback,
339
        &as
340
        );
341
    if (!s->voice) {
342
        Adlib_fini (s);
343
        error_setg (errp, "Initializing audio voice failed");
344
        return;
345
    }
346

    
347
    s->samples = AUD_get_buffer_size_out (s->voice) >> SHIFT;
348
    s->mixbuf = g_malloc0 (s->samples << SHIFT);
349

    
350
    adlib_portio_list[1].offset = s->port;
351
    adlib_portio_list[2].offset = s->port + 8;
352
    portio_list_init (port_list, adlib_portio_list, s, "adlib");
353
    portio_list_add (port_list, isa_address_space_io(&s->parent_obj), 0);
354
}
355

    
356
static Property adlib_properties[] = {
357
    DEFINE_PROP_HEX32  ("iobase",  AdlibState, port, 0x220),
358
    DEFINE_PROP_UINT32 ("freq",    AdlibState, freq,  44100),
359
    DEFINE_PROP_END_OF_LIST (),
360
};
361

    
362
static void adlib_class_initfn (ObjectClass *klass, void *data)
363
{
364
    DeviceClass *dc = DEVICE_CLASS (klass);
365

    
366
    dc->realize = adlib_realizefn;
367
    dc->desc = ADLIB_DESC;
368
    dc->props = adlib_properties;
369
}
370

    
371
static const TypeInfo adlib_info = {
372
    .name          = TYPE_ADLIB,
373
    .parent        = TYPE_ISA_DEVICE,
374
    .instance_size = sizeof (AdlibState),
375
    .class_init    = adlib_class_initfn,
376
};
377

    
378
static int Adlib_init (ISABus *bus)
379
{
380
    isa_create_simple (bus, TYPE_ADLIB);
381
    return 0;
382
}
383

    
384
static void adlib_register_types (void)
385
{
386
    type_register_static (&adlib_info);
387
    isa_register_soundhw("adlib", ADLIB_DESC, Adlib_init);
388
}
389

    
390
type_init (adlib_register_types)