Statistics
| Branch: | Revision:

root / hw / adlib.c @ 1ea879e5

History | View | Annotate | Download (7.7 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 <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
}