From nobody Fri May 3 21:57:15 2024 Delivered-To: importer2@patchew.org Authentication-Results: mx.zohomail.com; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer2=patchew.org@nongnu.org Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1673788195198192.30994973337692; Sun, 15 Jan 2023 05:09:55 -0800 (PST) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1pH2ks-0002TY-DD; Sun, 15 Jan 2023 08:09:03 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1pH2kY-0002RS-0U for qemu-devel@nongnu.org; Sun, 15 Jan 2023 08:08:38 -0500 Received: from mailout11.t-online.de ([194.25.134.85]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1pH2kV-0004N1-Fh for qemu-devel@nongnu.org; Sun, 15 Jan 2023 08:08:37 -0500 Received: from fwd73.dcpf.telekom.de (fwd73.aul.t-online.de [10.223.144.99]) by mailout11.t-online.de (Postfix) with SMTP id 3887820853; Sun, 15 Jan 2023 14:08:30 +0100 (CET) Received: from [192.168.211.200] ([79.208.25.151]) by fwd73.t-online.de with (TLSv1.3:TLS_AES_256_GCM_SHA384 encrypted) esmtp id 1pH2kP-2DOg6L0; Sun, 15 Jan 2023 14:08:29 +0100 Message-ID: <61bd351f-0683-7f58-b746-66c9578a7cdc@t-online.de> Date: Sun, 15 Jan 2023 14:08:29 +0100 MIME-Version: 1.0 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Thunderbird/102.6.1 From: =?UTF-8?Q?Volker_R=c3=bcmelin?= Subject: [PATCH 00/17] audio: improve callback interface for audio frontends To: Gerd Hoffmann Cc: qemu-devel@nongnu.org Content-Language: en-US Content-Type: text/plain; charset="utf-8"; format="flowed" Content-Transfer-Encoding: quoted-printable X-TOI-MSGID: a6a0d38d-34c3-4013-9537-0cacada1b854 Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer2=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: none client-ip=194.25.134.85; envelope-from=vr_qemu@t-online.de; helo=mailout11.t-online.de X-Spam_score_int: -25 X-Spam_score: -2.6 X-Spam_bar: -- X-Spam_report: (-2.6 / 5.0 requ) BAYES_00=-1.9, FREEMAIL_FROM=0.001, RCVD_IN_DNSWL_LOW=-0.7, RCVD_IN_MSPIKE_H2=-0.001, SPF_HELO_NONE=0.001, SPF_NONE=0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer2=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer2=patchew.org@nongnu.org X-ZM-MESSAGEID: 1673788196412100001 Based-on: <3b1404eb-a7c5-f64c-3e47-1397c54c45bb@t-online.de> ([PATCH 00/11] audio: more improvements) The callback interface for emulated audio devices is strange. The=20 callback function has an 'avail' parameter that passes the number of=20 bytes that can be written or read. Unfortunately, this value sometimes=20 is only an imprecise estimate and the callback functions must check the=20 actual bytes written or read. For playback devices, this means that they=20 either need a ring buffer or have to write the unwritten bytes again the=20 next time. For recording devices, things are a bit easier. They only=20 need to continue with the actual number of bytes read. After this patch series, the 'avail' argument for the -audiodev=20 out.mixing-engine=3Don and in.mixing-engine=3Don cases is exact. Audio=20 frontends only need a linear frame buffer and there's a guarantee they=20 can write or read 'avail' bytes. The -audiodev out.mixing-engine=3Doff case is also mostly accurate. Only=20 the D-Bus audio backend is still missing a required function. The=20 -audiodev in.mixing-engine=3Doff case always passes a much too large=20 'avail' value. I haven't worked on this yet, because there was no reason=20 for it so far. The following logs show the improvements. Not only the audio frontends=20 can write or read all needed or available bytes. The same is true for=20 the audio backends. For playback, the first six lines in the logs are=20 expected. Here you can see how quickly the guest fills the empty=20 downstream buffers after playback starts. QEMU was started with -device ich9-intel-hda,addr=3D0x1b -device=20 hda-duplex,audiodev=3Daudio0 -audiodev=20 pa,out.frequency=3D96000,in.frequency=3D96000,id=3Daudio0 playback guest 44100Hz =3D> host 96000Hz unpatched version: hda_audio_output_cb: to write 8188, written 1704 audio_run_out: free 4458, played 926 hda_audio_output_cb: to write 6488, written 2384 audio_run_out: free 3532, played 1297 hda_audio_output_cb: to write 4104, written 2648 audio_run_out: free 2235, played 1441 audio_run_out: free 794, played 793 audio_run_out: free 897, played 896 audio_run_out: free 831, played 829 ... hda_audio_output_cb: could not write 4 bytes hda_audio_output_cb: to write 1764, written 1760 audio_run_out: free 960, played 958 ... patched version: hda_audio_output_cb: to write 8192, written 1620 audio_run_out: free 4458, played 880 hda_audio_output_cb: to write 6576, written 2508 audio_run_out: free 3578, played 1365 hda_audio_output_cb: to write 4068, written 2500 audio_run_out: free 2213, played 1360 record host 96000Hz =3D> guest 44100Hz unpatched version: audio_run_in: avail 4458, acquired 4454 audio_run_in: avail 1574, acquired 1572 audio_run_in: avail 766, acquired 764 audio_run_in: avail 1052, acquired 1051 audio_run_in: avail 761, acquired 760 audio_run_in: avail 1123, acquired 1121 ... hda_audio_input_cb: could not read 4 bytes hda_audio_input_cb: to read 1988, read 1984 audio_run_in: avail 1082, acquired 1080 ... patched version: (no output) QEMU was started with -device ich9-intel-hda,addr=3D0x1b -device=20 hda-duplex,audiodev=3Daudio0 -audiodev=20 pa,out.frequency=3D32000,in.frequency=3D32000,id=3Daudio0 playback guest 44100Hz =3D> host 32000Hz unpatched version: hda_audio_output_cb: to write 8188, written 1620 audio_run_out: free 1486, played 294 hda_audio_output_cb: to write 6568, written 2512 audio_run_out: free 1192, played 455 hda_audio_output_cb: to write 4060, written 2504 audio_run_out: free 737, played 455 audio_run_out: free 282, played 281 audio_run_out: free 357, played 356 audio_run_out: free 314, played 313 ... hda_audio_output_cb: could not write 4 bytes hda_audio_output_cb: to write 1416, written 1412 audio_run_out: free 257, played 256 ... patched version: hda_audio_output_cb: to write 8192, written 1656 audio_run_out: free 1486, played 300 hda_audio_output_cb: to write 6536, written 2516 audio_run_out: free 1186, played 457 hda_audio_output_cb: to write 4020, written 2540 audio_run_out: free 729, played 460 record host 32000Hz =3D> guest 44100Hz unpatched version: audio_run_in: avail 1486, acquired 1485 audio_run_in: avail 272, acquired 271 audio_run_in: avail 366, acquired 365 hda_audio_input_cb: could not read 4 bytes hda_audio_input_cb: to read 1420, read 1416 audio_run_in: avail 258, acquired 257 audio_run_in: avail 375, acquired 374 hda_audio_input_cb: could not read 4 bytes hda_audio_input_cb: to read 2056, read 2052 audio_run_in: avail 260, acquired 259 ... patched version: (no output) This is the debug code for the logs above. Acked-by: Mark Cave-Ayland ---snip-- --- a/audio/audio.c=C2=A0=C2=A0 =C2=A02022-12-13 19:14:31.793153558 +0100 +++ b/audio/audio.c=C2=A0=C2=A0 =C2=A02022-12-11 16:24:48.842649711 +0100 @@ -1228,6 +1228,10 @@ static void audio_run_out (AudioState *s =C2=A0#ifdef DEBUG_OUT =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 dolog("played=3D%zu\n", p= layed); =C2=A0#endif +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 if (hw_free - played) { +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 fprintf= (stderr, "%s: free %zu, played %zu\n", +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 __func__, hw_free, played); +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 } =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 if (played) { =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 h= w->ts_helper +=3D played; @@ -1318,6 +1322,7 @@ static void audio_run_in (AudioState *s) =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 i= f (sw->active) { =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0=C2=A0 size_t sw_avail =3D audio_get_avail(sw); =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0=C2=A0 size_t avail; +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 size_t prev_acquired =3D sw->total_hw_samples_acquired; =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0=C2=A0 avail =3D st_rate_frames_out(sw->rate, sw_avail); =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0=C2=A0 if (avail > 0) { @@ -1325,6 +1330,11 @@ static void audio_run_in (AudioState *s) =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 sw->callback.fn(sw->callba= ck.opaque, =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2= =A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 avail= * sw->info.bytes_per_frame); =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0=C2=A0 } + +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 if (sw_avail + prev_acquired -=20 sw->total_hw_samples_acquired) { +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 fprintf(stderr, "%s: avail %zu, = acquired %zu\n",=20 __func__, +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2= =A0=C2=A0=C2=A0 sw_avail, sw->total_hw_samples_acquired -=20 prev_acquired); +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 } =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 } =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 } =C2=A0=C2=A0=C2=A0=C2=A0 } --- a/hw/audio/hda-codec.c=C2=A0=C2=A0 =C2=A02023-01-04 14:07:31.954304889 = +0100 +++ b/hw/audio/hda-codec.c=C2=A0=C2=A0 =C2=A02023-01-04 13:57:47.687320406 = +0100 @@ -265,20 +265,28 @@ static void hda_audio_input_cb(void *opa =C2=A0=C2=A0=C2=A0=C2=A0 int64_t rpos =3D st->rpos; =C2=A0=C2=A0=C2=A0=C2=A0 int64_t to_transfer =3D MIN(B_SIZE - (wpos - rpos= ), avail); +=C2=A0=C2=A0=C2=A0 unsigned int total_read =3D 0; =C2=A0=C2=A0=C2=A0=C2=A0 while (to_transfer) { =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 uint32_t start =3D (uint3= 2_t) (wpos & B_MASK); =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 uint32_t chunk =3D (uint3= 2_t) MIN(B_SIZE - start, to_transfer); =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 uint32_t read =3D AUD_rea= d(st->voice.in, st->buf + start, chunk); =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 wpos +=3D read; +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 total_read +=3D read; =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 to_transfer -=3D read; =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 st->wpos +=3D read; =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 if (chunk !=3D read) { +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 fprintf= (stderr, "%s: could not read %u bytes\n", __func__, +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 chunk - read); =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 b= reak; =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 } =C2=A0=C2=A0=C2=A0=C2=A0 } =C2=A0=C2=A0=C2=A0=C2=A0 hda_timer_sync_adjust(st, -((wpos - rpos) - (B_SI= ZE >> 1))); +=C2=A0=C2=A0=C2=A0 if (avail !=3D total_read) { +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 fprintf(stderr, "%s: to read %d= , read %u\n", __func__, +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 avail, total_read); +=C2=A0=C2=A0=C2=A0 } =C2=A0} =C2=A0static void hda_audio_output_timer(void *opaque) @@ -329,6 +337,7 @@ static void hda_audio_output_cb(void *op =C2=A0=C2=A0=C2=A0=C2=A0 int64_t rpos =3D st->rpos; =C2=A0=C2=A0=C2=A0=C2=A0 int64_t to_transfer =3D MIN(wpos - rpos, avail); +=C2=A0=C2=A0=C2=A0 unsigned int total_written =3D 0; =C2=A0=C2=A0=C2=A0=C2=A0 if (wpos - rpos =3D=3D B_SIZE) { =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 /* drop buffer, reset tim= er adjust */ @@ -343,15 +352,22 @@ static void hda_audio_output_cb(void *op =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 uint32_t start =3D (uint3= 2_t) (rpos & B_MASK); =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 uint32_t chunk =3D (uint3= 2_t) MIN(B_SIZE - start, to_transfer); =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 uint32_t written =3D AUD_= write(st->voice.out, st->buf + start,=20 chunk); +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 total_written +=3D written; =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 rpos +=3D written; =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 to_transfer -=3D written; =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 st->rpos +=3D written; =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 if (chunk !=3D written) { +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 fprintf= (stderr, "%s: could not write %u bytes\n", __func__, +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 chunk - written); =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 b= reak; =C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 } =C2=A0=C2=A0=C2=A0=C2=A0 } =C2=A0=C2=A0=C2=A0=C2=A0 hda_timer_sync_adjust(st, (wpos - rpos) - (B_SIZE= >> 1)); +=C2=A0=C2=A0=C2=A0 if (avail !=3D total_written) { +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 fprintf(stderr, "%s: to write %= d, written %u\n", __func__, +=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0= =C2=A0=C2=A0=C2=A0 avail, total_written); +=C2=A0=C2=A0=C2=A0 } =C2=A0} =C2=A0static void hda_audio_compat_input_cb(void *opaque, int avail) ---snip-- Volker R=C3=BCmelin (17): =C2=A0 audio: change type of mix_buf and conv_buf =C2=A0 audio: change type and name of the resample buffer =C2=A0 audio: make the resampling code greedy =C2=A0 audio: replace the resampling loop in audio_pcm_sw_write() =C2=A0 audio: remove sw =3D=3D NULL check =C2=A0 audio: rename variables in audio_pcm_sw_write() =C2=A0 audio: don't misuse audio_pcm_sw_write() =C2=A0 audio: remove unused noop_conv() function =C2=A0 audio/mixeng: calculate number of input frames =C2=A0 audio: wire up st_rate_frames_in() =C2=A0 audio: replace the resampling loop in audio_pcm_sw_read() =C2=A0 audio: rename variables in audio_pcm_sw_read() =C2=A0 audio/mixeng: calculate number of output frames =C2=A0 audio: wire up st_rate_frames_out() =C2=A0 audio: handle leftover audio frame from upsampling =C2=A0 audio/audio_template: substitute sw->hw with hw =C2=A0 audio: remove sw->ratio =C2=A0audio/audio.c=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 = | 366 +++++++++++++++++++++-------------------- =C2=A0audio/audio_int.h=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 |=C2=A0 12 +- =C2=A0audio/audio_template.h |=C2=A0 41 +++-- =C2=A0audio/mixeng.c=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 |=C2= =A0 73 ++++++++ =C2=A0audio/mixeng.h=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 |=C2= =A0=C2=A0 2 + =C2=A0audio/rate_template.h=C2=A0 |=C2=A0 21 ++- =C2=A06 files changed, 304 insertions(+), 211 deletions(-) --=20 2.35.3