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