qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: "Volker Rümelin" <vr_qemu@t-online.de>
To: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk>, laurent@vivier.eu
Cc: qemu-devel@nongnu.org
Subject: Re: [PATCH v2 07/20] audio: add Apple Sound Chip (ASC) emulation
Date: Thu, 14 Sep 2023 09:06:08 +0200	[thread overview]
Message-ID: <373b3abd-a726-e795-eaee-0389a25c662f@t-online.de> (raw)
In-Reply-To: <20230909094827.33871-8-mark.cave-ayland@ilande.co.uk>

Am 09.09.23 um 11:48 schrieb Mark Cave-Ayland:
> The Apple Sound Chip was primarily used by the Macintosh II to generate sound
> in hardware which was previously handled by the toolbox ROM with software
> interrupts.
>
> Implement both the standard ASC and also the enhanced ASC (EASC) functionality
> which is used in the Quadra 800.
>
> Note that whilst real ASC hardware uses AUDIO_FORMAT_S8, this implementation uses
> AUDIO_FORMAT_U8 instead because AUDIO_FORMAT_S8 is rarely used and not supported
> by some audio backends like PulseAudio and DirectSound when played directly with
> -audiodev out.mixing-engine=off.
>
> Co-developed-by: Laurent Vivier <laurent@vivier.eu>
> Co-developed-by: Volker Rümelin <vr_qemu@t-online.de>
> Signed-off-by: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk>
> ---
>  MAINTAINERS            |   2 +
>  hw/audio/Kconfig       |   3 +
>  hw/audio/asc.c         | 699 +++++++++++++++++++++++++++++++++++++++++
>  hw/audio/meson.build   |   1 +
>  hw/audio/trace-events  |  10 +
>  hw/m68k/Kconfig        |   1 +
>  include/hw/audio/asc.h |  84 +++++
>  7 files changed, 800 insertions(+)
>  create mode 100644 hw/audio/asc.c
>  create mode 100644 include/hw/audio/asc.h

Hi Mark,

the function generate_fifo() has four issues. Only the first one
is noticeable.

1. The calculation of the variable limit assumes generate_fifo()
generates one output sample from every input byte. This is correct
for the raw mode, but not for the CD-XA BRR mode. This mode
generates 28 output samples from 15 input bytes. This is the
reason for the stuttering end of a CD-XA BRR mode sound. Every
generate_fifo() call generates approximately only half of the
possible samples when the fifo bytes are running low.

2. generate_fifo() doesn't generate the last output sample from
a CD-XA BRR mode sound. The last sample is generated from internal
state and the code will not be called without at least one byte
in the fifo.

3. It's not necessary to wait for a complete 15 byte packet in
CD-XA BRR mode. Audio playback devices should write all
requested samples immediately if possible.

4. The saturation function in CD-XA BRR mode works with 16 bit
integers. It should saturate at +32767 and -32768.

Since I think a few lines of code explain the issues better
than my words, I've attached a patch below.

With best regards,
Volker

> +static int generate_fifo(ASCState *s, int maxsamples)
> +{
> +    int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
> +    uint8_t *buf = s->mixbuf;
> +    int i, limit, count = 0;
> +
> +    limit = MIN(MAX(s->fifos[0].cnt, s->fifos[1].cnt), maxsamples);
> +    while (count < limit) {
> +        uint8_t val;
> +        int16_t d, f0, f1;
> +        int32_t t;
> +        int shift, filter;
> +        bool hasdata = true;
> +
> +        for (i = 0; i < 2; i++) {
> +            ASCFIFOState *fs = &s->fifos[i];
> +
> +            switch (fs->extregs[ASC_EXTREGS_FIFOCTRL] & 0x83) {
> +            case 0x82:
> +                /*
> +                 * CD-XA BRR mode: exit if there isn't enough data in the FIFO
> +                 * for a complete 15 byte packet
> +                 */
> +                if (fs->xa_cnt == -1 && fs->cnt < 15) {
> +                    hasdata = false;
> +                    continue;
> +                }
> +
> +                if (fs->xa_cnt == -1) {
> +                    /* Start of packet, get flags */
> +                    fs->xa_flags = asc_fifo_get(fs);
> +                    fs->xa_cnt = 0;
> +                }
> +
> +                shift = fs->xa_flags & 0xf;
> +                filter = fs->xa_flags >> 4;
> +                f0 = (int8_t)fs->extregs[ASC_EXTREGS_CDXA_DECOMP_FILT +
> +                                 (filter << 1) + 1];
> +                f1 = (int8_t)fs->extregs[ASC_EXTREGS_CDXA_DECOMP_FILT +
> +                                 (filter << 1)];
> +                if ((fs->xa_cnt & 1) == 0) {
> +                    fs->xa_val = asc_fifo_get(fs);
> +                    d = (fs->xa_val & 0xf) << 12;
> +                } else {
> +                    d = (fs->xa_val & 0xf0) << 8;
> +                }
> +                t = (d >> shift) + (((fs->xa_last[0] * f0) +
> +                                     (fs->xa_last[1] * f1) + 32) >> 6);
> +                if (t < -32768) {
> +                    t = -32768;
> +                } else if (t > 32768) {
> +                    t = 32768;
> +                }
> +
> +                /*
> +                 * CD-XA BRR generates 16-bit signed output, so convert to
> +                 * 8-bit before writing to buffer. Does real hardware do the
> +                 * same?
> +                 */
> +                buf[count * 2 + i] = (uint8_t)(t / 256) ^ 0x80;
> +                fs->xa_cnt++;
> +
> +                fs->xa_last[1] = fs->xa_last[0];
> +                fs->xa_last[0] = (int16_t)t;
> +
> +                if (fs->xa_cnt == 28) {
> +                    /* End of packet */
> +                    fs->xa_cnt = -1;
> +                }
> +                break;
> +
> +            default:
> +                /* fallthrough */
> +            case 0x80:
> +                /* Raw mode */
> +                if (fs->cnt) {
> +                    val = asc_fifo_get(fs);
> +                } else {
> +                    val = 0x80;
> +                }
> +
> +                buf[count * 2 + i] = val;
> +                break;
> +            }
> +        }
> +
> +        if (!hasdata) {
> +            break;
> +        }
> +
> +        count++;
> +    }
> +
> +    /*
> +     * MacOS (un)helpfully leaves the FIFO engine running even when it has
> +     * finished writing out samples, but still expects the FIFO empty
> +     * interrupts to be generated for each FIFO cycle (without these interrupts
> +     * MacOS will freeze)
> +     */
> +    if (s->fifos[0].cnt == 0 && s->fifos[1].cnt == 0) {
> +        if (!s->fifo_empty_ns) {
> +            /* FIFO has completed first empty cycle */
> +            s->fifo_empty_ns = now;
> +        } else if (now > (s->fifo_empty_ns + ASC_FIFO_CYCLE_TIME)) {
> +            /* FIFO has completed entire cycle with no data */
> +            s->fifos[0].int_status |= ASC_FIFO_STATUS_HALF_FULL |
> +                                      ASC_FIFO_STATUS_FULL_EMPTY;
> +            s->fifos[1].int_status |= ASC_FIFO_STATUS_HALF_FULL |
> +                                      ASC_FIFO_STATUS_FULL_EMPTY;
> +            s->fifo_empty_ns = now;
> +            asc_raise_irq(s);
> +        }
> +    } else {
> +        /* FIFO contains data, reset empty time */
> +        s->fifo_empty_ns = 0;
> +    }
> +
> +    return count;
> +}
>

diff --git a/hw/audio/asc.c b/hw/audio/asc.c
index b01b285512..74988fef9c 100644
--- a/hw/audio/asc.c
+++ b/hw/audio/asc.c
@@ -155,31 +155,26 @@ static int generate_fifo(ASCState *s, int maxsamples)
 {
     int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
     uint8_t *buf = s->mixbuf;
-    int i, limit, count = 0;
+    int i, wcount = 0;
 
-    limit = MIN(MAX(s->fifos[0].cnt, s->fifos[1].cnt), maxsamples);
-    while (count < limit) {
+    while (wcount < maxsamples) {
         uint8_t val;
         int16_t d, f0, f1;
         int32_t t;
         int shift, filter;
-        bool hasdata = true;
+        bool hasdata = false;
 
         for (i = 0; i < 2; i++) {
             ASCFIFOState *fs = &s->fifos[i];
 
             switch (fs->extregs[ASC_EXTREGS_FIFOCTRL] & 0x83) {
             case 0x82:
-                /*
-                 * CD-XA BRR mode: exit if there isn't enough data in
the FIFO
-                 * for a complete 15 byte packet
-                 */
-                if (fs->xa_cnt == -1 && fs->cnt < 15) {
-                    hasdata = false;
-                    continue;
-                }
-
+                /* CD-XA BRR mode: decompress 15 bytes into 28 16bit
samples */
                 if (fs->xa_cnt == -1) {
+                    if (!fs->cnt) {
+                        val = 0x80;
+                        break;
+                    }
                     /* Start of packet, get flags */
                     fs->xa_flags = asc_fifo_get(fs);
                     fs->xa_cnt = 0;
@@ -192,6 +187,10 @@ static int generate_fifo(ASCState *s, int maxsamples)
                 f1 = (int8_t)fs->extregs[ASC_EXTREGS_CDXA_DECOMP_FILT +
                                  (filter << 1)];
                 if ((fs->xa_cnt & 1) == 0) {
+                    if (!fs->cnt) {
+                        val = 0x80;
+                        break;
+                    }
                     fs->xa_val = asc_fifo_get(fs);
                     d = (fs->xa_val & 0xf) << 12;
                 } else {
@@ -201,8 +200,8 @@ static int generate_fifo(ASCState *s, int maxsamples)
                                      (fs->xa_last[1] * f1) + 32) >> 6);
                 if (t < -32768) {
                     t = -32768;
-                } else if (t > 32768) {
-                    t = 32768;
+                } else if (t > 32767) {
+                    t = 32767;
                 }
 
                 /*
@@ -210,7 +209,8 @@ static int generate_fifo(ASCState *s, int maxsamples)
                  * 8-bit before writing to buffer. Does real hardware
do the
                  * same?
                  */
-                buf[count * 2 + i] = (uint8_t)(t / 256) ^ 0x80;
+                val = (uint8_t)(t / 256) ^ 0x80;
+                hasdata = true;
                 fs->xa_cnt++;
 
                 fs->xa_last[1] = fs->xa_last[0];
@@ -228,20 +228,21 @@ static int generate_fifo(ASCState *s, int maxsamples)
                 /* Raw mode */
                 if (fs->cnt) {
                     val = asc_fifo_get(fs);
+                    hasdata = true;
                 } else {
                     val = 0x80;
                 }
-
-                buf[count * 2 + i] = val;
                 break;
             }
+
+            buf[wcount * 2 + i] = val;
         }
 
         if (!hasdata) {
             break;
         }
 
-        count++;
+        wcount++;
     }
 
     /*
@@ -268,7 +269,7 @@ static int generate_fifo(ASCState *s, int maxsamples)
         s->fifo_empty_ns = 0;
     }
 
-    return count;
+    return wcount;
 }
 
 static int generate_wavetable(ASCState *s, int maxsamples)
-- 
2.35.3



  reply	other threads:[~2023-09-14  7:07 UTC|newest]

Thread overview: 44+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-09-09  9:48 [PATCH v2 00/20] q800: add support for booting MacOS Classic - part 2 Mark Cave-Ayland
2023-09-09  9:48 ` [PATCH v2 01/20] q800-glue.c: convert to Resettable interface Mark Cave-Ayland
2023-09-25 17:03   ` Laurent Vivier
2023-09-09  9:48 ` [PATCH v2 02/20] q800: add djMEMC memory controller Mark Cave-Ayland
2023-09-09  9:48 ` [PATCH v2 03/20] q800: add machine id register Mark Cave-Ayland
2023-09-09  9:48 ` [PATCH v2 04/20] q800: implement additional machine id bits on VIA1 port A Mark Cave-Ayland
2023-09-09  9:48 ` [PATCH v2 05/20] q800: add IOSB subsystem Mark Cave-Ayland
2023-09-09  9:48 ` [PATCH v2 06/20] q800: allow accesses to RAM area even if less memory is available Mark Cave-Ayland
2023-09-09  9:48 ` [PATCH v2 07/20] audio: add Apple Sound Chip (ASC) emulation Mark Cave-Ayland
2023-09-14  7:06   ` Volker Rümelin [this message]
2023-09-20 15:39     ` Mark Cave-Ayland
2023-09-23  7:09       ` Volker Rümelin
2023-09-09  9:48 ` [PATCH v2 08/20] asc: generate silence if FIFO empty but engine still running Mark Cave-Ayland
2023-09-14  7:56   ` Philippe Mathieu-Daudé
2023-09-14 13:16     ` Philippe Mathieu-Daudé
2023-09-16  8:19     ` Volker Rümelin
2023-09-17 13:42       ` Philippe Mathieu-Daudé
2023-09-25 17:19   ` Laurent Vivier
2023-09-28 20:40     ` Mark Cave-Ayland
2023-09-09  9:48 ` [PATCH v2 09/20] q800: add Apple Sound Chip (ASC) audio to machine Mark Cave-Ayland
2023-09-09  9:48 ` [PATCH v2 10/20] q800: add easc bool machine class property to switch between ASC and EASC Mark Cave-Ayland
2023-09-26  7:54   ` Laurent Vivier
2023-09-09  9:48 ` [PATCH v2 11/20] swim: add trace events for IWM and ISM registers Mark Cave-Ayland
2023-09-26  7:55   ` Laurent Vivier
2023-09-09  9:48 ` [PATCH v2 12/20] swim: split into separate IWM and ISM register blocks Mark Cave-Ayland
2023-09-26  8:09   ` Laurent Vivier
2023-09-28 20:47     ` Mark Cave-Ayland
2023-09-09  9:48 ` [PATCH v2 13/20] swim: update IWM/ISM register block decoding Mark Cave-Ayland
2023-09-26  8:10   ` Laurent Vivier
2023-09-09  9:48 ` [PATCH v2 14/20] mac_via: work around underflow in TimeDBRA timing loop in SETUPTIMEK Mark Cave-Ayland
2023-09-26  8:04   ` Laurent Vivier
2023-09-09  9:48 ` [PATCH v2 15/20] mac_via: workaround NetBSD ADB bus enumeration issue Mark Cave-Ayland
2023-09-26  8:04   ` Laurent Vivier
2023-09-28 20:45     ` Mark Cave-Ayland
2023-09-09  9:48 ` [PATCH v2 16/20] mac_via: implement ADB_STATE_IDLE state if shift register in input mode Mark Cave-Ayland
2023-09-26  8:05   ` Laurent Vivier
2023-09-09  9:48 ` [PATCH v2 17/20] mac_via: always clear ADB interrupt when switching to A/UX mode Mark Cave-Ayland
2023-09-26  8:06   ` Laurent Vivier
2023-09-09  9:48 ` [PATCH v2 18/20] q800: add ESCC alias at 0xc000 Mark Cave-Ayland
2023-09-26  8:06   ` Laurent Vivier
2023-09-09  9:48 ` [PATCH v2 19/20] q800: add alias for MacOS toolbox ROM at 0x40000000 Mark Cave-Ayland
2023-09-26  8:06   ` Laurent Vivier
2023-09-09  9:48 ` [PATCH v2 20/20] mac_via: extend timer calibration hack to work with A/UX Mark Cave-Ayland
2023-09-26  8:07   ` Laurent Vivier

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=373b3abd-a726-e795-eaee-0389a25c662f@t-online.de \
    --to=vr_qemu@t-online.de \
    --cc=laurent@vivier.eu \
    --cc=mark.cave-ayland@ilande.co.uk \
    --cc=qemu-devel@nongnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).