xref: /openbmc/qemu/audio/audio.c (revision 5713d6dd76d2bb10f8e2367512217ef49fe797ed)
185571bc7Sbellard /*
285571bc7Sbellard  * QEMU Audio subsystem
385571bc7Sbellard  *
41d14ffa9Sbellard  * Copyright (c) 2003-2005 Vassili Karpov (malc)
585571bc7Sbellard  *
685571bc7Sbellard  * Permission is hereby granted, free of charge, to any person obtaining a copy
785571bc7Sbellard  * of this software and associated documentation files (the "Software"), to deal
885571bc7Sbellard  * in the Software without restriction, including without limitation the rights
985571bc7Sbellard  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
1085571bc7Sbellard  * copies of the Software, and to permit persons to whom the Software is
1185571bc7Sbellard  * furnished to do so, subject to the following conditions:
1285571bc7Sbellard  *
1385571bc7Sbellard  * The above copyright notice and this permission notice shall be included in
1485571bc7Sbellard  * all copies or substantial portions of the Software.
1585571bc7Sbellard  *
1685571bc7Sbellard  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1785571bc7Sbellard  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1885571bc7Sbellard  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
1985571bc7Sbellard  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2085571bc7Sbellard  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2185571bc7Sbellard  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
2285571bc7Sbellard  * THE SOFTWARE.
2385571bc7Sbellard  */
240b8fa32fSMarkus Armbruster 
256086a565SPeter Maydell #include "qemu/osdep.h"
2687ecb68bSpbrook #include "audio.h"
27d6454270SMarkus Armbruster #include "migration/vmstate.h"
2883c9089eSPaolo Bonzini #include "monitor/monitor.h"
291de7afc9SPaolo Bonzini #include "qemu/timer.h"
3071830221SKővágó, Zoltán #include "qapi/error.h"
31637d1809SDaniel P. Berrangé #include "qapi/clone-visitor.h"
3271830221SKővágó, Zoltán #include "qapi/qobject-input-visitor.h"
3371830221SKővágó, Zoltán #include "qapi/qapi-visit-audio.h"
34637d1809SDaniel P. Berrangé #include "qapi/qapi-commands-audio.h"
3569a80279SPaolo Bonzini #include "qapi/qmp/qdict.h"
36f348b6d1SVeronia Bahaa #include "qemu/cutils.h"
37f6061733SPaolo Bonzini #include "qemu/error-report.h"
38b9ae74e2SVolker Rümelin #include "qemu/log.h"
390b8fa32fSMarkus Armbruster #include "qemu/module.h"
405e03b6daSClaudio Fontana #include "qemu/help_option.h"
4189fc45d5SMarc-André Lureau #include "sysemu/sysemu.h"
423d4d16f4SPavel Dovgalyuk #include "sysemu/replay.h"
4354d31236SMarkus Armbruster #include "sysemu/runstate.h"
44f0c4555eSGerd Hoffmann #include "ui/qemu-spice.h"
451a961e78SGerd Hoffmann #include "trace.h"
4685571bc7Sbellard 
471d14ffa9Sbellard #define AUDIO_CAP "audio"
481d14ffa9Sbellard #include "audio_int.h"
4985571bc7Sbellard 
501d14ffa9Sbellard /* #define DEBUG_LIVE */
511d14ffa9Sbellard /* #define DEBUG_OUT */
528ead62cfSbellard /* #define DEBUG_CAPTURE */
53713a98f8Smalc /* #define DEBUG_POLL */
541d14ffa9Sbellard 
55c0fe3827Sbellard #define SW_NAME(sw) (sw)->name ? (sw)->name : "unknown"
56c0fe3827Sbellard 
572358a494SJuan Quintela 
582358a494SJuan Quintela /* Order of CONFIG_AUDIO_DRIVERS is import.
592358a494SJuan Quintela    The 1st one is the one used by default, that is the reason
602358a494SJuan Quintela     that we generate the list.
612358a494SJuan Quintela */
6271830221SKővágó, Zoltán const char *audio_prio_list[] = {
63d3893a39SGerd Hoffmann     "spice",
642358a494SJuan Quintela     CONFIG_AUDIO_DRIVERS
65d3893a39SGerd Hoffmann     "none",
6671830221SKővágó, Zoltán     NULL
671d14ffa9Sbellard };
6885571bc7Sbellard 
69d3893a39SGerd Hoffmann static QLIST_HEAD(, audio_driver) audio_drivers;
7069a80279SPaolo Bonzini static AudiodevListHead audiodevs =
7169a80279SPaolo Bonzini     QSIMPLEQ_HEAD_INITIALIZER(audiodevs);
7269a80279SPaolo Bonzini static AudiodevListHead default_audiodevs =
7369a80279SPaolo Bonzini     QSIMPLEQ_HEAD_INITIALIZER(default_audiodevs);
7469a80279SPaolo Bonzini 
75d3893a39SGerd Hoffmann 
audio_driver_register(audio_driver * drv)76d3893a39SGerd Hoffmann void audio_driver_register(audio_driver *drv)
77d3893a39SGerd Hoffmann {
78d3893a39SGerd Hoffmann     QLIST_INSERT_HEAD(&audio_drivers, drv, next);
79d3893a39SGerd Hoffmann }
80d3893a39SGerd Hoffmann 
audio_driver_lookup(const char * name)8169a80279SPaolo Bonzini static audio_driver *audio_driver_lookup(const char *name)
82d3893a39SGerd Hoffmann {
83d3893a39SGerd Hoffmann     struct audio_driver *d;
84c551fb0bSClaudio Fontana     Error *local_err = NULL;
85c551fb0bSClaudio Fontana     int rv;
86d3893a39SGerd Hoffmann 
87d3893a39SGerd Hoffmann     QLIST_FOREACH(d, &audio_drivers, next) {
88d3893a39SGerd Hoffmann         if (strcmp(name, d->name) == 0) {
89d3893a39SGerd Hoffmann             return d;
90d3893a39SGerd Hoffmann         }
91d3893a39SGerd Hoffmann     }
92c551fb0bSClaudio Fontana     rv = audio_module_load(name, &local_err);
93c551fb0bSClaudio Fontana     if (rv > 0) {
9465ba8696SGerd Hoffmann         QLIST_FOREACH(d, &audio_drivers, next) {
9565ba8696SGerd Hoffmann             if (strcmp(name, d->name) == 0) {
9665ba8696SGerd Hoffmann                 return d;
9765ba8696SGerd Hoffmann             }
9865ba8696SGerd Hoffmann         }
99c551fb0bSClaudio Fontana     } else if (rv < 0) {
100c551fb0bSClaudio Fontana         error_report_err(local_err);
101c551fb0bSClaudio Fontana     }
102d3893a39SGerd Hoffmann     return NULL;
103d3893a39SGerd Hoffmann }
104d3893a39SGerd Hoffmann 
105ecd97e95SKővágó, Zoltán static QTAILQ_HEAD(AudioStateHead, AudioState) audio_states =
106ecd97e95SKővágó, Zoltán     QTAILQ_HEAD_INITIALIZER(audio_states);
10722f84d4fSPaolo Bonzini static AudioState *default_audio_state;
108c0fe3827Sbellard 
10900e07679SMichael Walle const struct mixeng_volume nominal_volume = {
11014658cd1SJuan Quintela     .mute = 0,
1111d14ffa9Sbellard #ifdef FLOAT_MIXENG
11214658cd1SJuan Quintela     .r = 1.0,
11314658cd1SJuan Quintela     .l = 1.0,
1141d14ffa9Sbellard #else
11514658cd1SJuan Quintela     .r = 1ULL << 32,
11614658cd1SJuan Quintela     .l = 1ULL << 32,
1171d14ffa9Sbellard #endif
11885571bc7Sbellard };
11985571bc7Sbellard 
audio_bug(const char * funcname,int cond)1201d14ffa9Sbellard int audio_bug (const char *funcname, int cond)
12185571bc7Sbellard {
1221d14ffa9Sbellard     if (cond) {
1231d14ffa9Sbellard         static int shown;
1241d14ffa9Sbellard 
1258ead62cfSbellard         AUD_log (NULL, "A bug was just triggered in %s\n", funcname);
1261d14ffa9Sbellard         if (!shown) {
1271d14ffa9Sbellard             shown = 1;
1281d14ffa9Sbellard             AUD_log (NULL, "Save all your work and restart without audio\n");
1291d14ffa9Sbellard             AUD_log (NULL, "I am sorry\n");
1301d14ffa9Sbellard         }
1311d14ffa9Sbellard         AUD_log (NULL, "Context:\n");
1321d14ffa9Sbellard     }
1331d14ffa9Sbellard 
1341d14ffa9Sbellard     return cond;
1351d14ffa9Sbellard }
1361d14ffa9Sbellard 
audio_bits_to_index(int bits)137f941aa25Sths static inline int audio_bits_to_index (int bits)
138f941aa25Sths {
139f941aa25Sths     switch (bits) {
140f941aa25Sths     case 8:
141f941aa25Sths         return 0;
142f941aa25Sths 
143f941aa25Sths     case 16:
144f941aa25Sths         return 1;
145f941aa25Sths 
146f941aa25Sths     case 32:
147f941aa25Sths         return 2;
148f941aa25Sths 
149f941aa25Sths     default:
150f941aa25Sths         audio_bug ("bits_to_index", 1);
151f941aa25Sths         AUD_log (NULL, "invalid bits %d\n", bits);
15212f4abf6SVolker Rümelin         return 0;
153f941aa25Sths     }
154f941aa25Sths }
155f941aa25Sths 
AUD_vlog(const char * cap,const char * fmt,va_list ap)156541e0844Sbellard void AUD_vlog (const char *cap, const char *fmt, va_list ap)
157541e0844Sbellard {
158541e0844Sbellard     if (cap) {
159541e0844Sbellard         fprintf(stderr, "%s: ", cap);
160541e0844Sbellard     }
161541e0844Sbellard 
162541e0844Sbellard     vfprintf(stderr, fmt, ap);
163541e0844Sbellard }
164541e0844Sbellard 
AUD_log(const char * cap,const char * fmt,...)165fb065187Sbellard void AUD_log (const char *cap, const char *fmt, ...)
16685571bc7Sbellard {
16785571bc7Sbellard     va_list ap;
16885571bc7Sbellard 
169541e0844Sbellard     va_start (ap, fmt);
170541e0844Sbellard     AUD_vlog (cap, fmt, ap);
171541e0844Sbellard     va_end (ap);
17285571bc7Sbellard }
17385571bc7Sbellard 
audio_print_settings(struct audsettings * as)1741ea879e5Smalc static void audio_print_settings (struct audsettings *as)
175c0fe3827Sbellard {
176c0fe3827Sbellard     dolog ("frequency=%d nchannels=%d fmt=", as->freq, as->nchannels);
177c0fe3827Sbellard 
178c0fe3827Sbellard     switch (as->fmt) {
17985bc5852SKővágó, Zoltán     case AUDIO_FORMAT_S8:
180c0fe3827Sbellard         AUD_log (NULL, "S8");
181c0fe3827Sbellard         break;
18285bc5852SKővágó, Zoltán     case AUDIO_FORMAT_U8:
183c0fe3827Sbellard         AUD_log (NULL, "U8");
184c0fe3827Sbellard         break;
18585bc5852SKővágó, Zoltán     case AUDIO_FORMAT_S16:
186c0fe3827Sbellard         AUD_log (NULL, "S16");
187c0fe3827Sbellard         break;
18885bc5852SKővágó, Zoltán     case AUDIO_FORMAT_U16:
189c0fe3827Sbellard         AUD_log (NULL, "U16");
190c0fe3827Sbellard         break;
19185bc5852SKővágó, Zoltán     case AUDIO_FORMAT_S32:
192d50997f9Smalc         AUD_log (NULL, "S32");
193d50997f9Smalc         break;
19485bc5852SKővágó, Zoltán     case AUDIO_FORMAT_U32:
195d50997f9Smalc         AUD_log (NULL, "U32");
196d50997f9Smalc         break;
197ed2a4a79SKővágó, Zoltán     case AUDIO_FORMAT_F32:
198ed2a4a79SKővágó, Zoltán         AUD_log (NULL, "F32");
199ed2a4a79SKővágó, Zoltán         break;
200c0fe3827Sbellard     default:
201c0fe3827Sbellard         AUD_log (NULL, "invalid(%d)", as->fmt);
202c0fe3827Sbellard         break;
203c0fe3827Sbellard     }
204ec36b695Sbellard 
205d929eba5Sbellard     AUD_log (NULL, " endianness=");
206d929eba5Sbellard     switch (as->endianness) {
207d929eba5Sbellard     case 0:
208d929eba5Sbellard         AUD_log (NULL, "little");
209d929eba5Sbellard         break;
210d929eba5Sbellard     case 1:
211d929eba5Sbellard         AUD_log (NULL, "big");
212d929eba5Sbellard         break;
213d929eba5Sbellard     default:
214d929eba5Sbellard         AUD_log (NULL, "invalid");
215d929eba5Sbellard         break;
216d929eba5Sbellard     }
217c0fe3827Sbellard     AUD_log (NULL, "\n");
218c0fe3827Sbellard }
219c0fe3827Sbellard 
audio_validate_settings(struct audsettings * as)2201ea879e5Smalc static int audio_validate_settings (struct audsettings *as)
221c0fe3827Sbellard {
222c0fe3827Sbellard     int invalid;
223c0fe3827Sbellard 
224b5c7db3eSKővágó, Zoltán     invalid = as->nchannels < 1;
225d929eba5Sbellard     invalid |= as->endianness != 0 && as->endianness != 1;
226c0fe3827Sbellard 
227c0fe3827Sbellard     switch (as->fmt) {
22885bc5852SKővágó, Zoltán     case AUDIO_FORMAT_S8:
22985bc5852SKővágó, Zoltán     case AUDIO_FORMAT_U8:
23085bc5852SKővágó, Zoltán     case AUDIO_FORMAT_S16:
23185bc5852SKővágó, Zoltán     case AUDIO_FORMAT_U16:
23285bc5852SKővágó, Zoltán     case AUDIO_FORMAT_S32:
23385bc5852SKővágó, Zoltán     case AUDIO_FORMAT_U32:
234ed2a4a79SKővágó, Zoltán     case AUDIO_FORMAT_F32:
235c0fe3827Sbellard         break;
236c0fe3827Sbellard     default:
237c0fe3827Sbellard         invalid = 1;
238c0fe3827Sbellard         break;
239c0fe3827Sbellard     }
240c0fe3827Sbellard 
241c0fe3827Sbellard     invalid |= as->freq <= 0;
242d929eba5Sbellard     return invalid ? -1 : 0;
243c0fe3827Sbellard }
244c0fe3827Sbellard 
audio_pcm_info_eq(struct audio_pcm_info * info,struct audsettings * as)2451ea879e5Smalc static int audio_pcm_info_eq (struct audio_pcm_info *info, struct audsettings *as)
24685571bc7Sbellard {
247ed2a4a79SKővágó, Zoltán     int bits = 8;
248ed2a4a79SKővágó, Zoltán     bool is_signed = false, is_float = false;
24985571bc7Sbellard 
250c0fe3827Sbellard     switch (as->fmt) {
25185bc5852SKővágó, Zoltán     case AUDIO_FORMAT_S8:
252ed2a4a79SKővágó, Zoltán         is_signed = true;
253b4bd0b16SStefan Weil         /* fall through */
25485bc5852SKővágó, Zoltán     case AUDIO_FORMAT_U8:
25585571bc7Sbellard         break;
25685571bc7Sbellard 
25785bc5852SKővágó, Zoltán     case AUDIO_FORMAT_S16:
258ed2a4a79SKővágó, Zoltán         is_signed = true;
259b4bd0b16SStefan Weil         /* fall through */
26085bc5852SKővágó, Zoltán     case AUDIO_FORMAT_U16:
26185571bc7Sbellard         bits = 16;
26285571bc7Sbellard         break;
263f941aa25Sths 
264ed2a4a79SKővágó, Zoltán     case AUDIO_FORMAT_F32:
265ed2a4a79SKővágó, Zoltán         is_float = true;
266ed2a4a79SKővágó, Zoltán         /* fall through */
26785bc5852SKővágó, Zoltán     case AUDIO_FORMAT_S32:
268ed2a4a79SKővágó, Zoltán         is_signed = true;
269b4bd0b16SStefan Weil         /* fall through */
27085bc5852SKővágó, Zoltán     case AUDIO_FORMAT_U32:
271f941aa25Sths         bits = 32;
272f941aa25Sths         break;
27385bc5852SKővágó, Zoltán 
27485bc5852SKővágó, Zoltán     default:
27585bc5852SKővágó, Zoltán         abort();
27685571bc7Sbellard     }
277c0fe3827Sbellard     return info->freq == as->freq
278c0fe3827Sbellard         && info->nchannels == as->nchannels
279ed2a4a79SKővágó, Zoltán         && info->is_signed == is_signed
280ed2a4a79SKővágó, Zoltán         && info->is_float == is_float
281d929eba5Sbellard         && info->bits == bits
282d929eba5Sbellard         && info->swap_endianness == (as->endianness != AUDIO_HOST_ENDIANNESS);
28385571bc7Sbellard }
28485571bc7Sbellard 
audio_pcm_init_info(struct audio_pcm_info * info,struct audsettings * as)2851ea879e5Smalc void audio_pcm_init_info (struct audio_pcm_info *info, struct audsettings *as)
28685571bc7Sbellard {
287ed2a4a79SKővágó, Zoltán     int bits = 8, mul;
288ed2a4a79SKővágó, Zoltán     bool is_signed = false, is_float = false;
28985571bc7Sbellard 
290c0fe3827Sbellard     switch (as->fmt) {
29185bc5852SKővágó, Zoltán     case AUDIO_FORMAT_S8:
292ed2a4a79SKővágó, Zoltán         is_signed = true;
293f7621fd1SPhilippe Mathieu-Daudé         /* fall through */
29485bc5852SKővágó, Zoltán     case AUDIO_FORMAT_U8:
2952b9cce8cSKővágó, Zoltán         mul = 1;
29685571bc7Sbellard         break;
29785571bc7Sbellard 
29885bc5852SKővágó, Zoltán     case AUDIO_FORMAT_S16:
299ed2a4a79SKővágó, Zoltán         is_signed = true;
300e4634941SStefan Weil         /* fall through */
30185bc5852SKővágó, Zoltán     case AUDIO_FORMAT_U16:
30285571bc7Sbellard         bits = 16;
3032b9cce8cSKővágó, Zoltán         mul = 2;
304f941aa25Sths         break;
305f941aa25Sths 
306ed2a4a79SKővágó, Zoltán     case AUDIO_FORMAT_F32:
307ed2a4a79SKővágó, Zoltán         is_float = true;
308ed2a4a79SKővágó, Zoltán         /* fall through */
30985bc5852SKővágó, Zoltán     case AUDIO_FORMAT_S32:
310ed2a4a79SKővágó, Zoltán         is_signed = true;
311e4634941SStefan Weil         /* fall through */
31285bc5852SKővágó, Zoltán     case AUDIO_FORMAT_U32:
313f941aa25Sths         bits = 32;
3142b9cce8cSKővágó, Zoltán         mul = 4;
31585571bc7Sbellard         break;
31685bc5852SKővágó, Zoltán 
31785bc5852SKővágó, Zoltán     default:
31885bc5852SKővágó, Zoltán         abort();
31985571bc7Sbellard     }
32085571bc7Sbellard 
321c0fe3827Sbellard     info->freq = as->freq;
3221d14ffa9Sbellard     info->bits = bits;
323ed2a4a79SKővágó, Zoltán     info->is_signed = is_signed;
324ed2a4a79SKővágó, Zoltán     info->is_float = is_float;
325c0fe3827Sbellard     info->nchannels = as->nchannels;
3262b9cce8cSKővágó, Zoltán     info->bytes_per_frame = as->nchannels * mul;
3272b9cce8cSKővágó, Zoltán     info->bytes_per_second = info->freq * info->bytes_per_frame;
328d929eba5Sbellard     info->swap_endianness = (as->endianness != AUDIO_HOST_ENDIANNESS);
3291d14ffa9Sbellard }
3301d14ffa9Sbellard 
audio_pcm_info_clear_buf(struct audio_pcm_info * info,void * buf,int len)3311d14ffa9Sbellard void audio_pcm_info_clear_buf (struct audio_pcm_info *info, void *buf, int len)
3321d14ffa9Sbellard {
3331d14ffa9Sbellard     if (!len) {
3341d14ffa9Sbellard         return;
3351d14ffa9Sbellard     }
3361d14ffa9Sbellard 
337ed2a4a79SKővágó, Zoltán     if (info->is_signed || info->is_float) {
3382b9cce8cSKővágó, Zoltán         memset(buf, 0x00, len * info->bytes_per_frame);
3396c6886bdSZhang Han     } else {
340f941aa25Sths         switch (info->bits) {
341f941aa25Sths         case 8:
3422b9cce8cSKővágó, Zoltán             memset(buf, 0x80, len * info->bytes_per_frame);
343f941aa25Sths             break;
344f941aa25Sths 
345f941aa25Sths         case 16:
346f941aa25Sths             {
3471d14ffa9Sbellard                 int i;
3481d14ffa9Sbellard                 uint16_t *p = buf;
3491d14ffa9Sbellard                 short s = INT16_MAX;
3501d14ffa9Sbellard 
351d929eba5Sbellard                 if (info->swap_endianness) {
3521d14ffa9Sbellard                     s = bswap16 (s);
3531d14ffa9Sbellard                 }
3541d14ffa9Sbellard 
3552b9cce8cSKővágó, Zoltán                 for (i = 0; i < len * info->nchannels; i++) {
3561d14ffa9Sbellard                     p[i] = s;
3571d14ffa9Sbellard                 }
3581d14ffa9Sbellard             }
359f941aa25Sths             break;
360f941aa25Sths 
361f941aa25Sths         case 32:
362f941aa25Sths             {
363f941aa25Sths                 int i;
364f941aa25Sths                 uint32_t *p = buf;
365f941aa25Sths                 int32_t s = INT32_MAX;
366f941aa25Sths 
367f941aa25Sths                 if (info->swap_endianness) {
368f941aa25Sths                     s = bswap32 (s);
369f941aa25Sths                 }
370f941aa25Sths 
3712b9cce8cSKővágó, Zoltán                 for (i = 0; i < len * info->nchannels; i++) {
372f941aa25Sths                     p[i] = s;
373f941aa25Sths                 }
374f941aa25Sths             }
375f941aa25Sths             break;
376f941aa25Sths 
377f941aa25Sths         default:
378f941aa25Sths             AUD_log (NULL, "audio_pcm_info_clear_buf: invalid bits %d\n",
379f941aa25Sths                      info->bits);
380f941aa25Sths             break;
381f941aa25Sths         }
3821d14ffa9Sbellard     }
3831d14ffa9Sbellard }
3841d14ffa9Sbellard 
3851d14ffa9Sbellard /*
3868ead62cfSbellard  * Capture
3878ead62cfSbellard  */
audio_pcm_capture_find_specific(AudioState * s,struct audsettings * as)388526fb058SKővágó, Zoltán static CaptureVoiceOut *audio_pcm_capture_find_specific(AudioState *s,
389526fb058SKővágó, Zoltán                                                         struct audsettings *as)
3908ead62cfSbellard {
3918ead62cfSbellard     CaptureVoiceOut *cap;
3928ead62cfSbellard 
3938ead62cfSbellard     for (cap = s->cap_head.lh_first; cap; cap = cap->entries.le_next) {
394d929eba5Sbellard         if (audio_pcm_info_eq (&cap->hw.info, as)) {
3958ead62cfSbellard             return cap;
3968ead62cfSbellard         }
3978ead62cfSbellard     }
3988ead62cfSbellard     return NULL;
3998ead62cfSbellard }
4008ead62cfSbellard 
audio_notify_capture(CaptureVoiceOut * cap,audcnotification_e cmd)401ec36b695Sbellard static void audio_notify_capture (CaptureVoiceOut *cap, audcnotification_e cmd)
4028ead62cfSbellard {
4038ead62cfSbellard     struct capture_callback *cb;
4048ead62cfSbellard 
405ec36b695Sbellard #ifdef DEBUG_CAPTURE
406ec36b695Sbellard     dolog ("notification %d sent\n", cmd);
407ec36b695Sbellard #endif
4088ead62cfSbellard     for (cb = cap->cb_head.lh_first; cb; cb = cb->entries.le_next) {
409ec36b695Sbellard         cb->ops.notify (cb->opaque, cmd);
4108ead62cfSbellard     }
4118ead62cfSbellard }
412ec36b695Sbellard 
audio_capture_maybe_changed(CaptureVoiceOut * cap,int enabled)413ec36b695Sbellard static void audio_capture_maybe_changed (CaptureVoiceOut *cap, int enabled)
414ec36b695Sbellard {
415ec36b695Sbellard     if (cap->hw.enabled != enabled) {
416ec36b695Sbellard         audcnotification_e cmd;
417ec36b695Sbellard         cap->hw.enabled = enabled;
418ec36b695Sbellard         cmd = enabled ? AUD_CNOTIFY_ENABLE : AUD_CNOTIFY_DISABLE;
419ec36b695Sbellard         audio_notify_capture (cap, cmd);
420ec36b695Sbellard     }
4218ead62cfSbellard }
4228ead62cfSbellard 
audio_recalc_and_notify_capture(CaptureVoiceOut * cap)4238ead62cfSbellard static void audio_recalc_and_notify_capture (CaptureVoiceOut *cap)
4248ead62cfSbellard {
4258ead62cfSbellard     HWVoiceOut *hw = &cap->hw;
4268ead62cfSbellard     SWVoiceOut *sw;
4278ead62cfSbellard     int enabled = 0;
4288ead62cfSbellard 
429ec36b695Sbellard     for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) {
4308ead62cfSbellard         if (sw->active) {
4318ead62cfSbellard             enabled = 1;
4328ead62cfSbellard             break;
4338ead62cfSbellard         }
4348ead62cfSbellard     }
435ec36b695Sbellard     audio_capture_maybe_changed (cap, enabled);
4368ead62cfSbellard }
4378ead62cfSbellard 
audio_detach_capture(HWVoiceOut * hw)4388ead62cfSbellard static void audio_detach_capture (HWVoiceOut *hw)
4398ead62cfSbellard {
440ec36b695Sbellard     SWVoiceCap *sc = hw->cap_head.lh_first;
4418ead62cfSbellard 
442ec36b695Sbellard     while (sc) {
443ec36b695Sbellard         SWVoiceCap *sc1 = sc->entries.le_next;
444ec36b695Sbellard         SWVoiceOut *sw = &sc->sw;
445ec36b695Sbellard         CaptureVoiceOut *cap = sc->cap;
446ec36b695Sbellard         int was_active = sw->active;
447ec36b695Sbellard 
4488ead62cfSbellard         if (sw->rate) {
4498ead62cfSbellard             st_rate_stop (sw->rate);
4508ead62cfSbellard             sw->rate = NULL;
4518ead62cfSbellard         }
4528ead62cfSbellard 
45372cf2d4fSBlue Swirl         QLIST_REMOVE (sw, entries);
45472cf2d4fSBlue Swirl         QLIST_REMOVE (sc, entries);
4557267c094SAnthony Liguori         g_free (sc);
456ec36b695Sbellard         if (was_active) {
457ec36b695Sbellard             /* We have removed soft voice from the capture:
458ec36b695Sbellard                this might have changed the overall status of the capture
459ec36b695Sbellard                since this might have been the only active voice */
460ec36b695Sbellard             audio_recalc_and_notify_capture (cap);
461ec36b695Sbellard         }
462ec36b695Sbellard         sc = sc1;
4638ead62cfSbellard     }
4648ead62cfSbellard }
4658ead62cfSbellard 
audio_attach_capture(HWVoiceOut * hw)4661a7dafceSmalc static int audio_attach_capture (HWVoiceOut *hw)
4678ead62cfSbellard {
468526fb058SKővágó, Zoltán     AudioState *s = hw->s;
4698ead62cfSbellard     CaptureVoiceOut *cap;
4708ead62cfSbellard 
4718ead62cfSbellard     audio_detach_capture (hw);
4728ead62cfSbellard     for (cap = s->cap_head.lh_first; cap; cap = cap->entries.le_next) {
473ec36b695Sbellard         SWVoiceCap *sc;
4748ead62cfSbellard         SWVoiceOut *sw;
475ec36b695Sbellard         HWVoiceOut *hw_cap = &cap->hw;
4768ead62cfSbellard 
477e8d85444SFrediano Ziglio         sc = g_malloc0(sizeof(*sc));
4788ead62cfSbellard 
479ec36b695Sbellard         sc->cap = cap;
480ec36b695Sbellard         sw = &sc->sw;
4818ead62cfSbellard         sw->hw = hw_cap;
482ec36b695Sbellard         sw->info = hw->info;
4838ead62cfSbellard         sw->empty = 1;
4848ead62cfSbellard         sw->active = hw->enabled;
48583617103Smalc         sw->vol = nominal_volume;
4868ead62cfSbellard         sw->rate = st_rate_start (sw->info.freq, hw_cap->info.freq);
48772cf2d4fSBlue Swirl         QLIST_INSERT_HEAD (&hw_cap->sw_head, sw, entries);
48872cf2d4fSBlue Swirl         QLIST_INSERT_HEAD (&hw->cap_head, sc, entries);
489ec36b695Sbellard #ifdef DEBUG_CAPTURE
490457b6543SStefan Weil         sw->name = g_strdup_printf ("for %p %d,%d,%d",
491457b6543SStefan Weil                                     hw, sw->info.freq, sw->info.bits,
492457b6543SStefan Weil                                     sw->info.nchannels);
493ec36b695Sbellard         dolog ("Added %s active = %d\n", sw->name, sw->active);
494ec36b695Sbellard #endif
4958ead62cfSbellard         if (sw->active) {
496ec36b695Sbellard             audio_capture_maybe_changed (cap, 1);
4978ead62cfSbellard         }
4988ead62cfSbellard     }
4998ead62cfSbellard     return 0;
5008ead62cfSbellard }
5018ead62cfSbellard 
5028ead62cfSbellard /*
5031d14ffa9Sbellard  * Hard voice (capture)
5041d14ffa9Sbellard  */
audio_pcm_hw_find_min_in(HWVoiceIn * hw)5057520462bSKővágó, Zoltán static size_t audio_pcm_hw_find_min_in (HWVoiceIn *hw)
50685571bc7Sbellard {
5071d14ffa9Sbellard     SWVoiceIn *sw;
5087520462bSKővágó, Zoltán     size_t m = hw->total_samples_captured;
5091d14ffa9Sbellard 
5101d14ffa9Sbellard     for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) {
5111d14ffa9Sbellard         if (sw->active) {
51258935915SKővágó, Zoltán             m = MIN (m, sw->total_hw_samples_acquired);
51385571bc7Sbellard         }
5141d14ffa9Sbellard     }
5151d14ffa9Sbellard     return m;
5161d14ffa9Sbellard }
5171d14ffa9Sbellard 
audio_pcm_hw_get_live_in(HWVoiceIn * hw)5183f5bbfc2SKővágó, Zoltán static size_t audio_pcm_hw_get_live_in(HWVoiceIn *hw)
5191d14ffa9Sbellard {
5207520462bSKővágó, Zoltán     size_t live = hw->total_samples_captured - audio_pcm_hw_find_min_in (hw);
5218dbd3d17SVolker Rümelin     if (audio_bug(__func__, live > hw->conv_buf.size)) {
5228dbd3d17SVolker Rümelin         dolog("live=%zu hw->conv_buf.size=%zu\n", live, hw->conv_buf.size);
52312f4abf6SVolker Rümelin         return 0;
52485571bc7Sbellard     }
5251d14ffa9Sbellard     return live;
52685571bc7Sbellard }
52785571bc7Sbellard 
audio_pcm_hw_conv_in(HWVoiceIn * hw,void * pcm_buf,size_t samples)528251f1549SVolker Rümelin static size_t audio_pcm_hw_conv_in(HWVoiceIn *hw, void *pcm_buf, size_t samples)
529251f1549SVolker Rümelin {
530251f1549SVolker Rümelin     size_t conv = 0;
5318dbd3d17SVolker Rümelin     STSampleBuffer *conv_buf = &hw->conv_buf;
532251f1549SVolker Rümelin 
533251f1549SVolker Rümelin     while (samples) {
534251f1549SVolker Rümelin         uint8_t *src = advance(pcm_buf, conv * hw->info.bytes_per_frame);
535251f1549SVolker Rümelin         size_t proc = MIN(samples, conv_buf->size - conv_buf->pos);
536251f1549SVolker Rümelin 
5378dbd3d17SVolker Rümelin         hw->conv(conv_buf->buffer + conv_buf->pos, src, proc);
538251f1549SVolker Rümelin         conv_buf->pos = (conv_buf->pos + proc) % conv_buf->size;
539251f1549SVolker Rümelin         samples -= proc;
540251f1549SVolker Rümelin         conv += proc;
541251f1549SVolker Rümelin     }
542251f1549SVolker Rümelin 
543251f1549SVolker Rümelin     return conv;
544251f1549SVolker Rümelin }
545251f1549SVolker Rümelin 
5461d14ffa9Sbellard /*
5471d14ffa9Sbellard  * Soft voice (capture)
5481d14ffa9Sbellard  */
audio_pcm_sw_resample_in(SWVoiceIn * sw,size_t frames_in_max,size_t frames_out_max,size_t * total_in,size_t * total_out)5491c49c5f1SVolker Rümelin static void audio_pcm_sw_resample_in(SWVoiceIn *sw,
5501c49c5f1SVolker Rümelin     size_t frames_in_max, size_t frames_out_max,
5511c49c5f1SVolker Rümelin     size_t *total_in, size_t *total_out)
5521c49c5f1SVolker Rümelin {
5531c49c5f1SVolker Rümelin     HWVoiceIn *hw = sw->hw;
5541c49c5f1SVolker Rümelin     struct st_sample *src, *dst;
5551c49c5f1SVolker Rümelin     size_t live, rpos, frames_in, frames_out;
5561c49c5f1SVolker Rümelin 
5571c49c5f1SVolker Rümelin     live = hw->total_samples_captured - sw->total_hw_samples_acquired;
5581c49c5f1SVolker Rümelin     rpos = audio_ring_posb(hw->conv_buf.pos, live, hw->conv_buf.size);
5591c49c5f1SVolker Rümelin 
5601c49c5f1SVolker Rümelin     /* resample conv_buf from rpos to end of buffer */
5611c49c5f1SVolker Rümelin     src = hw->conv_buf.buffer + rpos;
5621c49c5f1SVolker Rümelin     frames_in = MIN(frames_in_max, hw->conv_buf.size - rpos);
5631c49c5f1SVolker Rümelin     dst = sw->resample_buf.buffer;
5641c49c5f1SVolker Rümelin     frames_out = frames_out_max;
5651c49c5f1SVolker Rümelin     st_rate_flow(sw->rate, src, dst, &frames_in, &frames_out);
5661c49c5f1SVolker Rümelin     rpos += frames_in;
5671c49c5f1SVolker Rümelin     *total_in = frames_in;
5681c49c5f1SVolker Rümelin     *total_out = frames_out;
5691c49c5f1SVolker Rümelin 
5701c49c5f1SVolker Rümelin     /* resample conv_buf from start of buffer if there are input frames left */
5711c49c5f1SVolker Rümelin     if (frames_in_max - frames_in && rpos == hw->conv_buf.size) {
5721c49c5f1SVolker Rümelin         src = hw->conv_buf.buffer;
5731c49c5f1SVolker Rümelin         frames_in = frames_in_max - frames_in;
5741c49c5f1SVolker Rümelin         dst += frames_out;
5751c49c5f1SVolker Rümelin         frames_out = frames_out_max - frames_out;
5761c49c5f1SVolker Rümelin         st_rate_flow(sw->rate, src, dst, &frames_in, &frames_out);
5771c49c5f1SVolker Rümelin         *total_in += frames_in;
5781c49c5f1SVolker Rümelin         *total_out += frames_out;
5791c49c5f1SVolker Rümelin     }
5801c49c5f1SVolker Rümelin }
5811c49c5f1SVolker Rümelin 
audio_pcm_sw_read(SWVoiceIn * sw,void * buf,size_t buf_len)582fbde1edfSVolker Rümelin static size_t audio_pcm_sw_read(SWVoiceIn *sw, void *buf, size_t buf_len)
583fb065187Sbellard {
5841d14ffa9Sbellard     HWVoiceIn *hw = sw->hw;
585a9ea5678SVolker Rümelin     size_t live, frames_out_max, total_in, total_out;
586fb065187Sbellard 
5871d14ffa9Sbellard     live = hw->total_samples_captured - sw->total_hw_samples_acquired;
5880ceb26afSVolker Rümelin     if (!live) {
5890ceb26afSVolker Rümelin         return 0;
5900ceb26afSVolker Rümelin     }
5918dbd3d17SVolker Rümelin     if (audio_bug(__func__, live > hw->conv_buf.size)) {
5928dbd3d17SVolker Rümelin         dolog("live_in=%zu hw->conv_buf.size=%zu\n", live, hw->conv_buf.size);
59312f4abf6SVolker Rümelin         return 0;
594fb065187Sbellard     }
595fb065187Sbellard 
596a9ea5678SVolker Rümelin     frames_out_max = MIN(buf_len / sw->info.bytes_per_frame,
597a9ea5678SVolker Rümelin                          sw->resample_buf.size);
5981d14ffa9Sbellard 
599a9ea5678SVolker Rümelin     audio_pcm_sw_resample_in(sw, live, frames_out_max, &total_in, &total_out);
6001d14ffa9Sbellard 
601669b9522SVolker Rümelin     if (!hw->pcm_ops->volume_in) {
602fbde1edfSVolker Rümelin         mixeng_volume(sw->resample_buf.buffer, total_out, &sw->vol);
603c01b2456SMarc-André Lureau     }
604fbde1edfSVolker Rümelin     sw->clip(buf, sw->resample_buf.buffer, total_out);
60500e07679SMichael Walle 
606fbde1edfSVolker Rümelin     sw->total_hw_samples_acquired += total_in;
607fbde1edfSVolker Rümelin     return total_out * sw->info.bytes_per_frame;
6081d14ffa9Sbellard }
6091d14ffa9Sbellard 
6101d14ffa9Sbellard /*
6111d14ffa9Sbellard  * Hard voice (playback)
6121d14ffa9Sbellard  */
audio_pcm_hw_find_min_out(HWVoiceOut * hw,int * nb_livep)6137520462bSKővágó, Zoltán static size_t audio_pcm_hw_find_min_out (HWVoiceOut *hw, int *nb_livep)
6141d14ffa9Sbellard {
6151d14ffa9Sbellard     SWVoiceOut *sw;
6167520462bSKővágó, Zoltán     size_t m = SIZE_MAX;
6171d14ffa9Sbellard     int nb_live = 0;
6181d14ffa9Sbellard 
6191d14ffa9Sbellard     for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) {
6201d14ffa9Sbellard         if (sw->active || !sw->empty) {
62158935915SKővágó, Zoltán             m = MIN (m, sw->total_hw_samples_mixed);
6221d14ffa9Sbellard             nb_live += 1;
6231d14ffa9Sbellard         }
6241d14ffa9Sbellard     }
6251d14ffa9Sbellard 
6261d14ffa9Sbellard     *nb_livep = nb_live;
6271d14ffa9Sbellard     return m;
6281d14ffa9Sbellard }
6291d14ffa9Sbellard 
audio_pcm_hw_get_live_out(HWVoiceOut * hw,int * nb_live)6307520462bSKővágó, Zoltán static size_t audio_pcm_hw_get_live_out (HWVoiceOut *hw, int *nb_live)
6311d14ffa9Sbellard {
6327520462bSKővágó, Zoltán     size_t smin;
633bdff253cSmalc     int nb_live1;
6341d14ffa9Sbellard 
635bdff253cSmalc     smin = audio_pcm_hw_find_min_out (hw, &nb_live1);
636bdff253cSmalc     if (nb_live) {
637bdff253cSmalc         *nb_live = nb_live1;
6381d14ffa9Sbellard     }
639bdff253cSmalc 
640bdff253cSmalc     if (nb_live1) {
6417520462bSKővágó, Zoltán         size_t live = smin;
6421d14ffa9Sbellard 
6438dbd3d17SVolker Rümelin         if (audio_bug(__func__, live > hw->mix_buf.size)) {
6448dbd3d17SVolker Rümelin             dolog("live=%zu hw->mix_buf.size=%zu\n", live, hw->mix_buf.size);
64512f4abf6SVolker Rümelin             return 0;
6461d14ffa9Sbellard         }
6471d14ffa9Sbellard         return live;
6481d14ffa9Sbellard     }
6491d14ffa9Sbellard     return 0;
6501d14ffa9Sbellard }
6511d14ffa9Sbellard 
audio_pcm_hw_get_free(HWVoiceOut * hw)6529833438eSVolker Rümelin static size_t audio_pcm_hw_get_free(HWVoiceOut *hw)
6539833438eSVolker Rümelin {
6549833438eSVolker Rümelin     return (hw->pcm_ops->buffer_get_free ? hw->pcm_ops->buffer_get_free(hw) :
6559833438eSVolker Rümelin             INT_MAX) / hw->info.bytes_per_frame;
6569833438eSVolker Rümelin }
6579833438eSVolker Rümelin 
audio_pcm_hw_clip_out(HWVoiceOut * hw,void * pcm_buf,size_t len)6588e56a172SVolker Rümelin static void audio_pcm_hw_clip_out(HWVoiceOut *hw, void *pcm_buf, size_t len)
6598e56a172SVolker Rümelin {
6608e56a172SVolker Rümelin     size_t clipped = 0;
6618dbd3d17SVolker Rümelin     size_t pos = hw->mix_buf.pos;
6628e56a172SVolker Rümelin 
6638e56a172SVolker Rümelin     while (len) {
6648dbd3d17SVolker Rümelin         st_sample *src = hw->mix_buf.buffer + pos;
6658e56a172SVolker Rümelin         uint8_t *dst = advance(pcm_buf, clipped * hw->info.bytes_per_frame);
6668dbd3d17SVolker Rümelin         size_t samples_till_end_of_buf = hw->mix_buf.size - pos;
6678e56a172SVolker Rümelin         size_t samples_to_clip = MIN(len, samples_till_end_of_buf);
6688e56a172SVolker Rümelin 
6698e56a172SVolker Rümelin         hw->clip(dst, src, samples_to_clip);
6708e56a172SVolker Rümelin 
6718dbd3d17SVolker Rümelin         pos = (pos + samples_to_clip) % hw->mix_buf.size;
6728e56a172SVolker Rümelin         len -= samples_to_clip;
6738e56a172SVolker Rümelin         clipped += samples_to_clip;
6748e56a172SVolker Rümelin     }
6758e56a172SVolker Rümelin }
6768e56a172SVolker Rümelin 
6771d14ffa9Sbellard /*
6781d14ffa9Sbellard  * Soft voice (playback)
6791d14ffa9Sbellard  */
audio_pcm_sw_resample_out(SWVoiceOut * sw,size_t frames_in_max,size_t frames_out_max,size_t * total_in,size_t * total_out)6808a81abeeSVolker Rümelin static void audio_pcm_sw_resample_out(SWVoiceOut *sw,
6818a81abeeSVolker Rümelin     size_t frames_in_max, size_t frames_out_max,
6828a81abeeSVolker Rümelin     size_t *total_in, size_t *total_out)
6838a81abeeSVolker Rümelin {
6848a81abeeSVolker Rümelin     HWVoiceOut *hw = sw->hw;
6858a81abeeSVolker Rümelin     struct st_sample *src, *dst;
6868a81abeeSVolker Rümelin     size_t live, wpos, frames_in, frames_out;
6878a81abeeSVolker Rümelin 
6888a81abeeSVolker Rümelin     live = sw->total_hw_samples_mixed;
6898a81abeeSVolker Rümelin     wpos = (hw->mix_buf.pos + live) % hw->mix_buf.size;
6908a81abeeSVolker Rümelin 
6918a81abeeSVolker Rümelin     /* write to mix_buf from wpos to end of buffer */
6928a81abeeSVolker Rümelin     src = sw->resample_buf.buffer;
6938a81abeeSVolker Rümelin     frames_in = frames_in_max;
6948a81abeeSVolker Rümelin     dst = hw->mix_buf.buffer + wpos;
6958a81abeeSVolker Rümelin     frames_out = MIN(frames_out_max, hw->mix_buf.size - wpos);
6968a81abeeSVolker Rümelin     st_rate_flow_mix(sw->rate, src, dst, &frames_in, &frames_out);
6978a81abeeSVolker Rümelin     wpos += frames_out;
6988a81abeeSVolker Rümelin     *total_in = frames_in;
6998a81abeeSVolker Rümelin     *total_out = frames_out;
7008a81abeeSVolker Rümelin 
7018a81abeeSVolker Rümelin     /* write to mix_buf from start of buffer if there are input frames left */
7028a81abeeSVolker Rümelin     if (frames_in_max - frames_in > 0 && wpos == hw->mix_buf.size) {
7038a81abeeSVolker Rümelin         src += frames_in;
7048a81abeeSVolker Rümelin         frames_in = frames_in_max - frames_in;
7058a81abeeSVolker Rümelin         dst = hw->mix_buf.buffer;
7068a81abeeSVolker Rümelin         frames_out = frames_out_max - frames_out;
7078a81abeeSVolker Rümelin         st_rate_flow_mix(sw->rate, src, dst, &frames_in, &frames_out);
7088a81abeeSVolker Rümelin         *total_in += frames_in;
7098a81abeeSVolker Rümelin         *total_out += frames_out;
7108a81abeeSVolker Rümelin     }
7118a81abeeSVolker Rümelin }
7128a81abeeSVolker Rümelin 
audio_pcm_sw_write(SWVoiceOut * sw,void * buf,size_t buf_len)713d5647bd9SVolker Rümelin static size_t audio_pcm_sw_write(SWVoiceOut *sw, void *buf, size_t buf_len)
7141d14ffa9Sbellard {
715d5647bd9SVolker Rümelin     HWVoiceOut *hw = sw->hw;
7161a01df3dSVolker Rümelin     size_t live, dead, hw_free, sw_max, fe_max;
7171a01df3dSVolker Rümelin     size_t frames_in_max, frames_out_max, total_in, total_out;
7181d14ffa9Sbellard 
7191d14ffa9Sbellard     live = sw->total_hw_samples_mixed;
720d5647bd9SVolker Rümelin     if (audio_bug(__func__, live > hw->mix_buf.size)) {
721d5647bd9SVolker Rümelin         dolog("live=%zu hw->mix_buf.size=%zu\n", live, hw->mix_buf.size);
72212f4abf6SVolker Rümelin         return 0;
7231d14ffa9Sbellard     }
7241d14ffa9Sbellard 
725d5647bd9SVolker Rümelin     if (live == hw->mix_buf.size) {
726ec36b695Sbellard #ifdef DEBUG_OUT
7270c29b786SAkihiko Odaki         dolog ("%s is full %zu\n", sw->name, live);
728ec36b695Sbellard #endif
7291d14ffa9Sbellard         return 0;
7301d14ffa9Sbellard     }
7311d14ffa9Sbellard 
732d5647bd9SVolker Rümelin     dead = hw->mix_buf.size - live;
733d5647bd9SVolker Rümelin     hw_free = audio_pcm_hw_get_free(hw);
7349833438eSVolker Rümelin     hw_free = hw_free > live ? hw_free - live : 0;
7351a01df3dSVolker Rümelin     frames_out_max = MIN(dead, hw_free);
7361a01df3dSVolker Rümelin     sw_max = st_rate_frames_in(sw->rate, frames_out_max);
737e1e6a6fcSVolker Rümelin     fe_max = MIN(buf_len / sw->info.bytes_per_frame + sw->resample_buf.pos,
738e1e6a6fcSVolker Rümelin                  sw->resample_buf.size);
7391a01df3dSVolker Rümelin     frames_in_max = MIN(sw_max, fe_max);
740c01b2456SMarc-André Lureau 
7411a01df3dSVolker Rümelin     if (!frames_in_max) {
7421a01df3dSVolker Rümelin         return 0;
7431a01df3dSVolker Rümelin     }
7441a01df3dSVolker Rümelin 
745e1e6a6fcSVolker Rümelin     if (frames_in_max > sw->resample_buf.pos) {
746e1e6a6fcSVolker Rümelin         sw->conv(sw->resample_buf.buffer + sw->resample_buf.pos,
747e1e6a6fcSVolker Rümelin                  buf, frames_in_max - sw->resample_buf.pos);
748669b9522SVolker Rümelin         if (!sw->hw->pcm_ops->volume_out) {
749e1e6a6fcSVolker Rümelin             mixeng_volume(sw->resample_buf.buffer + sw->resample_buf.pos,
750e1e6a6fcSVolker Rümelin                           frames_in_max - sw->resample_buf.pos, &sw->vol);
751e1e6a6fcSVolker Rümelin         }
7521d14ffa9Sbellard     }
7531d14ffa9Sbellard 
7541a01df3dSVolker Rümelin     audio_pcm_sw_resample_out(sw, frames_in_max, frames_out_max,
755d5647bd9SVolker Rümelin                               &total_in, &total_out);
7561d14ffa9Sbellard 
757d5647bd9SVolker Rümelin     sw->total_hw_samples_mixed += total_out;
7581d14ffa9Sbellard     sw->empty = sw->total_hw_samples_mixed == 0;
7591d14ffa9Sbellard 
760e1e6a6fcSVolker Rümelin     /*
761e1e6a6fcSVolker Rümelin      * Upsampling may leave one audio frame in the resample buffer. Decrement
762e1e6a6fcSVolker Rümelin      * total_in by one if there was a leftover frame from the previous resample
763e1e6a6fcSVolker Rümelin      * pass in the resample buffer. Increment total_in by one if the current
764e1e6a6fcSVolker Rümelin      * resample pass left one frame in the resample buffer.
765e1e6a6fcSVolker Rümelin      */
766e1e6a6fcSVolker Rümelin     if (frames_in_max - total_in == 1) {
767e1e6a6fcSVolker Rümelin         /* copy one leftover audio frame to the beginning of the buffer */
768e1e6a6fcSVolker Rümelin         *sw->resample_buf.buffer = *(sw->resample_buf.buffer + total_in);
769e1e6a6fcSVolker Rümelin         total_in += 1 - sw->resample_buf.pos;
770e1e6a6fcSVolker Rümelin         sw->resample_buf.pos = 1;
771e1e6a6fcSVolker Rümelin     } else if (total_in >= sw->resample_buf.pos) {
772e1e6a6fcSVolker Rümelin         total_in -= sw->resample_buf.pos;
773e1e6a6fcSVolker Rümelin         sw->resample_buf.pos = 0;
774e1e6a6fcSVolker Rümelin     }
775e1e6a6fcSVolker Rümelin 
7761d14ffa9Sbellard #ifdef DEBUG_OUT
7771d14ffa9Sbellard     dolog (
778d5647bd9SVolker Rümelin         "%s: write size %zu written %zu total mixed %zu\n",
779c0fe3827Sbellard         SW_NAME(sw),
780d5647bd9SVolker Rümelin         buf_len / sw->info.bytes_per_frame,
781d5647bd9SVolker Rümelin         total_in,
782c0fe3827Sbellard         sw->total_hw_samples_mixed
7831d14ffa9Sbellard         );
7841d14ffa9Sbellard #endif
7851d14ffa9Sbellard 
786d5647bd9SVolker Rümelin     return total_in * sw->info.bytes_per_frame;
7871d14ffa9Sbellard }
7881d14ffa9Sbellard 
7891d14ffa9Sbellard #ifdef DEBUG_AUDIO
audio_pcm_print_info(const char * cap,struct audio_pcm_info * info)7901d14ffa9Sbellard static void audio_pcm_print_info (const char *cap, struct audio_pcm_info *info)
7911d14ffa9Sbellard {
792ed2a4a79SKővágó, Zoltán     dolog("%s: bits %d, sign %d, float %d, freq %d, nchan %d\n",
793ed2a4a79SKővágó, Zoltán           cap, info->bits, info->is_signed, info->is_float, info->freq,
794ed2a4a79SKővágó, Zoltán           info->nchannels);
7951d14ffa9Sbellard }
7961d14ffa9Sbellard #endif
7971d14ffa9Sbellard 
7981d14ffa9Sbellard #define DAC
7991d14ffa9Sbellard #include "audio_template.h"
8001d14ffa9Sbellard #undef DAC
8011d14ffa9Sbellard #include "audio_template.h"
8021d14ffa9Sbellard 
803713a98f8Smalc /*
804713a98f8Smalc  * Timer
805713a98f8Smalc  */
audio_is_timer_needed(AudioState * s)806526fb058SKővágó, Zoltán static int audio_is_timer_needed(AudioState *s)
807713a98f8Smalc {
808713a98f8Smalc     HWVoiceIn *hwi = NULL;
809713a98f8Smalc     HWVoiceOut *hwo = NULL;
810713a98f8Smalc 
811526fb058SKővágó, Zoltán     while ((hwo = audio_pcm_hw_find_any_enabled_out(s, hwo))) {
8126c6886bdSZhang Han         if (!hwo->poll_mode) {
8136c6886bdSZhang Han             return 1;
8146c6886bdSZhang Han         }
815713a98f8Smalc     }
816526fb058SKővágó, Zoltán     while ((hwi = audio_pcm_hw_find_any_enabled_in(s, hwi))) {
8176c6886bdSZhang Han         if (!hwi->poll_mode) {
8186c6886bdSZhang Han             return 1;
8196c6886bdSZhang Han         }
820713a98f8Smalc     }
821713a98f8Smalc     return 0;
822713a98f8Smalc }
823713a98f8Smalc 
audio_reset_timer(AudioState * s)82439deb1e4Smalc static void audio_reset_timer (AudioState *s)
825713a98f8Smalc {
826526fb058SKővágó, Zoltán     if (audio_is_timer_needed(s)) {
8271ffc2665SPavel Dovgalyuk         timer_mod_anticipate_ns(s->ts,
82871830221SKővágó, Zoltán             qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + s->period_ticks);
829526fb058SKővágó, Zoltán         if (!s->timer_running) {
830526fb058SKővágó, Zoltán             s->timer_running = true;
831526fb058SKővágó, Zoltán             s->timer_last = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
83271830221SKővágó, Zoltán             trace_audio_timer_start(s->period_ticks / SCALE_MS);
833713a98f8Smalc         }
8341a961e78SGerd Hoffmann     } else {
835bc72ad67SAlex Bligh         timer_del(s->ts);
836526fb058SKővágó, Zoltán         if (s->timer_running) {
837526fb058SKővágó, Zoltán             s->timer_running = false;
8381a961e78SGerd Hoffmann             trace_audio_timer_stop();
8391a961e78SGerd Hoffmann         }
840713a98f8Smalc     }
841713a98f8Smalc }
842713a98f8Smalc 
audio_timer(void * opaque)84339deb1e4Smalc static void audio_timer (void *opaque)
84439deb1e4Smalc {
8451a961e78SGerd Hoffmann     int64_t now, diff;
84671830221SKővágó, Zoltán     AudioState *s = opaque;
8471a961e78SGerd Hoffmann 
8481a961e78SGerd Hoffmann     now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
849526fb058SKővágó, Zoltán     diff = now - s->timer_last;
85071830221SKővágó, Zoltán     if (diff > s->period_ticks * 3 / 2) {
8511a961e78SGerd Hoffmann         trace_audio_timer_delayed(diff / SCALE_MS);
8521a961e78SGerd Hoffmann     }
853526fb058SKővágó, Zoltán     s->timer_last = now;
8541a961e78SGerd Hoffmann 
85518e2c177SKővágó, Zoltán     audio_run(s, "timer");
85671830221SKővágó, Zoltán     audio_reset_timer(s);
85739deb1e4Smalc }
85839deb1e4Smalc 
859713a98f8Smalc /*
860713a98f8Smalc  * Public API
861713a98f8Smalc  */
AUD_write(SWVoiceOut * sw,void * buf,size_t size)8627520462bSKővágó, Zoltán size_t AUD_write(SWVoiceOut *sw, void *buf, size_t size)
86385571bc7Sbellard {
8641930616bSKővágó, Zoltán     HWVoiceOut *hw;
8651930616bSKővágó, Zoltán 
8661d14ffa9Sbellard     if (!sw) {
8671d14ffa9Sbellard         /* XXX: Consider options */
8681d14ffa9Sbellard         return size;
8691d14ffa9Sbellard     }
8701930616bSKővágó, Zoltán     hw = sw->hw;
8711d14ffa9Sbellard 
8721930616bSKővágó, Zoltán     if (!hw->enabled) {
873c0fe3827Sbellard         dolog ("Writing to disabled voice %s\n", SW_NAME (sw));
8741d14ffa9Sbellard         return 0;
8751d14ffa9Sbellard     }
8761d14ffa9Sbellard 
8771930616bSKővágó, Zoltán     if (audio_get_pdo_out(hw->s->dev)->mixing_engine) {
8781d793fecSKővágó, Zoltán         return audio_pcm_sw_write(sw, buf, size);
8791930616bSKővágó, Zoltán     } else {
8801930616bSKővágó, Zoltán         return hw->pcm_ops->write(hw, buf, size);
8811930616bSKővágó, Zoltán     }
88285571bc7Sbellard }
88385571bc7Sbellard 
AUD_read(SWVoiceIn * sw,void * buf,size_t size)8847520462bSKővágó, Zoltán size_t AUD_read(SWVoiceIn *sw, void *buf, size_t size)
88585571bc7Sbellard {
8861930616bSKővágó, Zoltán     HWVoiceIn *hw;
8871930616bSKővágó, Zoltán 
8881d14ffa9Sbellard     if (!sw) {
8891d14ffa9Sbellard         /* XXX: Consider options */
8901d14ffa9Sbellard         return size;
89185571bc7Sbellard     }
8921930616bSKővágó, Zoltán     hw = sw->hw;
89385571bc7Sbellard 
8941930616bSKővágó, Zoltán     if (!hw->enabled) {
895c0fe3827Sbellard         dolog ("Reading from disabled voice %s\n", SW_NAME (sw));
8961d14ffa9Sbellard         return 0;
89785571bc7Sbellard     }
89885571bc7Sbellard 
8991930616bSKővágó, Zoltán     if (audio_get_pdo_in(hw->s->dev)->mixing_engine) {
9001d793fecSKővágó, Zoltán         return audio_pcm_sw_read(sw, buf, size);
9011930616bSKővágó, Zoltán     } else {
9021930616bSKővágó, Zoltán         return hw->pcm_ops->read(hw, buf, size);
9031930616bSKővágó, Zoltán     }
90485571bc7Sbellard }
90585571bc7Sbellard 
AUD_get_buffer_size_out(SWVoiceOut * sw)9061d14ffa9Sbellard int AUD_get_buffer_size_out(SWVoiceOut *sw)
90785571bc7Sbellard {
90869ac0786SVolker Rümelin     return sw->hw->samples * sw->hw->info.bytes_per_frame;
90985571bc7Sbellard }
91085571bc7Sbellard 
AUD_set_active_out(SWVoiceOut * sw,int on)9111d14ffa9Sbellard void AUD_set_active_out (SWVoiceOut *sw, int on)
91285571bc7Sbellard {
9131d14ffa9Sbellard     HWVoiceOut *hw;
9141d14ffa9Sbellard 
9151d14ffa9Sbellard     if (!sw) {
91685571bc7Sbellard         return;
91785571bc7Sbellard     }
91885571bc7Sbellard 
91985571bc7Sbellard     hw = sw->hw;
92085571bc7Sbellard     if (sw->active != on) {
921526fb058SKővágó, Zoltán         AudioState *s = sw->s;
9221d14ffa9Sbellard         SWVoiceOut *temp_sw;
923ec36b695Sbellard         SWVoiceCap *sc;
9241d14ffa9Sbellard 
92585571bc7Sbellard         if (on) {
92685571bc7Sbellard             hw->pending_disable = 0;
92785571bc7Sbellard             if (!hw->enabled) {
92885571bc7Sbellard                 hw->enabled = 1;
929978dd635Smalc                 if (s->vm_running) {
930571a8c52SKővágó, Zoltán                     if (hw->pcm_ops->enable_out) {
931571a8c52SKővágó, Zoltán                         hw->pcm_ops->enable_out(hw, true);
932571a8c52SKővágó, Zoltán                     }
93339deb1e4Smalc                     audio_reset_timer (s);
93485571bc7Sbellard                 }
93585571bc7Sbellard             }
9366c6886bdSZhang Han         } else {
9371d14ffa9Sbellard             if (hw->enabled) {
93885571bc7Sbellard                 int nb_active = 0;
9391d14ffa9Sbellard 
9401d14ffa9Sbellard                 for (temp_sw = hw->sw_head.lh_first; temp_sw;
9411d14ffa9Sbellard                      temp_sw = temp_sw->entries.le_next) {
9421d14ffa9Sbellard                     nb_active += temp_sw->active != 0;
9431d14ffa9Sbellard                 }
9441d14ffa9Sbellard 
9451d14ffa9Sbellard                 hw->pending_disable = nb_active == 1;
9461d14ffa9Sbellard             }
9471d14ffa9Sbellard         }
948ec36b695Sbellard 
949ec36b695Sbellard         for (sc = hw->cap_head.lh_first; sc; sc = sc->entries.le_next) {
950ec36b695Sbellard             sc->sw.active = hw->enabled;
9518ead62cfSbellard             if (hw->enabled) {
952ec36b695Sbellard                 audio_capture_maybe_changed (sc->cap, 1);
9538ead62cfSbellard             }
9548ead62cfSbellard         }
9551d14ffa9Sbellard         sw->active = on;
9561d14ffa9Sbellard     }
9571d14ffa9Sbellard }
9581d14ffa9Sbellard 
AUD_set_active_in(SWVoiceIn * sw,int on)9591d14ffa9Sbellard void AUD_set_active_in (SWVoiceIn *sw, int on)
9601d14ffa9Sbellard {
9611d14ffa9Sbellard     HWVoiceIn *hw;
9621d14ffa9Sbellard 
9631d14ffa9Sbellard     if (!sw) {
9641d14ffa9Sbellard         return;
9651d14ffa9Sbellard     }
9661d14ffa9Sbellard 
9671d14ffa9Sbellard     hw = sw->hw;
9681d14ffa9Sbellard     if (sw->active != on) {
969526fb058SKővágó, Zoltán         AudioState *s = sw->s;
9701d14ffa9Sbellard         SWVoiceIn *temp_sw;
9711d14ffa9Sbellard 
9721d14ffa9Sbellard         if (on) {
9731d14ffa9Sbellard             if (!hw->enabled) {
9741d14ffa9Sbellard                 hw->enabled = 1;
975978dd635Smalc                 if (s->vm_running) {
976571a8c52SKővágó, Zoltán                     if (hw->pcm_ops->enable_in) {
977571a8c52SKővágó, Zoltán                         hw->pcm_ops->enable_in(hw, true);
978571a8c52SKővágó, Zoltán                     }
97939deb1e4Smalc                     audio_reset_timer (s);
9801d14ffa9Sbellard                 }
981978dd635Smalc             }
9821d14ffa9Sbellard             sw->total_hw_samples_acquired = hw->total_samples_captured;
9836c6886bdSZhang Han         } else {
9841d14ffa9Sbellard             if (hw->enabled) {
9851d14ffa9Sbellard                 int nb_active = 0;
9861d14ffa9Sbellard 
9871d14ffa9Sbellard                 for (temp_sw = hw->sw_head.lh_first; temp_sw;
9881d14ffa9Sbellard                      temp_sw = temp_sw->entries.le_next) {
9891d14ffa9Sbellard                     nb_active += temp_sw->active != 0;
99085571bc7Sbellard                 }
99185571bc7Sbellard 
99285571bc7Sbellard                 if (nb_active == 1) {
9931d14ffa9Sbellard                     hw->enabled = 0;
994571a8c52SKővágó, Zoltán                     if (hw->pcm_ops->enable_in) {
995571a8c52SKővágó, Zoltán                         hw->pcm_ops->enable_in(hw, false);
996571a8c52SKővágó, Zoltán                     }
99785571bc7Sbellard                 }
99885571bc7Sbellard             }
99985571bc7Sbellard         }
100085571bc7Sbellard         sw->active = on;
100185571bc7Sbellard     }
100285571bc7Sbellard }
100385571bc7Sbellard 
audio_get_avail(SWVoiceIn * sw)10047520462bSKővágó, Zoltán static size_t audio_get_avail (SWVoiceIn *sw)
10051d14ffa9Sbellard {
10067520462bSKővágó, Zoltán     size_t live;
10071d14ffa9Sbellard 
10081d14ffa9Sbellard     if (!sw) {
10091d14ffa9Sbellard         return 0;
10101d14ffa9Sbellard     }
10111d14ffa9Sbellard 
10121d14ffa9Sbellard     live = sw->hw->total_samples_captured - sw->total_hw_samples_acquired;
10138dbd3d17SVolker Rümelin     if (audio_bug(__func__, live > sw->hw->conv_buf.size)) {
10148dbd3d17SVolker Rümelin         dolog("live=%zu sw->hw->conv_buf.size=%zu\n", live,
10158dbd3d17SVolker Rümelin               sw->hw->conv_buf.size);
101612f4abf6SVolker Rümelin         return 0;
10171d14ffa9Sbellard     }
10181d14ffa9Sbellard 
10191d14ffa9Sbellard     ldebug (
1020a9ea5678SVolker Rümelin         "%s: get_avail live %zu frontend frames %u\n",
1021c0fe3827Sbellard         SW_NAME (sw),
1022a9ea5678SVolker Rümelin         live, st_rate_frames_out(sw->rate, live)
10231d14ffa9Sbellard         );
10241d14ffa9Sbellard 
10250724c579SVolker Rümelin     return live;
10261d14ffa9Sbellard }
10271d14ffa9Sbellard 
audio_get_free(SWVoiceOut * sw)10287520462bSKővágó, Zoltán static size_t audio_get_free(SWVoiceOut *sw)
10291d14ffa9Sbellard {
10307520462bSKővágó, Zoltán     size_t live, dead;
10311d14ffa9Sbellard 
10321d14ffa9Sbellard     if (!sw) {
10331d14ffa9Sbellard         return 0;
10341d14ffa9Sbellard     }
10351d14ffa9Sbellard 
10361d14ffa9Sbellard     live = sw->total_hw_samples_mixed;
10371d14ffa9Sbellard 
10388dbd3d17SVolker Rümelin     if (audio_bug(__func__, live > sw->hw->mix_buf.size)) {
10398dbd3d17SVolker Rümelin         dolog("live=%zu sw->hw->mix_buf.size=%zu\n", live,
10408dbd3d17SVolker Rümelin               sw->hw->mix_buf.size);
104112f4abf6SVolker Rümelin         return 0;
10421d14ffa9Sbellard     }
10431d14ffa9Sbellard 
10448dbd3d17SVolker Rümelin     dead = sw->hw->mix_buf.size - live;
10451d14ffa9Sbellard 
10461d14ffa9Sbellard #ifdef DEBUG_OUT
10471a01df3dSVolker Rümelin     dolog("%s: get_free live %zu dead %zu frontend frames %u\n",
10481a01df3dSVolker Rümelin           SW_NAME(sw), live, dead, st_rate_frames_in(sw->rate, dead));
104985571bc7Sbellard #endif
10501d14ffa9Sbellard 
10519833438eSVolker Rümelin     return dead;
10521d14ffa9Sbellard }
10531d14ffa9Sbellard 
audio_capture_mix_and_clear(HWVoiceOut * hw,size_t rpos,size_t samples)10547520462bSKővágó, Zoltán static void audio_capture_mix_and_clear(HWVoiceOut *hw, size_t rpos,
10557520462bSKővágó, Zoltán                                         size_t samples)
10568ead62cfSbellard {
10577520462bSKővágó, Zoltán     size_t n;
10588ead62cfSbellard 
10598ead62cfSbellard     if (hw->enabled) {
1060ec36b695Sbellard         SWVoiceCap *sc;
10618ead62cfSbellard 
1062ec36b695Sbellard         for (sc = hw->cap_head.lh_first; sc; sc = sc->entries.le_next) {
1063ec36b695Sbellard             SWVoiceOut *sw = &sc->sw;
1064671cca35SVolker Rümelin             size_t rpos2 = rpos;
10658ead62cfSbellard 
10668ead62cfSbellard             n = samples;
10678ead62cfSbellard             while (n) {
10688dbd3d17SVolker Rümelin                 size_t till_end_of_hw = hw->mix_buf.size - rpos2;
1069671cca35SVolker Rümelin                 size_t to_read = MIN(till_end_of_hw, n);
1070671cca35SVolker Rümelin                 size_t live, frames_in, frames_out;
10718ead62cfSbellard 
10722c3f9a0aSVolker Rümelin                 sw->resample_buf.buffer = hw->mix_buf.buffer + rpos2;
1073671cca35SVolker Rümelin                 sw->resample_buf.size = to_read;
1074671cca35SVolker Rümelin                 live = sw->total_hw_samples_mixed;
1075671cca35SVolker Rümelin 
1076671cca35SVolker Rümelin                 audio_pcm_sw_resample_out(sw,
1077671cca35SVolker Rümelin                                           to_read, sw->hw->mix_buf.size - live,
1078671cca35SVolker Rümelin                                           &frames_in, &frames_out);
1079671cca35SVolker Rümelin 
1080671cca35SVolker Rümelin                 sw->total_hw_samples_mixed += frames_out;
1081671cca35SVolker Rümelin                 sw->empty = sw->total_hw_samples_mixed == 0;
1082671cca35SVolker Rümelin 
1083671cca35SVolker Rümelin                 if (to_read - frames_in) {
1084671cca35SVolker Rümelin                     dolog("Could not mix %zu frames into a capture "
10857520462bSKővágó, Zoltán                           "buffer, mixed %zu\n",
1086671cca35SVolker Rümelin                           to_read, frames_in);
10878ead62cfSbellard                     break;
10888ead62cfSbellard                 }
1089671cca35SVolker Rümelin                 n -= to_read;
1090671cca35SVolker Rümelin                 rpos2 = (rpos2 + to_read) % hw->mix_buf.size;
10918ead62cfSbellard             }
10928ead62cfSbellard         }
10938ead62cfSbellard     }
10948ead62cfSbellard 
10958dbd3d17SVolker Rümelin     n = MIN(samples, hw->mix_buf.size - rpos);
10968dbd3d17SVolker Rümelin     mixeng_clear(hw->mix_buf.buffer + rpos, n);
10978dbd3d17SVolker Rümelin     mixeng_clear(hw->mix_buf.buffer, samples - n);
10988ead62cfSbellard }
10998ead62cfSbellard 
audio_pcm_hw_run_out(HWVoiceOut * hw,size_t live)1100ff095e52SKővágó, Zoltán static size_t audio_pcm_hw_run_out(HWVoiceOut *hw, size_t live)
1101ff095e52SKővágó, Zoltán {
1102ff095e52SKővágó, Zoltán     size_t clipped = 0;
1103ff095e52SKővágó, Zoltán 
1104ff095e52SKővágó, Zoltán     while (live) {
1105aec6d0dcSVolker Rümelin         size_t size = live * hw->info.bytes_per_frame;
1106aec6d0dcSVolker Rümelin         size_t decr, proc;
1107ff095e52SKővágó, Zoltán         void *buf = hw->pcm_ops->get_buffer_out(hw, &size);
11084c3356f9SVolker Rümelin 
11094c3356f9SVolker Rümelin         if (size == 0) {
1110fb35c2ceSKővágó, Zoltán             break;
1111ff095e52SKővágó, Zoltán         }
1112ff095e52SKővágó, Zoltán 
11132b9cce8cSKővágó, Zoltán         decr = MIN(size / hw->info.bytes_per_frame, live);
11144c3356f9SVolker Rümelin         if (buf) {
11153f5bbfc2SKővágó, Zoltán             audio_pcm_hw_clip_out(hw, buf, decr);
11164c3356f9SVolker Rümelin         }
11172b9cce8cSKővágó, Zoltán         proc = hw->pcm_ops->put_buffer_out(hw, buf,
11182b9cce8cSKővágó, Zoltán                                            decr * hw->info.bytes_per_frame) /
11192b9cce8cSKővágó, Zoltán             hw->info.bytes_per_frame;
1120ff095e52SKővágó, Zoltán 
1121ff095e52SKővágó, Zoltán         live -= proc;
1122ff095e52SKővágó, Zoltán         clipped += proc;
11238dbd3d17SVolker Rümelin         hw->mix_buf.pos = (hw->mix_buf.pos + proc) % hw->mix_buf.size;
1124ff095e52SKővágó, Zoltán 
1125ff095e52SKővágó, Zoltán         if (proc == 0 || proc < decr) {
1126ff095e52SKővágó, Zoltán             break;
1127ff095e52SKővágó, Zoltán         }
1128ff095e52SKővágó, Zoltán     }
1129ff095e52SKővágó, Zoltán 
1130fdc8c5f4SVolker Rümelin     if (hw->pcm_ops->run_buffer_out) {
1131fdc8c5f4SVolker Rümelin         hw->pcm_ops->run_buffer_out(hw);
1132fdc8c5f4SVolker Rümelin     }
1133fdc8c5f4SVolker Rümelin 
1134ff095e52SKővágó, Zoltán     return clipped;
1135ff095e52SKővágó, Zoltán }
1136ff095e52SKővágó, Zoltán 
audio_run_out(AudioState * s)1137c0fe3827Sbellard static void audio_run_out (AudioState *s)
11381d14ffa9Sbellard {
11391d14ffa9Sbellard     HWVoiceOut *hw = NULL;
11401d14ffa9Sbellard     SWVoiceOut *sw;
11411d14ffa9Sbellard 
11421930616bSKővágó, Zoltán     while ((hw = audio_pcm_hw_find_any_enabled_out(s, hw))) {
11434d31ff32SVolker Rümelin         size_t played, live, prev_rpos;
11444d31ff32SVolker Rümelin         size_t hw_free = audio_pcm_hw_get_free(hw);
11454d31ff32SVolker Rümelin         int nb_live;
11464d31ff32SVolker Rümelin 
11474d31ff32SVolker Rümelin         if (!audio_get_pdo_out(s->dev)->mixing_engine) {
11481930616bSKővágó, Zoltán             /* there is exactly 1 sw for each hw with no mixeng */
11491930616bSKővágó, Zoltán             sw = hw->sw_head.lh_first;
11501930616bSKővágó, Zoltán 
11511930616bSKővágó, Zoltán             if (hw->pending_disable) {
11521930616bSKővágó, Zoltán                 hw->enabled = 0;
11531930616bSKővágó, Zoltán                 hw->pending_disable = 0;
11541930616bSKővágó, Zoltán                 if (hw->pcm_ops->enable_out) {
11551930616bSKővágó, Zoltán                     hw->pcm_ops->enable_out(hw, false);
11561930616bSKővágó, Zoltán                 }
11571930616bSKővágó, Zoltán             }
11581930616bSKővágó, Zoltán 
11591930616bSKővágó, Zoltán             if (sw->active) {
11607099a6a2SVolker Rümelin                 sw->callback.fn(sw->callback.opaque,
11617099a6a2SVolker Rümelin                                 hw_free * sw->info.bytes_per_frame);
11621930616bSKővágó, Zoltán             }
11631930616bSKővágó, Zoltán 
1164dd052dbfSVolker Rümelin             if (hw->pcm_ops->run_buffer_out) {
1165dd052dbfSVolker Rümelin                 hw->pcm_ops->run_buffer_out(hw);
1166dd052dbfSVolker Rümelin             }
1167dd052dbfSVolker Rümelin 
11684d31ff32SVolker Rümelin             continue;
11694d31ff32SVolker Rümelin         }
11701d14ffa9Sbellard 
1171a806f959SVolker Rümelin         for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) {
1172a806f959SVolker Rümelin             if (sw->active) {
11739833438eSVolker Rümelin                 size_t sw_free = audio_get_free(sw);
11749833438eSVolker Rümelin                 size_t free;
11759833438eSVolker Rümelin 
11769833438eSVolker Rümelin                 if (hw_free > sw->total_hw_samples_mixed) {
11771a01df3dSVolker Rümelin                     free = st_rate_frames_in(sw->rate,
11789833438eSVolker Rümelin                         MIN(sw_free, hw_free - sw->total_hw_samples_mixed));
11799833438eSVolker Rümelin                 } else {
11809833438eSVolker Rümelin                     free = 0;
11819833438eSVolker Rümelin                 }
1182e1e6a6fcSVolker Rümelin                 if (free > sw->resample_buf.pos) {
1183e1e6a6fcSVolker Rümelin                     free = MIN(free, sw->resample_buf.size)
1184e1e6a6fcSVolker Rümelin                            - sw->resample_buf.pos;
1185c4e59264SVolker Rümelin                     sw->callback.fn(sw->callback.opaque,
1186c4e59264SVolker Rümelin                                     free * sw->info.bytes_per_frame);
1187a806f959SVolker Rümelin                 }
1188a806f959SVolker Rümelin             }
1189a806f959SVolker Rümelin         }
1190a806f959SVolker Rümelin 
1191bdff253cSmalc         live = audio_pcm_hw_get_live_out (hw, &nb_live);
11921d14ffa9Sbellard         if (!nb_live) {
11931d14ffa9Sbellard             live = 0;
11941d14ffa9Sbellard         }
1195c0fe3827Sbellard 
11968dbd3d17SVolker Rümelin         if (audio_bug(__func__, live > hw->mix_buf.size)) {
11978dbd3d17SVolker Rümelin             dolog("live=%zu hw->mix_buf.size=%zu\n", live, hw->mix_buf.size);
119812f4abf6SVolker Rümelin             continue;
11991d14ffa9Sbellard         }
12001d14ffa9Sbellard 
12011d14ffa9Sbellard         if (hw->pending_disable && !nb_live) {
1202ec36b695Sbellard             SWVoiceCap *sc;
12031d14ffa9Sbellard #ifdef DEBUG_OUT
12041d14ffa9Sbellard             dolog ("Disabling voice\n");
120585571bc7Sbellard #endif
12061d14ffa9Sbellard             hw->enabled = 0;
12071d14ffa9Sbellard             hw->pending_disable = 0;
1208571a8c52SKővágó, Zoltán             if (hw->pcm_ops->enable_out) {
1209571a8c52SKővágó, Zoltán                 hw->pcm_ops->enable_out(hw, false);
1210571a8c52SKővágó, Zoltán             }
1211ec36b695Sbellard             for (sc = hw->cap_head.lh_first; sc; sc = sc->entries.le_next) {
1212ec36b695Sbellard                 sc->sw.active = 0;
1213ec36b695Sbellard                 audio_recalc_and_notify_capture (sc->cap);
12148ead62cfSbellard             }
12151d14ffa9Sbellard             continue;
12161d14ffa9Sbellard         }
12171d14ffa9Sbellard 
12181d14ffa9Sbellard         if (!live) {
1219a8a98cfdSVolker Rümelin             if (hw->pcm_ops->run_buffer_out) {
1220a8a98cfdSVolker Rümelin                 hw->pcm_ops->run_buffer_out(hw);
1221a8a98cfdSVolker Rümelin             }
12221d14ffa9Sbellard             continue;
12231d14ffa9Sbellard         }
12241d14ffa9Sbellard 
12258dbd3d17SVolker Rümelin         prev_rpos = hw->mix_buf.pos;
1226ff095e52SKővágó, Zoltán         played = audio_pcm_hw_run_out(hw, live);
12273d4d16f4SPavel Dovgalyuk         replay_audio_out(&played);
12288dbd3d17SVolker Rümelin         if (audio_bug(__func__, hw->mix_buf.pos >= hw->mix_buf.size)) {
12298dbd3d17SVolker Rümelin             dolog("hw->mix_buf.pos=%zu hw->mix_buf.size=%zu played=%zu\n",
12308dbd3d17SVolker Rümelin                   hw->mix_buf.pos, hw->mix_buf.size, played);
12318dbd3d17SVolker Rümelin             hw->mix_buf.pos = 0;
12321d14ffa9Sbellard         }
12331d14ffa9Sbellard 
12341d14ffa9Sbellard #ifdef DEBUG_OUT
12357520462bSKővágó, Zoltán         dolog("played=%zu\n", played);
123685571bc7Sbellard #endif
12371d14ffa9Sbellard 
12381d14ffa9Sbellard         if (played) {
12391d14ffa9Sbellard             hw->ts_helper += played;
12408ead62cfSbellard             audio_capture_mix_and_clear (hw, prev_rpos, played);
12411d14ffa9Sbellard         }
12421d14ffa9Sbellard 
12431d14ffa9Sbellard         for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) {
12441d14ffa9Sbellard             if (!sw->active && sw->empty) {
12451d14ffa9Sbellard                 continue;
12461d14ffa9Sbellard             }
12471d14ffa9Sbellard 
1248470bcabdSAlistair Francis             if (audio_bug(__func__, played > sw->total_hw_samples_mixed)) {
12497520462bSKővágó, Zoltán                 dolog("played=%zu sw->total_hw_samples_mixed=%zu\n",
12501d14ffa9Sbellard                       played, sw->total_hw_samples_mixed);
125112f4abf6SVolker Rümelin                 played = sw->total_hw_samples_mixed;
12521d14ffa9Sbellard             }
12531d14ffa9Sbellard 
12541d14ffa9Sbellard             sw->total_hw_samples_mixed -= played;
12551d14ffa9Sbellard 
12561d14ffa9Sbellard             if (!sw->total_hw_samples_mixed) {
12571d14ffa9Sbellard                 sw->empty = 1;
12581d14ffa9Sbellard             }
12591d14ffa9Sbellard         }
12601d14ffa9Sbellard     }
12611d14ffa9Sbellard }
12621d14ffa9Sbellard 
audio_pcm_hw_run_in(HWVoiceIn * hw,size_t samples)1263ff095e52SKővágó, Zoltán static size_t audio_pcm_hw_run_in(HWVoiceIn *hw, size_t samples)
1264ff095e52SKővágó, Zoltán {
1265ff095e52SKővágó, Zoltán     size_t conv = 0;
1266ff095e52SKővágó, Zoltán 
1267a2893c83SVolker Rümelin     if (hw->pcm_ops->run_buffer_in) {
1268a2893c83SVolker Rümelin         hw->pcm_ops->run_buffer_in(hw);
1269a2893c83SVolker Rümelin     }
1270a2893c83SVolker Rümelin 
1271ff095e52SKővágó, Zoltán     while (samples) {
1272ff095e52SKővágó, Zoltán         size_t proc;
12732b9cce8cSKővágó, Zoltán         size_t size = samples * hw->info.bytes_per_frame;
1274ff095e52SKővágó, Zoltán         void *buf = hw->pcm_ops->get_buffer_in(hw, &size);
1275ff095e52SKővágó, Zoltán 
12762b9cce8cSKővágó, Zoltán         assert(size % hw->info.bytes_per_frame == 0);
1277ff095e52SKővágó, Zoltán         if (size == 0) {
1278ff095e52SKővágó, Zoltán             break;
1279ff095e52SKővágó, Zoltán         }
1280ff095e52SKővágó, Zoltán 
1281251f1549SVolker Rümelin         proc = audio_pcm_hw_conv_in(hw, buf, size / hw->info.bytes_per_frame);
1282ff095e52SKővágó, Zoltán 
1283ff095e52SKővágó, Zoltán         samples -= proc;
1284ff095e52SKővágó, Zoltán         conv += proc;
12852b9cce8cSKővágó, Zoltán         hw->pcm_ops->put_buffer_in(hw, buf, proc * hw->info.bytes_per_frame);
1286ff095e52SKővágó, Zoltán     }
1287ff095e52SKővágó, Zoltán 
1288ff095e52SKővágó, Zoltán     return conv;
1289ff095e52SKővágó, Zoltán }
1290ff095e52SKővágó, Zoltán 
audio_run_in(AudioState * s)1291c0fe3827Sbellard static void audio_run_in (AudioState *s)
12921d14ffa9Sbellard {
12931d14ffa9Sbellard     HWVoiceIn *hw = NULL;
12941d14ffa9Sbellard 
12951930616bSKővágó, Zoltán     if (!audio_get_pdo_in(s->dev)->mixing_engine) {
12961930616bSKővágó, Zoltán         while ((hw = audio_pcm_hw_find_any_enabled_in(s, hw))) {
12971930616bSKővágó, Zoltán             /* there is exactly 1 sw for each hw with no mixeng */
12981930616bSKővágó, Zoltán             SWVoiceIn *sw = hw->sw_head.lh_first;
12991930616bSKővágó, Zoltán             if (sw->active) {
13001930616bSKővágó, Zoltán                 sw->callback.fn(sw->callback.opaque, INT_MAX);
13011930616bSKővágó, Zoltán             }
13021930616bSKővágó, Zoltán         }
13031930616bSKővágó, Zoltán         return;
13041930616bSKővágó, Zoltán     }
13051930616bSKővágó, Zoltán 
1306526fb058SKővágó, Zoltán     while ((hw = audio_pcm_hw_find_any_enabled_in(s, hw))) {
13071d14ffa9Sbellard         SWVoiceIn *sw;
13087520462bSKővágó, Zoltán         size_t captured = 0, min;
13091d14ffa9Sbellard 
13103d4d16f4SPavel Dovgalyuk         if (replay_mode != REPLAY_MODE_PLAY) {
1311ff095e52SKővágó, Zoltán             captured = audio_pcm_hw_run_in(
13128dbd3d17SVolker Rümelin                 hw, hw->conv_buf.size - audio_pcm_hw_get_live_in(hw));
1313ff095e52SKővágó, Zoltán         }
13148dbd3d17SVolker Rümelin         replay_audio_in(&captured, hw->conv_buf.buffer, &hw->conv_buf.pos,
13158dbd3d17SVolker Rümelin                         hw->conv_buf.size);
13161d14ffa9Sbellard 
13171d14ffa9Sbellard         min = audio_pcm_hw_find_min_in (hw);
13181d14ffa9Sbellard         hw->total_samples_captured += captured - min;
13191d14ffa9Sbellard         hw->ts_helper += captured;
13201d14ffa9Sbellard 
13211d14ffa9Sbellard         for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) {
13221d14ffa9Sbellard             sw->total_hw_samples_acquired -= min;
13231d14ffa9Sbellard 
13241d14ffa9Sbellard             if (sw->active) {
13250724c579SVolker Rümelin                 size_t sw_avail = audio_get_avail(sw);
13267520462bSKővágó, Zoltán                 size_t avail;
13271d14ffa9Sbellard 
1328a9ea5678SVolker Rümelin                 avail = st_rate_frames_out(sw->rate, sw_avail);
13291d14ffa9Sbellard                 if (avail > 0) {
1330a9ea5678SVolker Rümelin                     avail = MIN(avail, sw->resample_buf.size);
13310724c579SVolker Rümelin                     sw->callback.fn(sw->callback.opaque,
13320724c579SVolker Rümelin                                     avail * sw->info.bytes_per_frame);
13331d14ffa9Sbellard                 }
13341d14ffa9Sbellard             }
13351d14ffa9Sbellard         }
13361d14ffa9Sbellard     }
13371d14ffa9Sbellard }
13381d14ffa9Sbellard 
audio_run_capture(AudioState * s)13398ead62cfSbellard static void audio_run_capture (AudioState *s)
13408ead62cfSbellard {
13418ead62cfSbellard     CaptureVoiceOut *cap;
13428ead62cfSbellard 
13438ead62cfSbellard     for (cap = s->cap_head.lh_first; cap; cap = cap->entries.le_next) {
13447520462bSKővágó, Zoltán         size_t live, rpos, captured;
13458ead62cfSbellard         HWVoiceOut *hw = &cap->hw;
13468ead62cfSbellard         SWVoiceOut *sw;
13478ead62cfSbellard 
1348bdff253cSmalc         captured = live = audio_pcm_hw_get_live_out (hw, NULL);
13498dbd3d17SVolker Rümelin         rpos = hw->mix_buf.pos;
13508ead62cfSbellard         while (live) {
13518dbd3d17SVolker Rümelin             size_t left = hw->mix_buf.size - rpos;
13527520462bSKővágó, Zoltán             size_t to_capture = MIN(live, left);
13531ea879e5Smalc             struct st_sample *src;
13548ead62cfSbellard             struct capture_callback *cb;
13558ead62cfSbellard 
13568dbd3d17SVolker Rümelin             src = hw->mix_buf.buffer + rpos;
13578ead62cfSbellard             hw->clip (cap->buf, src, to_capture);
13588ead62cfSbellard             mixeng_clear (src, to_capture);
13598ead62cfSbellard 
13608ead62cfSbellard             for (cb = cap->cb_head.lh_first; cb; cb = cb->entries.le_next) {
13618ead62cfSbellard                 cb->ops.capture (cb->opaque, cap->buf,
13622b9cce8cSKővágó, Zoltán                                  to_capture * hw->info.bytes_per_frame);
13638ead62cfSbellard             }
13648dbd3d17SVolker Rümelin             rpos = (rpos + to_capture) % hw->mix_buf.size;
13658ead62cfSbellard             live -= to_capture;
13668ead62cfSbellard         }
13678dbd3d17SVolker Rümelin         hw->mix_buf.pos = rpos;
13688ead62cfSbellard 
13698ead62cfSbellard         for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) {
13708ead62cfSbellard             if (!sw->active && sw->empty) {
13718ead62cfSbellard                 continue;
13728ead62cfSbellard             }
13738ead62cfSbellard 
1374470bcabdSAlistair Francis             if (audio_bug(__func__, captured > sw->total_hw_samples_mixed)) {
13757520462bSKővágó, Zoltán                 dolog("captured=%zu sw->total_hw_samples_mixed=%zu\n",
13768ead62cfSbellard                       captured, sw->total_hw_samples_mixed);
137712f4abf6SVolker Rümelin                 captured = sw->total_hw_samples_mixed;
13788ead62cfSbellard             }
13798ead62cfSbellard 
13808ead62cfSbellard             sw->total_hw_samples_mixed -= captured;
13818ead62cfSbellard             sw->empty = sw->total_hw_samples_mixed == 0;
13828ead62cfSbellard         }
13838ead62cfSbellard     }
13848ead62cfSbellard }
13858ead62cfSbellard 
audio_run(AudioState * s,const char * msg)138618e2c177SKővágó, Zoltán void audio_run(AudioState *s, const char *msg)
1387571ec3d6Sbellard {
1388571ec3d6Sbellard     audio_run_out(s);
1389571ec3d6Sbellard     audio_run_in(s);
13908ead62cfSbellard     audio_run_capture(s);
1391ecd97e95SKővágó, Zoltán 
1392713a98f8Smalc #ifdef DEBUG_POLL
1393713a98f8Smalc     {
1394713a98f8Smalc         static double prevtime;
1395713a98f8Smalc         double currtime;
1396713a98f8Smalc         struct timeval tv;
1397571ec3d6Sbellard 
1398713a98f8Smalc         if (gettimeofday (&tv, NULL)) {
1399713a98f8Smalc             perror ("audio_run: gettimeofday");
1400713a98f8Smalc             return;
1401713a98f8Smalc         }
1402713a98f8Smalc 
1403713a98f8Smalc         currtime = tv.tv_sec + tv.tv_usec * 1e-6;
1404713a98f8Smalc         dolog ("Elapsed since last %s: %f\n", msg, currtime - prevtime);
1405713a98f8Smalc         prevtime = currtime;
1406713a98f8Smalc     }
1407713a98f8Smalc #endif
1408571ec3d6Sbellard }
1409571ec3d6Sbellard 
audio_generic_run_buffer_in(HWVoiceIn * hw)1410a2893c83SVolker Rümelin void audio_generic_run_buffer_in(HWVoiceIn *hw)
1411ff095e52SKővágó, Zoltán {
1412ff095e52SKővágó, Zoltán     if (unlikely(!hw->buf_emul)) {
14131d8549adSVolker Rümelin         hw->size_emul = hw->samples * hw->info.bytes_per_frame;
14141d8549adSVolker Rümelin         hw->buf_emul = g_malloc(hw->size_emul);
1415ff095e52SKővágó, Zoltán         hw->pos_emul = hw->pending_emul = 0;
1416ff095e52SKővágó, Zoltán     }
1417ff095e52SKővágó, Zoltán 
1418ff095e52SKővágó, Zoltán     while (hw->pending_emul < hw->size_emul) {
1419ff095e52SKővágó, Zoltán         size_t read_len = MIN(hw->size_emul - hw->pos_emul,
1420ff095e52SKővágó, Zoltán                               hw->size_emul - hw->pending_emul);
1421ff095e52SKővágó, Zoltán         size_t read = hw->pcm_ops->read(hw, hw->buf_emul + hw->pos_emul,
1422ff095e52SKővágó, Zoltán                                         read_len);
1423ff095e52SKővágó, Zoltán         hw->pending_emul += read;
14247ffc90f3SVolker Rümelin         hw->pos_emul = (hw->pos_emul + read) % hw->size_emul;
1425ff095e52SKővágó, Zoltán         if (read < read_len) {
1426ff095e52SKővágó, Zoltán             break;
1427ff095e52SKővágó, Zoltán         }
1428ff095e52SKővágó, Zoltán     }
1429a2893c83SVolker Rümelin }
1430ff095e52SKővágó, Zoltán 
audio_generic_get_buffer_in(HWVoiceIn * hw,size_t * size)1431a2893c83SVolker Rümelin void *audio_generic_get_buffer_in(HWVoiceIn *hw, size_t *size)
1432a2893c83SVolker Rümelin {
143318404ff1SVolker Rümelin     size_t start;
1434a2893c83SVolker Rümelin 
143518404ff1SVolker Rümelin     start = audio_ring_posb(hw->pos_emul, hw->pending_emul, hw->size_emul);
143618404ff1SVolker Rümelin     assert(start < hw->size_emul);
1437ff095e52SKővágó, Zoltán 
1438599eac4eSVolker Rümelin     *size = MIN(*size, hw->pending_emul);
1439599eac4eSVolker Rümelin     *size = MIN(*size, hw->size_emul - start);
1440ff095e52SKővágó, Zoltán     return hw->buf_emul + start;
1441ff095e52SKővágó, Zoltán }
1442ff095e52SKővágó, Zoltán 
audio_generic_put_buffer_in(HWVoiceIn * hw,void * buf,size_t size)1443ff095e52SKővágó, Zoltán void audio_generic_put_buffer_in(HWVoiceIn *hw, void *buf, size_t size)
1444ff095e52SKővágó, Zoltán {
1445ff095e52SKővágó, Zoltán     assert(size <= hw->pending_emul);
1446ff095e52SKővágó, Zoltán     hw->pending_emul -= size;
1447ff095e52SKővágó, Zoltán }
1448ff095e52SKővágó, Zoltán 
audio_generic_buffer_get_free(HWVoiceOut * hw)14499833438eSVolker Rümelin size_t audio_generic_buffer_get_free(HWVoiceOut *hw)
14509833438eSVolker Rümelin {
14519833438eSVolker Rümelin     if (hw->buf_emul) {
14529833438eSVolker Rümelin         return hw->size_emul - hw->pending_emul;
14539833438eSVolker Rümelin     } else {
14549833438eSVolker Rümelin         return hw->samples * hw->info.bytes_per_frame;
14559833438eSVolker Rümelin     }
14569833438eSVolker Rümelin }
14579833438eSVolker Rümelin 
audio_generic_run_buffer_out(HWVoiceOut * hw)1458fdc8c5f4SVolker Rümelin void audio_generic_run_buffer_out(HWVoiceOut *hw)
1459fdc8c5f4SVolker Rümelin {
1460fdc8c5f4SVolker Rümelin     while (hw->pending_emul) {
146118404ff1SVolker Rümelin         size_t write_len, written, start;
1462fdc8c5f4SVolker Rümelin 
146318404ff1SVolker Rümelin         start = audio_ring_posb(hw->pos_emul, hw->pending_emul, hw->size_emul);
146418404ff1SVolker Rümelin         assert(start < hw->size_emul);
1465fdc8c5f4SVolker Rümelin 
1466fdc8c5f4SVolker Rümelin         write_len = MIN(hw->pending_emul, hw->size_emul - start);
1467fdc8c5f4SVolker Rümelin 
1468fdc8c5f4SVolker Rümelin         written = hw->pcm_ops->write(hw, hw->buf_emul + start, write_len);
1469fdc8c5f4SVolker Rümelin         hw->pending_emul -= written;
1470fdc8c5f4SVolker Rümelin 
1471fdc8c5f4SVolker Rümelin         if (written < write_len) {
1472fdc8c5f4SVolker Rümelin             break;
1473fdc8c5f4SVolker Rümelin         }
1474fdc8c5f4SVolker Rümelin     }
1475fdc8c5f4SVolker Rümelin }
1476fdc8c5f4SVolker Rümelin 
audio_generic_get_buffer_out(HWVoiceOut * hw,size_t * size)1477ff095e52SKővágó, Zoltán void *audio_generic_get_buffer_out(HWVoiceOut *hw, size_t *size)
1478ff095e52SKővágó, Zoltán {
1479ff095e52SKővágó, Zoltán     if (unlikely(!hw->buf_emul)) {
14801d8549adSVolker Rümelin         hw->size_emul = hw->samples * hw->info.bytes_per_frame;
14811d8549adSVolker Rümelin         hw->buf_emul = g_malloc(hw->size_emul);
1482ff095e52SKővágó, Zoltán         hw->pos_emul = hw->pending_emul = 0;
1483ff095e52SKővágó, Zoltán     }
1484ff095e52SKővágó, Zoltán 
1485ff095e52SKővágó, Zoltán     *size = MIN(hw->size_emul - hw->pending_emul,
1486ff095e52SKővágó, Zoltán                 hw->size_emul - hw->pos_emul);
1487ff095e52SKővágó, Zoltán     return hw->buf_emul + hw->pos_emul;
1488ff095e52SKővágó, Zoltán }
1489ff095e52SKővágó, Zoltán 
audio_generic_put_buffer_out(HWVoiceOut * hw,void * buf,size_t size)1490fdc8c5f4SVolker Rümelin size_t audio_generic_put_buffer_out(HWVoiceOut *hw, void *buf, size_t size)
1491ff095e52SKővágó, Zoltán {
1492ff095e52SKővágó, Zoltán     assert(buf == hw->buf_emul + hw->pos_emul &&
1493ff095e52SKővágó, Zoltán            size + hw->pending_emul <= hw->size_emul);
1494ff095e52SKővágó, Zoltán 
1495ff095e52SKővágó, Zoltán     hw->pending_emul += size;
1496ff095e52SKővágó, Zoltán     hw->pos_emul = (hw->pos_emul + size) % hw->size_emul;
1497ff095e52SKővágó, Zoltán 
1498ff095e52SKővágó, Zoltán     return size;
1499ff095e52SKővágó, Zoltán }
1500ff095e52SKővágó, Zoltán 
audio_generic_write(HWVoiceOut * hw,void * buf,size_t size)1501ff095e52SKővágó, Zoltán size_t audio_generic_write(HWVoiceOut *hw, void *buf, size_t size)
1502ff095e52SKővágó, Zoltán {
15032d882307SVolker Rümelin     size_t total = 0;
1504ff095e52SKővágó, Zoltán 
15059833438eSVolker Rümelin     if (hw->pcm_ops->buffer_get_free) {
15069833438eSVolker Rümelin         size_t free = hw->pcm_ops->buffer_get_free(hw);
15079833438eSVolker Rümelin 
15089833438eSVolker Rümelin         size = MIN(size, free);
15099833438eSVolker Rümelin     }
15109833438eSVolker Rümelin 
15112d882307SVolker Rümelin     while (total < size) {
15122d882307SVolker Rümelin         size_t dst_size = size - total;
15132d882307SVolker Rümelin         size_t copy_size, proc;
15142d882307SVolker Rümelin         void *dst = hw->pcm_ops->get_buffer_out(hw, &dst_size);
15152d882307SVolker Rümelin 
15162d882307SVolker Rümelin         if (dst_size == 0) {
15172d882307SVolker Rümelin             break;
15182d882307SVolker Rümelin         }
15192d882307SVolker Rümelin 
15202d882307SVolker Rümelin         copy_size = MIN(size - total, dst_size);
15212d882307SVolker Rümelin         if (dst) {
15222d882307SVolker Rümelin             memcpy(dst, (char *)buf + total, copy_size);
15232d882307SVolker Rümelin         }
15242d882307SVolker Rümelin         proc = hw->pcm_ops->put_buffer_out(hw, dst, copy_size);
15252d882307SVolker Rümelin         total += proc;
15262d882307SVolker Rümelin 
15272d882307SVolker Rümelin         if (proc == 0 || proc < copy_size) {
15282d882307SVolker Rümelin             break;
15292d882307SVolker Rümelin         }
15302d882307SVolker Rümelin     }
15312d882307SVolker Rümelin 
15322d882307SVolker Rümelin     return total;
1533ff095e52SKővágó, Zoltán }
1534ff095e52SKővágó, Zoltán 
audio_generic_read(HWVoiceIn * hw,void * buf,size_t size)1535ff095e52SKővágó, Zoltán size_t audio_generic_read(HWVoiceIn *hw, void *buf, size_t size)
1536ff095e52SKővágó, Zoltán {
1537b9896dc5SVolker Rümelin     size_t total = 0;
1538ff095e52SKővágó, Zoltán 
1539a2893c83SVolker Rümelin     if (hw->pcm_ops->run_buffer_in) {
1540a2893c83SVolker Rümelin         hw->pcm_ops->run_buffer_in(hw);
1541a2893c83SVolker Rümelin     }
1542a2893c83SVolker Rümelin 
1543b9896dc5SVolker Rümelin     while (total < size) {
1544b9896dc5SVolker Rümelin         size_t src_size = size - total;
1545b9896dc5SVolker Rümelin         void *src = hw->pcm_ops->get_buffer_in(hw, &src_size);
15468d1439b6SVolker Rümelin 
1547b9896dc5SVolker Rümelin         if (src_size == 0) {
1548b9896dc5SVolker Rümelin             break;
1549b9896dc5SVolker Rümelin         }
1550b9896dc5SVolker Rümelin 
1551b9896dc5SVolker Rümelin         memcpy((char *)buf + total, src, src_size);
1552b9896dc5SVolker Rümelin         hw->pcm_ops->put_buffer_in(hw, src, src_size);
1553b9896dc5SVolker Rümelin         total += src_size;
1554b9896dc5SVolker Rümelin     }
1555b9896dc5SVolker Rümelin 
1556b9896dc5SVolker Rümelin     return total;
1557ff095e52SKővágó, Zoltán }
1558ff095e52SKővágó, Zoltán 
audio_driver_init(AudioState * s,struct audio_driver * drv,Audiodev * dev,Error ** errp)155971830221SKővágó, Zoltán static int audio_driver_init(AudioState *s, struct audio_driver *drv,
1560cb94ff5fSMartin Kletzander                              Audiodev *dev, Error **errp)
15611d14ffa9Sbellard {
1562f6061733SPaolo Bonzini     Error *local_err = NULL;
1563f6061733SPaolo Bonzini 
1564f6061733SPaolo Bonzini     s->drv_opaque = drv->init(dev, &local_err);
15651d14ffa9Sbellard 
1566c0fe3827Sbellard     if (s->drv_opaque) {
1567ff095e52SKővágó, Zoltán         if (!drv->pcm_ops->get_buffer_in) {
1568ff095e52SKővágó, Zoltán             drv->pcm_ops->get_buffer_in = audio_generic_get_buffer_in;
1569ff095e52SKővágó, Zoltán             drv->pcm_ops->put_buffer_in = audio_generic_put_buffer_in;
1570ff095e52SKővágó, Zoltán         }
1571ff095e52SKővágó, Zoltán         if (!drv->pcm_ops->get_buffer_out) {
1572ff095e52SKővágó, Zoltán             drv->pcm_ops->get_buffer_out = audio_generic_get_buffer_out;
1573ff095e52SKővágó, Zoltán             drv->pcm_ops->put_buffer_out = audio_generic_put_buffer_out;
1574ff095e52SKővágó, Zoltán         }
1575ff095e52SKővágó, Zoltán 
15765c63d141SPaolo Bonzini         audio_init_nb_voices_out(s, drv, 1);
15775c63d141SPaolo Bonzini         audio_init_nb_voices_in(s, drv, 0);
1578c0fe3827Sbellard         s->drv = drv;
157985571bc7Sbellard         return 0;
15806c6886bdSZhang Han     } else {
1581cb94ff5fSMartin Kletzander         if (local_err) {
1582cb94ff5fSMartin Kletzander             error_propagate(errp, local_err);
1583f6061733SPaolo Bonzini         } else {
1584cb94ff5fSMartin Kletzander             error_setg(errp, "Could not init `%s' audio driver", drv->name);
1585fb37ce92SGerd Hoffmann         }
15861d14ffa9Sbellard         return -1;
15871d14ffa9Sbellard     }
158885571bc7Sbellard }
158985571bc7Sbellard 
audio_vm_change_state_handler(void * opaque,bool running,RunState state)1590538f0497SPhilippe Mathieu-Daudé static void audio_vm_change_state_handler (void *opaque, bool running,
15911dfb4dd9SLuiz Capitulino                                            RunState state)
159285571bc7Sbellard {
1593c0fe3827Sbellard     AudioState *s = opaque;
15941d14ffa9Sbellard     HWVoiceOut *hwo = NULL;
15951d14ffa9Sbellard     HWVoiceIn *hwi = NULL;
159685571bc7Sbellard 
1597978dd635Smalc     s->vm_running = running;
1598526fb058SKővágó, Zoltán     while ((hwo = audio_pcm_hw_find_any_enabled_out(s, hwo))) {
1599571a8c52SKővágó, Zoltán         if (hwo->pcm_ops->enable_out) {
1600571a8c52SKővágó, Zoltán             hwo->pcm_ops->enable_out(hwo, running);
1601571a8c52SKővágó, Zoltán         }
16021d14ffa9Sbellard     }
16031d14ffa9Sbellard 
1604526fb058SKővágó, Zoltán     while ((hwi = audio_pcm_hw_find_any_enabled_in(s, hwi))) {
1605571a8c52SKővágó, Zoltán         if (hwi->pcm_ops->enable_in) {
1606571a8c52SKővágó, Zoltán             hwi->pcm_ops->enable_in(hwi, running);
1607571a8c52SKővágó, Zoltán         }
16081d14ffa9Sbellard     }
160939deb1e4Smalc     audio_reset_timer (s);
161085571bc7Sbellard }
161185571bc7Sbellard 
free_audio_state(AudioState * s)1612ecd97e95SKővágó, Zoltán static void free_audio_state(AudioState *s)
161385571bc7Sbellard {
1614a384c205SMarc-André Lureau     HWVoiceOut *hwo, *hwon;
1615a384c205SMarc-André Lureau     HWVoiceIn *hwi, *hwin;
161685571bc7Sbellard 
1617526fb058SKővágó, Zoltán     QLIST_FOREACH_SAFE(hwo, &s->hw_head_out, entries, hwon) {
1618ec36b695Sbellard         SWVoiceCap *sc;
16198ead62cfSbellard 
1620571a8c52SKővágó, Zoltán         if (hwo->enabled && hwo->pcm_ops->enable_out) {
1621571a8c52SKővágó, Zoltán             hwo->pcm_ops->enable_out(hwo, false);
1622aeb29b64SJan Kiszka         }
16231d14ffa9Sbellard         hwo->pcm_ops->fini_out (hwo);
16248ead62cfSbellard 
1625ec36b695Sbellard         for (sc = hwo->cap_head.lh_first; sc; sc = sc->entries.le_next) {
1626ec36b695Sbellard             CaptureVoiceOut *cap = sc->cap;
1627ec36b695Sbellard             struct capture_callback *cb;
1628ec36b695Sbellard 
1629ec36b695Sbellard             for (cb = cap->cb_head.lh_first; cb; cb = cb->entries.le_next) {
1630ec36b695Sbellard                 cb->ops.destroy (cb->opaque);
1631ec36b695Sbellard             }
16328ead62cfSbellard         }
1633a384c205SMarc-André Lureau         QLIST_REMOVE(hwo, entries);
16341d14ffa9Sbellard     }
16351d14ffa9Sbellard 
1636526fb058SKővágó, Zoltán     QLIST_FOREACH_SAFE(hwi, &s->hw_head_in, entries, hwin) {
1637571a8c52SKővágó, Zoltán         if (hwi->enabled && hwi->pcm_ops->enable_in) {
1638571a8c52SKővágó, Zoltán             hwi->pcm_ops->enable_in(hwi, false);
1639aeb29b64SJan Kiszka         }
16401d14ffa9Sbellard         hwi->pcm_ops->fini_in (hwi);
1641a384c205SMarc-André Lureau         QLIST_REMOVE(hwi, entries);
164285571bc7Sbellard     }
1643c0fe3827Sbellard 
1644c0fe3827Sbellard     if (s->drv) {
1645c0fe3827Sbellard         s->drv->fini (s->drv_opaque);
1646a384c205SMarc-André Lureau         s->drv = NULL;
1647c0fe3827Sbellard     }
164871830221SKővágó, Zoltán 
164971830221SKővágó, Zoltán     if (s->dev) {
165071830221SKővágó, Zoltán         qapi_free_Audiodev(s->dev);
165171830221SKővágó, Zoltán         s->dev = NULL;
165271830221SKővágó, Zoltán     }
1653e76ba19aSKővágó, Zoltán 
1654e76ba19aSKővágó, Zoltán     if (s->ts) {
1655e76ba19aSKővágó, Zoltán         timer_free(s->ts);
1656e76ba19aSKővágó, Zoltán         s->ts = NULL;
1657e76ba19aSKővágó, Zoltán     }
1658e76ba19aSKővágó, Zoltán 
1659ecd97e95SKővágó, Zoltán     g_free(s);
1660ecd97e95SKővágó, Zoltán }
1661ecd97e95SKővágó, Zoltán 
audio_cleanup(void)1662ecd97e95SKővágó, Zoltán void audio_cleanup(void)
1663ecd97e95SKővágó, Zoltán {
166422f84d4fSPaolo Bonzini     default_audio_state = NULL;
1665ecd97e95SKővágó, Zoltán     while (!QTAILQ_EMPTY(&audio_states)) {
1666ecd97e95SKővágó, Zoltán         AudioState *s = QTAILQ_FIRST(&audio_states);
1667ecd97e95SKővágó, Zoltán         QTAILQ_REMOVE(&audio_states, s, list);
1668ecd97e95SKővágó, Zoltán         free_audio_state(s);
1669ecd97e95SKővágó, Zoltán     }
167085571bc7Sbellard }
167185571bc7Sbellard 
vmstate_audio_needed(void * opaque)1672da77adbaSDr. David Alan Gilbert static bool vmstate_audio_needed(void *opaque)
1673da77adbaSDr. David Alan Gilbert {
1674da77adbaSDr. David Alan Gilbert     /*
1675da77adbaSDr. David Alan Gilbert      * Never needed, this vmstate only exists in case
1676da77adbaSDr. David Alan Gilbert      * an old qemu sends it to us.
1677da77adbaSDr. David Alan Gilbert      */
1678da77adbaSDr. David Alan Gilbert     return false;
1679da77adbaSDr. David Alan Gilbert }
1680da77adbaSDr. David Alan Gilbert 
1681d959fce9SJuan Quintela static const VMStateDescription vmstate_audio = {
1682d959fce9SJuan Quintela     .name = "audio",
1683d959fce9SJuan Quintela     .version_id = 1,
1684d959fce9SJuan Quintela     .minimum_version_id = 1,
1685da77adbaSDr. David Alan Gilbert     .needed = vmstate_audio_needed,
1686d959fce9SJuan Quintela     .fields = (const VMStateField[]) {
1687d959fce9SJuan Quintela         VMSTATE_END_OF_LIST()
168885571bc7Sbellard     }
1689d959fce9SJuan Quintela };
169085571bc7Sbellard 
audio_create_default_audiodevs(void)1691c753bf47SPaolo Bonzini void audio_create_default_audiodevs(void)
169271830221SKővágó, Zoltán {
169369a80279SPaolo Bonzini     for (int i = 0; audio_prio_list[i]; i++) {
169469a80279SPaolo Bonzini         if (audio_driver_lookup(audio_prio_list[i])) {
169569a80279SPaolo Bonzini             QDict *dict = qdict_new();
169669a80279SPaolo Bonzini             Audiodev *dev = NULL;
169769a80279SPaolo Bonzini             Visitor *v;
169869a80279SPaolo Bonzini 
169969a80279SPaolo Bonzini             qdict_put_str(dict, "driver", audio_prio_list[i]);
170069a80279SPaolo Bonzini             qdict_put_str(dict, "id", "#default");
170169a80279SPaolo Bonzini 
170269a80279SPaolo Bonzini             v = qobject_input_visitor_new_keyval(QOBJECT(dict));
170369a80279SPaolo Bonzini             qobject_unref(dict);
170469a80279SPaolo Bonzini             visit_type_Audiodev(v, NULL, &dev, &error_fatal);
170569a80279SPaolo Bonzini             visit_free(v);
170669a80279SPaolo Bonzini 
17078f527a3cSPaolo Bonzini             audio_define_default(dev, &error_abort);
170869a80279SPaolo Bonzini         }
170969a80279SPaolo Bonzini     }
171071830221SKővágó, Zoltán }
171171830221SKővágó, Zoltán 
1712ecd97e95SKővágó, Zoltán /*
1713ecd97e95SKővágó, Zoltán  * if we have dev, this function was called because of an -audiodev argument =>
1714ecd97e95SKővágó, Zoltán  *   initialize a new state with it
1715ecd97e95SKővágó, Zoltán  * if dev == NULL => legacy implicit initialization, return the already created
1716ecd97e95SKővágó, Zoltán  *   state or create a new one
1717ecd97e95SKővágó, Zoltán  */
audio_init(Audiodev * dev,Error ** errp)1718cb94ff5fSMartin Kletzander static AudioState *audio_init(Audiodev *dev, Error **errp)
171985571bc7Sbellard {
1720ecd97e95SKővágó, Zoltán     static bool atexit_registered;
172185571bc7Sbellard     int done = 0;
1722e3299631SPaolo Bonzini     const char *drvname;
1723417f8c8eSPaolo Bonzini     VMChangeStateEntry *vmse;
1724ecd97e95SKővágó, Zoltán     AudioState *s;
1725d3893a39SGerd Hoffmann     struct audio_driver *driver;
1726f0c4555eSGerd Hoffmann 
1727e3299631SPaolo Bonzini     s = g_new0(AudioState, 1);
1728e3299631SPaolo Bonzini 
1729e3299631SPaolo Bonzini     QLIST_INIT (&s->hw_head_out);
1730e3299631SPaolo Bonzini     QLIST_INIT (&s->hw_head_in);
1731e3299631SPaolo Bonzini     QLIST_INIT (&s->cap_head);
1732e3299631SPaolo Bonzini     if (!atexit_registered) {
1733e3299631SPaolo Bonzini         atexit(audio_cleanup);
1734e3299631SPaolo Bonzini         atexit_registered = true;
1735e3299631SPaolo Bonzini     }
1736e3299631SPaolo Bonzini 
1737e3299631SPaolo Bonzini     s->ts = timer_new_ns(QEMU_CLOCK_VIRTUAL, audio_timer, s);
1738e3299631SPaolo Bonzini 
173971830221SKővágó, Zoltán     if (dev) {
174071830221SKővágó, Zoltán         /* -audiodev option */
1741e3299631SPaolo Bonzini         s->dev = dev;
174271830221SKővágó, Zoltán         drvname = AudiodevDriver_str(dev->driver);
1743e3299631SPaolo Bonzini         driver = audio_driver_lookup(drvname);
1744e3299631SPaolo Bonzini         if (driver) {
1745cb94ff5fSMartin Kletzander             done = !audio_driver_init(s, driver, dev, errp);
1746e3299631SPaolo Bonzini         } else {
1747*5713d6ddSMichael Tokarev             error_setg(errp, "Unknown audio driver `%s'", drvname);
1748af2041edSKővágó, Zoltán         }
1749e3299631SPaolo Bonzini         if (!done) {
175069a80279SPaolo Bonzini             goto out;
1751e3299631SPaolo Bonzini         }
175271830221SKővágó, Zoltán     } else {
175322f84d4fSPaolo Bonzini         assert(!default_audio_state);
175469a80279SPaolo Bonzini         for (;;) {
175569a80279SPaolo Bonzini             AudiodevListEntry *e = QSIMPLEQ_FIRST(&default_audiodevs);
175669a80279SPaolo Bonzini             if (!e) {
1757cb94ff5fSMartin Kletzander                 error_setg(errp, "no default audio driver available");
175869a80279SPaolo Bonzini                 goto out;
175969a80279SPaolo Bonzini             }
176071830221SKővágó, Zoltán             s->dev = dev = e->dev;
1761e4b9d199SAkihiko Odaki             QSIMPLEQ_REMOVE_HEAD(&default_audiodevs, next);
1762e4b9d199SAkihiko Odaki             g_free(e);
176369a80279SPaolo Bonzini             drvname = AudiodevDriver_str(dev->driver);
176469a80279SPaolo Bonzini             driver = audio_driver_lookup(drvname);
1765cb94ff5fSMartin Kletzander             if (!audio_driver_init(s, driver, dev, NULL)) {
176671830221SKővágó, Zoltán                 break;
176785571bc7Sbellard             }
1768e4b9d199SAkihiko Odaki             qapi_free_Audiodev(dev);
1769e4b9d199SAkihiko Odaki             s->dev = NULL;
177085571bc7Sbellard         }
17711d14ffa9Sbellard     }
1772e3299631SPaolo Bonzini 
177371830221SKővágó, Zoltán     if (dev->timer_period <= 0) {
177471830221SKővágó, Zoltán         s->period_ticks = 1;
17750d9acba8SPaul Brook     } else {
177640ad46d3SVolker Rümelin         s->period_ticks = dev->timer_period * (int64_t)SCALE_US;
17771d14ffa9Sbellard     }
17781d14ffa9Sbellard 
1779417f8c8eSPaolo Bonzini     vmse = qemu_add_vm_change_state_handler (audio_vm_change_state_handler, s);
1780417f8c8eSPaolo Bonzini     if (!vmse) {
1781571ec3d6Sbellard         dolog ("warning: Could not register change state handler\n"
1782571ec3d6Sbellard                "(Audio can continue looping even after stopping the VM)\n");
1783571ec3d6Sbellard     }
1784c0fe3827Sbellard 
17850f957c53SMarc-André Lureau     QTAILQ_INSERT_TAIL(&audio_states, s, list);
178672cf2d4fSBlue Swirl     QLIST_INIT (&s->card_head);
1787a9500913SJuan Quintela     vmstate_register_any(NULL, &vmstate_audio, s);
1788ecd97e95SKővágó, Zoltán     return s;
178971830221SKővágó, Zoltán 
179069a80279SPaolo Bonzini out:
179169a80279SPaolo Bonzini     free_audio_state(s);
179269a80279SPaolo Bonzini     return NULL;
179385571bc7Sbellard }
17948ead62cfSbellard 
audio_get_default_audio_state(Error ** errp)179563a13c08SPaolo Bonzini AudioState *audio_get_default_audio_state(Error **errp)
17961a7dafceSmalc {
179722f84d4fSPaolo Bonzini     if (!default_audio_state) {
179822f84d4fSPaolo Bonzini         default_audio_state = audio_init(NULL, errp);
179922f84d4fSPaolo Bonzini         if (!default_audio_state) {
18009f8cf356SPaolo Bonzini             if (!QSIMPLEQ_EMPTY(&audiodevs)) {
18011ebdbff4SPaolo Bonzini                 error_append_hint(errp, "Perhaps you wanted to use -audio or set audiodev=%s?\n",
18029f8cf356SPaolo Bonzini                                   QSIMPLEQ_FIRST(&audiodevs)->dev->id);
18039f8cf356SPaolo Bonzini             }
180463a13c08SPaolo Bonzini         }
180563a13c08SPaolo Bonzini     }
180663a13c08SPaolo Bonzini 
180763a13c08SPaolo Bonzini     return default_audio_state;
180863a13c08SPaolo Bonzini }
180963a13c08SPaolo Bonzini 
AUD_register_card(const char * name,QEMUSoundCard * card,Error ** errp)181063a13c08SPaolo Bonzini bool AUD_register_card (const char *name, QEMUSoundCard *card, Error **errp)
181163a13c08SPaolo Bonzini {
181263a13c08SPaolo Bonzini     if (!card->state) {
181363a13c08SPaolo Bonzini         card->state = audio_get_default_audio_state(errp);
181463a13c08SPaolo Bonzini         if (!card->state) {
1815cb94ff5fSMartin Kletzander             return false;
1816cb94ff5fSMartin Kletzander         }
1817e3299631SPaolo Bonzini     }
1818ecd97e95SKővágó, Zoltán 
18197267c094SAnthony Liguori     card->name = g_strdup (name);
18201a7dafceSmalc     memset (&card->entries, 0, sizeof (card->entries));
1821ecd97e95SKővágó, Zoltán     QLIST_INSERT_HEAD(&card->state->card_head, card, entries);
1822cb94ff5fSMartin Kletzander 
1823cb94ff5fSMartin Kletzander     return true;
18241a7dafceSmalc }
18251a7dafceSmalc 
AUD_remove_card(QEMUSoundCard * card)18261a7dafceSmalc void AUD_remove_card (QEMUSoundCard *card)
18271a7dafceSmalc {
182872cf2d4fSBlue Swirl     QLIST_REMOVE (card, entries);
18297267c094SAnthony Liguori     g_free (card->name);
18301a7dafceSmalc }
18311a7dafceSmalc 
183233940dd3SVolker Rümelin static struct audio_pcm_ops capture_pcm_ops;
18331a7dafceSmalc 
AUD_add_capture(AudioState * s,struct audsettings * as,struct audio_capture_ops * ops,void * cb_opaque)1834ec36b695Sbellard CaptureVoiceOut *AUD_add_capture(
1835ecd97e95SKővágó, Zoltán     AudioState *s,
18361ea879e5Smalc     struct audsettings *as,
18378ead62cfSbellard     struct audio_capture_ops *ops,
18388ead62cfSbellard     void *cb_opaque
18398ead62cfSbellard     )
18408ead62cfSbellard {
18418ead62cfSbellard     CaptureVoiceOut *cap;
18428ead62cfSbellard     struct capture_callback *cb;
18438ead62cfSbellard 
1844ecd97e95SKővágó, Zoltán     if (!s) {
1845aaa6a6f9SMartin Kletzander         error_report("Capturing without setting an audiodev is not supported");
1846aaa6a6f9SMartin Kletzander         abort();
1847ecd97e95SKővágó, Zoltán     }
1848ecd97e95SKővágó, Zoltán 
18491930616bSKővágó, Zoltán     if (!audio_get_pdo_out(s->dev)->mixing_engine) {
18501930616bSKővágó, Zoltán         dolog("Can't capture with mixeng disabled\n");
18511930616bSKővágó, Zoltán         return NULL;
18521930616bSKővágó, Zoltán     }
18531930616bSKővágó, Zoltán 
1854ec36b695Sbellard     if (audio_validate_settings (as)) {
18558ead62cfSbellard         dolog ("Invalid settings were passed when trying to add capture\n");
18568ead62cfSbellard         audio_print_settings (as);
1857e8d85444SFrediano Ziglio         return NULL;
18588ead62cfSbellard     }
18598ead62cfSbellard 
1860e8d85444SFrediano Ziglio     cb = g_malloc0(sizeof(*cb));
18618ead62cfSbellard     cb->ops = *ops;
18628ead62cfSbellard     cb->opaque = cb_opaque;
18638ead62cfSbellard 
1864526fb058SKővágó, Zoltán     cap = audio_pcm_capture_find_specific(s, as);
18658ead62cfSbellard     if (cap) {
186672cf2d4fSBlue Swirl         QLIST_INSERT_HEAD (&cap->cb_head, cb, entries);
18676c6886bdSZhang Han     } else {
18688ead62cfSbellard         HWVoiceOut *hw;
18698ead62cfSbellard 
1870e8d85444SFrediano Ziglio         cap = g_malloc0(sizeof(*cap));
18718ead62cfSbellard 
18728ead62cfSbellard         hw = &cap->hw;
1873526fb058SKővágó, Zoltán         hw->s = s;
187433940dd3SVolker Rümelin         hw->pcm_ops = &capture_pcm_ops;
187572cf2d4fSBlue Swirl         QLIST_INIT (&hw->sw_head);
187672cf2d4fSBlue Swirl         QLIST_INIT (&cap->cb_head);
18778ead62cfSbellard 
18788ead62cfSbellard         /* XXX find a more elegant way */
18798ead62cfSbellard         hw->samples = 4096 * 4;
1880dc88e38fSKővágó, Zoltán         audio_pcm_hw_alloc_resources_out(hw);
18818ead62cfSbellard 
1882d929eba5Sbellard         audio_pcm_init_info (&hw->info, as);
18838ead62cfSbellard 
18848dbd3d17SVolker Rümelin         cap->buf = g_malloc0_n(hw->mix_buf.size, hw->info.bytes_per_frame);
18858ead62cfSbellard 
1886ed2a4a79SKővágó, Zoltán         if (hw->info.is_float) {
1887ed2a4a79SKővágó, Zoltán             hw->clip = mixeng_clip_float[hw->info.nchannels == 2];
1888ed2a4a79SKővágó, Zoltán         } else {
18898ead62cfSbellard             hw->clip = mixeng_clip
18908ead62cfSbellard                 [hw->info.nchannels == 2]
1891ed2a4a79SKővágó, Zoltán                 [hw->info.is_signed]
1892d929eba5Sbellard                 [hw->info.swap_endianness]
1893f941aa25Sths                 [audio_bits_to_index(hw->info.bits)];
1894ed2a4a79SKővágó, Zoltán         }
18958ead62cfSbellard 
189672cf2d4fSBlue Swirl         QLIST_INSERT_HEAD (&s->cap_head, cap, entries);
189772cf2d4fSBlue Swirl         QLIST_INSERT_HEAD (&cap->cb_head, cb, entries);
18988ead62cfSbellard 
1899526fb058SKővágó, Zoltán         QLIST_FOREACH(hw, &s->hw_head_out, entries) {
19001a7dafceSmalc             audio_attach_capture (hw);
19018ead62cfSbellard         }
1902ec36b695Sbellard     }
1903417f8c8eSPaolo Bonzini 
1904417f8c8eSPaolo Bonzini     return cap;
1905ec36b695Sbellard }
1906ec36b695Sbellard 
AUD_del_capture(CaptureVoiceOut * cap,void * cb_opaque)1907ec36b695Sbellard void AUD_del_capture (CaptureVoiceOut *cap, void *cb_opaque)
1908ec36b695Sbellard {
1909ec36b695Sbellard     struct capture_callback *cb;
1910ec36b695Sbellard 
1911ec36b695Sbellard     for (cb = cap->cb_head.lh_first; cb; cb = cb->entries.le_next) {
1912ec36b695Sbellard         if (cb->opaque == cb_opaque) {
1913ec36b695Sbellard             cb->ops.destroy (cb_opaque);
191472cf2d4fSBlue Swirl             QLIST_REMOVE (cb, entries);
19157267c094SAnthony Liguori             g_free (cb);
1916ec36b695Sbellard 
1917ec36b695Sbellard             if (!cap->cb_head.lh_first) {
1918ec36b695Sbellard                 SWVoiceOut *sw = cap->hw.sw_head.lh_first, *sw1;
1919a3c25997Sbellard 
1920ec36b695Sbellard                 while (sw) {
1921a3c25997Sbellard                     SWVoiceCap *sc = (SWVoiceCap *) sw;
1922ec36b695Sbellard #ifdef DEBUG_CAPTURE
1923ec36b695Sbellard                     dolog ("freeing %s\n", sw->name);
1924ec36b695Sbellard #endif
1925a3c25997Sbellard 
1926ec36b695Sbellard                     sw1 = sw->entries.le_next;
1927ec36b695Sbellard                     if (sw->rate) {
1928ec36b695Sbellard                         st_rate_stop (sw->rate);
1929ec36b695Sbellard                         sw->rate = NULL;
1930ec36b695Sbellard                     }
193172cf2d4fSBlue Swirl                     QLIST_REMOVE (sw, entries);
193272cf2d4fSBlue Swirl                     QLIST_REMOVE (sc, entries);
19337267c094SAnthony Liguori                     g_free (sc);
1934ec36b695Sbellard                     sw = sw1;
1935ec36b695Sbellard                 }
193672cf2d4fSBlue Swirl                 QLIST_REMOVE (cap, entries);
19378dbd3d17SVolker Rümelin                 g_free(cap->hw.mix_buf.buffer);
19383268a845SGerd Hoffmann                 g_free (cap->buf);
19397267c094SAnthony Liguori                 g_free (cap);
1940ec36b695Sbellard             }
1941ec36b695Sbellard             return;
1942ec36b695Sbellard         }
19438ead62cfSbellard     }
19448ead62cfSbellard }
1945683efdcbSbalrog 
AUD_set_volume_out(SWVoiceOut * sw,int mute,uint8_t lvol,uint8_t rvol)1946683efdcbSbalrog void AUD_set_volume_out (SWVoiceOut *sw, int mute, uint8_t lvol, uint8_t rvol)
1947683efdcbSbalrog {
1948cecc1e79SKővágó, Zoltán     Volume vol = { .mute = mute, .channels = 2, .vol = { lvol, rvol } };
1949cecc1e79SKővágó, Zoltán     audio_set_volume_out(sw, &vol);
1950cecc1e79SKővágó, Zoltán }
1951cecc1e79SKővágó, Zoltán 
audio_set_volume_out(SWVoiceOut * sw,Volume * vol)1952cecc1e79SKővágó, Zoltán void audio_set_volume_out(SWVoiceOut *sw, Volume *vol)
1953cecc1e79SKővágó, Zoltán {
1954683efdcbSbalrog     if (sw) {
19556c95ab94SMarc-André Lureau         HWVoiceOut *hw = sw->hw;
19566c95ab94SMarc-André Lureau 
1957cecc1e79SKővágó, Zoltán         sw->vol.mute = vol->mute;
1958cecc1e79SKővágó, Zoltán         sw->vol.l = nominal_volume.l * vol->vol[0] / 255;
1959cecc1e79SKővágó, Zoltán         sw->vol.r = nominal_volume.l * vol->vol[vol->channels > 1 ? 1 : 0] /
1960cecc1e79SKővágó, Zoltán             255;
19616c95ab94SMarc-André Lureau 
1962571a8c52SKővágó, Zoltán         if (hw->pcm_ops->volume_out) {
1963cecc1e79SKővágó, Zoltán             hw->pcm_ops->volume_out(hw, vol);
19646c95ab94SMarc-André Lureau         }
1965683efdcbSbalrog     }
1966683efdcbSbalrog }
1967683efdcbSbalrog 
AUD_set_volume_in(SWVoiceIn * sw,int mute,uint8_t lvol,uint8_t rvol)1968683efdcbSbalrog void AUD_set_volume_in (SWVoiceIn *sw, int mute, uint8_t lvol, uint8_t rvol)
1969683efdcbSbalrog {
1970cecc1e79SKővágó, Zoltán     Volume vol = { .mute = mute, .channels = 2, .vol = { lvol, rvol } };
1971cecc1e79SKővágó, Zoltán     audio_set_volume_in(sw, &vol);
1972cecc1e79SKővágó, Zoltán }
1973cecc1e79SKővágó, Zoltán 
audio_set_volume_in(SWVoiceIn * sw,Volume * vol)1974cecc1e79SKővágó, Zoltán void audio_set_volume_in(SWVoiceIn *sw, Volume *vol)
1975cecc1e79SKővágó, Zoltán {
1976683efdcbSbalrog     if (sw) {
19776c95ab94SMarc-André Lureau         HWVoiceIn *hw = sw->hw;
19786c95ab94SMarc-André Lureau 
1979cecc1e79SKővágó, Zoltán         sw->vol.mute = vol->mute;
1980cecc1e79SKővágó, Zoltán         sw->vol.l = nominal_volume.l * vol->vol[0] / 255;
1981cecc1e79SKővágó, Zoltán         sw->vol.r = nominal_volume.r * vol->vol[vol->channels > 1 ? 1 : 0] /
1982cecc1e79SKővágó, Zoltán             255;
19836c95ab94SMarc-André Lureau 
1984571a8c52SKővágó, Zoltán         if (hw->pcm_ops->volume_in) {
1985cecc1e79SKővágó, Zoltán             hw->pcm_ops->volume_in(hw, vol);
19866c95ab94SMarc-André Lureau         }
1987683efdcbSbalrog     }
1988683efdcbSbalrog }
198971830221SKővágó, Zoltán 
audio_create_pdos(Audiodev * dev)199071830221SKővágó, Zoltán void audio_create_pdos(Audiodev *dev)
199171830221SKővágó, Zoltán {
199271830221SKővágó, Zoltán     switch (dev->driver) {
199371830221SKővágó, Zoltán #define CASE(DRIVER, driver, pdo_name)                              \
199471830221SKővágó, Zoltán     case AUDIODEV_DRIVER_##DRIVER:                                  \
1995ceb19c8fSMarkus Armbruster         if (!dev->u.driver.in) {                                    \
199671830221SKővágó, Zoltán             dev->u.driver.in = g_malloc0(                           \
199771830221SKővágó, Zoltán                 sizeof(Audiodev##pdo_name##PerDirectionOptions));   \
199871830221SKővágó, Zoltán         }                                                           \
1999ceb19c8fSMarkus Armbruster         if (!dev->u.driver.out) {                                   \
200071830221SKővágó, Zoltán             dev->u.driver.out = g_malloc0(                          \
2001725662d6SKővágó, Zoltán                 sizeof(Audiodev##pdo_name##PerDirectionOptions));   \
200271830221SKővágó, Zoltán         }                                                           \
200371830221SKővágó, Zoltán         break
200471830221SKővágó, Zoltán 
200571830221SKővágó, Zoltán         CASE(NONE, none, );
20067a92a857SDaniel P. Berrangé #ifdef CONFIG_AUDIO_ALSA
200771830221SKővágó, Zoltán         CASE(ALSA, alsa, Alsa);
20087a92a857SDaniel P. Berrangé #endif
20097a92a857SDaniel P. Berrangé #ifdef CONFIG_AUDIO_COREAUDIO
201071830221SKővágó, Zoltán         CASE(COREAUDIO, coreaudio, Coreaudio);
20117a92a857SDaniel P. Berrangé #endif
20127a92a857SDaniel P. Berrangé #ifdef CONFIG_DBUS_DISPLAY
2013739362d4SMarc-André Lureau         CASE(DBUS, dbus, );
20147a92a857SDaniel P. Berrangé #endif
20157a92a857SDaniel P. Berrangé #ifdef CONFIG_AUDIO_DSOUND
201671830221SKővágó, Zoltán         CASE(DSOUND, dsound, );
20177a92a857SDaniel P. Berrangé #endif
20187a92a857SDaniel P. Berrangé #ifdef CONFIG_AUDIO_JACK
20192e445703SGeoffrey McRae         CASE(JACK, jack, Jack);
20207a92a857SDaniel P. Berrangé #endif
20217a92a857SDaniel P. Berrangé #ifdef CONFIG_AUDIO_OSS
202271830221SKővágó, Zoltán         CASE(OSS, oss, Oss);
20237a92a857SDaniel P. Berrangé #endif
20247a92a857SDaniel P. Berrangé #ifdef CONFIG_AUDIO_PA
202571830221SKővágó, Zoltán         CASE(PA, pa, Pa);
20267a92a857SDaniel P. Berrangé #endif
2027c2d3d1c2SDorinda Bassey #ifdef CONFIG_AUDIO_PIPEWIRE
2028c2d3d1c2SDorinda Bassey         CASE(PIPEWIRE, pipewire, Pipewire);
2029c2d3d1c2SDorinda Bassey #endif
20307a92a857SDaniel P. Berrangé #ifdef CONFIG_AUDIO_SDL
20315a0926c2SVolker Rümelin         CASE(SDL, sdl, Sdl);
20327a92a857SDaniel P. Berrangé #endif
20337a92a857SDaniel P. Berrangé #ifdef CONFIG_AUDIO_SNDIO
2034663df1ccSAlexandre Ratchov         CASE(SNDIO, sndio, );
20357a92a857SDaniel P. Berrangé #endif
20367a92a857SDaniel P. Berrangé #ifdef CONFIG_SPICE
203771830221SKővágó, Zoltán         CASE(SPICE, spice, );
20387a92a857SDaniel P. Berrangé #endif
203971830221SKővágó, Zoltán         CASE(WAV, wav, );
204071830221SKővágó, Zoltán 
204171830221SKővágó, Zoltán     case AUDIODEV_DRIVER__MAX:
204271830221SKővágó, Zoltán         abort();
204371830221SKővágó, Zoltán     };
204471830221SKővágó, Zoltán }
204571830221SKővágó, Zoltán 
audio_validate_per_direction_opts(AudiodevPerDirectionOptions * pdo,Error ** errp)204671830221SKővágó, Zoltán static void audio_validate_per_direction_opts(
204771830221SKővágó, Zoltán     AudiodevPerDirectionOptions *pdo, Error **errp)
204871830221SKővágó, Zoltán {
20491930616bSKővágó, Zoltán     if (!pdo->has_mixing_engine) {
20501930616bSKővágó, Zoltán         pdo->has_mixing_engine = true;
20511930616bSKővágó, Zoltán         pdo->mixing_engine = true;
20521930616bSKővágó, Zoltán     }
205371830221SKővágó, Zoltán     if (!pdo->has_fixed_settings) {
205471830221SKővágó, Zoltán         pdo->has_fixed_settings = true;
20551930616bSKővágó, Zoltán         pdo->fixed_settings = pdo->mixing_engine;
205671830221SKővágó, Zoltán     }
205771830221SKővágó, Zoltán     if (!pdo->fixed_settings &&
205871830221SKővágó, Zoltán         (pdo->has_frequency || pdo->has_channels || pdo->has_format)) {
205971830221SKővágó, Zoltán         error_setg(errp,
206071830221SKővágó, Zoltán                    "You can't use frequency, channels or format with fixed-settings=off");
206171830221SKővágó, Zoltán         return;
206271830221SKővágó, Zoltán     }
20631930616bSKővágó, Zoltán     if (!pdo->mixing_engine && pdo->fixed_settings) {
20641930616bSKővágó, Zoltán         error_setg(errp, "You can't use fixed-settings without mixeng");
20651930616bSKővágó, Zoltán         return;
20661930616bSKővágó, Zoltán     }
206771830221SKővágó, Zoltán 
206871830221SKővágó, Zoltán     if (!pdo->has_frequency) {
206971830221SKővágó, Zoltán         pdo->has_frequency = true;
207071830221SKővágó, Zoltán         pdo->frequency = 44100;
207171830221SKővágó, Zoltán     }
207271830221SKővágó, Zoltán     if (!pdo->has_channels) {
207371830221SKővágó, Zoltán         pdo->has_channels = true;
207471830221SKővágó, Zoltán         pdo->channels = 2;
207571830221SKővágó, Zoltán     }
207671830221SKővágó, Zoltán     if (!pdo->has_voices) {
207771830221SKővágó, Zoltán         pdo->has_voices = true;
20781930616bSKővágó, Zoltán         pdo->voices = pdo->mixing_engine ? 1 : INT_MAX;
207971830221SKővágó, Zoltán     }
208071830221SKővágó, Zoltán     if (!pdo->has_format) {
208171830221SKővágó, Zoltán         pdo->has_format = true;
208271830221SKővágó, Zoltán         pdo->format = AUDIO_FORMAT_S16;
208371830221SKővágó, Zoltán     }
208471830221SKővágó, Zoltán }
208571830221SKővágó, Zoltán 
audio_validate_opts(Audiodev * dev,Error ** errp)208671830221SKővágó, Zoltán static void audio_validate_opts(Audiodev *dev, Error **errp)
208771830221SKővágó, Zoltán {
208871830221SKővágó, Zoltán     Error *err = NULL;
208971830221SKővágó, Zoltán 
209071830221SKővágó, Zoltán     audio_create_pdos(dev);
209171830221SKővágó, Zoltán 
209271830221SKővágó, Zoltán     audio_validate_per_direction_opts(audio_get_pdo_in(dev), &err);
209371830221SKővágó, Zoltán     if (err) {
209471830221SKővágó, Zoltán         error_propagate(errp, err);
209571830221SKővágó, Zoltán         return;
209671830221SKővágó, Zoltán     }
209771830221SKővágó, Zoltán 
209871830221SKővágó, Zoltán     audio_validate_per_direction_opts(audio_get_pdo_out(dev), &err);
209971830221SKővágó, Zoltán     if (err) {
210071830221SKővágó, Zoltán         error_propagate(errp, err);
210171830221SKővágó, Zoltán         return;
210271830221SKővágó, Zoltán     }
210371830221SKővágó, Zoltán 
210471830221SKővágó, Zoltán     if (!dev->has_timer_period) {
210571830221SKővágó, Zoltán         dev->has_timer_period = true;
210671830221SKővágó, Zoltán         dev->timer_period = 10000; /* 100Hz -> 10ms */
210771830221SKővágó, Zoltán     }
210871830221SKővágó, Zoltán }
210971830221SKővágó, Zoltán 
audio_help(void)21105e03b6daSClaudio Fontana void audio_help(void)
21115e03b6daSClaudio Fontana {
21125e03b6daSClaudio Fontana     int i;
21135e03b6daSClaudio Fontana 
21145e03b6daSClaudio Fontana     printf("Available audio drivers:\n");
21155e03b6daSClaudio Fontana 
21165e03b6daSClaudio Fontana     for (i = 0; i < AUDIODEV_DRIVER__MAX; i++) {
21175e03b6daSClaudio Fontana         audio_driver *driver = audio_driver_lookup(AudiodevDriver_str(i));
21185e03b6daSClaudio Fontana         if (driver) {
21195e03b6daSClaudio Fontana             printf("%s\n", driver->name);
21205e03b6daSClaudio Fontana         }
21215e03b6daSClaudio Fontana     }
21225e03b6daSClaudio Fontana }
21235e03b6daSClaudio Fontana 
audio_parse_option(const char * opt)212471830221SKővágó, Zoltán void audio_parse_option(const char *opt)
212571830221SKővágó, Zoltán {
212671830221SKővágó, Zoltán     Audiodev *dev = NULL;
212771830221SKővágó, Zoltán 
21285e03b6daSClaudio Fontana     if (is_help_option(opt)) {
21295e03b6daSClaudio Fontana         audio_help();
21305e03b6daSClaudio Fontana         exit(EXIT_SUCCESS);
21315e03b6daSClaudio Fontana     }
213271830221SKővágó, Zoltán     Visitor *v = qobject_input_visitor_new_str(opt, "driver", &error_fatal);
213371830221SKővágó, Zoltán     visit_type_Audiodev(v, NULL, &dev, &error_fatal);
213471830221SKővágó, Zoltán     visit_free(v);
213571830221SKővágó, Zoltán 
2136039a6837SPaolo Bonzini     audio_define(dev);
2137039a6837SPaolo Bonzini }
2138039a6837SPaolo Bonzini 
audio_define(Audiodev * dev)2139039a6837SPaolo Bonzini void audio_define(Audiodev *dev)
2140039a6837SPaolo Bonzini {
2141039a6837SPaolo Bonzini     AudiodevListEntry *e;
2142039a6837SPaolo Bonzini 
214371830221SKővágó, Zoltán     audio_validate_opts(dev, &error_fatal);
214471830221SKővágó, Zoltán 
2145b21e2380SMarkus Armbruster     e = g_new0(AudiodevListEntry, 1);
214671830221SKővágó, Zoltán     e->dev = dev;
214771830221SKővágó, Zoltán     QSIMPLEQ_INSERT_TAIL(&audiodevs, e, next);
214871830221SKővágó, Zoltán }
214971830221SKővágó, Zoltán 
audio_define_default(Audiodev * dev,Error ** errp)21508f527a3cSPaolo Bonzini void audio_define_default(Audiodev *dev, Error **errp)
21518f527a3cSPaolo Bonzini {
21528f527a3cSPaolo Bonzini     AudiodevListEntry *e;
21538f527a3cSPaolo Bonzini 
21548f527a3cSPaolo Bonzini     audio_validate_opts(dev, errp);
21558f527a3cSPaolo Bonzini 
21568f527a3cSPaolo Bonzini     e = g_new0(AudiodevListEntry, 1);
21578f527a3cSPaolo Bonzini     e->dev = dev;
21588f527a3cSPaolo Bonzini     QSIMPLEQ_INSERT_TAIL(&default_audiodevs, e, next);
21598f527a3cSPaolo Bonzini }
21608f527a3cSPaolo Bonzini 
audio_init_audiodevs(void)2161cb94ff5fSMartin Kletzander void audio_init_audiodevs(void)
216271830221SKővágó, Zoltán {
216371830221SKővágó, Zoltán     AudiodevListEntry *e;
216471830221SKővágó, Zoltán 
216571830221SKővágó, Zoltán     QSIMPLEQ_FOREACH(e, &audiodevs, next) {
2166cb94ff5fSMartin Kletzander         audio_init(e->dev, &error_fatal);
216771830221SKővágó, Zoltán     }
216871830221SKővágó, Zoltán }
216971830221SKővágó, Zoltán 
audiodev_to_audsettings(AudiodevPerDirectionOptions * pdo)217071830221SKővágó, Zoltán audsettings audiodev_to_audsettings(AudiodevPerDirectionOptions *pdo)
217171830221SKővágó, Zoltán {
217271830221SKővágó, Zoltán     return (audsettings) {
217371830221SKővágó, Zoltán         .freq = pdo->frequency,
217471830221SKővágó, Zoltán         .nchannels = pdo->channels,
217571830221SKővágó, Zoltán         .fmt = pdo->format,
217671830221SKővágó, Zoltán         .endianness = AUDIO_HOST_ENDIANNESS,
217771830221SKővágó, Zoltán     };
217871830221SKővágó, Zoltán }
217971830221SKővágó, Zoltán 
audioformat_bytes_per_sample(AudioFormat fmt)218071830221SKővágó, Zoltán int audioformat_bytes_per_sample(AudioFormat fmt)
218171830221SKővágó, Zoltán {
218271830221SKővágó, Zoltán     switch (fmt) {
218371830221SKővágó, Zoltán     case AUDIO_FORMAT_U8:
218471830221SKővágó, Zoltán     case AUDIO_FORMAT_S8:
218571830221SKővágó, Zoltán         return 1;
218671830221SKővágó, Zoltán 
218771830221SKővágó, Zoltán     case AUDIO_FORMAT_U16:
218871830221SKővágó, Zoltán     case AUDIO_FORMAT_S16:
218971830221SKővágó, Zoltán         return 2;
219071830221SKővágó, Zoltán 
219171830221SKővágó, Zoltán     case AUDIO_FORMAT_U32:
219271830221SKővágó, Zoltán     case AUDIO_FORMAT_S32:
2193ed2a4a79SKővágó, Zoltán     case AUDIO_FORMAT_F32:
219471830221SKővágó, Zoltán         return 4;
219571830221SKővágó, Zoltán 
219671830221SKővágó, Zoltán     case AUDIO_FORMAT__MAX:
219771830221SKővágó, Zoltán         ;
219871830221SKővágó, Zoltán     }
219971830221SKővágó, Zoltán     abort();
220071830221SKővágó, Zoltán }
220171830221SKővágó, Zoltán 
220271830221SKővágó, Zoltán 
220371830221SKővágó, Zoltán /* frames = freq * usec / 1e6 */
audio_buffer_frames(AudiodevPerDirectionOptions * pdo,audsettings * as,int def_usecs)220471830221SKővágó, Zoltán int audio_buffer_frames(AudiodevPerDirectionOptions *pdo,
220571830221SKővágó, Zoltán                         audsettings *as, int def_usecs)
220671830221SKővágó, Zoltán {
220771830221SKővágó, Zoltán     uint64_t usecs = pdo->has_buffer_length ? pdo->buffer_length : def_usecs;
220871830221SKővágó, Zoltán     return (as->freq * usecs + 500000) / 1000000;
220971830221SKővágó, Zoltán }
221071830221SKővágó, Zoltán 
221171830221SKővágó, Zoltán /* samples = channels * frames = channels * freq * usec / 1e6 */
audio_buffer_samples(AudiodevPerDirectionOptions * pdo,audsettings * as,int def_usecs)221271830221SKővágó, Zoltán int audio_buffer_samples(AudiodevPerDirectionOptions *pdo,
221371830221SKővágó, Zoltán                          audsettings *as, int def_usecs)
221471830221SKővágó, Zoltán {
221571830221SKővágó, Zoltán     return as->nchannels * audio_buffer_frames(pdo, as, def_usecs);
221671830221SKővágó, Zoltán }
221771830221SKővágó, Zoltán 
221871830221SKővágó, Zoltán /*
221971830221SKővágó, Zoltán  * bytes = bytes_per_sample * samples =
222071830221SKővágó, Zoltán  *     bytes_per_sample * channels * freq * usec / 1e6
222171830221SKővágó, Zoltán  */
audio_buffer_bytes(AudiodevPerDirectionOptions * pdo,audsettings * as,int def_usecs)222271830221SKővágó, Zoltán int audio_buffer_bytes(AudiodevPerDirectionOptions *pdo,
222371830221SKővágó, Zoltán                        audsettings *as, int def_usecs)
222471830221SKővágó, Zoltán {
222571830221SKővágó, Zoltán     return audio_buffer_samples(pdo, as, def_usecs) *
222671830221SKővágó, Zoltán         audioformat_bytes_per_sample(as->fmt);
222771830221SKővágó, Zoltán }
2228ecd97e95SKővágó, Zoltán 
audio_state_by_name(const char * name,Error ** errp)2229176adafcSPaolo Bonzini AudioState *audio_state_by_name(const char *name, Error **errp)
2230ecd97e95SKővágó, Zoltán {
2231ecd97e95SKővágó, Zoltán     AudioState *s;
2232ecd97e95SKővágó, Zoltán     QTAILQ_FOREACH(s, &audio_states, list) {
2233ecd97e95SKővágó, Zoltán         assert(s->dev);
2234ecd97e95SKővágó, Zoltán         if (strcmp(name, s->dev->id) == 0) {
2235ecd97e95SKővágó, Zoltán             return s;
2236ecd97e95SKővágó, Zoltán         }
2237ecd97e95SKővágó, Zoltán     }
2238176adafcSPaolo Bonzini     error_setg(errp, "audiodev '%s' not found", name);
2239ecd97e95SKővágó, Zoltán     return NULL;
2240ecd97e95SKővágó, Zoltán }
2241ecd97e95SKővágó, Zoltán 
audio_get_id(QEMUSoundCard * card)2242ecd97e95SKővágó, Zoltán const char *audio_get_id(QEMUSoundCard *card)
2243ecd97e95SKővágó, Zoltán {
2244ecd97e95SKővágó, Zoltán     if (card->state) {
2245ecd97e95SKővágó, Zoltán         assert(card->state->dev);
2246ecd97e95SKővágó, Zoltán         return card->state->dev->id;
2247ecd97e95SKővágó, Zoltán     } else {
2248ecd97e95SKővágó, Zoltán         return "";
2249ecd97e95SKővágó, Zoltán     }
2250ecd97e95SKővágó, Zoltán }
2251857271a2SKővágó, Zoltán 
audio_application_name(void)225237a54d05SVolker Rümelin const char *audio_application_name(void)
225337a54d05SVolker Rümelin {
225437a54d05SVolker Rümelin     const char *vm_name;
225537a54d05SVolker Rümelin 
225637a54d05SVolker Rümelin     vm_name = qemu_get_vm_name();
225737a54d05SVolker Rümelin     return vm_name ? vm_name : "qemu";
225837a54d05SVolker Rümelin }
225937a54d05SVolker Rümelin 
audio_rate_start(RateCtl * rate)2260857271a2SKővágó, Zoltán void audio_rate_start(RateCtl *rate)
2261857271a2SKővágó, Zoltán {
2262857271a2SKővágó, Zoltán     memset(rate, 0, sizeof(RateCtl));
2263857271a2SKővágó, Zoltán     rate->start_ticks = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
2264857271a2SKővágó, Zoltán }
2265857271a2SKővágó, Zoltán 
audio_rate_peek_bytes(RateCtl * rate,struct audio_pcm_info * info)226602732641SVolker Rümelin size_t audio_rate_peek_bytes(RateCtl *rate, struct audio_pcm_info *info)
2267857271a2SKővágó, Zoltán {
2268857271a2SKővágó, Zoltán     int64_t now;
2269857271a2SKővágó, Zoltán     int64_t ticks;
2270857271a2SKővágó, Zoltán     int64_t bytes;
227102732641SVolker Rümelin     int64_t frames;
2272857271a2SKővágó, Zoltán 
2273857271a2SKővágó, Zoltán     now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
2274857271a2SKővágó, Zoltán     ticks = now - rate->start_ticks;
2275857271a2SKővágó, Zoltán     bytes = muldiv64(ticks, info->bytes_per_second, NANOSECONDS_PER_SECOND);
227602732641SVolker Rümelin     frames = (bytes - rate->bytes_sent) / info->bytes_per_frame;
227702732641SVolker Rümelin     if (frames < 0 || frames > 65536) {
227802732641SVolker Rümelin         AUD_log(NULL, "Resetting rate control (%" PRId64 " frames)\n", frames);
2279857271a2SKővágó, Zoltán         audio_rate_start(rate);
228002732641SVolker Rümelin         frames = 0;
2281857271a2SKővágó, Zoltán     }
2282857271a2SKővágó, Zoltán 
228302732641SVolker Rümelin     return frames * info->bytes_per_frame;
228402732641SVolker Rümelin }
228502732641SVolker Rümelin 
audio_rate_add_bytes(RateCtl * rate,size_t bytes_used)228602732641SVolker Rümelin void audio_rate_add_bytes(RateCtl *rate, size_t bytes_used)
228702732641SVolker Rümelin {
228802732641SVolker Rümelin     rate->bytes_sent += bytes_used;
228902732641SVolker Rümelin }
229002732641SVolker Rümelin 
audio_rate_get_bytes(RateCtl * rate,struct audio_pcm_info * info,size_t bytes_avail)2291613fe02bSVolker Rümelin size_t audio_rate_get_bytes(RateCtl *rate, struct audio_pcm_info *info,
229202732641SVolker Rümelin                             size_t bytes_avail)
229302732641SVolker Rümelin {
229402732641SVolker Rümelin     size_t bytes;
229502732641SVolker Rümelin 
229602732641SVolker Rümelin     bytes = audio_rate_peek_bytes(rate, info);
229702732641SVolker Rümelin     bytes = MIN(bytes, bytes_avail);
229802732641SVolker Rümelin     audio_rate_add_bytes(rate, bytes);
229902732641SVolker Rümelin 
230002732641SVolker Rümelin     return bytes;
2301857271a2SKővágó, Zoltán }
2302637d1809SDaniel P. Berrangé 
qmp_query_audiodevs(Error ** errp)2303637d1809SDaniel P. Berrangé AudiodevList *qmp_query_audiodevs(Error **errp)
2304637d1809SDaniel P. Berrangé {
2305637d1809SDaniel P. Berrangé     AudiodevList *ret = NULL;
2306637d1809SDaniel P. Berrangé     AudiodevListEntry *e;
2307637d1809SDaniel P. Berrangé     QSIMPLEQ_FOREACH(e, &audiodevs, next) {
2308637d1809SDaniel P. Berrangé         QAPI_LIST_PREPEND(ret, QAPI_CLONE(Audiodev, e->dev));
2309637d1809SDaniel P. Berrangé     }
2310637d1809SDaniel P. Berrangé     return ret;
2311637d1809SDaniel P. Berrangé }
2312