11d14ffa9Sbellard /*
21d14ffa9Sbellard * QEMU Audio subsystem header
31d14ffa9Sbellard *
41d14ffa9Sbellard * Copyright (c) 2005 Vassili Karpov (malc)
51d14ffa9Sbellard *
61d14ffa9Sbellard * Permission is hereby granted, free of charge, to any person obtaining a copy
71d14ffa9Sbellard * of this software and associated documentation files (the "Software"), to deal
81d14ffa9Sbellard * in the Software without restriction, including without limitation the rights
91d14ffa9Sbellard * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
101d14ffa9Sbellard * copies of the Software, and to permit persons to whom the Software is
111d14ffa9Sbellard * furnished to do so, subject to the following conditions:
121d14ffa9Sbellard *
131d14ffa9Sbellard * The above copyright notice and this permission notice shall be included in
141d14ffa9Sbellard * all copies or substantial portions of the Software.
151d14ffa9Sbellard *
161d14ffa9Sbellard * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
171d14ffa9Sbellard * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
181d14ffa9Sbellard * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
191d14ffa9Sbellard * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
201d14ffa9Sbellard * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
211d14ffa9Sbellard * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
221d14ffa9Sbellard * THE SOFTWARE.
231d14ffa9Sbellard */
241d14ffa9Sbellard
251d14ffa9Sbellard #ifdef DAC
26571ec3d6Sbellard #define NAME "playback"
27571ec3d6Sbellard #define HWBUF hw->mix_buf
281d14ffa9Sbellard #define TYPE out
29571ec3d6Sbellard #define HW HWVoiceOut
30571ec3d6Sbellard #define SW SWVoiceOut
311d14ffa9Sbellard #else
32571ec3d6Sbellard #define NAME "capture"
331d14ffa9Sbellard #define TYPE in
34571ec3d6Sbellard #define HW HWVoiceIn
35571ec3d6Sbellard #define SW SWVoiceIn
36571ec3d6Sbellard #define HWBUF hw->conv_buf
371d14ffa9Sbellard #endif
381d14ffa9Sbellard
glue(audio_init_nb_voices_,TYPE)39526fb058SKővágó, Zoltán static void glue(audio_init_nb_voices_, TYPE)(AudioState *s,
405c63d141SPaolo Bonzini struct audio_driver *drv, int min_voices)
41c0fe3827Sbellard {
42571ec3d6Sbellard int max_voices = glue (drv->max_voices_, TYPE);
433724ab3bSVolker Rümelin size_t voice_size = glue(drv->voice_size_, TYPE);
44c0fe3827Sbellard
455c63d141SPaolo Bonzini glue (s->nb_hw_voices_, TYPE) = glue(audio_get_pdo_, TYPE)(s->dev)->voices;
46571ec3d6Sbellard if (glue (s->nb_hw_voices_, TYPE) > max_voices) {
47571ec3d6Sbellard if (!max_voices) {
48c0fe3827Sbellard #ifdef DAC
49571ec3d6Sbellard dolog ("Driver `%s' does not support " NAME "\n", drv->name);
50c0fe3827Sbellard #endif
516c6886bdSZhang Han } else {
52571ec3d6Sbellard dolog ("Driver `%s' does not support %d " NAME " voices, max %d\n",
53571ec3d6Sbellard drv->name,
54571ec3d6Sbellard glue (s->nb_hw_voices_, TYPE),
55571ec3d6Sbellard max_voices);
56571ec3d6Sbellard }
57571ec3d6Sbellard glue (s->nb_hw_voices_, TYPE) = max_voices;
58571ec3d6Sbellard }
59c0fe3827Sbellard
605c63d141SPaolo Bonzini if (glue (s->nb_hw_voices_, TYPE) < min_voices) {
615c63d141SPaolo Bonzini dolog ("Bogus number of " NAME " voices %d, setting to %d\n",
625c63d141SPaolo Bonzini glue (s->nb_hw_voices_, TYPE),
635c63d141SPaolo Bonzini min_voices);
645c63d141SPaolo Bonzini }
655c63d141SPaolo Bonzini
66470bcabdSAlistair Francis if (audio_bug(__func__, !voice_size && max_voices)) {
67571ec3d6Sbellard dolog ("drv=`%s' voice_size=0 max_voices=%d\n",
68571ec3d6Sbellard drv->name, max_voices);
6912f4abf6SVolker Rümelin glue (s->nb_hw_voices_, TYPE) = 0;
70571ec3d6Sbellard }
71571ec3d6Sbellard
72470bcabdSAlistair Francis if (audio_bug(__func__, voice_size && !max_voices)) {
733724ab3bSVolker Rümelin dolog("drv=`%s' voice_size=%zu max_voices=0\n",
74571ec3d6Sbellard drv->name, voice_size);
75571ec3d6Sbellard }
76571ec3d6Sbellard }
77571ec3d6Sbellard
glue(audio_pcm_hw_free_resources_,TYPE)78571ec3d6Sbellard static void glue (audio_pcm_hw_free_resources_, TYPE) (HW *hw)
79571ec3d6Sbellard {
80ff095e52SKővágó, Zoltán g_free(hw->buf_emul);
818dbd3d17SVolker Rümelin g_free(HWBUF.buffer);
828dbd3d17SVolker Rümelin HWBUF.buffer = NULL;
838dbd3d17SVolker Rümelin HWBUF.size = 0;
84571ec3d6Sbellard }
85571ec3d6Sbellard
glue(audio_pcm_hw_alloc_resources_,TYPE)86dc88e38fSKővágó, Zoltán static void glue(audio_pcm_hw_alloc_resources_, TYPE)(HW *hw)
87571ec3d6Sbellard {
881930616bSKővágó, Zoltán if (glue(audio_get_pdo_, TYPE)(hw->s->dev)->mixing_engine) {
89dc88e38fSKővágó, Zoltán size_t samples = hw->samples;
90dc88e38fSKővágó, Zoltán if (audio_bug(__func__, samples == 0)) {
91dc88e38fSKővágó, Zoltán dolog("Attempted to allocate empty buffer\n");
92c0fe3827Sbellard }
93c0fe3827Sbellard
948dbd3d17SVolker Rümelin HWBUF.buffer = g_new0(st_sample, samples);
958dbd3d17SVolker Rümelin HWBUF.size = samples;
968dbd3d17SVolker Rümelin HWBUF.pos = 0;
971930616bSKővágó, Zoltán } else {
988dbd3d17SVolker Rümelin HWBUF.buffer = NULL;
998dbd3d17SVolker Rümelin HWBUF.size = 0;
1001930616bSKővágó, Zoltán }
101c0fe3827Sbellard }
102c0fe3827Sbellard
glue(audio_pcm_sw_free_resources_,TYPE)103571ec3d6Sbellard static void glue (audio_pcm_sw_free_resources_, TYPE) (SW *sw)
104571ec3d6Sbellard {
1052c3f9a0aSVolker Rümelin g_free(sw->resample_buf.buffer);
1062c3f9a0aSVolker Rümelin sw->resample_buf.buffer = NULL;
1072c3f9a0aSVolker Rümelin sw->resample_buf.size = 0;
108571ec3d6Sbellard
109571ec3d6Sbellard if (sw->rate) {
110571ec3d6Sbellard st_rate_stop (sw->rate);
111571ec3d6Sbellard }
112571ec3d6Sbellard sw->rate = NULL;
113571ec3d6Sbellard }
114571ec3d6Sbellard
glue(audio_pcm_sw_alloc_resources_,TYPE)115571ec3d6Sbellard static int glue (audio_pcm_sw_alloc_resources_, TYPE) (SW *sw)
116571ec3d6Sbellard {
117148392abSVolker Rümelin HW *hw = sw->hw;
1182f886a34SVolker Rümelin uint64_t samples;
119571ec3d6Sbellard
1201930616bSKővágó, Zoltán if (!glue(audio_get_pdo_, TYPE)(sw->s->dev)->mixing_engine) {
1211930616bSKővágó, Zoltán return 0;
1221930616bSKővágó, Zoltán }
1231930616bSKővágó, Zoltán
1242f886a34SVolker Rümelin samples = muldiv64(HWBUF.size, sw->info.freq, hw->info.freq);
125b9ae74e2SVolker Rümelin if (samples == 0) {
1262f886a34SVolker Rümelin uint64_t f_fe_min;
1272f886a34SVolker Rümelin uint64_t f_be = (uint32_t)hw->info.freq;
128b9ae74e2SVolker Rümelin
129b9ae74e2SVolker Rümelin /* f_fe_min = ceil(1 [frames] * f_be [Hz] / size_be [frames]) */
1302f886a34SVolker Rümelin f_fe_min = (f_be + HWBUF.size - 1) / HWBUF.size;
131b9ae74e2SVolker Rümelin qemu_log_mask(LOG_UNIMP,
132b9ae74e2SVolker Rümelin AUDIO_CAP ": The guest selected a " NAME " sample rate"
1332f886a34SVolker Rümelin " of %d Hz for %s. Only sample rates >= %" PRIu64 " Hz"
1342f886a34SVolker Rümelin " are supported.\n",
135b9ae74e2SVolker Rümelin sw->info.freq, sw->name, f_fe_min);
136b9ae74e2SVolker Rümelin return -1;
137b9ae74e2SVolker Rümelin }
138571ec3d6Sbellard
139e1e6a6fcSVolker Rümelin /*
140e1e6a6fcSVolker Rümelin * Allocate one additional audio frame that is needed for upsampling
141e1e6a6fcSVolker Rümelin * if the resample buffer size is small. For large buffer sizes take
1422f886a34SVolker Rümelin * care of overflows and truncation.
143e1e6a6fcSVolker Rümelin */
1442f886a34SVolker Rümelin samples = samples < SIZE_MAX ? samples + 1 : SIZE_MAX;
1452c3f9a0aSVolker Rümelin sw->resample_buf.buffer = g_new0(st_sample, samples);
1462c3f9a0aSVolker Rümelin sw->resample_buf.size = samples;
1472c3f9a0aSVolker Rümelin sw->resample_buf.pos = 0;
148571ec3d6Sbellard
149571ec3d6Sbellard #ifdef DAC
150148392abSVolker Rümelin sw->rate = st_rate_start(sw->info.freq, hw->info.freq);
151571ec3d6Sbellard #else
152148392abSVolker Rümelin sw->rate = st_rate_start(hw->info.freq, sw->info.freq);
153571ec3d6Sbellard #endif
15425bf0c2dSVolker Rümelin
155571ec3d6Sbellard return 0;
156571ec3d6Sbellard }
157571ec3d6Sbellard
glue(audio_pcm_sw_init_,TYPE)158571ec3d6Sbellard static int glue (audio_pcm_sw_init_, TYPE) (
159571ec3d6Sbellard SW *sw,
160571ec3d6Sbellard HW *hw,
161571ec3d6Sbellard const char *name,
1621ea879e5Smalc struct audsettings *as
163571ec3d6Sbellard )
164571ec3d6Sbellard {
165571ec3d6Sbellard int err;
166571ec3d6Sbellard
167d929eba5Sbellard audio_pcm_init_info (&sw->info, as);
168571ec3d6Sbellard sw->hw = hw;
169571ec3d6Sbellard sw->active = 0;
170571ec3d6Sbellard #ifdef DAC
171571ec3d6Sbellard sw->total_hw_samples_mixed = 0;
172571ec3d6Sbellard sw->empty = 1;
173571ec3d6Sbellard #endif
174571ec3d6Sbellard
175ed2a4a79SKővágó, Zoltán if (sw->info.is_float) {
176ed2a4a79SKővágó, Zoltán #ifdef DAC
177*5d978c5dSVolker Rümelin sw->conv = mixeng_conv_float[sw->info.nchannels == 2]
178*5d978c5dSVolker Rümelin [sw->info.swap_endianness];
179ed2a4a79SKővágó, Zoltán #else
180*5d978c5dSVolker Rümelin sw->clip = mixeng_clip_float[sw->info.nchannels == 2]
181*5d978c5dSVolker Rümelin [sw->info.swap_endianness];
182ed2a4a79SKővágó, Zoltán #endif
183ed2a4a79SKővágó, Zoltán } else {
184571ec3d6Sbellard #ifdef DAC
185571ec3d6Sbellard sw->conv = mixeng_conv
186571ec3d6Sbellard #else
187571ec3d6Sbellard sw->clip = mixeng_clip
188571ec3d6Sbellard #endif
189571ec3d6Sbellard [sw->info.nchannels == 2]
190ed2a4a79SKővágó, Zoltán [sw->info.is_signed]
191d929eba5Sbellard [sw->info.swap_endianness]
192f941aa25Sths [audio_bits_to_index(sw->info.bits)];
193ed2a4a79SKővágó, Zoltán }
194571ec3d6Sbellard
1957267c094SAnthony Liguori sw->name = g_strdup (name);
196571ec3d6Sbellard err = glue (audio_pcm_sw_alloc_resources_, TYPE) (sw);
197571ec3d6Sbellard if (err) {
1987267c094SAnthony Liguori g_free (sw->name);
199571ec3d6Sbellard sw->name = NULL;
200571ec3d6Sbellard }
201571ec3d6Sbellard return err;
202571ec3d6Sbellard }
203571ec3d6Sbellard
glue(audio_pcm_sw_fini_,TYPE)2041d14ffa9Sbellard static void glue (audio_pcm_sw_fini_, TYPE) (SW *sw)
2051d14ffa9Sbellard {
2061d14ffa9Sbellard glue (audio_pcm_sw_free_resources_, TYPE) (sw);
2077267c094SAnthony Liguori g_free (sw->name);
2081d14ffa9Sbellard sw->name = NULL;
2091d14ffa9Sbellard }
2101d14ffa9Sbellard
glue(audio_pcm_hw_add_sw_,TYPE)2111d14ffa9Sbellard static void glue (audio_pcm_hw_add_sw_, TYPE) (HW *hw, SW *sw)
2121d14ffa9Sbellard {
21372cf2d4fSBlue Swirl QLIST_INSERT_HEAD (&hw->sw_head, sw, entries);
2141d14ffa9Sbellard }
2151d14ffa9Sbellard
glue(audio_pcm_hw_del_sw_,TYPE)2161d14ffa9Sbellard static void glue (audio_pcm_hw_del_sw_, TYPE) (SW *sw)
2171d14ffa9Sbellard {
21872cf2d4fSBlue Swirl QLIST_REMOVE (sw, entries);
2191d14ffa9Sbellard }
2201d14ffa9Sbellard
glue(audio_pcm_hw_gc_,TYPE)2211a7dafceSmalc static void glue (audio_pcm_hw_gc_, TYPE) (HW **hwp)
2221d14ffa9Sbellard {
223c0fe3827Sbellard HW *hw = *hwp;
224526fb058SKővágó, Zoltán AudioState *s = hw->s;
225c0fe3827Sbellard
226c0fe3827Sbellard if (!hw->sw_head.lh_first) {
2278ead62cfSbellard #ifdef DAC
2288ead62cfSbellard audio_detach_capture(hw);
2298ead62cfSbellard #endif
23072cf2d4fSBlue Swirl QLIST_REMOVE(hw, entries);
231b28fb27bSPeter Maydell glue(hw->pcm_ops->fini_, TYPE) (hw);
232c0fe3827Sbellard glue(s->nb_hw_voices_, TYPE) += 1;
2331d14ffa9Sbellard glue(audio_pcm_hw_free_resources_ , TYPE) (hw);
2347267c094SAnthony Liguori g_free(hw);
235c0fe3827Sbellard *hwp = NULL;
2361d14ffa9Sbellard }
2371d14ffa9Sbellard }
2381d14ffa9Sbellard
glue(audio_pcm_hw_find_any_,TYPE)239526fb058SKővágó, Zoltán static HW *glue(audio_pcm_hw_find_any_, TYPE)(AudioState *s, HW *hw)
2401d14ffa9Sbellard {
2411a7dafceSmalc return hw ? hw->entries.le_next : glue (s->hw_head_, TYPE).lh_first;
2421d14ffa9Sbellard }
2431d14ffa9Sbellard
glue(audio_pcm_hw_find_any_enabled_,TYPE)244526fb058SKővágó, Zoltán static HW *glue(audio_pcm_hw_find_any_enabled_, TYPE)(AudioState *s, HW *hw)
2451d14ffa9Sbellard {
246526fb058SKővágó, Zoltán while ((hw = glue(audio_pcm_hw_find_any_, TYPE)(s, hw))) {
247c0fe3827Sbellard if (hw->enabled) {
2481d14ffa9Sbellard return hw;
2491d14ffa9Sbellard }
2501d14ffa9Sbellard }
2511d14ffa9Sbellard return NULL;
2521d14ffa9Sbellard }
2531d14ffa9Sbellard
glue(audio_pcm_hw_find_specific_,TYPE)254526fb058SKővágó, Zoltán static HW *glue(audio_pcm_hw_find_specific_, TYPE)(AudioState *s, HW *hw,
255526fb058SKővágó, Zoltán struct audsettings *as)
2561d14ffa9Sbellard {
257526fb058SKővágó, Zoltán while ((hw = glue(audio_pcm_hw_find_any_, TYPE)(s, hw))) {
258c0fe3827Sbellard if (audio_pcm_info_eq (&hw->info, as)) {
2591d14ffa9Sbellard return hw;
2601d14ffa9Sbellard }
2611d14ffa9Sbellard }
2621d14ffa9Sbellard return NULL;
2631d14ffa9Sbellard }
2641d14ffa9Sbellard
glue(audio_pcm_hw_add_new_,TYPE)265526fb058SKővágó, Zoltán static HW *glue(audio_pcm_hw_add_new_, TYPE)(AudioState *s,
266526fb058SKővágó, Zoltán struct audsettings *as)
2671d14ffa9Sbellard {
2681d14ffa9Sbellard HW *hw;
269571ec3d6Sbellard struct audio_driver *drv = s->drv;
2701d14ffa9Sbellard
271571ec3d6Sbellard if (!glue (s->nb_hw_voices_, TYPE)) {
2721d14ffa9Sbellard return NULL;
2731d14ffa9Sbellard }
2741d14ffa9Sbellard
275470bcabdSAlistair Francis if (audio_bug(__func__, !drv)) {
276571ec3d6Sbellard dolog ("No host audio driver\n");
27712f4abf6SVolker Rümelin return NULL;
2781d14ffa9Sbellard }
279571ec3d6Sbellard
280470bcabdSAlistair Francis if (audio_bug(__func__, !drv->pcm_ops)) {
281571ec3d6Sbellard dolog ("Host audio driver without pcm_ops\n");
28212f4abf6SVolker Rümelin return NULL;
283571ec3d6Sbellard }
284571ec3d6Sbellard
2853724ab3bSVolker Rümelin /*
2863724ab3bSVolker Rümelin * Since glue(s->nb_hw_voices_, TYPE) is != 0, glue(drv->voice_size_, TYPE)
2873724ab3bSVolker Rümelin * is guaranteed to be != 0. See the audio_init_nb_voices_* functions.
2883724ab3bSVolker Rümelin */
2893724ab3bSVolker Rümelin hw = g_malloc0(glue(drv->voice_size_, TYPE));
290526fb058SKővágó, Zoltán hw->s = s;
291571ec3d6Sbellard hw->pcm_ops = drv->pcm_ops;
292c01b2456SMarc-André Lureau
29372cf2d4fSBlue Swirl QLIST_INIT (&hw->sw_head);
2948ead62cfSbellard #ifdef DAC
29572cf2d4fSBlue Swirl QLIST_INIT (&hw->cap_head);
2968ead62cfSbellard #endif
2975706db1dSKővágó, Zoltán if (glue (hw->pcm_ops->init_, TYPE) (hw, as, s->drv_opaque)) {
29812f4abf6SVolker Rümelin goto err0;
299571ec3d6Sbellard }
300571ec3d6Sbellard
301470bcabdSAlistair Francis if (audio_bug(__func__, hw->samples <= 0)) {
3027520462bSKővágó, Zoltán dolog("hw->samples=%zd\n", hw->samples);
30312f4abf6SVolker Rümelin goto err1;
304571ec3d6Sbellard }
305571ec3d6Sbellard
306ed2a4a79SKővágó, Zoltán if (hw->info.is_float) {
307180b044fSVolker Rümelin #ifdef DAC
308*5d978c5dSVolker Rümelin hw->clip = mixeng_clip_float[hw->info.nchannels == 2]
309*5d978c5dSVolker Rümelin [hw->info.swap_endianness];
310180b044fSVolker Rümelin #else
311*5d978c5dSVolker Rümelin hw->conv = mixeng_conv_float[hw->info.nchannels == 2]
312*5d978c5dSVolker Rümelin [hw->info.swap_endianness];
313180b044fSVolker Rümelin #endif
314ed2a4a79SKővágó, Zoltán } else {
315571ec3d6Sbellard #ifdef DAC
316571ec3d6Sbellard hw->clip = mixeng_clip
317571ec3d6Sbellard #else
318571ec3d6Sbellard hw->conv = mixeng_conv
319571ec3d6Sbellard #endif
320571ec3d6Sbellard [hw->info.nchannels == 2]
321ed2a4a79SKővágó, Zoltán [hw->info.is_signed]
322d929eba5Sbellard [hw->info.swap_endianness]
323f941aa25Sths [audio_bits_to_index(hw->info.bits)];
324ed2a4a79SKővágó, Zoltán }
325571ec3d6Sbellard
326dc88e38fSKővágó, Zoltán glue(audio_pcm_hw_alloc_resources_, TYPE)(hw);
327571ec3d6Sbellard
32872cf2d4fSBlue Swirl QLIST_INSERT_HEAD (&s->glue (hw_head_, TYPE), hw, entries);
329571ec3d6Sbellard glue (s->nb_hw_voices_, TYPE) -= 1;
3308ead62cfSbellard #ifdef DAC
3311a7dafceSmalc audio_attach_capture (hw);
3328ead62cfSbellard #endif
3331d14ffa9Sbellard return hw;
33412f4abf6SVolker Rümelin
33512f4abf6SVolker Rümelin err1:
33612f4abf6SVolker Rümelin glue (hw->pcm_ops->fini_, TYPE) (hw);
33712f4abf6SVolker Rümelin err0:
33812f4abf6SVolker Rümelin g_free (hw);
33912f4abf6SVolker Rümelin return NULL;
3401d14ffa9Sbellard }
3411d14ffa9Sbellard
glue(audio_get_pdo_,TYPE)34271830221SKővágó, Zoltán AudiodevPerDirectionOptions *glue(audio_get_pdo_, TYPE)(Audiodev *dev)
34371830221SKővágó, Zoltán {
34471830221SKővágó, Zoltán switch (dev->driver) {
34571830221SKővágó, Zoltán case AUDIODEV_DRIVER_NONE:
34671830221SKővágó, Zoltán return dev->u.none.TYPE;
3477a92a857SDaniel P. Berrangé #ifdef CONFIG_AUDIO_ALSA
34871830221SKővágó, Zoltán case AUDIODEV_DRIVER_ALSA:
34971830221SKővágó, Zoltán return qapi_AudiodevAlsaPerDirectionOptions_base(dev->u.alsa.TYPE);
3507a92a857SDaniel P. Berrangé #endif
3517a92a857SDaniel P. Berrangé #ifdef CONFIG_AUDIO_COREAUDIO
35271830221SKővágó, Zoltán case AUDIODEV_DRIVER_COREAUDIO:
35371830221SKővágó, Zoltán return qapi_AudiodevCoreaudioPerDirectionOptions_base(
35471830221SKővágó, Zoltán dev->u.coreaudio.TYPE);
3557a92a857SDaniel P. Berrangé #endif
3567a92a857SDaniel P. Berrangé #ifdef CONFIG_DBUS_DISPLAY
357739362d4SMarc-André Lureau case AUDIODEV_DRIVER_DBUS:
358739362d4SMarc-André Lureau return dev->u.dbus.TYPE;
3597a92a857SDaniel P. Berrangé #endif
3607a92a857SDaniel P. Berrangé #ifdef CONFIG_AUDIO_DSOUND
36171830221SKővágó, Zoltán case AUDIODEV_DRIVER_DSOUND:
36271830221SKővágó, Zoltán return dev->u.dsound.TYPE;
3637a92a857SDaniel P. Berrangé #endif
3647a92a857SDaniel P. Berrangé #ifdef CONFIG_AUDIO_JACK
3652e445703SGeoffrey McRae case AUDIODEV_DRIVER_JACK:
3662e445703SGeoffrey McRae return qapi_AudiodevJackPerDirectionOptions_base(dev->u.jack.TYPE);
3677a92a857SDaniel P. Berrangé #endif
3687a92a857SDaniel P. Berrangé #ifdef CONFIG_AUDIO_OSS
36971830221SKővágó, Zoltán case AUDIODEV_DRIVER_OSS:
37071830221SKővágó, Zoltán return qapi_AudiodevOssPerDirectionOptions_base(dev->u.oss.TYPE);
3717a92a857SDaniel P. Berrangé #endif
3727a92a857SDaniel P. Berrangé #ifdef CONFIG_AUDIO_PA
37371830221SKővágó, Zoltán case AUDIODEV_DRIVER_PA:
37471830221SKővágó, Zoltán return qapi_AudiodevPaPerDirectionOptions_base(dev->u.pa.TYPE);
3757a92a857SDaniel P. Berrangé #endif
376c2d3d1c2SDorinda Bassey #ifdef CONFIG_AUDIO_PIPEWIRE
377c2d3d1c2SDorinda Bassey case AUDIODEV_DRIVER_PIPEWIRE:
378c2d3d1c2SDorinda Bassey return qapi_AudiodevPipewirePerDirectionOptions_base(dev->u.pipewire.TYPE);
379c2d3d1c2SDorinda Bassey #endif
3807a92a857SDaniel P. Berrangé #ifdef CONFIG_AUDIO_SDL
38171830221SKővágó, Zoltán case AUDIODEV_DRIVER_SDL:
3825a0926c2SVolker Rümelin return qapi_AudiodevSdlPerDirectionOptions_base(dev->u.sdl.TYPE);
3837a92a857SDaniel P. Berrangé #endif
3847a92a857SDaniel P. Berrangé #ifdef CONFIG_AUDIO_SNDIO
385663df1ccSAlexandre Ratchov case AUDIODEV_DRIVER_SNDIO:
386663df1ccSAlexandre Ratchov return dev->u.sndio.TYPE;
3877a92a857SDaniel P. Berrangé #endif
3887a92a857SDaniel P. Berrangé #ifdef CONFIG_SPICE
38971830221SKővágó, Zoltán case AUDIODEV_DRIVER_SPICE:
39071830221SKővágó, Zoltán return dev->u.spice.TYPE;
3917a92a857SDaniel P. Berrangé #endif
39271830221SKővágó, Zoltán case AUDIODEV_DRIVER_WAV:
39371830221SKővágó, Zoltán return dev->u.wav.TYPE;
39471830221SKővágó, Zoltán
39571830221SKővágó, Zoltán case AUDIODEV_DRIVER__MAX:
39671830221SKővágó, Zoltán break;
39771830221SKővágó, Zoltán }
39871830221SKővágó, Zoltán abort();
39971830221SKővágó, Zoltán }
40071830221SKővágó, Zoltán
glue(audio_pcm_hw_add_,TYPE)401526fb058SKővágó, Zoltán static HW *glue(audio_pcm_hw_add_, TYPE)(AudioState *s, struct audsettings *as)
4021d14ffa9Sbellard {
4031d14ffa9Sbellard HW *hw;
40471830221SKővágó, Zoltán AudiodevPerDirectionOptions *pdo = glue(audio_get_pdo_, TYPE)(s->dev);
4051d14ffa9Sbellard
4061930616bSKővágó, Zoltán if (!pdo->mixing_engine || pdo->fixed_settings) {
407526fb058SKővágó, Zoltán hw = glue(audio_pcm_hw_add_new_, TYPE)(s, as);
4081930616bSKővágó, Zoltán if (!pdo->mixing_engine || hw) {
4091d14ffa9Sbellard return hw;
4101d14ffa9Sbellard }
4111d14ffa9Sbellard }
4121d14ffa9Sbellard
413526fb058SKővágó, Zoltán hw = glue(audio_pcm_hw_find_specific_, TYPE)(s, NULL, as);
4141d14ffa9Sbellard if (hw) {
4151d14ffa9Sbellard return hw;
4161d14ffa9Sbellard }
4171d14ffa9Sbellard
418526fb058SKővágó, Zoltán hw = glue(audio_pcm_hw_add_new_, TYPE)(s, as);
4191d14ffa9Sbellard if (hw) {
4201d14ffa9Sbellard return hw;
4211d14ffa9Sbellard }
4221d14ffa9Sbellard
423526fb058SKővágó, Zoltán return glue(audio_pcm_hw_find_any_, TYPE)(s, NULL);
4241d14ffa9Sbellard }
4251d14ffa9Sbellard
glue(audio_pcm_create_voice_pair_,TYPE)4261d14ffa9Sbellard static SW *glue(audio_pcm_create_voice_pair_, TYPE)(
427526fb058SKővágó, Zoltán AudioState *s,
428c0fe3827Sbellard const char *sw_name,
4291ea879e5Smalc struct audsettings *as
4301d14ffa9Sbellard )
4311d14ffa9Sbellard {
4321d14ffa9Sbellard SW *sw;
4331d14ffa9Sbellard HW *hw;
4341ea879e5Smalc struct audsettings hw_as;
43571830221SKővágó, Zoltán AudiodevPerDirectionOptions *pdo = glue(audio_get_pdo_, TYPE)(s->dev);
4361d14ffa9Sbellard
43771830221SKővágó, Zoltán if (pdo->fixed_settings) {
43871830221SKővágó, Zoltán hw_as = audiodev_to_audsettings(pdo);
4396c6886bdSZhang Han } else {
440c0fe3827Sbellard hw_as = *as;
4411d14ffa9Sbellard }
4421d14ffa9Sbellard
443c6b69a81SVolker Rümelin sw = g_new0(SW, 1);
444526fb058SKővágó, Zoltán sw->s = s;
4451d14ffa9Sbellard
446526fb058SKővágó, Zoltán hw = glue(audio_pcm_hw_add_, TYPE)(s, &hw_as);
4471d14ffa9Sbellard if (!hw) {
44890394fe1SVolker Rümelin dolog("Could not create a backend for voice `%s'\n", sw_name);
449c6b69a81SVolker Rümelin goto err1;
4501d14ffa9Sbellard }
4511d14ffa9Sbellard
4521d14ffa9Sbellard glue (audio_pcm_hw_add_sw_, TYPE) (hw, sw);
4531d14ffa9Sbellard
454d929eba5Sbellard if (glue (audio_pcm_sw_init_, TYPE) (sw, hw, sw_name, as)) {
455c6b69a81SVolker Rümelin goto err2;
4561d14ffa9Sbellard }
4571d14ffa9Sbellard
4581d14ffa9Sbellard return sw;
4591d14ffa9Sbellard
460c6b69a81SVolker Rümelin err2:
4611d14ffa9Sbellard glue (audio_pcm_hw_del_sw_, TYPE) (sw);
4621a7dafceSmalc glue (audio_pcm_hw_gc_, TYPE) (&hw);
4631d14ffa9Sbellard err1:
464c6b69a81SVolker Rümelin g_free(sw);
4651d14ffa9Sbellard return NULL;
4661d14ffa9Sbellard }
4671d14ffa9Sbellard
glue(audio_close_,TYPE)4681a7dafceSmalc static void glue (audio_close_, TYPE) (SW *sw)
4691d14ffa9Sbellard {
4701d14ffa9Sbellard glue (audio_pcm_sw_fini_, TYPE) (sw);
4711d14ffa9Sbellard glue (audio_pcm_hw_del_sw_, TYPE) (sw);
4721a7dafceSmalc glue (audio_pcm_hw_gc_, TYPE) (&sw->hw);
4737267c094SAnthony Liguori g_free (sw);
4741d14ffa9Sbellard }
475571ec3d6Sbellard
glue(AUD_close_,TYPE)476c0fe3827Sbellard void glue (AUD_close_, TYPE) (QEMUSoundCard *card, SW *sw)
477c0fe3827Sbellard {
478c0fe3827Sbellard if (sw) {
479470bcabdSAlistair Francis if (audio_bug(__func__, !card)) {
4801a7dafceSmalc dolog ("card=%p\n", card);
48112f4abf6SVolker Rümelin return;
482c0fe3827Sbellard }
483c0fe3827Sbellard
4841a7dafceSmalc glue (audio_close_, TYPE) (sw);
485c0fe3827Sbellard }
4861d14ffa9Sbellard }
4871d14ffa9Sbellard
glue(AUD_open_,TYPE)4881d14ffa9Sbellard SW *glue (AUD_open_, TYPE) (
489c0fe3827Sbellard QEMUSoundCard *card,
4901d14ffa9Sbellard SW *sw,
4911d14ffa9Sbellard const char *name,
4921d14ffa9Sbellard void *callback_opaque ,
493cb4f03e8Smalc audio_callback_fn callback_fn,
4941ea879e5Smalc struct audsettings *as
4951d14ffa9Sbellard )
4961d14ffa9Sbellard {
497d1670b20SKővágó, Zoltán AudioState *s;
498d1670b20SKővágó, Zoltán AudiodevPerDirectionOptions *pdo;
4991d14ffa9Sbellard
500470bcabdSAlistair Francis if (audio_bug(__func__, !card || !name || !callback_fn || !as)) {
5011a7dafceSmalc dolog ("card=%p name=%p callback_fn=%p as=%p\n",
5021a7dafceSmalc card, name, callback_fn, as);
50312f4abf6SVolker Rümelin goto fail;
5041d14ffa9Sbellard }
5051d14ffa9Sbellard
506d1670b20SKővágó, Zoltán s = card->state;
507d1670b20SKővágó, Zoltán pdo = glue(audio_get_pdo_, TYPE)(s->dev);
508d1670b20SKővágó, Zoltán
50993b65997SStefan Weil ldebug ("open %s, freq %d, nchannels %d, fmt %d\n",
51093b65997SStefan Weil name, as->freq, as->nchannels, as->fmt);
51193b65997SStefan Weil
512470bcabdSAlistair Francis if (audio_bug(__func__, audio_validate_settings(as))) {
513c0fe3827Sbellard audio_print_settings (as);
51412f4abf6SVolker Rümelin goto fail;
5151d14ffa9Sbellard }
5161d14ffa9Sbellard
517470bcabdSAlistair Francis if (audio_bug(__func__, !s->drv)) {
518c0fe3827Sbellard dolog ("Can not open `%s' (no host audio driver)\n", name);
51912f4abf6SVolker Rümelin goto fail;
5201d14ffa9Sbellard }
5211d14ffa9Sbellard
522c0fe3827Sbellard if (sw && audio_pcm_info_eq (&sw->info, as)) {
5231d14ffa9Sbellard return sw;
5241d14ffa9Sbellard }
5251d14ffa9Sbellard
52671830221SKővágó, Zoltán if (!pdo->fixed_settings && sw) {
527c0fe3827Sbellard glue (AUD_close_, TYPE) (card, sw);
5281d14ffa9Sbellard sw = NULL;
5291d14ffa9Sbellard }
5301d14ffa9Sbellard
5311d14ffa9Sbellard if (sw) {
5321d14ffa9Sbellard HW *hw = sw->hw;
5331d14ffa9Sbellard
5341d14ffa9Sbellard if (!hw) {
535b637a61cSVolker Rümelin dolog("Internal logic error: voice `%s' has no backend\n",
536c0fe3827Sbellard SW_NAME(sw));
5371d14ffa9Sbellard goto fail;
5381d14ffa9Sbellard }
5391d14ffa9Sbellard
540571ec3d6Sbellard glue (audio_pcm_sw_fini_, TYPE) (sw);
541d929eba5Sbellard if (glue (audio_pcm_sw_init_, TYPE) (sw, hw, name, as)) {
5421d14ffa9Sbellard goto fail;
5431d14ffa9Sbellard }
5446c6886bdSZhang Han } else {
545526fb058SKővágó, Zoltán sw = glue(audio_pcm_create_voice_pair_, TYPE)(s, name, as);
5461d14ffa9Sbellard if (!sw) {
547571ec3d6Sbellard return NULL;
5481d14ffa9Sbellard }
5491d14ffa9Sbellard }
5501d14ffa9Sbellard
5511a7dafceSmalc sw->card = card;
5521d14ffa9Sbellard sw->vol = nominal_volume;
5531d14ffa9Sbellard sw->callback.fn = callback_fn;
5541d14ffa9Sbellard sw->callback.opaque = callback_opaque;
5551d14ffa9Sbellard
5561d14ffa9Sbellard #ifdef DEBUG_AUDIO
5571d14ffa9Sbellard dolog ("%s\n", name);
5581d14ffa9Sbellard audio_pcm_print_info ("hw", &sw->hw->info);
5591d14ffa9Sbellard audio_pcm_print_info ("sw", &sw->info);
5601d14ffa9Sbellard #endif
5611d14ffa9Sbellard
5621d14ffa9Sbellard return sw;
5631d14ffa9Sbellard
5641d14ffa9Sbellard fail:
565c0fe3827Sbellard glue (AUD_close_, TYPE) (card, sw);
5661d14ffa9Sbellard return NULL;
5671d14ffa9Sbellard }
5681d14ffa9Sbellard
glue(AUD_is_active_,TYPE)5691d14ffa9Sbellard int glue (AUD_is_active_, TYPE) (SW *sw)
5701d14ffa9Sbellard {
5711d14ffa9Sbellard return sw ? sw->active : 0;
5721d14ffa9Sbellard }
5731d14ffa9Sbellard
glue(AUD_init_time_stamp_,TYPE)5741d14ffa9Sbellard void glue (AUD_init_time_stamp_, TYPE) (SW *sw, QEMUAudioTimeStamp *ts)
5751d14ffa9Sbellard {
5761d14ffa9Sbellard if (!sw) {
5771d14ffa9Sbellard return;
5781d14ffa9Sbellard }
5791d14ffa9Sbellard
5801d14ffa9Sbellard ts->old_ts = sw->hw->ts_helper;
5811d14ffa9Sbellard }
5821d14ffa9Sbellard
glue(AUD_get_elapsed_usec_,TYPE)583c0fe3827Sbellard uint64_t glue (AUD_get_elapsed_usec_, TYPE) (SW *sw, QEMUAudioTimeStamp *ts)
5841d14ffa9Sbellard {
5851d14ffa9Sbellard uint64_t delta, cur_ts, old_ts;
5861d14ffa9Sbellard
5871d14ffa9Sbellard if (!sw) {
5881d14ffa9Sbellard return 0;
5891d14ffa9Sbellard }
5901d14ffa9Sbellard
5911d14ffa9Sbellard cur_ts = sw->hw->ts_helper;
5921d14ffa9Sbellard old_ts = ts->old_ts;
5930bfcd599SBlue Swirl /* dolog ("cur %" PRId64 " old %" PRId64 "\n", cur_ts, old_ts); */
5941d14ffa9Sbellard
5951d14ffa9Sbellard if (cur_ts >= old_ts) {
5961d14ffa9Sbellard delta = cur_ts - old_ts;
5976c6886bdSZhang Han } else {
5981d14ffa9Sbellard delta = UINT64_MAX - old_ts + cur_ts;
5991d14ffa9Sbellard }
6001d14ffa9Sbellard
6011d14ffa9Sbellard if (!delta) {
6021d14ffa9Sbellard return 0;
6031d14ffa9Sbellard }
6041d14ffa9Sbellard
6054f4cc0efSmalc return muldiv64 (delta, sw->hw->info.freq, 1000000);
6061d14ffa9Sbellard }
6071d14ffa9Sbellard
6081d14ffa9Sbellard #undef TYPE
6091d14ffa9Sbellard #undef HW
6101d14ffa9Sbellard #undef SW
611571ec3d6Sbellard #undef HWBUF
612571ec3d6Sbellard #undef NAME
613