qemu/hw/pcspk.c
<<
>>
Prefs
   1/*
   2 * QEMU PC speaker emulation
   3 *
   4 * Copyright (c) 2006 Joachim Henke
   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.h"
  26#include "pc.h"
  27#include "isa.h"
  28#include "audio/audio.h"
  29#include "qemu-timer.h"
  30
  31#define PCSPK_BUF_LEN 1792
  32#define PCSPK_SAMPLE_RATE 32000
  33#define PCSPK_MAX_FREQ (PCSPK_SAMPLE_RATE >> 1)
  34#define PCSPK_MIN_COUNT ((PIT_FREQ + PCSPK_MAX_FREQ - 1) / PCSPK_MAX_FREQ)
  35
  36typedef struct {
  37    uint8_t sample_buf[PCSPK_BUF_LEN];
  38    QEMUSoundCard card;
  39    SWVoiceOut *voice;
  40    PITState *pit;
  41    unsigned int pit_count;
  42    unsigned int samples;
  43    unsigned int play_pos;
  44    int data_on;
  45    int dummy_refresh_clock;
  46} PCSpkState;
  47
  48static const char *s_spk = "pcspk";
  49static PCSpkState pcspk_state;
  50
  51static inline void generate_samples(PCSpkState *s)
  52{
  53    unsigned int i;
  54
  55    if (s->pit_count) {
  56        const uint32_t m = PCSPK_SAMPLE_RATE * s->pit_count;
  57        const uint32_t n = ((uint64_t)PIT_FREQ << 32) / m;
  58
  59        /* multiple of wavelength for gapless looping */
  60        s->samples = (PCSPK_BUF_LEN * PIT_FREQ / m * m / (PIT_FREQ >> 1) + 1) >> 1;
  61        for (i = 0; i < s->samples; ++i)
  62            s->sample_buf[i] = (64 & (n * i >> 25)) - 32;
  63    } else {
  64        s->samples = PCSPK_BUF_LEN;
  65        for (i = 0; i < PCSPK_BUF_LEN; ++i)
  66            s->sample_buf[i] = 128; /* silence */
  67    }
  68}
  69
  70static void pcspk_callback(void *opaque, int free)
  71{
  72    PCSpkState *s = opaque;
  73    unsigned int n;
  74
  75    if (pit_get_mode(s->pit, 2) != 3)
  76        return;
  77
  78    n = pit_get_initial_count(s->pit, 2);
  79    /* avoid frequencies that are not reproducible with sample rate */
  80    if (n < PCSPK_MIN_COUNT)
  81        n = 0;
  82
  83    if (s->pit_count != n) {
  84        s->pit_count = n;
  85        s->play_pos = 0;
  86        generate_samples(s);
  87    }
  88
  89    while (free > 0) {
  90        n = audio_MIN(s->samples - s->play_pos, (unsigned int)free);
  91        n = AUD_write(s->voice, &s->sample_buf[s->play_pos], n);
  92        if (!n)
  93            break;
  94        s->play_pos = (s->play_pos + n) % s->samples;
  95        free -= n;
  96    }
  97}
  98
  99int pcspk_audio_init(qemu_irq *pic)
 100{
 101    PCSpkState *s = &pcspk_state;
 102    struct audsettings as = {PCSPK_SAMPLE_RATE, 1, AUD_FMT_U8, 0};
 103
 104    AUD_register_card(s_spk, &s->card);
 105
 106    s->voice = AUD_open_out(&s->card, s->voice, s_spk, s, pcspk_callback, &as);
 107    if (!s->voice) {
 108        AUD_log(s_spk, "Could not open voice\n");
 109        return -1;
 110    }
 111
 112    return 0;
 113}
 114
 115static uint32_t pcspk_ioport_read(void *opaque, uint32_t addr)
 116{
 117    PCSpkState *s = opaque;
 118    int out;
 119
 120    s->dummy_refresh_clock ^= (1 << 4);
 121    out = pit_get_out(s->pit, 2, qemu_get_clock(vm_clock)) << 5;
 122
 123    return pit_get_gate(s->pit, 2) | (s->data_on << 1) | s->dummy_refresh_clock | out;
 124}
 125
 126static void pcspk_ioport_write(void *opaque, uint32_t addr, uint32_t val)
 127{
 128    PCSpkState *s = opaque;
 129    const int gate = val & 1;
 130
 131    s->data_on = (val >> 1) & 1;
 132    pit_set_gate(s->pit, 2, gate);
 133    if (s->voice) {
 134        if (gate) /* restart */
 135            s->play_pos = 0;
 136        AUD_set_active_out(s->voice, gate & s->data_on);
 137    }
 138}
 139
 140void pcspk_init(PITState *pit)
 141{
 142    PCSpkState *s = &pcspk_state;
 143
 144    s->pit = pit;
 145    register_ioport_read(0x61, 1, 1, pcspk_ioport_read, s);
 146    register_ioport_write(0x61, 1, 1, pcspk_ioport_write, s);
 147}
 148