The Soundblaster 16 audio device is strange and works differently compared to the other audio frontends. For audio playback, a BH routine copies the audio samples from the DMA buffer to the audio backend. The BH routine then immediately reschedules itself. As far fewer samples are required from the backend than the BH routine can supply, this means that most of the BH routine calls don't copy any audio samples. The buffers of the audio backend are simply full. This is a very inefficient design.
Use the DREQ signal of the ISA DMA controller to throttle the audio stream. The callback routine of the audio subsystem raises the DREQ signal as soon as new samples are needed and also starts the DMA callback routine. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/469 Signed-off-by: Volker Rümelin <vr_q...@t-online.de> --- hw/audio/sb16.c | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/hw/audio/sb16.c b/hw/audio/sb16.c index 5c51940596..714144eb61 100644 --- a/hw/audio/sb16.c +++ b/hw/audio/sb16.c @@ -200,7 +200,9 @@ static void control (SB16State *s, int hold) ldebug("hold %d high %d nchan %d\n", hold, s->use_hdma, nchan); if (hold) { - hold_DREQ(s, nchan); + if (!s->voice) { + hold_DREQ(s, nchan); + } AUD_set_active_out (s->voice, 1); } else { @@ -890,6 +892,8 @@ static void legacy_reset (SB16State *s) s->fmt_bits = 8; s->fmt_stereo = 0; + s->audio_free = 0; + as.freq = s->freq; as.nchannels = 1; as.fmt = AUDIO_FORMAT_U8; @@ -1243,8 +1247,12 @@ static int SB_read_DMA (void *opaque, int nchan, int dma_pos, int dma_len) } if (s->voice) { + if (!dma_len) { + return dma_pos; + } free = s->audio_free & ~s->align; - if ((free <= 0) || !dma_len) { + if (free <= 0) { + release_DREQ(s, nchan); return dma_pos; } } @@ -1269,6 +1277,7 @@ static int SB_read_DMA (void *opaque, int nchan, int dma_pos, int dma_len) written = write_audio (s, nchan, dma_pos, dma_len, copy); dma_pos = (dma_pos + written) % dma_len; s->left_till_irq -= written; + s->audio_free -= written; if (s->left_till_irq <= 0) { s->mixer_regs[0x82] |= (nchan & 4) ? 2 : 1; @@ -1289,13 +1298,23 @@ static int SB_read_DMA (void *opaque, int nchan, int dma_pos, int dma_len) s->left_till_irq = s->block_size + s->left_till_irq; } + if (s->voice) { + free = s->audio_free & ~s->align; + if (free <= 0) { + release_DREQ(s, nchan); + } + } return dma_pos; } static void SB_audio_callback (void *opaque, int free) { SB16State *s = opaque; + int nchan = s->use_hdma ? s->hdma : s->dma; + s->audio_free = free; + /* run the DMA engine to call SB_read_DMA immediately */ + hold_DREQ(s, nchan); } static int sb16_post_load (void *opaque, int version_id) -- 2.43.0