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