xref: /qemu/audio/audio_template.h (revision 2f886a34bb7e6f6fcf39d64829f4499476f26dba)
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 
39526fb058SKővágó, Zoltán static void glue(audio_init_nb_voices_, TYPE)(AudioState *s,
40526fb058SKővágó, Zoltán                                               struct audio_driver *drv)
41c0fe3827Sbellard {
42571ec3d6Sbellard     int max_voices = glue (drv->max_voices_, TYPE);
433724ab3bSVolker Rümelin     size_t voice_size = glue(drv->voice_size_, TYPE);
44c0fe3827Sbellard 
45571ec3d6Sbellard     if (glue (s->nb_hw_voices_, TYPE) > max_voices) {
46571ec3d6Sbellard         if (!max_voices) {
47c0fe3827Sbellard #ifdef DAC
48571ec3d6Sbellard             dolog ("Driver `%s' does not support " NAME "\n", drv->name);
49c0fe3827Sbellard #endif
506c6886bdSZhang Han         } else {
51571ec3d6Sbellard             dolog ("Driver `%s' does not support %d " NAME " voices, max %d\n",
52571ec3d6Sbellard                    drv->name,
53571ec3d6Sbellard                    glue (s->nb_hw_voices_, TYPE),
54571ec3d6Sbellard                    max_voices);
55571ec3d6Sbellard         }
56571ec3d6Sbellard         glue (s->nb_hw_voices_, TYPE) = max_voices;
57571ec3d6Sbellard     }
58c0fe3827Sbellard 
59470bcabdSAlistair Francis     if (audio_bug(__func__, !voice_size && max_voices)) {
60571ec3d6Sbellard         dolog ("drv=`%s' voice_size=0 max_voices=%d\n",
61571ec3d6Sbellard                drv->name, max_voices);
6212f4abf6SVolker Rümelin         glue (s->nb_hw_voices_, TYPE) = 0;
63571ec3d6Sbellard     }
64571ec3d6Sbellard 
65470bcabdSAlistair Francis     if (audio_bug(__func__, voice_size && !max_voices)) {
663724ab3bSVolker Rümelin         dolog("drv=`%s' voice_size=%zu max_voices=0\n",
67571ec3d6Sbellard               drv->name, voice_size);
68571ec3d6Sbellard     }
69571ec3d6Sbellard }
70571ec3d6Sbellard 
71571ec3d6Sbellard static void glue (audio_pcm_hw_free_resources_, TYPE) (HW *hw)
72571ec3d6Sbellard {
73ff095e52SKővágó, Zoltán     g_free(hw->buf_emul);
748dbd3d17SVolker Rümelin     g_free(HWBUF.buffer);
758dbd3d17SVolker Rümelin     HWBUF.buffer = NULL;
768dbd3d17SVolker Rümelin     HWBUF.size = 0;
77571ec3d6Sbellard }
78571ec3d6Sbellard 
79dc88e38fSKővágó, Zoltán static void glue(audio_pcm_hw_alloc_resources_, TYPE)(HW *hw)
80571ec3d6Sbellard {
811930616bSKővágó, Zoltán     if (glue(audio_get_pdo_, TYPE)(hw->s->dev)->mixing_engine) {
82dc88e38fSKővágó, Zoltán         size_t samples = hw->samples;
83dc88e38fSKővágó, Zoltán         if (audio_bug(__func__, samples == 0)) {
84dc88e38fSKővágó, Zoltán             dolog("Attempted to allocate empty buffer\n");
85c0fe3827Sbellard         }
86c0fe3827Sbellard 
878dbd3d17SVolker Rümelin         HWBUF.buffer = g_new0(st_sample, samples);
888dbd3d17SVolker Rümelin         HWBUF.size = samples;
898dbd3d17SVolker Rümelin         HWBUF.pos = 0;
901930616bSKővágó, Zoltán     } else {
918dbd3d17SVolker Rümelin         HWBUF.buffer = NULL;
928dbd3d17SVolker Rümelin         HWBUF.size = 0;
931930616bSKővágó, Zoltán     }
94c0fe3827Sbellard }
95c0fe3827Sbellard 
96571ec3d6Sbellard static void glue (audio_pcm_sw_free_resources_, TYPE) (SW *sw)
97571ec3d6Sbellard {
982c3f9a0aSVolker Rümelin     g_free(sw->resample_buf.buffer);
992c3f9a0aSVolker Rümelin     sw->resample_buf.buffer = NULL;
1002c3f9a0aSVolker Rümelin     sw->resample_buf.size = 0;
101571ec3d6Sbellard 
102571ec3d6Sbellard     if (sw->rate) {
103571ec3d6Sbellard         st_rate_stop (sw->rate);
104571ec3d6Sbellard     }
105571ec3d6Sbellard     sw->rate = NULL;
106571ec3d6Sbellard }
107571ec3d6Sbellard 
108571ec3d6Sbellard static int glue (audio_pcm_sw_alloc_resources_, TYPE) (SW *sw)
109571ec3d6Sbellard {
110148392abSVolker Rümelin     HW *hw = sw->hw;
111*2f886a34SVolker Rümelin     uint64_t samples;
112571ec3d6Sbellard 
1131930616bSKővágó, Zoltán     if (!glue(audio_get_pdo_, TYPE)(sw->s->dev)->mixing_engine) {
1141930616bSKővágó, Zoltán         return 0;
1151930616bSKővágó, Zoltán     }
1161930616bSKővágó, Zoltán 
117*2f886a34SVolker Rümelin     samples = muldiv64(HWBUF.size, sw->info.freq, hw->info.freq);
118b9ae74e2SVolker Rümelin     if (samples == 0) {
119*2f886a34SVolker Rümelin         uint64_t f_fe_min;
120*2f886a34SVolker Rümelin         uint64_t f_be = (uint32_t)hw->info.freq;
121b9ae74e2SVolker Rümelin 
122b9ae74e2SVolker Rümelin         /* f_fe_min = ceil(1 [frames] * f_be [Hz] / size_be [frames]) */
123*2f886a34SVolker Rümelin         f_fe_min = (f_be + HWBUF.size - 1) / HWBUF.size;
124b9ae74e2SVolker Rümelin         qemu_log_mask(LOG_UNIMP,
125b9ae74e2SVolker Rümelin                       AUDIO_CAP ": The guest selected a " NAME " sample rate"
126*2f886a34SVolker Rümelin                       " of %d Hz for %s. Only sample rates >= %" PRIu64 " Hz"
127*2f886a34SVolker Rümelin                       " are supported.\n",
128b9ae74e2SVolker Rümelin                       sw->info.freq, sw->name, f_fe_min);
129b9ae74e2SVolker Rümelin         return -1;
130b9ae74e2SVolker Rümelin     }
131571ec3d6Sbellard 
132e1e6a6fcSVolker Rümelin     /*
133e1e6a6fcSVolker Rümelin      * Allocate one additional audio frame that is needed for upsampling
134e1e6a6fcSVolker Rümelin      * if the resample buffer size is small. For large buffer sizes take
135*2f886a34SVolker Rümelin      * care of overflows and truncation.
136e1e6a6fcSVolker Rümelin      */
137*2f886a34SVolker Rümelin     samples = samples < SIZE_MAX ? samples + 1 : SIZE_MAX;
1382c3f9a0aSVolker Rümelin     sw->resample_buf.buffer = g_new0(st_sample, samples);
1392c3f9a0aSVolker Rümelin     sw->resample_buf.size = samples;
1402c3f9a0aSVolker Rümelin     sw->resample_buf.pos = 0;
141571ec3d6Sbellard 
142571ec3d6Sbellard #ifdef DAC
143148392abSVolker Rümelin     sw->rate = st_rate_start(sw->info.freq, hw->info.freq);
144571ec3d6Sbellard #else
145148392abSVolker Rümelin     sw->rate = st_rate_start(hw->info.freq, sw->info.freq);
146571ec3d6Sbellard #endif
14725bf0c2dSVolker Rümelin 
148571ec3d6Sbellard     return 0;
149571ec3d6Sbellard }
150571ec3d6Sbellard 
151571ec3d6Sbellard static int glue (audio_pcm_sw_init_, TYPE) (
152571ec3d6Sbellard     SW *sw,
153571ec3d6Sbellard     HW *hw,
154571ec3d6Sbellard     const char *name,
1551ea879e5Smalc     struct audsettings *as
156571ec3d6Sbellard     )
157571ec3d6Sbellard {
158571ec3d6Sbellard     int err;
159571ec3d6Sbellard 
160d929eba5Sbellard     audio_pcm_init_info (&sw->info, as);
161571ec3d6Sbellard     sw->hw = hw;
162571ec3d6Sbellard     sw->active = 0;
163571ec3d6Sbellard #ifdef DAC
164571ec3d6Sbellard     sw->total_hw_samples_mixed = 0;
165571ec3d6Sbellard     sw->empty = 1;
166571ec3d6Sbellard #endif
167571ec3d6Sbellard 
168ed2a4a79SKővágó, Zoltán     if (sw->info.is_float) {
169ed2a4a79SKővágó, Zoltán #ifdef DAC
170ed2a4a79SKővágó, Zoltán         sw->conv = mixeng_conv_float[sw->info.nchannels == 2];
171ed2a4a79SKővágó, Zoltán #else
172ed2a4a79SKővágó, Zoltán         sw->clip = mixeng_clip_float[sw->info.nchannels == 2];
173ed2a4a79SKővágó, Zoltán #endif
174ed2a4a79SKővágó, Zoltán     } else {
175571ec3d6Sbellard #ifdef DAC
176571ec3d6Sbellard         sw->conv = mixeng_conv
177571ec3d6Sbellard #else
178571ec3d6Sbellard         sw->clip = mixeng_clip
179571ec3d6Sbellard #endif
180571ec3d6Sbellard             [sw->info.nchannels == 2]
181ed2a4a79SKővágó, Zoltán             [sw->info.is_signed]
182d929eba5Sbellard             [sw->info.swap_endianness]
183f941aa25Sths             [audio_bits_to_index(sw->info.bits)];
184ed2a4a79SKővágó, Zoltán     }
185571ec3d6Sbellard 
1867267c094SAnthony Liguori     sw->name = g_strdup (name);
187571ec3d6Sbellard     err = glue (audio_pcm_sw_alloc_resources_, TYPE) (sw);
188571ec3d6Sbellard     if (err) {
1897267c094SAnthony Liguori         g_free (sw->name);
190571ec3d6Sbellard         sw->name = NULL;
191571ec3d6Sbellard     }
192571ec3d6Sbellard     return err;
193571ec3d6Sbellard }
194571ec3d6Sbellard 
1951d14ffa9Sbellard static void glue (audio_pcm_sw_fini_, TYPE) (SW *sw)
1961d14ffa9Sbellard {
1971d14ffa9Sbellard     glue (audio_pcm_sw_free_resources_, TYPE) (sw);
1987267c094SAnthony Liguori     g_free (sw->name);
1991d14ffa9Sbellard     sw->name = NULL;
2001d14ffa9Sbellard }
2011d14ffa9Sbellard 
2021d14ffa9Sbellard static void glue (audio_pcm_hw_add_sw_, TYPE) (HW *hw, SW *sw)
2031d14ffa9Sbellard {
20472cf2d4fSBlue Swirl     QLIST_INSERT_HEAD (&hw->sw_head, sw, entries);
2051d14ffa9Sbellard }
2061d14ffa9Sbellard 
2071d14ffa9Sbellard static void glue (audio_pcm_hw_del_sw_, TYPE) (SW *sw)
2081d14ffa9Sbellard {
20972cf2d4fSBlue Swirl     QLIST_REMOVE (sw, entries);
2101d14ffa9Sbellard }
2111d14ffa9Sbellard 
2121a7dafceSmalc static void glue (audio_pcm_hw_gc_, TYPE) (HW **hwp)
2131d14ffa9Sbellard {
214c0fe3827Sbellard     HW *hw = *hwp;
215526fb058SKővágó, Zoltán     AudioState *s = hw->s;
216c0fe3827Sbellard 
217c0fe3827Sbellard     if (!hw->sw_head.lh_first) {
2188ead62cfSbellard #ifdef DAC
2198ead62cfSbellard         audio_detach_capture(hw);
2208ead62cfSbellard #endif
22172cf2d4fSBlue Swirl         QLIST_REMOVE(hw, entries);
222b28fb27bSPeter Maydell         glue(hw->pcm_ops->fini_, TYPE) (hw);
223c0fe3827Sbellard         glue(s->nb_hw_voices_, TYPE) += 1;
2241d14ffa9Sbellard         glue(audio_pcm_hw_free_resources_ , TYPE) (hw);
2257267c094SAnthony Liguori         g_free(hw);
226c0fe3827Sbellard         *hwp = NULL;
2271d14ffa9Sbellard     }
2281d14ffa9Sbellard }
2291d14ffa9Sbellard 
230526fb058SKővágó, Zoltán static HW *glue(audio_pcm_hw_find_any_, TYPE)(AudioState *s, HW *hw)
2311d14ffa9Sbellard {
2321a7dafceSmalc     return hw ? hw->entries.le_next : glue (s->hw_head_, TYPE).lh_first;
2331d14ffa9Sbellard }
2341d14ffa9Sbellard 
235526fb058SKővágó, Zoltán static HW *glue(audio_pcm_hw_find_any_enabled_, TYPE)(AudioState *s, HW *hw)
2361d14ffa9Sbellard {
237526fb058SKővágó, Zoltán     while ((hw = glue(audio_pcm_hw_find_any_, TYPE)(s, hw))) {
238c0fe3827Sbellard         if (hw->enabled) {
2391d14ffa9Sbellard             return hw;
2401d14ffa9Sbellard         }
2411d14ffa9Sbellard     }
2421d14ffa9Sbellard     return NULL;
2431d14ffa9Sbellard }
2441d14ffa9Sbellard 
245526fb058SKővágó, Zoltán static HW *glue(audio_pcm_hw_find_specific_, TYPE)(AudioState *s, HW *hw,
246526fb058SKővágó, Zoltán                                                    struct audsettings *as)
2471d14ffa9Sbellard {
248526fb058SKővágó, Zoltán     while ((hw = glue(audio_pcm_hw_find_any_, TYPE)(s, hw))) {
249c0fe3827Sbellard         if (audio_pcm_info_eq (&hw->info, as)) {
2501d14ffa9Sbellard             return hw;
2511d14ffa9Sbellard         }
2521d14ffa9Sbellard     }
2531d14ffa9Sbellard     return NULL;
2541d14ffa9Sbellard }
2551d14ffa9Sbellard 
256526fb058SKővágó, Zoltán static HW *glue(audio_pcm_hw_add_new_, TYPE)(AudioState *s,
257526fb058SKővágó, Zoltán                                              struct audsettings *as)
2581d14ffa9Sbellard {
2591d14ffa9Sbellard     HW *hw;
260571ec3d6Sbellard     struct audio_driver *drv = s->drv;
2611d14ffa9Sbellard 
262571ec3d6Sbellard     if (!glue (s->nb_hw_voices_, TYPE)) {
2631d14ffa9Sbellard         return NULL;
2641d14ffa9Sbellard     }
2651d14ffa9Sbellard 
266470bcabdSAlistair Francis     if (audio_bug(__func__, !drv)) {
267571ec3d6Sbellard         dolog ("No host audio driver\n");
26812f4abf6SVolker Rümelin         return NULL;
2691d14ffa9Sbellard     }
270571ec3d6Sbellard 
271470bcabdSAlistair Francis     if (audio_bug(__func__, !drv->pcm_ops)) {
272571ec3d6Sbellard         dolog ("Host audio driver without pcm_ops\n");
27312f4abf6SVolker Rümelin         return NULL;
274571ec3d6Sbellard     }
275571ec3d6Sbellard 
2763724ab3bSVolker Rümelin     /*
2773724ab3bSVolker Rümelin      * Since glue(s->nb_hw_voices_, TYPE) is != 0, glue(drv->voice_size_, TYPE)
2783724ab3bSVolker Rümelin      * is guaranteed to be != 0. See the audio_init_nb_voices_* functions.
2793724ab3bSVolker Rümelin      */
2803724ab3bSVolker Rümelin     hw = g_malloc0(glue(drv->voice_size_, TYPE));
281526fb058SKővágó, Zoltán     hw->s = s;
282571ec3d6Sbellard     hw->pcm_ops = drv->pcm_ops;
283c01b2456SMarc-André Lureau 
28472cf2d4fSBlue Swirl     QLIST_INIT (&hw->sw_head);
2858ead62cfSbellard #ifdef DAC
28672cf2d4fSBlue Swirl     QLIST_INIT (&hw->cap_head);
2878ead62cfSbellard #endif
2885706db1dSKővágó, Zoltán     if (glue (hw->pcm_ops->init_, TYPE) (hw, as, s->drv_opaque)) {
28912f4abf6SVolker Rümelin         goto err0;
290571ec3d6Sbellard     }
291571ec3d6Sbellard 
292470bcabdSAlistair Francis     if (audio_bug(__func__, hw->samples <= 0)) {
2937520462bSKővágó, Zoltán         dolog("hw->samples=%zd\n", hw->samples);
29412f4abf6SVolker Rümelin         goto err1;
295571ec3d6Sbellard     }
296571ec3d6Sbellard 
297ed2a4a79SKővágó, Zoltán     if (hw->info.is_float) {
298180b044fSVolker Rümelin #ifdef DAC
299ed2a4a79SKővágó, Zoltán         hw->clip = mixeng_clip_float[hw->info.nchannels == 2];
300180b044fSVolker Rümelin #else
301ed2a4a79SKővágó, Zoltán         hw->conv = mixeng_conv_float[hw->info.nchannels == 2];
302180b044fSVolker Rümelin #endif
303ed2a4a79SKővágó, Zoltán     } else {
304571ec3d6Sbellard #ifdef DAC
305571ec3d6Sbellard         hw->clip = mixeng_clip
306571ec3d6Sbellard #else
307571ec3d6Sbellard         hw->conv = mixeng_conv
308571ec3d6Sbellard #endif
309571ec3d6Sbellard             [hw->info.nchannels == 2]
310ed2a4a79SKővágó, Zoltán             [hw->info.is_signed]
311d929eba5Sbellard             [hw->info.swap_endianness]
312f941aa25Sths             [audio_bits_to_index(hw->info.bits)];
313ed2a4a79SKővágó, Zoltán     }
314571ec3d6Sbellard 
315dc88e38fSKővágó, Zoltán     glue(audio_pcm_hw_alloc_resources_, TYPE)(hw);
316571ec3d6Sbellard 
31772cf2d4fSBlue Swirl     QLIST_INSERT_HEAD (&s->glue (hw_head_, TYPE), hw, entries);
318571ec3d6Sbellard     glue (s->nb_hw_voices_, TYPE) -= 1;
3198ead62cfSbellard #ifdef DAC
3201a7dafceSmalc     audio_attach_capture (hw);
3218ead62cfSbellard #endif
3221d14ffa9Sbellard     return hw;
32312f4abf6SVolker Rümelin 
32412f4abf6SVolker Rümelin  err1:
32512f4abf6SVolker Rümelin     glue (hw->pcm_ops->fini_, TYPE) (hw);
32612f4abf6SVolker Rümelin  err0:
32712f4abf6SVolker Rümelin     g_free (hw);
32812f4abf6SVolker Rümelin     return NULL;
3291d14ffa9Sbellard }
3301d14ffa9Sbellard 
33171830221SKővágó, Zoltán AudiodevPerDirectionOptions *glue(audio_get_pdo_, TYPE)(Audiodev *dev)
33271830221SKővágó, Zoltán {
33371830221SKővágó, Zoltán     switch (dev->driver) {
33471830221SKővágó, Zoltán     case AUDIODEV_DRIVER_NONE:
33571830221SKővágó, Zoltán         return dev->u.none.TYPE;
3367a92a857SDaniel P. Berrangé #ifdef CONFIG_AUDIO_ALSA
33771830221SKővágó, Zoltán     case AUDIODEV_DRIVER_ALSA:
33871830221SKővágó, Zoltán         return qapi_AudiodevAlsaPerDirectionOptions_base(dev->u.alsa.TYPE);
3397a92a857SDaniel P. Berrangé #endif
3407a92a857SDaniel P. Berrangé #ifdef CONFIG_AUDIO_COREAUDIO
34171830221SKővágó, Zoltán     case AUDIODEV_DRIVER_COREAUDIO:
34271830221SKővágó, Zoltán         return qapi_AudiodevCoreaudioPerDirectionOptions_base(
34371830221SKővágó, Zoltán             dev->u.coreaudio.TYPE);
3447a92a857SDaniel P. Berrangé #endif
3457a92a857SDaniel P. Berrangé #ifdef CONFIG_DBUS_DISPLAY
346739362d4SMarc-André Lureau     case AUDIODEV_DRIVER_DBUS:
347739362d4SMarc-André Lureau         return dev->u.dbus.TYPE;
3487a92a857SDaniel P. Berrangé #endif
3497a92a857SDaniel P. Berrangé #ifdef CONFIG_AUDIO_DSOUND
35071830221SKővágó, Zoltán     case AUDIODEV_DRIVER_DSOUND:
35171830221SKővágó, Zoltán         return dev->u.dsound.TYPE;
3527a92a857SDaniel P. Berrangé #endif
3537a92a857SDaniel P. Berrangé #ifdef CONFIG_AUDIO_JACK
3542e445703SGeoffrey McRae     case AUDIODEV_DRIVER_JACK:
3552e445703SGeoffrey McRae         return qapi_AudiodevJackPerDirectionOptions_base(dev->u.jack.TYPE);
3567a92a857SDaniel P. Berrangé #endif
3577a92a857SDaniel P. Berrangé #ifdef CONFIG_AUDIO_OSS
35871830221SKővágó, Zoltán     case AUDIODEV_DRIVER_OSS:
35971830221SKővágó, Zoltán         return qapi_AudiodevOssPerDirectionOptions_base(dev->u.oss.TYPE);
3607a92a857SDaniel P. Berrangé #endif
3617a92a857SDaniel P. Berrangé #ifdef CONFIG_AUDIO_PA
36271830221SKővágó, Zoltán     case AUDIODEV_DRIVER_PA:
36371830221SKővágó, Zoltán         return qapi_AudiodevPaPerDirectionOptions_base(dev->u.pa.TYPE);
3647a92a857SDaniel P. Berrangé #endif
3657a92a857SDaniel P. Berrangé #ifdef CONFIG_AUDIO_SDL
36671830221SKővágó, Zoltán     case AUDIODEV_DRIVER_SDL:
3675a0926c2SVolker Rümelin         return qapi_AudiodevSdlPerDirectionOptions_base(dev->u.sdl.TYPE);
3687a92a857SDaniel P. Berrangé #endif
3697a92a857SDaniel P. Berrangé #ifdef CONFIG_AUDIO_SNDIO
370663df1ccSAlexandre Ratchov     case AUDIODEV_DRIVER_SNDIO:
371663df1ccSAlexandre Ratchov         return dev->u.sndio.TYPE;
3727a92a857SDaniel P. Berrangé #endif
3737a92a857SDaniel P. Berrangé #ifdef CONFIG_SPICE
37471830221SKővágó, Zoltán     case AUDIODEV_DRIVER_SPICE:
37571830221SKővágó, Zoltán         return dev->u.spice.TYPE;
3767a92a857SDaniel P. Berrangé #endif
37771830221SKővágó, Zoltán     case AUDIODEV_DRIVER_WAV:
37871830221SKővágó, Zoltán         return dev->u.wav.TYPE;
37971830221SKővágó, Zoltán 
38071830221SKővágó, Zoltán     case AUDIODEV_DRIVER__MAX:
38171830221SKővágó, Zoltán         break;
38271830221SKővágó, Zoltán     }
38371830221SKővágó, Zoltán     abort();
38471830221SKővágó, Zoltán }
38571830221SKővágó, Zoltán 
386526fb058SKővágó, Zoltán static HW *glue(audio_pcm_hw_add_, TYPE)(AudioState *s, struct audsettings *as)
3871d14ffa9Sbellard {
3881d14ffa9Sbellard     HW *hw;
38971830221SKővágó, Zoltán     AudiodevPerDirectionOptions *pdo = glue(audio_get_pdo_, TYPE)(s->dev);
3901d14ffa9Sbellard 
3911930616bSKővágó, Zoltán     if (!pdo->mixing_engine || pdo->fixed_settings) {
392526fb058SKővágó, Zoltán         hw = glue(audio_pcm_hw_add_new_, TYPE)(s, as);
3931930616bSKővágó, Zoltán         if (!pdo->mixing_engine || hw) {
3941d14ffa9Sbellard             return hw;
3951d14ffa9Sbellard         }
3961d14ffa9Sbellard     }
3971d14ffa9Sbellard 
398526fb058SKővágó, Zoltán     hw = glue(audio_pcm_hw_find_specific_, TYPE)(s, NULL, as);
3991d14ffa9Sbellard     if (hw) {
4001d14ffa9Sbellard         return hw;
4011d14ffa9Sbellard     }
4021d14ffa9Sbellard 
403526fb058SKővágó, Zoltán     hw = glue(audio_pcm_hw_add_new_, TYPE)(s, as);
4041d14ffa9Sbellard     if (hw) {
4051d14ffa9Sbellard         return hw;
4061d14ffa9Sbellard     }
4071d14ffa9Sbellard 
408526fb058SKővágó, Zoltán     return glue(audio_pcm_hw_find_any_, TYPE)(s, NULL);
4091d14ffa9Sbellard }
4101d14ffa9Sbellard 
4111d14ffa9Sbellard static SW *glue(audio_pcm_create_voice_pair_, TYPE)(
412526fb058SKővágó, Zoltán     AudioState *s,
413c0fe3827Sbellard     const char *sw_name,
4141ea879e5Smalc     struct audsettings *as
4151d14ffa9Sbellard     )
4161d14ffa9Sbellard {
4171d14ffa9Sbellard     SW *sw;
4181d14ffa9Sbellard     HW *hw;
4191ea879e5Smalc     struct audsettings hw_as;
42071830221SKővágó, Zoltán     AudiodevPerDirectionOptions *pdo = glue(audio_get_pdo_, TYPE)(s->dev);
4211d14ffa9Sbellard 
42271830221SKővágó, Zoltán     if (pdo->fixed_settings) {
42371830221SKővágó, Zoltán         hw_as = audiodev_to_audsettings(pdo);
4246c6886bdSZhang Han     } else {
425c0fe3827Sbellard         hw_as = *as;
4261d14ffa9Sbellard     }
4271d14ffa9Sbellard 
428c6b69a81SVolker Rümelin     sw = g_new0(SW, 1);
429526fb058SKővágó, Zoltán     sw->s = s;
4301d14ffa9Sbellard 
431526fb058SKővágó, Zoltán     hw = glue(audio_pcm_hw_add_, TYPE)(s, &hw_as);
4321d14ffa9Sbellard     if (!hw) {
43390394fe1SVolker Rümelin         dolog("Could not create a backend for voice `%s'\n", sw_name);
434c6b69a81SVolker Rümelin         goto err1;
4351d14ffa9Sbellard     }
4361d14ffa9Sbellard 
4371d14ffa9Sbellard     glue (audio_pcm_hw_add_sw_, TYPE) (hw, sw);
4381d14ffa9Sbellard 
439d929eba5Sbellard     if (glue (audio_pcm_sw_init_, TYPE) (sw, hw, sw_name, as)) {
440c6b69a81SVolker Rümelin         goto err2;
4411d14ffa9Sbellard     }
4421d14ffa9Sbellard 
4431d14ffa9Sbellard     return sw;
4441d14ffa9Sbellard 
445c6b69a81SVolker Rümelin err2:
4461d14ffa9Sbellard     glue (audio_pcm_hw_del_sw_, TYPE) (sw);
4471a7dafceSmalc     glue (audio_pcm_hw_gc_, TYPE) (&hw);
4481d14ffa9Sbellard err1:
449c6b69a81SVolker Rümelin     g_free(sw);
4501d14ffa9Sbellard     return NULL;
4511d14ffa9Sbellard }
4521d14ffa9Sbellard 
4531a7dafceSmalc static void glue (audio_close_, TYPE) (SW *sw)
4541d14ffa9Sbellard {
4551d14ffa9Sbellard     glue (audio_pcm_sw_fini_, TYPE) (sw);
4561d14ffa9Sbellard     glue (audio_pcm_hw_del_sw_, TYPE) (sw);
4571a7dafceSmalc     glue (audio_pcm_hw_gc_, TYPE) (&sw->hw);
4587267c094SAnthony Liguori     g_free (sw);
4591d14ffa9Sbellard }
460571ec3d6Sbellard 
461c0fe3827Sbellard void glue (AUD_close_, TYPE) (QEMUSoundCard *card, SW *sw)
462c0fe3827Sbellard {
463c0fe3827Sbellard     if (sw) {
464470bcabdSAlistair Francis         if (audio_bug(__func__, !card)) {
4651a7dafceSmalc             dolog ("card=%p\n", card);
46612f4abf6SVolker Rümelin             return;
467c0fe3827Sbellard         }
468c0fe3827Sbellard 
4691a7dafceSmalc         glue (audio_close_, TYPE) (sw);
470c0fe3827Sbellard     }
4711d14ffa9Sbellard }
4721d14ffa9Sbellard 
4731d14ffa9Sbellard SW *glue (AUD_open_, TYPE) (
474c0fe3827Sbellard     QEMUSoundCard *card,
4751d14ffa9Sbellard     SW *sw,
4761d14ffa9Sbellard     const char *name,
4771d14ffa9Sbellard     void *callback_opaque ,
478cb4f03e8Smalc     audio_callback_fn callback_fn,
4791ea879e5Smalc     struct audsettings *as
4801d14ffa9Sbellard     )
4811d14ffa9Sbellard {
482d1670b20SKővágó, Zoltán     AudioState *s;
483d1670b20SKővágó, Zoltán     AudiodevPerDirectionOptions *pdo;
4841d14ffa9Sbellard 
485470bcabdSAlistair Francis     if (audio_bug(__func__, !card || !name || !callback_fn || !as)) {
4861a7dafceSmalc         dolog ("card=%p name=%p callback_fn=%p as=%p\n",
4871a7dafceSmalc                card, name, callback_fn, as);
48812f4abf6SVolker Rümelin         goto fail;
4891d14ffa9Sbellard     }
4901d14ffa9Sbellard 
491d1670b20SKővágó, Zoltán     s = card->state;
492d1670b20SKővágó, Zoltán     pdo = glue(audio_get_pdo_, TYPE)(s->dev);
493d1670b20SKővágó, Zoltán 
49493b65997SStefan Weil     ldebug ("open %s, freq %d, nchannels %d, fmt %d\n",
49593b65997SStefan Weil             name, as->freq, as->nchannels, as->fmt);
49693b65997SStefan Weil 
497470bcabdSAlistair Francis     if (audio_bug(__func__, audio_validate_settings(as))) {
498c0fe3827Sbellard         audio_print_settings (as);
49912f4abf6SVolker Rümelin         goto fail;
5001d14ffa9Sbellard     }
5011d14ffa9Sbellard 
502470bcabdSAlistair Francis     if (audio_bug(__func__, !s->drv)) {
503c0fe3827Sbellard         dolog ("Can not open `%s' (no host audio driver)\n", name);
50412f4abf6SVolker Rümelin         goto fail;
5051d14ffa9Sbellard     }
5061d14ffa9Sbellard 
507c0fe3827Sbellard     if (sw && audio_pcm_info_eq (&sw->info, as)) {
5081d14ffa9Sbellard         return sw;
5091d14ffa9Sbellard     }
5101d14ffa9Sbellard 
51171830221SKővágó, Zoltán     if (!pdo->fixed_settings && sw) {
512c0fe3827Sbellard         glue (AUD_close_, TYPE) (card, sw);
5131d14ffa9Sbellard         sw = NULL;
5141d14ffa9Sbellard     }
5151d14ffa9Sbellard 
5161d14ffa9Sbellard     if (sw) {
5171d14ffa9Sbellard         HW *hw = sw->hw;
5181d14ffa9Sbellard 
5191d14ffa9Sbellard         if (!hw) {
520b637a61cSVolker Rümelin             dolog("Internal logic error: voice `%s' has no backend\n",
521c0fe3827Sbellard                   SW_NAME(sw));
5221d14ffa9Sbellard             goto fail;
5231d14ffa9Sbellard         }
5241d14ffa9Sbellard 
525571ec3d6Sbellard         glue (audio_pcm_sw_fini_, TYPE) (sw);
526d929eba5Sbellard         if (glue (audio_pcm_sw_init_, TYPE) (sw, hw, name, as)) {
5271d14ffa9Sbellard             goto fail;
5281d14ffa9Sbellard         }
5296c6886bdSZhang Han     } else {
530526fb058SKővágó, Zoltán         sw = glue(audio_pcm_create_voice_pair_, TYPE)(s, name, as);
5311d14ffa9Sbellard         if (!sw) {
532571ec3d6Sbellard             return NULL;
5331d14ffa9Sbellard         }
5341d14ffa9Sbellard     }
5351d14ffa9Sbellard 
5361a7dafceSmalc     sw->card = card;
5371d14ffa9Sbellard     sw->vol = nominal_volume;
5381d14ffa9Sbellard     sw->callback.fn = callback_fn;
5391d14ffa9Sbellard     sw->callback.opaque = callback_opaque;
5401d14ffa9Sbellard 
5411d14ffa9Sbellard #ifdef DEBUG_AUDIO
5421d14ffa9Sbellard     dolog ("%s\n", name);
5431d14ffa9Sbellard     audio_pcm_print_info ("hw", &sw->hw->info);
5441d14ffa9Sbellard     audio_pcm_print_info ("sw", &sw->info);
5451d14ffa9Sbellard #endif
5461d14ffa9Sbellard 
5471d14ffa9Sbellard     return sw;
5481d14ffa9Sbellard 
5491d14ffa9Sbellard  fail:
550c0fe3827Sbellard     glue (AUD_close_, TYPE) (card, sw);
5511d14ffa9Sbellard     return NULL;
5521d14ffa9Sbellard }
5531d14ffa9Sbellard 
5541d14ffa9Sbellard int glue (AUD_is_active_, TYPE) (SW *sw)
5551d14ffa9Sbellard {
5561d14ffa9Sbellard     return sw ? sw->active : 0;
5571d14ffa9Sbellard }
5581d14ffa9Sbellard 
5591d14ffa9Sbellard void glue (AUD_init_time_stamp_, TYPE) (SW *sw, QEMUAudioTimeStamp *ts)
5601d14ffa9Sbellard {
5611d14ffa9Sbellard     if (!sw) {
5621d14ffa9Sbellard         return;
5631d14ffa9Sbellard     }
5641d14ffa9Sbellard 
5651d14ffa9Sbellard     ts->old_ts = sw->hw->ts_helper;
5661d14ffa9Sbellard }
5671d14ffa9Sbellard 
568c0fe3827Sbellard uint64_t glue (AUD_get_elapsed_usec_, TYPE) (SW *sw, QEMUAudioTimeStamp *ts)
5691d14ffa9Sbellard {
5701d14ffa9Sbellard     uint64_t delta, cur_ts, old_ts;
5711d14ffa9Sbellard 
5721d14ffa9Sbellard     if (!sw) {
5731d14ffa9Sbellard         return 0;
5741d14ffa9Sbellard     }
5751d14ffa9Sbellard 
5761d14ffa9Sbellard     cur_ts = sw->hw->ts_helper;
5771d14ffa9Sbellard     old_ts = ts->old_ts;
5780bfcd599SBlue Swirl     /* dolog ("cur %" PRId64 " old %" PRId64 "\n", cur_ts, old_ts); */
5791d14ffa9Sbellard 
5801d14ffa9Sbellard     if (cur_ts >= old_ts) {
5811d14ffa9Sbellard         delta = cur_ts - old_ts;
5826c6886bdSZhang Han     } else {
5831d14ffa9Sbellard         delta = UINT64_MAX - old_ts + cur_ts;
5841d14ffa9Sbellard     }
5851d14ffa9Sbellard 
5861d14ffa9Sbellard     if (!delta) {
5871d14ffa9Sbellard         return 0;
5881d14ffa9Sbellard     }
5891d14ffa9Sbellard 
5904f4cc0efSmalc     return muldiv64 (delta, sw->hw->info.freq, 1000000);
5911d14ffa9Sbellard }
5921d14ffa9Sbellard 
5931d14ffa9Sbellard #undef TYPE
5941d14ffa9Sbellard #undef HW
5951d14ffa9Sbellard #undef SW
596571ec3d6Sbellard #undef HWBUF
597571ec3d6Sbellard #undef NAME
598