audio fixes + initial audio capture support (malc)
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@2040 c046a42c-6fe2-441c-8c8c-71466251a162
This commit is contained in:
parent
feea13e186
commit
8ead62cfc2
@ -61,8 +61,8 @@ static struct {
|
|||||||
.size_in_usec_in = 1,
|
.size_in_usec_in = 1,
|
||||||
.size_in_usec_out = 1,
|
.size_in_usec_out = 1,
|
||||||
#endif
|
#endif
|
||||||
.pcm_name_out = "hw:0,0",
|
.pcm_name_out = "default",
|
||||||
.pcm_name_in = "hw:0,0",
|
.pcm_name_in = "default",
|
||||||
#ifdef HIGH_LATENCY
|
#ifdef HIGH_LATENCY
|
||||||
.buffer_size_in = 400000,
|
.buffer_size_in = 400000,
|
||||||
.period_size_in = 400000 / 4,
|
.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;
|
rpos = (rpos + written) % hw->samples;
|
||||||
samples -= written;
|
samples -= written;
|
||||||
len -= written;
|
len -= written;
|
||||||
|
327
audio/audio.c
327
audio/audio.c
@ -29,6 +29,7 @@
|
|||||||
/* #define DEBUG_PLIVE */
|
/* #define DEBUG_PLIVE */
|
||||||
/* #define DEBUG_LIVE */
|
/* #define DEBUG_LIVE */
|
||||||
/* #define DEBUG_OUT */
|
/* #define DEBUG_OUT */
|
||||||
|
/* #define DEBUG_CAPTURE */
|
||||||
|
|
||||||
#define SW_NAME(sw) (sw)->name ? (sw)->name : "unknown"
|
#define SW_NAME(sw) (sw)->name ? (sw)->name : "unknown"
|
||||||
|
|
||||||
@ -137,7 +138,7 @@ int audio_bug (const char *funcname, int cond)
|
|||||||
if (cond) {
|
if (cond) {
|
||||||
static int shown;
|
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) {
|
if (!shown) {
|
||||||
shown = 1;
|
shown = 1;
|
||||||
AUD_log (NULL, "Save all your work and restart without audio\n");
|
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)
|
* Hard voice (capture)
|
||||||
*/
|
*/
|
||||||
@ -916,17 +1032,11 @@ void AUD_set_active_out (SWVoiceOut *sw, int on)
|
|||||||
SWVoiceOut *temp_sw;
|
SWVoiceOut *temp_sw;
|
||||||
|
|
||||||
if (on) {
|
if (on) {
|
||||||
int total;
|
|
||||||
|
|
||||||
hw->pending_disable = 0;
|
hw->pending_disable = 0;
|
||||||
if (!hw->enabled) {
|
if (!hw->enabled) {
|
||||||
hw->enabled = 1;
|
hw->enabled = 1;
|
||||||
hw->pcm_ops->ctl_out (hw, VOICE_ENABLE);
|
hw->pcm_ops->ctl_out (hw, VOICE_ENABLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sw->empty) {
|
|
||||||
total = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (hw->enabled) {
|
if (hw->enabled) {
|
||||||
@ -940,6 +1050,13 @@ void AUD_set_active_out (SWVoiceOut *sw, int on)
|
|||||||
hw->pending_disable = nb_active == 1;
|
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;
|
sw->active = on;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1031,6 +1148,41 @@ static int audio_get_free (SWVoiceOut *sw)
|
|||||||
return (((int64_t) dead << 32) / sw->ratio) << sw->info.shift;
|
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)
|
static void audio_run_out (AudioState *s)
|
||||||
{
|
{
|
||||||
HWVoiceOut *hw = NULL;
|
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))) {
|
while ((hw = audio_pcm_hw_find_any_enabled_out (s, hw))) {
|
||||||
int played;
|
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);
|
live = audio_pcm_hw_get_live_out2 (hw, &nb_live);
|
||||||
if (!nb_live) {
|
if (!nb_live) {
|
||||||
@ -1057,6 +1209,11 @@ static void audio_run_out (AudioState *s)
|
|||||||
hw->enabled = 0;
|
hw->enabled = 0;
|
||||||
hw->pending_disable = 0;
|
hw->pending_disable = 0;
|
||||||
hw->pcm_ops->ctl_out (hw, VOICE_DISABLE);
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1072,6 +1229,7 @@ static void audio_run_out (AudioState *s)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
prev_rpos = hw->rpos;
|
||||||
played = hw->pcm_ops->run_out (hw);
|
played = hw->pcm_ops->run_out (hw);
|
||||||
if (audio_bug (AUDIO_FUNC, hw->rpos >= hw->samples)) {
|
if (audio_bug (AUDIO_FUNC, hw->rpos >= hw->samples)) {
|
||||||
dolog ("hw->rpos=%d hw->samples=%d played=%d\n",
|
dolog ("hw->rpos=%d hw->samples=%d played=%d\n",
|
||||||
@ -1085,6 +1243,7 @@ static void audio_run_out (AudioState *s)
|
|||||||
|
|
||||||
if (played) {
|
if (played) {
|
||||||
hw->ts_helper += played;
|
hw->ts_helper += played;
|
||||||
|
audio_capture_mix_and_clear (hw, prev_rpos, played);
|
||||||
}
|
}
|
||||||
|
|
||||||
cleanup_required = 0;
|
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)
|
static void audio_timer (void *opaque)
|
||||||
{
|
{
|
||||||
AudioState *s = opaque;
|
AudioState *s = opaque;
|
||||||
|
|
||||||
audio_run_out (s);
|
audio_run_out (s);
|
||||||
audio_run_in (s);
|
audio_run_in (s);
|
||||||
|
audio_run_capture (s);
|
||||||
|
|
||||||
qemu_mod_timer (s->ts, qemu_get_clock (vm_clock) + conf.period.ticks);
|
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;
|
HWVoiceIn *hwi = NULL;
|
||||||
|
|
||||||
while ((hwo = audio_pcm_hw_find_any_enabled_out (s, hwo))) {
|
while ((hwo = audio_pcm_hw_find_any_enabled_out (s, hwo))) {
|
||||||
|
SWVoiceOut *sw;
|
||||||
|
|
||||||
hwo->pcm_ops->ctl_out (hwo, VOICE_DISABLE);
|
hwo->pcm_ops->ctl_out (hwo, VOICE_DISABLE);
|
||||||
hwo->pcm_ops->fini_out (hwo);
|
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))) {
|
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_out);
|
||||||
LIST_INIT (&s->hw_head_in);
|
LIST_INIT (&s->hw_head_in);
|
||||||
|
LIST_INIT (&s->cap_head);
|
||||||
atexit (audio_atexit);
|
atexit (audio_atexit);
|
||||||
|
|
||||||
s->ts = qemu_new_timer (vm_clock, audio_timer, s);
|
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);
|
qemu_mod_timer (s->ts, qemu_get_clock (vm_clock) + conf.period.ticks);
|
||||||
return s;
|
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;
|
audfmt_e fmt;
|
||||||
} audsettings_t;
|
} 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 AudioState AudioState;
|
||||||
typedef struct SWVoiceOut SWVoiceOut;
|
typedef struct SWVoiceOut SWVoiceOut;
|
||||||
typedef struct SWVoiceIn SWVoiceIn;
|
typedef struct SWVoiceIn SWVoiceIn;
|
||||||
@ -66,6 +71,13 @@ AudioState *AUD_init (void);
|
|||||||
void AUD_help (void);
|
void AUD_help (void);
|
||||||
void AUD_register_card (AudioState *s, const char *name, QEMUSoundCard *card);
|
void AUD_register_card (AudioState *s, const char *name, QEMUSoundCard *card);
|
||||||
void AUD_remove_card (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 (
|
SWVoiceOut *AUD_open_out (
|
||||||
QEMUSoundCard *card,
|
QEMUSoundCard *card,
|
||||||
@ -111,7 +123,7 @@ static inline void *advance (void *p, int incr)
|
|||||||
}
|
}
|
||||||
|
|
||||||
uint32_t popcount (uint32_t u);
|
uint32_t popcount (uint32_t u);
|
||||||
inline uint32_t lsbindex (uint32_t u);
|
uint32_t lsbindex (uint32_t u);
|
||||||
|
|
||||||
#ifdef __GNUC__
|
#ifdef __GNUC__
|
||||||
#define audio_MIN(a, b) ( __extension__ ({ \
|
#define audio_MIN(a, b) ( __extension__ ({ \
|
||||||
|
@ -79,6 +79,7 @@ typedef struct HWVoiceOut {
|
|||||||
|
|
||||||
int samples;
|
int samples;
|
||||||
LIST_HEAD (sw_out_listhead, SWVoiceOut) sw_head;
|
LIST_HEAD (sw_out_listhead, SWVoiceOut) sw_head;
|
||||||
|
LIST_HEAD (sw_cap_listhead, SWVoiceOut) sw_cap_head;
|
||||||
struct audio_pcm_ops *pcm_ops;
|
struct audio_pcm_ops *pcm_ops;
|
||||||
LIST_ENTRY (HWVoiceOut) entries;
|
LIST_ENTRY (HWVoiceOut) entries;
|
||||||
} HWVoiceOut;
|
} HWVoiceOut;
|
||||||
@ -115,6 +116,7 @@ struct SWVoiceOut {
|
|||||||
volume_t vol;
|
volume_t vol;
|
||||||
struct audio_callback callback;
|
struct audio_callback callback;
|
||||||
LIST_ENTRY (SWVoiceOut) entries;
|
LIST_ENTRY (SWVoiceOut) entries;
|
||||||
|
LIST_ENTRY (SWVoiceOut) cap_entries;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SWVoiceIn {
|
struct SWVoiceIn {
|
||||||
@ -160,14 +162,28 @@ struct audio_pcm_ops {
|
|||||||
int (*ctl_in) (HWVoiceIn *hw, int cmd, ...);
|
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 AudioState {
|
||||||
struct audio_driver *drv;
|
struct audio_driver *drv;
|
||||||
void *drv_opaque;
|
void *drv_opaque;
|
||||||
|
|
||||||
QEMUTimer *ts;
|
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_in_listhead, HWVoiceIn) hw_head_in;
|
||||||
LIST_HEAD (hw_out_listhead, HWVoiceOut) hw_head_out;
|
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_out;
|
||||||
int nb_hw_voices_in;
|
int nb_hw_voices_in;
|
||||||
};
|
};
|
||||||
|
@ -200,6 +200,9 @@ static void glue (audio_pcm_hw_gc_, TYPE) (AudioState *s, HW **hwp)
|
|||||||
HW *hw = *hwp;
|
HW *hw = *hwp;
|
||||||
|
|
||||||
if (!hw->sw_head.lh_first) {
|
if (!hw->sw_head.lh_first) {
|
||||||
|
#ifdef DAC
|
||||||
|
audio_detach_capture (hw);
|
||||||
|
#endif
|
||||||
LIST_REMOVE (hw, entries);
|
LIST_REMOVE (hw, entries);
|
||||||
glue (s->nb_hw_voices_, TYPE) += 1;
|
glue (s->nb_hw_voices_, TYPE) += 1;
|
||||||
glue (audio_pcm_hw_free_resources_ ,TYPE) (hw);
|
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;
|
hw->pcm_ops = drv->pcm_ops;
|
||||||
LIST_INIT (&hw->sw_head);
|
LIST_INIT (&hw->sw_head);
|
||||||
|
#ifdef DAC
|
||||||
|
LIST_INIT (&hw->sw_cap_head);
|
||||||
|
#endif
|
||||||
if (glue (hw->pcm_ops->init_, TYPE) (hw, as)) {
|
if (glue (hw->pcm_ops->init_, TYPE) (hw, as)) {
|
||||||
goto err0;
|
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);
|
LIST_INSERT_HEAD (&s->glue (hw_head_, TYPE), hw, entries);
|
||||||
glue (s->nb_hw_voices_, TYPE) -= 1;
|
glue (s->nb_hw_voices_, TYPE) -= 1;
|
||||||
|
#ifdef DAC
|
||||||
|
audio_attach_capture (s, hw);
|
||||||
|
#endif
|
||||||
return hw;
|
return hw;
|
||||||
|
|
||||||
err1:
|
err1:
|
||||||
@ -542,7 +550,7 @@ uint64_t glue (AUD_get_elapsed_usec_, TYPE) (SW *sw, QEMUAudioTimeStamp *ts)
|
|||||||
|
|
||||||
cur_ts = sw->hw->ts_helper;
|
cur_ts = sw->hw->ts_helper;
|
||||||
old_ts = ts->old_ts;
|
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) {
|
if (cur_ts >= old_ts) {
|
||||||
delta = cur_ts - old_ts;
|
delta = cur_ts - old_ts;
|
||||||
|
@ -275,8 +275,6 @@ static OSStatus audioDeviceIOProc(
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/* cleanup */
|
|
||||||
mixeng_clear (src, frameCount);
|
|
||||||
rpos = (rpos + frameCount) % hw->samples;
|
rpos = (rpos + frameCount) % hw->samples;
|
||||||
core->decr += frameCount;
|
core->decr += frameCount;
|
||||||
core->rpos = rpos;
|
core->rpos = rpos;
|
||||||
|
@ -70,7 +70,13 @@ static int glue (dsound_lock_, TYPE) (
|
|||||||
int i;
|
int i;
|
||||||
LPVOID p1 = NULL, p2 = NULL;
|
LPVOID p1 = NULL, p2 = NULL;
|
||||||
DWORD blen1 = 0, blen2 = 0;
|
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) {
|
for (i = 0; i < conf.lock_retries; ++i) {
|
||||||
hr = glue (IFACE, _Lock) (
|
hr = glue (IFACE, _Lock) (
|
||||||
buf,
|
buf,
|
||||||
@ -80,13 +86,7 @@ static int glue (dsound_lock_, TYPE) (
|
|||||||
&blen1,
|
&blen1,
|
||||||
&p2,
|
&p2,
|
||||||
&blen2,
|
&blen2,
|
||||||
(entire
|
flag
|
||||||
#ifdef DSBTYPE_IN
|
|
||||||
? DSCBLOCK_ENTIREBUFFER
|
|
||||||
#else
|
|
||||||
? DSBLOCK_ENTIREBUFFER
|
|
||||||
#endif
|
|
||||||
: 0)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (FAILED (hr)) {
|
if (FAILED (hr)) {
|
||||||
|
@ -453,13 +453,11 @@ static void dsound_write_sample (HWVoiceOut *hw, uint8_t *dst, int dst_len)
|
|||||||
|
|
||||||
if (src_len1) {
|
if (src_len1) {
|
||||||
hw->clip (dst, src1, src_len1);
|
hw->clip (dst, src1, src_len1);
|
||||||
mixeng_clear (src1, src_len1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (src_len2) {
|
if (src_len2) {
|
||||||
dst = advance (dst, src_len1 << hw->info.shift);
|
dst = advance (dst, src_len1 << hw->info.shift);
|
||||||
hw->clip (dst, src2, src_len2);
|
hw->clip (dst, src2, src_len2);
|
||||||
mixeng_clear (src2, src_len2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
hw->rpos = pos % hw->samples;
|
hw->rpos = pos % hw->samples;
|
||||||
@ -987,6 +985,12 @@ static void *dsound_audio_init (void)
|
|||||||
hr = IDirectSound_Initialize (s->dsound, NULL);
|
hr = IDirectSound_Initialize (s->dsound, NULL);
|
||||||
if (FAILED (hr)) {
|
if (FAILED (hr)) {
|
||||||
dsound_logerr (hr, "Could not initialize DirectSound\n");
|
dsound_logerr (hr, "Could not initialize DirectSound\n");
|
||||||
|
|
||||||
|
hr = IDirectSound_Release (s->dsound);
|
||||||
|
if (FAILED (hr)) {
|
||||||
|
dsound_logerr (hr, "Could not release DirectSound\n");
|
||||||
|
}
|
||||||
|
s->dsound = NULL;
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -153,13 +153,11 @@ static void fmod_write_sample (HWVoiceOut *hw, uint8_t *dst, int dst_len)
|
|||||||
|
|
||||||
if (src_len1) {
|
if (src_len1) {
|
||||||
hw->clip (dst, src1, src_len1);
|
hw->clip (dst, src1, src_len1);
|
||||||
mixeng_clear (src1, src_len1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (src_len2) {
|
if (src_len2) {
|
||||||
dst = advance (dst, src_len1 << hw->info.shift);
|
dst = advance (dst, src_len1 << hw->info.shift);
|
||||||
hw->clip (dst, src2, src_len2);
|
hw->clip (dst, src2, src_len2);
|
||||||
mixeng_clear (src2, src_len2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
hw->rpos = pos % hw->samples;
|
hw->rpos = pos % hw->samples;
|
||||||
|
@ -40,22 +40,21 @@ static int no_run_out (HWVoiceOut *hw)
|
|||||||
{
|
{
|
||||||
NoVoiceOut *no = (NoVoiceOut *) hw;
|
NoVoiceOut *no = (NoVoiceOut *) hw;
|
||||||
int live, decr, samples;
|
int live, decr, samples;
|
||||||
int64_t now = qemu_get_clock (vm_clock);
|
int64_t now;
|
||||||
int64_t ticks = now - no->old_ticks;
|
int64_t ticks;
|
||||||
int64_t bytes = (ticks * hw->info.bytes_per_second) / ticks_per_sec;
|
int64_t bytes;
|
||||||
|
|
||||||
if (bytes > INT_MAX) {
|
|
||||||
samples = INT_MAX >> hw->info.shift;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
samples = bytes >> hw->info.shift;
|
|
||||||
}
|
|
||||||
|
|
||||||
live = audio_pcm_hw_get_live_out (&no->hw);
|
live = audio_pcm_hw_get_live_out (&no->hw);
|
||||||
if (!live) {
|
if (!live) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
now = qemu_get_clock (vm_clock);
|
||||||
|
ticks = now - no->old_ticks;
|
||||||
|
bytes = (ticks * hw->info.bytes_per_second) / ticks_per_sec;
|
||||||
|
bytes = audio_MIN (bytes, INT_MAX);
|
||||||
|
samples = bytes >> hw->info.shift;
|
||||||
|
|
||||||
no->old_ticks = now;
|
no->old_ticks = now;
|
||||||
decr = audio_MIN (live, samples);
|
decr = audio_MIN (live, samples);
|
||||||
hw->rpos = (hw->rpos + decr) % hw->samples;
|
hw->rpos = (hw->rpos + decr) % hw->samples;
|
||||||
@ -101,17 +100,20 @@ static void no_fini_in (HWVoiceIn *hw)
|
|||||||
static int no_run_in (HWVoiceIn *hw)
|
static int no_run_in (HWVoiceIn *hw)
|
||||||
{
|
{
|
||||||
NoVoiceIn *no = (NoVoiceIn *) hw;
|
NoVoiceIn *no = (NoVoiceIn *) hw;
|
||||||
int64_t now = qemu_get_clock (vm_clock);
|
|
||||||
int64_t ticks = now - no->old_ticks;
|
|
||||||
int64_t bytes = (ticks * hw->info.bytes_per_second) / ticks_per_sec;
|
|
||||||
int live = audio_pcm_hw_get_live_in (hw);
|
int live = audio_pcm_hw_get_live_in (hw);
|
||||||
int dead = hw->samples - live;
|
int dead = hw->samples - live;
|
||||||
int samples;
|
int samples;
|
||||||
|
|
||||||
|
if (dead) {
|
||||||
|
int64_t now = qemu_get_clock (vm_clock);
|
||||||
|
int64_t ticks = now - no->old_ticks;
|
||||||
|
int64_t bytes = (ticks * hw->info.bytes_per_second) / ticks_per_sec;
|
||||||
|
|
||||||
|
no->old_ticks = now;
|
||||||
bytes = audio_MIN (bytes, INT_MAX);
|
bytes = audio_MIN (bytes, INT_MAX);
|
||||||
samples = bytes >> hw->info.shift;
|
samples = bytes >> hw->info.shift;
|
||||||
samples = audio_MIN (samples, dead);
|
samples = audio_MIN (samples, dead);
|
||||||
|
}
|
||||||
return samples;
|
return samples;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,12 +55,14 @@ static struct {
|
|||||||
int fragsize;
|
int fragsize;
|
||||||
const char *devpath_out;
|
const char *devpath_out;
|
||||||
const char *devpath_in;
|
const char *devpath_in;
|
||||||
|
int debug;
|
||||||
} conf = {
|
} conf = {
|
||||||
.try_mmap = 0,
|
.try_mmap = 0,
|
||||||
.nfrags = 4,
|
.nfrags = 4,
|
||||||
.fragsize = 4096,
|
.fragsize = 4096,
|
||||||
.devpath_out = "/dev/dsp",
|
.devpath_out = "/dev/dsp",
|
||||||
.devpath_in = "/dev/dsp"
|
.devpath_in = "/dev/dsp",
|
||||||
|
.debug = 0
|
||||||
};
|
};
|
||||||
|
|
||||||
struct oss_params {
|
struct oss_params {
|
||||||
@ -324,9 +326,20 @@ static int oss_run_out (HWVoiceOut *hw)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (abinfo.bytes < 0 || abinfo.bytes > bufsize) {
|
if (abinfo.bytes > bufsize) {
|
||||||
ldebug ("warning: Invalid available size, size=%d bufsize=%d\n",
|
if (conf.debug) {
|
||||||
|
dolog ("warning: Invalid available size, size=%d bufsize=%d\n"
|
||||||
|
"please report your OS/audio hw to malc@pulsesoft.com\n",
|
||||||
abinfo.bytes, bufsize);
|
abinfo.bytes, bufsize);
|
||||||
|
}
|
||||||
|
abinfo.bytes = bufsize;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (abinfo.bytes < 0) {
|
||||||
|
if (conf.debug) {
|
||||||
|
dolog ("warning: Invalid available size, size=%d bufsize=%d\n",
|
||||||
|
abinfo.bytes, bufsize);
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -369,15 +382,12 @@ static int oss_run_out (HWVoiceOut *hw)
|
|||||||
"alignment %d\n",
|
"alignment %d\n",
|
||||||
wbytes, written, hw->info.align + 1);
|
wbytes, written, hw->info.align + 1);
|
||||||
}
|
}
|
||||||
mixeng_clear (src, wsamples);
|
|
||||||
decr -= wsamples;
|
decr -= wsamples;
|
||||||
rpos = (rpos + wsamples) % hw->samples;
|
rpos = (rpos + wsamples) % hw->samples;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mixeng_clear (src, convert_samples);
|
|
||||||
|
|
||||||
rpos = (rpos + convert_samples) % hw->samples;
|
rpos = (rpos + convert_samples) % hw->samples;
|
||||||
samples -= convert_samples;
|
samples -= convert_samples;
|
||||||
}
|
}
|
||||||
@ -730,6 +740,8 @@ static struct audio_option oss_options[] = {
|
|||||||
"Path to DAC device", NULL, 0},
|
"Path to DAC device", NULL, 0},
|
||||||
{"ADC_DEV", AUD_OPT_STR, &conf.devpath_in,
|
{"ADC_DEV", AUD_OPT_STR, &conf.devpath_in,
|
||||||
"Path to ADC device", NULL, 0},
|
"Path to ADC device", NULL, 0},
|
||||||
|
{"DEBUG", AUD_OPT_BOOL, &conf.debug,
|
||||||
|
"Turn on some debugging messages", NULL, 0},
|
||||||
{NULL, 0, NULL, NULL, NULL, 0}
|
{NULL, 0, NULL, NULL, NULL, 0}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -240,7 +240,6 @@ static void sdl_callback (void *opaque, Uint8 *buf, int len)
|
|||||||
|
|
||||||
/* dolog ("in callback to_mix %d, chunk %d\n", to_mix, chunk); */
|
/* dolog ("in callback to_mix %d, chunk %d\n", to_mix, chunk); */
|
||||||
hw->clip (buf, src, chunk);
|
hw->clip (buf, src, chunk);
|
||||||
mixeng_clear (src, chunk);
|
|
||||||
sdl->rpos = (sdl->rpos + chunk) % hw->samples;
|
sdl->rpos = (sdl->rpos + chunk) % hw->samples;
|
||||||
to_mix -= chunk;
|
to_mix -= chunk;
|
||||||
buf += chunk << hw->info.shift;
|
buf += chunk << hw->info.shift;
|
||||||
|
@ -81,7 +81,6 @@ static int wav_run_out (HWVoiceOut *hw)
|
|||||||
|
|
||||||
hw->clip (dst, src, convert_samples);
|
hw->clip (dst, src, convert_samples);
|
||||||
qemu_put_buffer (wav->f, dst, convert_samples << hw->info.shift);
|
qemu_put_buffer (wav->f, dst, convert_samples << hw->info.shift);
|
||||||
mixeng_clear (src, convert_samples);
|
|
||||||
|
|
||||||
rpos = (rpos + convert_samples) % hw->samples;
|
rpos = (rpos + convert_samples) % hw->samples;
|
||||||
samples -= convert_samples;
|
samples -= convert_samples;
|
||||||
|
101
audio/wavcapture.c
Normal file
101
audio/wavcapture.c
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
#include "vl.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
QEMUFile *f;
|
||||||
|
int bytes;
|
||||||
|
} WAVState;
|
||||||
|
|
||||||
|
/* VICE code: Store number as little endian. */
|
||||||
|
static void le_store (uint8_t *buf, uint32_t val, int len)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < len; i++) {
|
||||||
|
buf[i] = (uint8_t) (val & 0xff);
|
||||||
|
val >>= 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void wav_state_cb (void *opaque, int enabled)
|
||||||
|
{
|
||||||
|
WAVState *wav = opaque;
|
||||||
|
|
||||||
|
if (!enabled) {
|
||||||
|
uint8_t rlen[4];
|
||||||
|
uint8_t dlen[4];
|
||||||
|
uint32_t datalen = wav->bytes;
|
||||||
|
uint32_t rifflen = datalen + 36;
|
||||||
|
|
||||||
|
if (!wav->f) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
le_store (rlen, rifflen, 4);
|
||||||
|
le_store (dlen, datalen, 4);
|
||||||
|
|
||||||
|
qemu_fseek (wav->f, 4, SEEK_SET);
|
||||||
|
qemu_put_buffer (wav->f, rlen, 4);
|
||||||
|
|
||||||
|
qemu_fseek (wav->f, 32, SEEK_CUR);
|
||||||
|
qemu_put_buffer (wav->f, dlen, 4);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
qemu_fseek (wav->f, 0, SEEK_END);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void wav_capture_cb (void *opaque, void *buf, int size)
|
||||||
|
{
|
||||||
|
WAVState *wav = opaque;
|
||||||
|
|
||||||
|
qemu_put_buffer (wav->f, buf, size);
|
||||||
|
wav->bytes += size;
|
||||||
|
}
|
||||||
|
|
||||||
|
void wav_capture (const char *path, int freq, int bits16, int stereo)
|
||||||
|
{
|
||||||
|
WAVState *wav;
|
||||||
|
uint8_t hdr[] = {
|
||||||
|
0x52, 0x49, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x57, 0x41, 0x56,
|
||||||
|
0x45, 0x66, 0x6d, 0x74, 0x20, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00,
|
||||||
|
0x02, 0x00, 0x44, 0xac, 0x00, 0x00, 0x10, 0xb1, 0x02, 0x00, 0x04,
|
||||||
|
0x00, 0x10, 0x00, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00
|
||||||
|
};
|
||||||
|
audsettings_t as;
|
||||||
|
struct audio_capture_ops ops;
|
||||||
|
int shift;
|
||||||
|
|
||||||
|
stereo = !!stereo;
|
||||||
|
bits16 = !!bits16;
|
||||||
|
|
||||||
|
as.freq = freq;
|
||||||
|
as.nchannels = 1 << stereo;
|
||||||
|
as.fmt = bits16 ? AUD_FMT_S16 : AUD_FMT_U8;
|
||||||
|
|
||||||
|
ops.state = wav_state_cb;
|
||||||
|
ops.capture = wav_capture_cb;
|
||||||
|
|
||||||
|
wav = qemu_mallocz (sizeof (*wav));
|
||||||
|
if (!wav) {
|
||||||
|
AUD_log ("wav", "Could not allocate memory (%zu bytes)", sizeof (*wav));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
shift = bits16 + stereo;
|
||||||
|
hdr[34] = bits16 ? 0x10 : 0x08;
|
||||||
|
|
||||||
|
le_store (hdr + 22, as.nchannels, 2);
|
||||||
|
le_store (hdr + 24, freq, 4);
|
||||||
|
le_store (hdr + 28, freq << shift, 4);
|
||||||
|
le_store (hdr + 32, 1 << shift, 2);
|
||||||
|
|
||||||
|
wav->f = fopen (path, "wb");
|
||||||
|
if (!wav->f) {
|
||||||
|
AUD_log ("wav", "Failed to open wave file `%s'\nReason: %s\n",
|
||||||
|
path, strerror (errno));
|
||||||
|
qemu_free (wav);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
qemu_put_buffer (wav->f, hdr, sizeof (hdr));
|
||||||
|
AUD_add_capture (NULL, &as, 0, &ops, wav);
|
||||||
|
}
|
@ -479,9 +479,10 @@ static inline uint32_t es1370_fixup (ES1370State *s, uint32_t addr)
|
|||||||
IO_WRITE_PROTO (es1370_writeb)
|
IO_WRITE_PROTO (es1370_writeb)
|
||||||
{
|
{
|
||||||
ES1370State *s = opaque;
|
ES1370State *s = opaque;
|
||||||
addr = es1370_fixup (s, addr);
|
|
||||||
uint32_t shift, mask;
|
uint32_t shift, mask;
|
||||||
|
|
||||||
|
addr = es1370_fixup (s, addr);
|
||||||
|
|
||||||
switch (addr) {
|
switch (addr) {
|
||||||
case ES1370_REG_CONTROL:
|
case ES1370_REG_CONTROL:
|
||||||
case ES1370_REG_CONTROL + 1:
|
case ES1370_REG_CONTROL + 1:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user