aboutsummaryrefslogtreecommitdiff
path: root/audio
diff options
context:
space:
mode:
Diffstat (limited to 'audio')
-rw-r--r--audio/audio.c216
-rw-r--r--audio/audio_int.h43
-rw-r--r--audio/audio_template.h1
3 files changed, 251 insertions, 9 deletions
diff --git a/audio/audio.c b/audio/audio.c
index e99fcd0694..e29a1e15eb 100644
--- a/audio/audio.c
+++ b/audio/audio.c
@@ -573,6 +573,25 @@ size_t audio_pcm_hw_clip_out(HWVoiceOut *hw, void *pcm_buf,
return clipped;
}
+static void audio_pcm_hw_clip_out2(HWVoiceOut *hw, void *pcm_buf, size_t len)
+{
+ size_t clipped = 0;
+ size_t pos = hw->rpos;
+
+ while (len) {
+ st_sample *src = hw->mix_buf + pos;
+ uint8_t *dst = advance(pcm_buf, clipped << hw->info.shift);
+ size_t samples_till_end_of_buf = hw->samples - pos;
+ size_t samples_to_clip = MIN(len, samples_till_end_of_buf);
+
+ hw->clip(dst, src, samples_to_clip);
+
+ pos = (pos + samples_to_clip) % hw->samples;
+ len -= samples_to_clip;
+ clipped += samples_to_clip;
+ }
+}
+
/*
* Soft voice (capture)
*/
@@ -1050,6 +1069,36 @@ static void audio_capture_mix_and_clear(HWVoiceOut *hw, size_t rpos,
mixeng_clear(hw->mix_buf, samples - n);
}
+static size_t audio_pcm_hw_run_out(HWVoiceOut *hw, size_t live)
+{
+ size_t clipped = 0;
+
+ while (live) {
+ size_t size, decr, proc;
+ void *buf = hw->pcm_ops->get_buffer_out(hw, &size);
+ if (!buf) {
+ /* retrying will likely won't help, drop everything. */
+ hw->rpos = (hw->rpos + live) % hw->samples;
+ return clipped + live;
+ }
+
+ decr = MIN(size >> hw->info.shift, live);
+ audio_pcm_hw_clip_out2(hw, buf, decr);
+ proc = hw->pcm_ops->put_buffer_out(hw, buf, decr << hw->info.shift) >>
+ hw->info.shift;
+
+ live -= proc;
+ clipped += proc;
+ hw->rpos = (hw->rpos + proc) % hw->samples;
+
+ if (proc == 0 || proc < decr) {
+ break;
+ }
+ }
+
+ return clipped;
+}
+
static void audio_run_out (AudioState *s)
{
HWVoiceOut *hw = NULL;
@@ -1097,7 +1146,11 @@ static void audio_run_out (AudioState *s)
}
prev_rpos = hw->rpos;
- played = hw->pcm_ops->run_out (hw, live);
+ if (hw->pcm_ops->run_out) {
+ played = hw->pcm_ops->run_out(hw, live);
+ } else {
+ played = audio_pcm_hw_run_out(hw, live);
+ }
replay_audio_out(&played);
if (audio_bug(__func__, hw->rpos >= hw->samples)) {
dolog("hw->rpos=%zu hw->samples=%zu played=%zu\n",
@@ -1156,6 +1209,35 @@ static void audio_run_out (AudioState *s)
}
}
+static size_t audio_pcm_hw_run_in(HWVoiceIn *hw, size_t samples)
+{
+ size_t conv = 0;
+
+ while (samples) {
+ size_t proc;
+ size_t size = samples << hw->info.shift;
+ void *buf = hw->pcm_ops->get_buffer_in(hw, &size);
+
+ assert((size & hw->info.align) == 0);
+ if (size == 0) {
+ hw->pcm_ops->put_buffer_in(hw, buf, size);
+ break;
+ }
+
+ proc = MIN(size >> hw->info.shift,
+ hw->samples - hw->wpos);
+
+ hw->conv(hw->conv_buf + hw->wpos, buf, proc);
+ hw->wpos = (hw->wpos + proc) % hw->samples;
+
+ samples -= proc;
+ conv += proc;
+ hw->pcm_ops->put_buffer_in(hw, buf, proc << hw->info.shift);
+ }
+
+ return conv;
+}
+
static void audio_run_in (AudioState *s)
{
HWVoiceIn *hw = NULL;
@@ -1165,7 +1247,12 @@ static void audio_run_in (AudioState *s)
size_t captured = 0, min;
if (replay_mode != REPLAY_MODE_PLAY) {
- captured = hw->pcm_ops->run_in(hw);
+ if (hw->pcm_ops->run_in) {
+ captured = hw->pcm_ops->run_in(hw);
+ } else {
+ captured = audio_pcm_hw_run_in(
+ hw, hw->samples - audio_pcm_hw_get_live_in(hw));
+ }
}
replay_audio_in(&captured, hw->conv_buf, &hw->wpos, hw->samples);
@@ -1259,12 +1346,137 @@ void audio_run(AudioState *s, const char *msg)
#endif
}
+void *audio_generic_get_buffer_in(HWVoiceIn *hw, size_t *size)
+{
+ ssize_t start;
+
+ if (unlikely(!hw->buf_emul)) {
+ size_t calc_size = hw->samples << hw->info.shift;
+ hw->buf_emul = g_malloc(calc_size);
+ hw->size_emul = calc_size;
+ hw->pos_emul = hw->pending_emul = 0;
+ }
+
+ while (hw->pending_emul < hw->size_emul) {
+ size_t read_len = MIN(hw->size_emul - hw->pos_emul,
+ hw->size_emul - hw->pending_emul);
+ size_t read = hw->pcm_ops->read(hw, hw->buf_emul + hw->pos_emul,
+ read_len);
+ hw->pending_emul += read;
+ if (read < read_len) {
+ break;
+ }
+ }
+
+ start = ((ssize_t) hw->pos_emul) - hw->pending_emul;
+ if (start < 0) {
+ start += hw->size_emul;
+ }
+ assert(start >= 0 && start < hw->size_emul);
+
+ *size = MIN(hw->pending_emul, hw->size_emul - start);
+ return hw->buf_emul + start;
+}
+
+void audio_generic_put_buffer_in(HWVoiceIn *hw, void *buf, size_t size)
+{
+ assert(size <= hw->pending_emul);
+ hw->pending_emul -= size;
+}
+
+void *audio_generic_get_buffer_out(HWVoiceOut *hw, size_t *size)
+{
+ if (unlikely(!hw->buf_emul)) {
+ size_t calc_size = hw->samples << hw->info.shift;
+
+ hw->buf_emul = g_malloc(calc_size);
+ hw->size_emul = calc_size;
+ hw->pos_emul = hw->pending_emul = 0;
+ }
+
+ *size = MIN(hw->size_emul - hw->pending_emul,
+ hw->size_emul - hw->pos_emul);
+ return hw->buf_emul + hw->pos_emul;
+}
+
+size_t audio_generic_put_buffer_out_nowrite(HWVoiceOut *hw, void *buf,
+ size_t size)
+{
+ assert(buf == hw->buf_emul + hw->pos_emul &&
+ size + hw->pending_emul <= hw->size_emul);
+
+ hw->pending_emul += size;
+ hw->pos_emul = (hw->pos_emul + size) % hw->size_emul;
+
+ return size;
+}
+
+size_t audio_generic_put_buffer_out(HWVoiceOut *hw, void *buf, size_t size)
+{
+ audio_generic_put_buffer_out_nowrite(hw, buf, size);
+
+ while (hw->pending_emul) {
+ size_t write_len, written;
+ ssize_t start = ((ssize_t) hw->pos_emul) - hw->pending_emul;
+ if (start < 0) {
+ start += hw->size_emul;
+ }
+ assert(start >= 0 && start < hw->size_emul);
+
+ write_len = MIN(hw->pending_emul, hw->size_emul - start);
+
+ written = hw->pcm_ops->write(hw, hw->buf_emul + start, write_len);
+ hw->pending_emul -= written;
+
+ if (written < write_len) {
+ break;
+ }
+ }
+
+ /*
+ * fake we have written everything. non-written data remain in pending_emul,
+ * so we do not have to clip them multiple times
+ */
+ return size;
+}
+
+size_t audio_generic_write(HWVoiceOut *hw, void *buf, size_t size)
+{
+ size_t dst_size, copy_size;
+ void *dst = hw->pcm_ops->get_buffer_out(hw, &dst_size);
+ copy_size = MIN(size, dst_size);
+
+ memcpy(dst, buf, copy_size);
+ return hw->pcm_ops->put_buffer_out(hw, buf, copy_size);
+}
+
+size_t audio_generic_read(HWVoiceIn *hw, void *buf, size_t size)
+{
+ size_t dst_size, copy_size;
+ void *dst = hw->pcm_ops->get_buffer_in(hw, &dst_size);
+ copy_size = MIN(size, dst_size);
+
+ memcpy(dst, buf, copy_size);
+ hw->pcm_ops->put_buffer_in(hw, buf, copy_size);
+ return copy_size;
+}
+
+
static int audio_driver_init(AudioState *s, struct audio_driver *drv,
bool msg, Audiodev *dev)
{
s->drv_opaque = drv->init(dev);
if (s->drv_opaque) {
+ if (!drv->pcm_ops->get_buffer_in) {
+ drv->pcm_ops->get_buffer_in = audio_generic_get_buffer_in;
+ drv->pcm_ops->put_buffer_in = audio_generic_put_buffer_in;
+ }
+ if (!drv->pcm_ops->get_buffer_out) {
+ drv->pcm_ops->get_buffer_out = audio_generic_get_buffer_out;
+ drv->pcm_ops->put_buffer_out = audio_generic_put_buffer_out;
+ }
+
audio_init_nb_voices_out(s, drv);
audio_init_nb_voices_in(s, drv);
s->drv = drv;
diff --git a/audio/audio_int.h b/audio/audio_int.h
index a674c5374a..8fb1ca8a8d 100644
--- a/audio/audio_int.h
+++ b/audio/audio_int.h
@@ -65,6 +65,8 @@ typedef struct HWVoiceOut {
uint64_t ts_helper;
struct st_sample *mix_buf;
+ void *buf_emul;
+ size_t pos_emul, pending_emul, size_emul;
size_t samples;
QLIST_HEAD (sw_out_listhead, SWVoiceOut) sw_head;
@@ -87,6 +89,8 @@ typedef struct HWVoiceIn {
uint64_t ts_helper;
struct st_sample *conv_buf;
+ void *buf_emul;
+ size_t pos_emul, pending_emul, size_emul;
size_t samples;
QLIST_HEAD (sw_in_listhead, SWVoiceIn) sw_head;
@@ -147,17 +151,42 @@ struct audio_driver {
};
struct audio_pcm_ops {
- int (*init_out)(HWVoiceOut *hw, struct audsettings *as, void *drv_opaque);
- void (*fini_out)(HWVoiceOut *hw);
+ int (*init_out)(HWVoiceOut *hw, audsettings *as, void *drv_opaque);
+ void (*fini_out)(HWVoiceOut *hw);
size_t (*run_out)(HWVoiceOut *hw, size_t live);
- int (*ctl_out) (HWVoiceOut *hw, int cmd, ...);
-
- int (*init_in) (HWVoiceIn *hw, struct audsettings *as, void *drv_opaque);
- void (*fini_in) (HWVoiceIn *hw);
+ size_t (*write) (HWVoiceOut *hw, void *buf, size_t size);
+ /*
+ * get a buffer that after later can be passed to put_buffer_out; optional
+ * returns the buffer, and writes it's size to size (in bytes)
+ * this is unrelated to the above buffer_size_out function
+ */
+ void *(*get_buffer_out)(HWVoiceOut *hw, size_t *size);
+ /*
+ * put back the buffer returned by get_buffer_out; optional
+ * buf must be equal the pointer returned by get_buffer_out,
+ * size may be smaller
+ */
+ size_t (*put_buffer_out)(HWVoiceOut *hw, void *buf, size_t size);
+ int (*ctl_out) (HWVoiceOut *hw, int cmd, ...);
+
+ int (*init_in) (HWVoiceIn *hw, audsettings *as, void *drv_opaque);
+ void (*fini_in) (HWVoiceIn *hw);
size_t (*run_in)(HWVoiceIn *hw);
- int (*ctl_in) (HWVoiceIn *hw, int cmd, ...);
+ size_t (*read) (HWVoiceIn *hw, void *buf, size_t size);
+ void *(*get_buffer_in)(HWVoiceIn *hw, size_t *size);
+ void (*put_buffer_in)(HWVoiceIn *hw, void *buf, size_t size);
+ int (*ctl_in) (HWVoiceIn *hw, int cmd, ...);
};
+void *audio_generic_get_buffer_in(HWVoiceIn *hw, size_t *size);
+void audio_generic_put_buffer_in(HWVoiceIn *hw, void *buf, size_t size);
+void *audio_generic_get_buffer_out(HWVoiceOut *hw, size_t *size);
+size_t audio_generic_put_buffer_out(HWVoiceOut *hw, void *buf, size_t size);
+size_t audio_generic_put_buffer_out_nowrite(HWVoiceOut *hw, void *buf,
+ size_t size);
+size_t audio_generic_write(HWVoiceOut *hw, void *buf, size_t size);
+size_t audio_generic_read(HWVoiceIn *hw, void *buf, size_t size);
+
struct capture_callback {
struct audio_capture_ops ops;
void *opaque;
diff --git a/audio/audio_template.h b/audio/audio_template.h
index 2562bf5f00..ff4a173f18 100644
--- a/audio/audio_template.h
+++ b/audio/audio_template.h
@@ -71,6 +71,7 @@ static void glue(audio_init_nb_voices_, TYPE)(AudioState *s,
static void glue (audio_pcm_hw_free_resources_, TYPE) (HW *hw)
{
+ g_free(hw->buf_emul);
g_free (HWBUF);
HWBUF = NULL;
}