Commit 8ead62cf authored by bellard's avatar bellard
Browse files

audio fixes + initial audio capture support (malc)


git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@2040 c046a42c-6fe2-441c-8c8c-71466251a162
parent feea13e1
......@@ -61,8 +61,8 @@ static struct {
.size_in_usec_in = 1,
.size_in_usec_out = 1,
#endif
.pcm_name_out = "hw:0,0",
.pcm_name_in = "hw:0,0",
.pcm_name_out = "default",
.pcm_name_in = "default",
#ifdef HIGH_LATENCY
.buffer_size_in = 400000,
.period_size_in = 400000 / 4,
......@@ -606,7 +606,6 @@ static int alsa_run_out (HWVoiceOut *hw)
}
}
mixeng_clear (src, written);
rpos = (rpos + written) % hw->samples;
samples -= written;
len -= written;
......
......@@ -29,6 +29,7 @@
/* #define DEBUG_PLIVE */
/* #define DEBUG_LIVE */
/* #define DEBUG_OUT */
/* #define DEBUG_CAPTURE */
#define SW_NAME(sw) (sw)->name ? (sw)->name : "unknown"
......@@ -137,7 +138,7 @@ int audio_bug (const char *funcname, int cond)
if (cond) {
static int shown;
AUD_log (NULL, "Error a bug that was just triggered in %s\n", funcname);
AUD_log (NULL, "A bug was just triggered in %s\n", funcname);
if (!shown) {
shown = 1;
AUD_log (NULL, "Save all your work and restart without audio\n");
......@@ -620,6 +621,121 @@ void audio_pcm_info_clear_buf (struct audio_pcm_info *info, void *buf, int len)
}
}
/*
* Capture
*/
static void noop_conv (st_sample_t *dst, const void *src,
int samples, volume_t *vol)
{
(void) src;
(void) dst;
(void) samples;
(void) vol;
}
static CaptureVoiceOut *audio_pcm_capture_find_specific (
AudioState *s,
audsettings_t *as,
int endian
)
{
CaptureVoiceOut *cap;
int swap_endian = audio_need_to_swap_endian (endian);
for (cap = s->cap_head.lh_first; cap; cap = cap->entries.le_next) {
if ((cap->hw.info.swap_endian == swap_endian)
&& audio_pcm_info_eq (&cap->hw.info, as)) {
return cap;
}
}
return NULL;
}
static void audio_notify_capture (CaptureVoiceOut *cap, int enabled)
{
if (cap->hw.enabled != enabled) {
struct capture_callback *cb;
cap->hw.enabled = enabled;
for (cb = cap->cb_head.lh_first; cb; cb = cb->entries.le_next) {
cb->ops.state (cb->opaque, enabled);
}
}
}
static void audio_recalc_and_notify_capture (CaptureVoiceOut *cap)
{
HWVoiceOut *hw = &cap->hw;
SWVoiceOut *sw;
int enabled = 0;
for (sw = hw->sw_cap_head.lh_first; sw; sw = sw->cap_entries.le_next) {
if (sw->active) {
enabled = 1;
break;
}
}
audio_notify_capture (cap, enabled);
}
static void audio_detach_capture (HWVoiceOut *hw)
{
SWVoiceOut *sw;
for (sw = hw->sw_cap_head.lh_first; sw; sw = sw->cap_entries.le_next) {
if (sw->rate) {
st_rate_stop (sw->rate);
sw->rate = NULL;
}
LIST_REMOVE (sw, entries);
LIST_REMOVE (sw, cap_entries);
qemu_free (sw);
audio_recalc_and_notify_capture ((CaptureVoiceOut *) sw->hw);
}
}
static int audio_attach_capture (AudioState *s, HWVoiceOut *hw)
{
CaptureVoiceOut *cap;
audio_detach_capture (hw);
for (cap = s->cap_head.lh_first; cap; cap = cap->entries.le_next) {
SWVoiceOut *sw;
HWVoiceOut *hw_cap;
hw_cap = &cap->hw;
sw = audio_calloc (AUDIO_FUNC, 1, sizeof (*sw));
if (!sw) {
dolog ("Could not allocate soft capture voice (%zu bytes)\n",
sizeof (*sw));
return -1;
}
sw->info = hw->info;
sw->hw = hw_cap;
sw->empty = 1;
sw->active = hw->enabled;
sw->conv = noop_conv;
sw->ratio = ((int64_t) hw_cap->info.freq << 32) / sw->info.freq;
sw->rate = st_rate_start (sw->info.freq, hw_cap->info.freq);
if (!sw->rate) {
dolog ("Could not start rate conversion for `%s'\n", SW_NAME (sw));
qemu_free (sw);
return -1;
}
LIST_INSERT_HEAD (&hw_cap->sw_head, sw, entries);
LIST_INSERT_HEAD (&hw->sw_cap_head, sw, cap_entries);
if (sw->active) {
audio_notify_capture (cap, 1);
}
else {
audio_recalc_and_notify_capture (cap);
}
}
return 0;
}
/*
* Hard voice (capture)
*/
......@@ -916,17 +1032,11 @@ void AUD_set_active_out (SWVoiceOut *sw, int on)
SWVoiceOut *temp_sw;
if (on) {
int total;
hw->pending_disable = 0;
if (!hw->enabled) {
hw->enabled = 1;
hw->pcm_ops->ctl_out (hw, VOICE_ENABLE);
}
if (sw->empty) {
total = 0;
}
}
else {
if (hw->enabled) {
......@@ -940,6 +1050,13 @@ void AUD_set_active_out (SWVoiceOut *sw, int on)
hw->pending_disable = nb_active == 1;
}
}
for (temp_sw = hw->sw_cap_head.lh_first; temp_sw;
temp_sw = temp_sw->entries.le_next) {
temp_sw->active = hw->enabled;
if (hw->enabled) {
audio_notify_capture ((CaptureVoiceOut *) temp_sw->hw, 1);
}
}
sw->active = on;
}
}
......@@ -1031,6 +1148,41 @@ static int audio_get_free (SWVoiceOut *sw)
return (((int64_t) dead << 32) / sw->ratio) << sw->info.shift;
}
static void audio_capture_mix_and_clear (HWVoiceOut *hw, int rpos, int samples)
{
int n;
if (hw->enabled) {
SWVoiceOut *sw;
for (sw = hw->sw_cap_head.lh_first; sw; sw = sw->cap_entries.le_next) {
int rpos2 = rpos;
n = samples;
while (n) {
int till_end_of_hw = hw->samples - rpos2;
int to_write = audio_MIN (till_end_of_hw, n);
int bytes = to_write << hw->info.shift;
int written;
sw->buf = hw->mix_buf + rpos2;
written = audio_pcm_sw_write (sw, NULL, bytes);
if (written - bytes) {
dolog ("Could not mix %d bytes into a capture buffer",
bytes);
break;
}
n -= to_write;
rpos2 = (rpos2 + to_write) % hw->samples;
}
}
}
n = audio_MIN (samples, hw->samples - rpos);
mixeng_clear (hw->mix_buf + rpos, n);
mixeng_clear (hw->mix_buf, samples - n);
}
static void audio_run_out (AudioState *s)
{
HWVoiceOut *hw = NULL;
......@@ -1038,7 +1190,7 @@ static void audio_run_out (AudioState *s)
while ((hw = audio_pcm_hw_find_any_enabled_out (s, hw))) {
int played;
int live, free, nb_live, cleanup_required;
int live, free, nb_live, cleanup_required, prev_rpos;
live = audio_pcm_hw_get_live_out2 (hw, &nb_live);
if (!nb_live) {
......@@ -1057,6 +1209,11 @@ static void audio_run_out (AudioState *s)
hw->enabled = 0;
hw->pending_disable = 0;
hw->pcm_ops->ctl_out (hw, VOICE_DISABLE);
for (sw = hw->sw_cap_head.lh_first; sw;
sw = sw->cap_entries.le_next) {
sw->active = 0;
audio_recalc_and_notify_capture ((CaptureVoiceOut *) sw->hw);
}
continue;
}
......@@ -1072,6 +1229,7 @@ static void audio_run_out (AudioState *s)
continue;
}
prev_rpos = hw->rpos;
played = hw->pcm_ops->run_out (hw);
if (audio_bug (AUDIO_FUNC, hw->rpos >= hw->samples)) {
dolog ("hw->rpos=%d hw->samples=%d played=%d\n",
......@@ -1085,6 +1243,7 @@ static void audio_run_out (AudioState *s)
if (played) {
hw->ts_helper += played;
audio_capture_mix_and_clear (hw, prev_rpos, played);
}
cleanup_required = 0;
......@@ -1158,12 +1317,60 @@ static void audio_run_in (AudioState *s)
}
}
static void audio_run_capture (AudioState *s)
{
CaptureVoiceOut *cap;
for (cap = s->cap_head.lh_first; cap; cap = cap->entries.le_next) {
int live, rpos, captured;
HWVoiceOut *hw = &cap->hw;
SWVoiceOut *sw;
captured = live = audio_pcm_hw_get_live_out (hw);
rpos = hw->rpos;
while (live) {
int left = hw->samples - rpos;
int to_capture = audio_MIN (live, left);
st_sample_t *src;
struct capture_callback *cb;
src = hw->mix_buf + rpos;
hw->clip (cap->buf, src, to_capture);
mixeng_clear (src, to_capture);
for (cb = cap->cb_head.lh_first; cb; cb = cb->entries.le_next) {
cb->ops.capture (cb->opaque, cap->buf,
to_capture << hw->info.shift);
}
rpos = (rpos + to_capture) % hw->samples;
live -= to_capture;
}
hw->rpos = rpos;
for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) {
if (!sw->active && sw->empty) {
continue;
}
if (audio_bug (AUDIO_FUNC, captured > sw->total_hw_samples_mixed)) {
dolog ("captured=%d sw->total_hw_samples_mixed=%d\n",
captured, sw->total_hw_samples_mixed);
captured = sw->total_hw_samples_mixed;
}
sw->total_hw_samples_mixed -= captured;
sw->empty = sw->total_hw_samples_mixed == 0;
}
}
}
static void audio_timer (void *opaque)
{
AudioState *s = opaque;
audio_run_out (s);
audio_run_in (s);
audio_run_capture (s);
qemu_mod_timer (s->ts, qemu_get_clock (vm_clock) + conf.period.ticks);
}
......@@ -1327,8 +1534,14 @@ static void audio_atexit (void)
HWVoiceIn *hwi = NULL;
while ((hwo = audio_pcm_hw_find_any_enabled_out (s, hwo))) {
SWVoiceOut *sw;
hwo->pcm_ops->ctl_out (hwo, VOICE_DISABLE);
hwo->pcm_ops->fini_out (hwo);
for (sw = hwo->sw_cap_head.lh_first; sw; sw = sw->entries.le_next) {
audio_notify_capture ((CaptureVoiceOut *) sw->hw, 0);
}
}
while ((hwi = audio_pcm_hw_find_any_enabled_in (s, hwi))) {
......@@ -1383,6 +1596,7 @@ AudioState *AUD_init (void)
LIST_INIT (&s->hw_head_out);
LIST_INIT (&s->hw_head_in);
LIST_INIT (&s->cap_head);
atexit (audio_atexit);
s->ts = qemu_new_timer (vm_clock, audio_timer, s);
......@@ -1479,3 +1693,100 @@ AudioState *AUD_init (void)
qemu_mod_timer (s->ts, qemu_get_clock (vm_clock) + conf.period.ticks);
return s;
}
int AUD_add_capture (
AudioState *s,
audsettings_t *as,
int endian,
struct audio_capture_ops *ops,
void *cb_opaque
)
{
CaptureVoiceOut *cap;
struct capture_callback *cb;
if (!s) {
/* XXX suppress */
s = &glob_audio_state;
}
if (audio_validate_settigs (as)) {
dolog ("Invalid settings were passed when trying to add capture\n");
audio_print_settings (as);
return -1;
}
cb = audio_calloc (AUDIO_FUNC, 1, sizeof (*cb));
if (!cb) {
dolog ("Could not allocate capture callback information, size %zu\n",
sizeof (*cb));
goto err0;
}
cb->ops = *ops;
cb->opaque = cb_opaque;
cap = audio_pcm_capture_find_specific (s, as, endian);
if (cap) {
LIST_INSERT_HEAD (&cap->cb_head, cb, entries);
return 0;
}
else {
HWVoiceOut *hw;
CaptureVoiceOut *cap;
cap = audio_calloc (AUDIO_FUNC, 1, sizeof (*cap));
if (!cap) {
dolog ("Could not allocate capture voice, size %zu\n",
sizeof (*cap));
goto err1;
}
hw = &cap->hw;
LIST_INIT (&hw->sw_head);
LIST_INIT (&cap->cb_head);
/* XXX find a more elegant way */
hw->samples = 4096 * 4;
hw->mix_buf = audio_calloc (AUDIO_FUNC, hw->samples,
sizeof (st_sample_t));
if (!hw->mix_buf) {
dolog ("Could not allocate capture mix buffer (%d samples)\n",
hw->samples);
goto err2;
}
audio_pcm_init_info (&hw->info, as, endian);
cap->buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
if (!cap->buf) {
dolog ("Could not allocate capture buffer "
"(%d samples, each %d bytes)\n",
hw->samples, 1 << hw->info.shift);
goto err3;
}
hw->clip = mixeng_clip
[hw->info.nchannels == 2]
[hw->info.sign]
[hw->info.swap_endian]
[hw->info.bits == 16];
LIST_INSERT_HEAD (&s->cap_head, cap, entries);
LIST_INSERT_HEAD (&cap->cb_head, cb, entries);
hw = NULL;
while ((hw = audio_pcm_hw_find_any_out (s, hw))) {
audio_attach_capture (s, hw);
}
return 0;
err3:
qemu_free (cap->hw.mix_buf);
err2:
qemu_free (cap);
err1:
qemu_free (cb);
err0:
return -1;
}
}
......@@ -41,6 +41,11 @@ typedef struct {
audfmt_e fmt;
} audsettings_t;
struct audio_capture_ops {
void (*state) (void *opaque, int enabled);
void (*capture) (void *opaque, void *buf, int size);
};
typedef struct AudioState AudioState;
typedef struct SWVoiceOut SWVoiceOut;
typedef struct SWVoiceIn SWVoiceIn;
......@@ -66,6 +71,13 @@ AudioState *AUD_init (void);
void AUD_help (void);
void AUD_register_card (AudioState *s, const char *name, QEMUSoundCard *card);
void AUD_remove_card (QEMUSoundCard *card);
int AUD_add_capture (
AudioState *s,
audsettings_t *as,
int endian,
struct audio_capture_ops *ops,
void *opaque
);
SWVoiceOut *AUD_open_out (
QEMUSoundCard *card,
......@@ -111,7 +123,7 @@ static inline void *advance (void *p, int incr)
}
uint32_t popcount (uint32_t u);
inline uint32_t lsbindex (uint32_t u);
uint32_t lsbindex (uint32_t u);
#ifdef __GNUC__
#define audio_MIN(a, b) ( __extension__ ({ \
......
......@@ -79,6 +79,7 @@ typedef struct HWVoiceOut {
int samples;
LIST_HEAD (sw_out_listhead, SWVoiceOut) sw_head;
LIST_HEAD (sw_cap_listhead, SWVoiceOut) sw_cap_head;
struct audio_pcm_ops *pcm_ops;
LIST_ENTRY (HWVoiceOut) entries;
} HWVoiceOut;
......@@ -115,6 +116,7 @@ struct SWVoiceOut {
volume_t vol;
struct audio_callback callback;
LIST_ENTRY (SWVoiceOut) entries;
LIST_ENTRY (SWVoiceOut) cap_entries;
};
struct SWVoiceIn {
......@@ -160,14 +162,28 @@ struct audio_pcm_ops {
int (*ctl_in) (HWVoiceIn *hw, int cmd, ...);
};
struct capture_callback {
struct audio_capture_ops ops;
void *opaque;
LIST_ENTRY (capture_callback) entries;
};
typedef struct CaptureVoiceOut {
HWVoiceOut hw;
void *buf;
LIST_HEAD (cb_listhead, capture_callback) cb_head;
LIST_ENTRY (CaptureVoiceOut) entries;
} CaptureVoiceOut;
struct AudioState {
struct audio_driver *drv;
void *drv_opaque;
QEMUTimer *ts;
LIST_HEAD (card_head, QEMUSoundCard) card_head;
LIST_HEAD (card_listhead, QEMUSoundCard) card_head;
LIST_HEAD (hw_in_listhead, HWVoiceIn) hw_head_in;
LIST_HEAD (hw_out_listhead, HWVoiceOut) hw_head_out;
LIST_HEAD (cap_listhead, CaptureVoiceOut) cap_head;
int nb_hw_voices_out;
int nb_hw_voices_in;
};
......
......@@ -200,6 +200,9 @@ static void glue (audio_pcm_hw_gc_, TYPE) (AudioState *s, HW **hwp)
HW *hw = *hwp;
if (!hw->sw_head.lh_first) {
#ifdef DAC
audio_detach_capture (hw);
#endif
LIST_REMOVE (hw, entries);
glue (s->nb_hw_voices_, TYPE) += 1;
glue (audio_pcm_hw_free_resources_ ,TYPE) (hw);
......@@ -266,7 +269,9 @@ static HW *glue (audio_pcm_hw_add_new_, TYPE) (AudioState *s, audsettings_t *as)
hw->pcm_ops = drv->pcm_ops;
LIST_INIT (&hw->sw_head);
#ifdef DAC
LIST_INIT (&hw->sw_cap_head);
#endif
if (glue (hw->pcm_ops->init_, TYPE) (hw, as)) {
goto err0;
}
......@@ -292,6 +297,9 @@ static HW *glue (audio_pcm_hw_add_new_, TYPE) (AudioState *s, audsettings_t *as)
LIST_INSERT_HEAD (&s->glue (hw_head_, TYPE), hw, entries);
glue (s->nb_hw_voices_, TYPE) -= 1;
#ifdef DAC
audio_attach_capture (s, hw);
#endif
return hw;
err1:
......@@ -542,7 +550,7 @@ uint64_t glue (AUD_get_elapsed_usec_, TYPE) (SW *sw, QEMUAudioTimeStamp *ts)
cur_ts = sw->hw->ts_helper;
old_ts = ts->old_ts;
/* dolog ("cur %" PRId64 " old %" PRId64 "\n", cur_ts, old_ts); */
/* dolog ("cur %lld old %lld\n", cur_ts, old_ts); */
if (cur_ts >= old_ts) {
delta = cur_ts - old_ts;
......
......@@ -275,8 +275,6 @@ static OSStatus audioDeviceIOProc(
#endif
}
/* cleanup */
mixeng_clear (src, frameCount);
rpos = (rpos + frameCount) % hw->samples;
core->decr += frameCount;
core->rpos = rpos;
......
......@@ -70,7 +70,13 @@ static int glue (dsound_lock_, TYPE) (
int i;
LPVOID p1 = NULL, p2 = NULL;
DWORD blen1 = 0, blen2 = 0;
DWORD flag;
#ifdef DSBTYPE_IN
flag = entire ? DSCBLOCK_ENTIREBUFFER : 0;
#else
flag = entire ? DSBLOCK_ENTIREBUFFER : 0;
#endif
for (i = 0; i < conf.lock_retries; ++i) {
hr = glue (IFACE, _Lock) (
buf,
......@@ -80,13 +86,7 @@ static int glue (dsound_lock_, TYPE) (
&blen1,
&p2,
&blen2,
(entire
#ifdef DSBTYPE_IN
? DSCBLOCK_ENTIREBUFFER
#else
? DSBLOCK_ENTIREBUFFER
#endif
: 0)
flag
);
if (FAILED (hr)) {
......
......@@ -453,13 +453,11 @@ static void dsound_write_sample (HWVoiceOut *hw, uint8_t *dst, int dst_len)
if (src_len1) {
hw->clip (dst, src1, src_len1);
mixeng_clear (src1, src_len1);
}
if (src_len2) {
dst = advance (dst, src_len1 << hw->info.shift);
hw->clip (dst, src2, src_len2);
mixeng_clear (src2, src_len2);
}
hw->rpos = pos % hw->samples;
......@@ -987,6 +985,12 @@ static void *dsound_audio_init (void)
hr = IDirectSound_Initialize (s->dsound, NULL);
if (FAILED (hr)) {
dsound_logerr (hr, "Could not initialize DirectSound\n");
hr = IDirectSound_Release (s->dsound);
if (FAILED (hr)) {
dsound_logerr (hr, "Could not release DirectSound\n");