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 "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"
  53void 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
  68typedef 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    PortioList port_list;
  90} AdlibState;
  91
  92static AdlibState *glob_adlib;
  93
  94static void adlib_stop_opl_timer (AdlibState *s, size_t n)
  95{
  96#ifdef HAS_YMF262
  97    YMF262TimerOver (0, n);
  98#else
  99    OPLTimerOver (s->opl, n);
 100#endif
 101    s->ticking[n] = 0;
 102}
 103
 104static void adlib_kill_timers (AdlibState *s)
 105{
 106    size_t i;
 107
 108    for (i = 0; i < 2; ++i) {
 109        if (s->ticking[i]) {
 110            uint64_t delta;
 111
 112            delta = AUD_get_elapsed_usec_out (s->voice, &s->ats);
 113            ldebug (
 114                "delta = %f dexp = %f expired => %d\n",
 115                delta / 1000000.0,
 116                s->dexp[i] / 1000000.0,
 117                delta >= s->dexp[i]
 118                );
 119            if (ADLIB_KILL_TIMERS || delta >= s->dexp[i]) {
 120                adlib_stop_opl_timer (s, i);
 121                AUD_init_time_stamp_out (s->voice, &s->ats);
 122            }
 123        }
 124    }
 125}
 126
 127static IO_WRITE_PROTO (adlib_write)
 128{
 129    AdlibState *s = opaque;
 130    int a = nport & 3;
 131
 132    s->active = 1;
 133    AUD_set_active_out (s->voice, 1);
 134
 135    adlib_kill_timers (s);
 136
 137#ifdef HAS_YMF262
 138    YMF262Write (0, a, val);
 139#else
 140    OPLWrite (s->opl, a, val);
 141#endif
 142}
 143
 144static IO_READ_PROTO (adlib_read)
 145{
 146    AdlibState *s = opaque;
 147    uint8_t data;
 148    int a = nport & 3;
 149
 150    adlib_kill_timers (s);
 151
 152#ifdef HAS_YMF262
 153    data = YMF262Read (0, a);
 154#else
 155    data = OPLRead (s->opl, a);
 156#endif
 157    return data;
 158}
 159
 160static void timer_handler (int c, double interval_Sec)
 161{
 162    AdlibState *s = glob_adlib;
 163    unsigned n = c & 1;
 164#ifdef DEBUG
 165    double interval;
 166    int64_t exp;
 167#endif
 168
 169    if (interval_Sec == 0.0) {
 170        s->ticking[n] = 0;
 171        return;
 172    }
 173
 174    s->ticking[n] = 1;
 175#ifdef DEBUG
 176    interval = get_ticks_per_sec () * interval_Sec;
 177    exp = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + interval;
 178    s->exp[n] = exp;
 179#endif
 180
 181    s->dexp[n] = interval_Sec * 1000000.0;
 182    AUD_init_time_stamp_out (s->voice, &s->ats);
 183}
 184
 185static int write_audio (AdlibState *s, int samples)
 186{
 187    int net = 0;
 188    int pos = s->pos;
 189
 190    while (samples) {
 191        int nbytes, wbytes, wsampl;
 192
 193        nbytes = samples << SHIFT;
 194        wbytes = AUD_write (
 195            s->voice,
 196            s->mixbuf + (pos << (SHIFT - 1)),
 197            nbytes
 198            );
 199
 200        if (wbytes) {
 201            wsampl = wbytes >> SHIFT;
 202
 203            samples -= wsampl;
 204            pos = (pos + wsampl) % s->samples;
 205
 206            net += wsampl;
 207        }
 208        else {
 209            break;
 210        }
 211    }
 212
 213    return net;
 214}
 215
 216static void adlib_callback (void *opaque, int free)
 217{
 218    AdlibState *s = opaque;
 219    int samples, net = 0, to_play, written;
 220
 221    samples = free >> SHIFT;
 222    if (!(s->active && s->enabled) || !samples) {
 223        return;
 224    }
 225
 226    to_play = audio_MIN (s->left, samples);
 227    while (to_play) {
 228        written = write_audio (s, to_play);
 229
 230        if (written) {
 231            s->left -= written;
 232            samples -= written;
 233            to_play -= written;
 234            s->pos = (s->pos + written) % s->samples;
 235        }
 236        else {
 237            return;
 238        }
 239    }
 240
 241    samples = audio_MIN (samples, s->samples - s->pos);
 242    if (!samples) {
 243        return;
 244    }
 245
 246#ifdef HAS_YMF262
 247    YMF262UpdateOneQEMU (0, s->mixbuf + s->pos * 2, samples);
 248#else
 249    YM3812UpdateOne (s->opl, s->mixbuf + s->pos, samples);
 250#endif
 251
 252    while (samples) {
 253        written = write_audio (s, samples);
 254
 255        if (written) {
 256            net += written;
 257            samples -= written;
 258            s->pos = (s->pos + written) % s->samples;
 259        }
 260        else {
 261            s->left = samples;
 262            return;
 263        }
 264    }
 265}
 266
 267static void Adlib_fini (AdlibState *s)
 268{
 269#ifdef HAS_YMF262
 270    YMF262Shutdown ();
 271#else
 272    if (s->opl) {
 273        OPLDestroy (s->opl);
 274        s->opl = NULL;
 275    }
 276#endif
 277
 278    g_free(s->mixbuf);
 279
 280    s->active = 0;
 281    s->enabled = 0;
 282    AUD_remove_card (&s->card);
 283}
 284
 285static MemoryRegionPortio adlib_portio_list[] = {
 286    { 0, 4, 1, .read = adlib_read, .write = adlib_write, },
 287    { 0, 2, 1, .read = adlib_read, .write = adlib_write, },
 288    { 0x388, 4, 1, .read = adlib_read, .write = adlib_write, },
 289    PORTIO_END_OF_LIST(),
 290};
 291
 292static void adlib_realizefn (DeviceState *dev, Error **errp)
 293{
 294    AdlibState *s = ADLIB(dev);
 295    struct audsettings as;
 296
 297    if (glob_adlib) {
 298        error_setg (errp, "Cannot create more than 1 adlib device");
 299        return;
 300    }
 301    glob_adlib = s;
 302
 303#ifdef HAS_YMF262
 304    if (YMF262Init (1, 14318180, s->freq)) {
 305        error_setg (errp, "YMF262Init %d failed", s->freq);
 306        return;
 307    }
 308    else {
 309        YMF262SetTimerHandler (0, timer_handler, 0);
 310        s->enabled = 1;
 311    }
 312#else
 313    s->opl = OPLCreate (OPL_TYPE_YM3812, 3579545, s->freq);
 314    if (!s->opl) {
 315        error_setg (errp, "OPLCreate %d failed", s->freq);
 316        return;
 317    }
 318    else {
 319        OPLSetTimerHandler (s->opl, timer_handler, 0);
 320        s->enabled = 1;
 321    }
 322#endif
 323
 324    as.freq = s->freq;
 325    as.nchannels = SHIFT;
 326    as.fmt = AUD_FMT_S16;
 327    as.endianness = AUDIO_HOST_ENDIANNESS;
 328
 329    AUD_register_card ("adlib", &s->card);
 330
 331    s->voice = AUD_open_out (
 332        &s->card,
 333        s->voice,
 334        "adlib",
 335        s,
 336        adlib_callback,
 337        &as
 338        );
 339    if (!s->voice) {
 340        Adlib_fini (s);
 341        error_setg (errp, "Initializing audio voice failed");
 342        return;
 343    }
 344
 345    s->samples = AUD_get_buffer_size_out (s->voice) >> SHIFT;
 346    s->mixbuf = g_malloc0 (s->samples << SHIFT);
 347
 348    adlib_portio_list[0].offset = s->port;
 349    adlib_portio_list[1].offset = s->port + 8;
 350    portio_list_init (&s->port_list, OBJECT(s), adlib_portio_list, s, "adlib");
 351    portio_list_add (&s->port_list, isa_address_space_io(&s->parent_obj), 0);
 352}
 353
 354static Property adlib_properties[] = {
 355    DEFINE_PROP_UINT32 ("iobase",  AdlibState, port, 0x220),
 356    DEFINE_PROP_UINT32 ("freq",    AdlibState, freq,  44100),
 357    DEFINE_PROP_END_OF_LIST (),
 358};
 359
 360static void adlib_class_initfn (ObjectClass *klass, void *data)
 361{
 362    DeviceClass *dc = DEVICE_CLASS (klass);
 363
 364    dc->realize = adlib_realizefn;
 365    set_bit(DEVICE_CATEGORY_SOUND, dc->categories);
 366    dc->desc = ADLIB_DESC;
 367    dc->props = adlib_properties;
 368}
 369
 370static const TypeInfo adlib_info = {
 371    .name          = TYPE_ADLIB,
 372    .parent        = TYPE_ISA_DEVICE,
 373    .instance_size = sizeof (AdlibState),
 374    .class_init    = adlib_class_initfn,
 375};
 376
 377static int Adlib_init (ISABus *bus)
 378{
 379    isa_create_simple (bus, TYPE_ADLIB);
 380    return 0;
 381}
 382
 383static void adlib_register_types (void)
 384{
 385    type_register_static (&adlib_info);
 386    isa_register_soundhw("adlib", ADLIB_DESC, Adlib_init);
 387}
 388
 389type_init (adlib_register_types)
 390