xref: /qemu/audio/audio_template.h (revision e7cad33853d24c0df1f4fb566485a866a8f0e40a)
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
261d14ffa9Sbellard #define TYPE out
271d14ffa9Sbellard #define HW glue (HWVoice, Out)
281d14ffa9Sbellard #define SW glue (SWVoice, Out)
291d14ffa9Sbellard #else
301d14ffa9Sbellard #define TYPE in
311d14ffa9Sbellard #define HW glue (HWVoice, In)
321d14ffa9Sbellard #define SW glue (SWVoice, In)
331d14ffa9Sbellard #endif
341d14ffa9Sbellard 
35c0fe3827Sbellard static int glue (audio_pcm_hw_init_, TYPE) (
36c0fe3827Sbellard     HW *hw,
37c0fe3827Sbellard     audsettings_t *as
38c0fe3827Sbellard     )
39c0fe3827Sbellard {
40c0fe3827Sbellard     glue (audio_pcm_hw_free_resources_, TYPE) (hw);
41c0fe3827Sbellard 
42c0fe3827Sbellard     if (glue (hw->pcm_ops->init_, TYPE) (hw, as)) {
43c0fe3827Sbellard         return -1;
44c0fe3827Sbellard     }
45c0fe3827Sbellard 
46c0fe3827Sbellard     if (audio_bug (AUDIO_FUNC, hw->samples <= 0)) {
47c0fe3827Sbellard         dolog ("hw->samples=%d\n", hw->samples);
48c0fe3827Sbellard         return -1;
49c0fe3827Sbellard     }
50c0fe3827Sbellard 
51c0fe3827Sbellard     LIST_INIT (&hw->sw_head);
52c0fe3827Sbellard #ifdef DAC
53c0fe3827Sbellard     hw->clip =
54c0fe3827Sbellard         mixeng_clip
55c0fe3827Sbellard #else
56c0fe3827Sbellard     hw->conv =
57c0fe3827Sbellard         mixeng_conv
58c0fe3827Sbellard #endif
59c0fe3827Sbellard         [hw->info.nchannels == 2]
60c0fe3827Sbellard         [hw->info.sign]
61c0fe3827Sbellard         [hw->info.swap_endian]
62c0fe3827Sbellard         [hw->info.bits == 16];
63c0fe3827Sbellard 
64c0fe3827Sbellard     if (glue (audio_pcm_hw_alloc_resources_, TYPE) (hw)) {
65c0fe3827Sbellard         glue (hw->pcm_ops->fini_, TYPE) (hw);
66c0fe3827Sbellard         return -1;
67c0fe3827Sbellard     }
68c0fe3827Sbellard 
69c0fe3827Sbellard     return 0;
70c0fe3827Sbellard }
71c0fe3827Sbellard 
721d14ffa9Sbellard static void glue (audio_pcm_sw_fini_, TYPE) (SW *sw)
731d14ffa9Sbellard {
741d14ffa9Sbellard     glue (audio_pcm_sw_free_resources_, TYPE) (sw);
751d14ffa9Sbellard     if (sw->name) {
761d14ffa9Sbellard         qemu_free (sw->name);
771d14ffa9Sbellard         sw->name = NULL;
781d14ffa9Sbellard     }
791d14ffa9Sbellard }
801d14ffa9Sbellard 
811d14ffa9Sbellard static void glue (audio_pcm_hw_add_sw_, TYPE) (HW *hw, SW *sw)
821d14ffa9Sbellard {
831d14ffa9Sbellard     LIST_INSERT_HEAD (&hw->sw_head, sw, entries);
841d14ffa9Sbellard }
851d14ffa9Sbellard 
861d14ffa9Sbellard static void glue (audio_pcm_hw_del_sw_, TYPE) (SW *sw)
871d14ffa9Sbellard {
881d14ffa9Sbellard     LIST_REMOVE (sw, entries);
891d14ffa9Sbellard }
901d14ffa9Sbellard 
91c0fe3827Sbellard static void glue (audio_pcm_hw_gc_, TYPE) (AudioState *s, HW **hwp)
921d14ffa9Sbellard {
93c0fe3827Sbellard     HW *hw = *hwp;
94c0fe3827Sbellard 
95c0fe3827Sbellard     if (!hw->sw_head.lh_first) {
96c0fe3827Sbellard         LIST_REMOVE (hw, entries);
97c0fe3827Sbellard         glue (s->nb_hw_voices_, TYPE) += 1;
981d14ffa9Sbellard         glue (audio_pcm_hw_free_resources_ ,TYPE) (hw);
991d14ffa9Sbellard         glue (hw->pcm_ops->fini_, TYPE) (hw);
100c0fe3827Sbellard         qemu_free (hw);
101c0fe3827Sbellard         *hwp = NULL;
1021d14ffa9Sbellard     }
1031d14ffa9Sbellard }
1041d14ffa9Sbellard 
105c0fe3827Sbellard static HW *glue (audio_pcm_hw_find_any_, TYPE) (AudioState *s, HW *hw)
1061d14ffa9Sbellard {
107c0fe3827Sbellard     return hw ? hw->entries.le_next : s->glue (hw_head_, TYPE).lh_first;
1081d14ffa9Sbellard }
1091d14ffa9Sbellard 
110c0fe3827Sbellard static HW *glue (audio_pcm_hw_find_any_enabled_, TYPE) (AudioState *s, HW *hw)
1111d14ffa9Sbellard {
112c0fe3827Sbellard     while ((hw = glue (audio_pcm_hw_find_any_, TYPE) (s, hw))) {
113c0fe3827Sbellard         if (hw->enabled) {
1141d14ffa9Sbellard             return hw;
1151d14ffa9Sbellard         }
1161d14ffa9Sbellard     }
1171d14ffa9Sbellard     return NULL;
1181d14ffa9Sbellard }
1191d14ffa9Sbellard 
120c0fe3827Sbellard static HW *glue (audio_pcm_hw_find_any_passive_, TYPE) (AudioState *s)
1211d14ffa9Sbellard {
122c0fe3827Sbellard     if (glue (s->nb_hw_voices_, TYPE)) {
123c0fe3827Sbellard         struct audio_driver *drv = s->drv;
124c0fe3827Sbellard 
125c0fe3827Sbellard         if (audio_bug (AUDIO_FUNC, !drv)) {
126c0fe3827Sbellard             dolog ("No host audio driver\n");
1271d14ffa9Sbellard             return NULL;
1281d14ffa9Sbellard         }
1291d14ffa9Sbellard 
130c0fe3827Sbellard         HW *hw = audio_calloc (AUDIO_FUNC, 1, glue (drv->voice_size_, TYPE));
131c0fe3827Sbellard         if (!hw) {
132c0fe3827Sbellard             dolog ("Can not allocate voice `%s' size %d\n",
133c0fe3827Sbellard                    drv->name, glue (drv->voice_size_, TYPE));
134*e7cad338Sbellard             return NULL;
135c0fe3827Sbellard         }
136c0fe3827Sbellard 
137c0fe3827Sbellard         LIST_INSERT_HEAD (&s->glue (hw_head_, TYPE), hw, entries);
138c0fe3827Sbellard         glue (s->nb_hw_voices_, TYPE) -= 1;
1391d14ffa9Sbellard         return hw;
1401d14ffa9Sbellard     }
141c0fe3827Sbellard 
1421d14ffa9Sbellard     return NULL;
1431d14ffa9Sbellard }
1441d14ffa9Sbellard 
1451d14ffa9Sbellard static HW *glue (audio_pcm_hw_find_specific_, TYPE) (
146c0fe3827Sbellard     AudioState *s,
1471d14ffa9Sbellard     HW *hw,
148c0fe3827Sbellard     audsettings_t *as
1491d14ffa9Sbellard     )
1501d14ffa9Sbellard {
151c0fe3827Sbellard     while ((hw = glue (audio_pcm_hw_find_any_, TYPE) (s, hw))) {
152c0fe3827Sbellard         if (audio_pcm_info_eq (&hw->info, as)) {
1531d14ffa9Sbellard             return hw;
1541d14ffa9Sbellard         }
1551d14ffa9Sbellard     }
1561d14ffa9Sbellard     return NULL;
1571d14ffa9Sbellard }
1581d14ffa9Sbellard 
159c0fe3827Sbellard static HW *glue (audio_pcm_hw_add_new_, TYPE) (AudioState *s, audsettings_t *as)
1601d14ffa9Sbellard {
1611d14ffa9Sbellard     HW *hw;
1621d14ffa9Sbellard 
163c0fe3827Sbellard     hw = glue (audio_pcm_hw_find_any_passive_, TYPE) (s);
1641d14ffa9Sbellard     if (hw) {
165c0fe3827Sbellard         hw->pcm_ops = s->drv->pcm_ops;
1661d14ffa9Sbellard         if (!hw->pcm_ops) {
1671d14ffa9Sbellard             return NULL;
1681d14ffa9Sbellard         }
1691d14ffa9Sbellard 
170c0fe3827Sbellard         if (glue (audio_pcm_hw_init_, TYPE) (hw, as)) {
171c0fe3827Sbellard             glue (audio_pcm_hw_gc_, TYPE) (s, &hw);
1721d14ffa9Sbellard             return NULL;
1731d14ffa9Sbellard         }
1741d14ffa9Sbellard         else {
1751d14ffa9Sbellard             return hw;
1761d14ffa9Sbellard         }
1771d14ffa9Sbellard     }
1781d14ffa9Sbellard 
1791d14ffa9Sbellard     return NULL;
1801d14ffa9Sbellard }
1811d14ffa9Sbellard 
182c0fe3827Sbellard static HW *glue (audio_pcm_hw_add_, TYPE) (AudioState *s, audsettings_t *as)
1831d14ffa9Sbellard {
1841d14ffa9Sbellard     HW *hw;
1851d14ffa9Sbellard 
186c0fe3827Sbellard     if (glue (conf.fixed_, TYPE).enabled && glue (conf.fixed_, TYPE).greedy) {
187c0fe3827Sbellard         hw = glue (audio_pcm_hw_add_new_, TYPE) (s, as);
1881d14ffa9Sbellard         if (hw) {
1891d14ffa9Sbellard             return hw;
1901d14ffa9Sbellard         }
1911d14ffa9Sbellard     }
1921d14ffa9Sbellard 
193c0fe3827Sbellard     hw = glue (audio_pcm_hw_find_specific_, TYPE) (s, NULL, as);
1941d14ffa9Sbellard     if (hw) {
1951d14ffa9Sbellard         return hw;
1961d14ffa9Sbellard     }
1971d14ffa9Sbellard 
198c0fe3827Sbellard     hw = glue (audio_pcm_hw_add_new_, TYPE) (s, as);
1991d14ffa9Sbellard     if (hw) {
2001d14ffa9Sbellard         return hw;
2011d14ffa9Sbellard     }
2021d14ffa9Sbellard 
203c0fe3827Sbellard     return glue (audio_pcm_hw_find_any_, TYPE) (s, NULL);
2041d14ffa9Sbellard }
2051d14ffa9Sbellard 
2061d14ffa9Sbellard static SW *glue (audio_pcm_create_voice_pair_, TYPE) (
207c0fe3827Sbellard     AudioState *s,
208c0fe3827Sbellard     const char *sw_name,
209c0fe3827Sbellard     audsettings_t *as
2101d14ffa9Sbellard     )
2111d14ffa9Sbellard {
2121d14ffa9Sbellard     SW *sw;
2131d14ffa9Sbellard     HW *hw;
214c0fe3827Sbellard     audsettings_t hw_as;
2151d14ffa9Sbellard 
216c0fe3827Sbellard     if (glue (conf.fixed_, TYPE).enabled) {
217c0fe3827Sbellard         hw_as = glue (conf.fixed_, TYPE).settings;
218c0fe3827Sbellard     }
219c0fe3827Sbellard     else {
220c0fe3827Sbellard         hw_as = *as;
2211d14ffa9Sbellard     }
2221d14ffa9Sbellard 
223c0fe3827Sbellard     sw = audio_calloc (AUDIO_FUNC, 1, sizeof (*sw));
2241d14ffa9Sbellard     if (!sw) {
225*e7cad338Sbellard         dolog ("Could not allocate soft voice `%s' (%zu bytes)\n",
226c0fe3827Sbellard                sw_name ? sw_name : "unknown", sizeof (*sw));
2271d14ffa9Sbellard         goto err1;
2281d14ffa9Sbellard     }
2291d14ffa9Sbellard 
230c0fe3827Sbellard     hw = glue (audio_pcm_hw_add_, TYPE) (s, &hw_as);
2311d14ffa9Sbellard     if (!hw) {
2321d14ffa9Sbellard         goto err2;
2331d14ffa9Sbellard     }
2341d14ffa9Sbellard 
2351d14ffa9Sbellard     glue (audio_pcm_hw_add_sw_, TYPE) (hw, sw);
2361d14ffa9Sbellard 
237c0fe3827Sbellard     if (glue (audio_pcm_sw_init_, TYPE) (sw, hw, sw_name, as)) {
2381d14ffa9Sbellard         goto err3;
2391d14ffa9Sbellard     }
2401d14ffa9Sbellard 
2411d14ffa9Sbellard     return sw;
2421d14ffa9Sbellard 
2431d14ffa9Sbellard err3:
2441d14ffa9Sbellard     glue (audio_pcm_hw_del_sw_, TYPE) (sw);
245c0fe3827Sbellard     glue (audio_pcm_hw_gc_, TYPE) (s, &hw);
2461d14ffa9Sbellard err2:
2471d14ffa9Sbellard     qemu_free (sw);
2481d14ffa9Sbellard err1:
2491d14ffa9Sbellard     return NULL;
2501d14ffa9Sbellard }
2511d14ffa9Sbellard 
252c0fe3827Sbellard static void glue (audio_close_, TYPE) (AudioState *s, SW *sw)
2531d14ffa9Sbellard {
2541d14ffa9Sbellard     glue (audio_pcm_sw_fini_, TYPE) (sw);
2551d14ffa9Sbellard     glue (audio_pcm_hw_del_sw_, TYPE) (sw);
256c0fe3827Sbellard     glue (audio_pcm_hw_gc_, TYPE) (s, &sw->hw);
2571d14ffa9Sbellard     qemu_free (sw);
2581d14ffa9Sbellard }
259c0fe3827Sbellard void glue (AUD_close_, TYPE) (QEMUSoundCard *card, SW *sw)
260c0fe3827Sbellard {
261c0fe3827Sbellard     if (sw) {
262c0fe3827Sbellard         if (audio_bug (AUDIO_FUNC, !card || !card->audio)) {
263c0fe3827Sbellard             dolog ("card=%p card->audio=%p\n",
264c0fe3827Sbellard                    card, card ? card->audio : NULL);
265c0fe3827Sbellard             return;
266c0fe3827Sbellard         }
267c0fe3827Sbellard 
268c0fe3827Sbellard         glue (audio_close_, TYPE) (card->audio, sw);
269c0fe3827Sbellard     }
2701d14ffa9Sbellard }
2711d14ffa9Sbellard 
2721d14ffa9Sbellard SW *glue (AUD_open_, TYPE) (
273c0fe3827Sbellard     QEMUSoundCard *card,
2741d14ffa9Sbellard     SW *sw,
2751d14ffa9Sbellard     const char *name,
2761d14ffa9Sbellard     void *callback_opaque ,
2771d14ffa9Sbellard     audio_callback_fn_t callback_fn,
278c0fe3827Sbellard     audsettings_t *as
2791d14ffa9Sbellard     )
2801d14ffa9Sbellard {
281c0fe3827Sbellard     AudioState *s;
2821d14ffa9Sbellard #ifdef DAC
2831d14ffa9Sbellard     int live = 0;
2841d14ffa9Sbellard     SW *old_sw = NULL;
2851d14ffa9Sbellard #endif
2861d14ffa9Sbellard 
287c0fe3827Sbellard     ldebug ("open %s, freq %d, nchannels %d, fmt %d\n",
288c0fe3827Sbellard             name, as->freq, as->nchannels, as->fmt);
289c0fe3827Sbellard 
290c0fe3827Sbellard     if (audio_bug (AUDIO_FUNC,
291c0fe3827Sbellard                    !card || !card->audio || !name || !callback_fn || !as)) {
292c0fe3827Sbellard         dolog ("card=%p card->audio=%p name=%p callback_fn=%p as=%p\n",
293c0fe3827Sbellard                card, card ? card->audio : NULL, name, callback_fn, as);
2941d14ffa9Sbellard         goto fail;
2951d14ffa9Sbellard     }
2961d14ffa9Sbellard 
297c0fe3827Sbellard     s = card->audio;
298c0fe3827Sbellard 
299c0fe3827Sbellard     if (audio_bug (AUDIO_FUNC, audio_validate_settigs (as))) {
300c0fe3827Sbellard         audio_print_settings (as);
3011d14ffa9Sbellard         goto fail;
3021d14ffa9Sbellard     }
3031d14ffa9Sbellard 
304c0fe3827Sbellard     if (audio_bug (AUDIO_FUNC, !s->drv)) {
305c0fe3827Sbellard         dolog ("Can not open `%s' (no host audio driver)\n", name);
3061d14ffa9Sbellard         goto fail;
3071d14ffa9Sbellard     }
3081d14ffa9Sbellard 
309c0fe3827Sbellard     if (sw && audio_pcm_info_eq (&sw->info, as)) {
3101d14ffa9Sbellard         return sw;
3111d14ffa9Sbellard     }
3121d14ffa9Sbellard 
3131d14ffa9Sbellard #ifdef DAC
314c0fe3827Sbellard     if (conf.plive && sw && (!sw->active && !sw->empty)) {
3151d14ffa9Sbellard         live = sw->total_hw_samples_mixed;
3161d14ffa9Sbellard 
3171d14ffa9Sbellard #ifdef DEBUG_PLIVE
318c0fe3827Sbellard         dolog ("Replacing voice %s with %d live samples\n", SW_NAME (sw), live);
3191d14ffa9Sbellard         dolog ("Old %s freq %d, bits %d, channels %d\n",
320c0fe3827Sbellard                SW_NAME (sw), sw->info.freq, sw->info.bits, sw->info.nchannels);
3211d14ffa9Sbellard         dolog ("New %s freq %d, bits %d, channels %d\n",
322c0fe3827Sbellard                name,
323c0fe3827Sbellard                freq,
324c0fe3827Sbellard                (fmt == AUD_FMT_S16 || fmt == AUD_FMT_U16) ? 16 : 8,
3251d14ffa9Sbellard                nchannels);
3261d14ffa9Sbellard #endif
3271d14ffa9Sbellard 
3281d14ffa9Sbellard         if (live) {
3291d14ffa9Sbellard             old_sw = sw;
3301d14ffa9Sbellard             old_sw->callback.fn = NULL;
3311d14ffa9Sbellard             sw = NULL;
3321d14ffa9Sbellard         }
3331d14ffa9Sbellard     }
3341d14ffa9Sbellard #endif
3351d14ffa9Sbellard 
336c0fe3827Sbellard     if (!glue (conf.fixed_, TYPE).enabled && sw) {
337c0fe3827Sbellard         glue (AUD_close_, TYPE) (card, sw);
3381d14ffa9Sbellard         sw = NULL;
3391d14ffa9Sbellard     }
3401d14ffa9Sbellard 
3411d14ffa9Sbellard     if (sw) {
3421d14ffa9Sbellard         HW *hw = sw->hw;
3431d14ffa9Sbellard 
3441d14ffa9Sbellard         if (!hw) {
345c0fe3827Sbellard             dolog ("Internal logic error voice `%s' has no hardware store\n",
346c0fe3827Sbellard                    SW_NAME (sw));
3471d14ffa9Sbellard             goto fail;
3481d14ffa9Sbellard         }
3491d14ffa9Sbellard 
350c0fe3827Sbellard         if (glue (audio_pcm_sw_init_, TYPE) (sw, hw, name, as)) {
3511d14ffa9Sbellard             goto fail;
3521d14ffa9Sbellard         }
3531d14ffa9Sbellard     }
3541d14ffa9Sbellard     else {
355c0fe3827Sbellard         sw = glue (audio_pcm_create_voice_pair_, TYPE) (s, name, as);
3561d14ffa9Sbellard         if (!sw) {
357c0fe3827Sbellard             dolog ("Failed to create voice `%s'\n", name);
3581d14ffa9Sbellard             goto fail;
3591d14ffa9Sbellard         }
3601d14ffa9Sbellard     }
3611d14ffa9Sbellard 
3621d14ffa9Sbellard     if (sw) {
3631d14ffa9Sbellard         sw->vol = nominal_volume;
3641d14ffa9Sbellard         sw->callback.fn = callback_fn;
3651d14ffa9Sbellard         sw->callback.opaque = callback_opaque;
3661d14ffa9Sbellard 
3671d14ffa9Sbellard #ifdef DAC
3681d14ffa9Sbellard         if (live) {
3691d14ffa9Sbellard             int mixed =
3701d14ffa9Sbellard                 (live << old_sw->info.shift)
3711d14ffa9Sbellard                 * old_sw->info.bytes_per_second
3721d14ffa9Sbellard                 / sw->info.bytes_per_second;
3731d14ffa9Sbellard 
3741d14ffa9Sbellard #ifdef DEBUG_PLIVE
3751d14ffa9Sbellard             dolog ("Silence will be mixed %d\n", mixed);
3761d14ffa9Sbellard #endif
3771d14ffa9Sbellard             sw->total_hw_samples_mixed += mixed;
3781d14ffa9Sbellard         }
3791d14ffa9Sbellard #endif
3801d14ffa9Sbellard 
3811d14ffa9Sbellard #ifdef DEBUG_AUDIO
3821d14ffa9Sbellard         dolog ("%s\n", name);
3831d14ffa9Sbellard         audio_pcm_print_info ("hw", &sw->hw->info);
3841d14ffa9Sbellard         audio_pcm_print_info ("sw", &sw->info);
3851d14ffa9Sbellard #endif
3861d14ffa9Sbellard     }
3871d14ffa9Sbellard 
3881d14ffa9Sbellard     return sw;
3891d14ffa9Sbellard 
3901d14ffa9Sbellard  fail:
391c0fe3827Sbellard     glue (AUD_close_, TYPE) (card, sw);
3921d14ffa9Sbellard     return NULL;
3931d14ffa9Sbellard }
3941d14ffa9Sbellard 
3951d14ffa9Sbellard int glue (AUD_is_active_, TYPE) (SW *sw)
3961d14ffa9Sbellard {
3971d14ffa9Sbellard     return sw ? sw->active : 0;
3981d14ffa9Sbellard }
3991d14ffa9Sbellard 
4001d14ffa9Sbellard void glue (AUD_init_time_stamp_, TYPE) (SW *sw, QEMUAudioTimeStamp *ts)
4011d14ffa9Sbellard {
4021d14ffa9Sbellard     if (!sw) {
4031d14ffa9Sbellard         return;
4041d14ffa9Sbellard     }
4051d14ffa9Sbellard 
4061d14ffa9Sbellard     ts->old_ts = sw->hw->ts_helper;
4071d14ffa9Sbellard }
4081d14ffa9Sbellard 
409c0fe3827Sbellard uint64_t glue (AUD_get_elapsed_usec_, TYPE) (SW *sw, QEMUAudioTimeStamp *ts)
4101d14ffa9Sbellard {
4111d14ffa9Sbellard     uint64_t delta, cur_ts, old_ts;
4121d14ffa9Sbellard 
4131d14ffa9Sbellard     if (!sw) {
4141d14ffa9Sbellard         return 0;
4151d14ffa9Sbellard     }
4161d14ffa9Sbellard 
4171d14ffa9Sbellard     cur_ts = sw->hw->ts_helper;
4181d14ffa9Sbellard     old_ts = ts->old_ts;
4191d14ffa9Sbellard     /* dolog ("cur %lld old %lld\n", cur_ts, old_ts); */
4201d14ffa9Sbellard 
4211d14ffa9Sbellard     if (cur_ts >= old_ts) {
4221d14ffa9Sbellard         delta = cur_ts - old_ts;
4231d14ffa9Sbellard     }
4241d14ffa9Sbellard     else {
4251d14ffa9Sbellard         delta = UINT64_MAX - old_ts + cur_ts;
4261d14ffa9Sbellard     }
4271d14ffa9Sbellard 
4281d14ffa9Sbellard     if (!delta) {
4291d14ffa9Sbellard         return 0;
4301d14ffa9Sbellard     }
4311d14ffa9Sbellard 
4321d14ffa9Sbellard     return (delta * sw->hw->info.freq) / 1000000;
4331d14ffa9Sbellard }
4341d14ffa9Sbellard 
4351d14ffa9Sbellard #undef TYPE
4361d14ffa9Sbellard #undef HW
4371d14ffa9Sbellard #undef SW
438