audioringbuffer: Add support for reordering of channels

This commit is contained in:
Sebastian Dröge 2011-12-20 16:20:06 +01:00
parent b23ff1b515
commit 225238a913
2 changed files with 164 additions and 30 deletions

View file

@ -539,11 +539,16 @@ gst_audio_ring_buffer_acquire (GstAudioRingBuffer * buf,
goto was_acquired;
buf->acquired = TRUE;
buf->need_reorder = FALSE;
rclass = GST_AUDIO_RING_BUFFER_GET_CLASS (buf);
if (G_LIKELY (rclass->acquire))
res = rclass->acquire (buf, spec);
/* Only reorder for raw audio */
buf->need_reorder = (buf->need_reorder
&& buf->spec.type == GST_AUDIO_RING_BUFFER_FORMAT_TYPE_RAW);
if (G_UNLIKELY (!res))
goto acquire_failed;
@ -1276,13 +1281,34 @@ no_start:
}
}
#define FWD_SAMPLES(s,se,d,de) \
#define REORDER_SAMPLE(d, s, l) \
G_STMT_START { \
gint i; \
for (i = 0; i < channels; i++) { \
memcpy (d + reorder_map[i] * bps, s + i * bps, bps); \
} \
} G_STMT_END
#define REORDER_SAMPLES(d, s, len) \
G_STMT_START { \
gint i, len_ = len / bpf; \
guint8 *d_ = d, *s_ = s; \
for (i = 0; i < len_; i++) { \
REORDER_SAMPLE(d_, s_, bpf); \
d_ += bpf; \
s_ += bpf; \
} \
} G_STMT_END
#define FWD_SAMPLES(s,se,d,de,F) \
G_STMT_START { \
/* no rate conversion */ \
guint towrite = MIN (se + bpf - s, de - d); \
/* simple copy */ \
if (!skip) \
memcpy (d, s, towrite); \
F (d, s, towrite); \
in_samples -= towrite / bpf; \
out_samples -= towrite / bpf; \
s += towrite; \
@ -1290,12 +1316,12 @@ G_STMT_START { \
} G_STMT_END
/* in_samples >= out_samples, rate > 1.0 */
#define FWD_UP_SAMPLES(s,se,d,de) \
#define FWD_UP_SAMPLES(s,se,d,de,F) \
G_STMT_START { \
guint8 *sb = s, *db = d; \
while (s <= se && d < de) { \
if (!skip) \
memcpy (d, s, bpf); \
F (d, s, bpf); \
s += bpf; \
*accum += outr; \
if ((*accum << 1) >= inr) { \
@ -1309,12 +1335,12 @@ G_STMT_START { \
} G_STMT_END
/* out_samples > in_samples, for rates smaller than 1.0 */
#define FWD_DOWN_SAMPLES(s,se,d,de) \
#define FWD_DOWN_SAMPLES(s,se,d,de,F) \
G_STMT_START { \
guint8 *sb = s, *db = d; \
while (s <= se && d < de) { \
if (!skip) \
memcpy (d, s, bpf); \
F (d, s, bpf); \
d += bpf; \
*accum += inr; \
if ((*accum << 1) >= outr) { \
@ -1327,12 +1353,12 @@ G_STMT_START { \
GST_DEBUG ("fwd_down end %d/%d",*accum,*toprocess); \
} G_STMT_END
#define REV_UP_SAMPLES(s,se,d,de) \
#define REV_UP_SAMPLES(s,se,d,de,F) \
G_STMT_START { \
guint8 *sb = se, *db = d; \
while (s <= se && d < de) { \
if (!skip) \
memcpy (d, se, bpf); \
F (d, se, bpf); \
se -= bpf; \
*accum += outr; \
while (d < de && (*accum << 1) >= inr) { \
@ -1345,12 +1371,12 @@ G_STMT_START { \
GST_DEBUG ("rev_up end %d/%d",*accum,*toprocess); \
} G_STMT_END
#define REV_DOWN_SAMPLES(s,se,d,de) \
#define REV_DOWN_SAMPLES(s,se,d,de,F) \
G_STMT_START { \
guint8 *sb = se, *db = d; \
while (s <= se && d < de) { \
if (!skip) \
memcpy (d, se, bpf); \
F (d, se, bpf); \
d += bpf; \
*accum += inr; \
while (s <= se && (*accum << 1) >= outr) { \
@ -1368,20 +1394,25 @@ default_commit (GstAudioRingBuffer * buf, guint64 * sample,
guint8 * data, gint in_samples, gint out_samples, gint * accum)
{
gint segdone;
gint segsize, segtotal, bpf, sps;
gint segsize, segtotal, channels, bps, bpf, sps;
guint8 *dest, *data_end;
gint writeseg, sampleoff;
gint *toprocess;
gint inr, outr;
gboolean reverse;
gboolean need_reorder;
g_return_val_if_fail (buf->memory != NULL, -1);
g_return_val_if_fail (data != NULL, -1);
need_reorder = buf->need_reorder;
channels = buf->spec.info.channels;
dest = buf->memory;
segsize = buf->spec.segsize;
segtotal = buf->spec.segtotal;
bpf = buf->spec.info.bpf;
bps = bpf / channels;
sps = buf->samples_per_seg;
reverse = out_samples < 0;
@ -1455,23 +1486,46 @@ default_commit (GstAudioRingBuffer * buf, guint64 * sample,
GST_DEBUG_OBJECT (buf, "write @%p seg %d, sps %d, off %d, avail %d",
dest + ws * segsize, ws, sps, sampleoff, avail);
if (G_LIKELY (inr == outr && !reverse)) {
/* no rate conversion, simply copy samples */
FWD_SAMPLES (data, data_end, d, d_end);
} else if (!reverse) {
if (inr >= outr)
/* forward speed up */
FWD_UP_SAMPLES (data, data_end, d, d_end);
else
/* forward slow down */
FWD_DOWN_SAMPLES (data, data_end, d, d_end);
if (need_reorder) {
gint *reorder_map = buf->channel_reorder_map;
if (G_LIKELY (inr == outr && !reverse)) {
/* no rate conversion, simply copy samples */
FWD_SAMPLES (data, data_end, d, d_end, REORDER_SAMPLES);
} else if (!reverse) {
if (inr >= outr)
/* forward speed up */
FWD_UP_SAMPLES (data, data_end, d, d_end, REORDER_SAMPLE);
else
/* forward slow down */
FWD_DOWN_SAMPLES (data, data_end, d, d_end, REORDER_SAMPLE);
} else {
if (inr >= outr)
/* reverse speed up */
REV_UP_SAMPLES (data, data_end, d, d_end, REORDER_SAMPLE);
else
/* reverse slow down */
REV_DOWN_SAMPLES (data, data_end, d, d_end, REORDER_SAMPLE);
}
} else {
if (inr >= outr)
/* reverse speed up */
REV_UP_SAMPLES (data, data_end, d, d_end);
else
/* reverse slow down */
REV_DOWN_SAMPLES (data, data_end, d, d_end);
if (G_LIKELY (inr == outr && !reverse)) {
/* no rate conversion, simply copy samples */
FWD_SAMPLES (data, data_end, d, d_end, memcpy);
} else if (!reverse) {
if (inr >= outr)
/* forward speed up */
FWD_UP_SAMPLES (data, data_end, d, d_end, memcpy);
else
/* forward slow down */
FWD_DOWN_SAMPLES (data, data_end, d, d_end, memcpy);
} else {
if (inr >= outr)
/* reverse speed up */
REV_UP_SAMPLES (data, data_end, d, d_end, memcpy);
else
/* reverse slow down */
REV_DOWN_SAMPLES (data, data_end, d, d_end, memcpy);
}
}
/* for the next iteration we write to the next segment at the beginning. */
@ -1572,18 +1626,22 @@ gst_audio_ring_buffer_read (GstAudioRingBuffer * buf, guint64 sample,
guint8 * data, guint len)
{
gint segdone;
gint segsize, segtotal, bpf, sps;
gint segsize, segtotal, channels, bps, bpf, sps;
guint8 *dest;
guint to_read;
gboolean need_reorder;
g_return_val_if_fail (GST_IS_AUDIO_RING_BUFFER (buf), -1);
g_return_val_if_fail (buf->memory != NULL, -1);
g_return_val_if_fail (data != NULL, -1);
need_reorder = buf->need_reorder;
dest = buf->memory;
segsize = buf->spec.segsize;
segtotal = buf->spec.segtotal;
channels = buf->spec.info.channels;
bpf = buf->spec.info.bpf;
bps = bpf / channels;
sps = buf->samples_per_seg;
to_read = len;
@ -1639,8 +1697,22 @@ gst_audio_ring_buffer_read (GstAudioRingBuffer * buf, guint64 sample,
GST_DEBUG_OBJECT (buf, "read @%p seg %d, off %d, sampleslen %d",
dest + readseg * segsize, readseg, sampleoff, sampleslen);
memcpy (data, dest + (readseg * segsize) + (sampleoff * bpf),
(sampleslen * bpf));
if (need_reorder) {
guint8 *ptr = dest + (readseg * segsize) + (sampleoff * bpf);
gint i, j;
gint *reorder_map = buf->channel_reorder_map;
/* Reorder from device order to GStreamer order */
for (i = 0; i < sampleslen; i++) {
for (j = 0; j < channels; j++) {
memcpy (data + reorder_map[j] * bps, ptr + j * bps, bps);
}
ptr += bpf;
}
} else {
memcpy (data, dest + (readseg * segsize) + (sampleoff * bpf),
(sampleslen * bpf));
}
next:
to_read -= sampleslen;
@ -1796,3 +1868,57 @@ gst_audio_ring_buffer_may_start (GstAudioRingBuffer * buf, gboolean allowed)
GST_LOG_OBJECT (buf, "may start: %d", allowed);
g_atomic_int_set (&buf->may_start, allowed);
}
/**
* gst_audio_ring_buffer_set_channel_positions:
* @buf: the #GstAudioRingBuffer
* @position: the device channel positions
*
* Tell the ringbuffer about the device's channel positions. This must
* be called in when the ringbuffer is acquired.
*/
void
gst_audio_ring_buffer_set_channel_positions (GstAudioRingBuffer * buf,
const GstAudioChannelPosition * position)
{
const GstAudioChannelPosition *to;
gint channels;
gint i, j;
g_return_if_fail (GST_IS_AUDIO_RING_BUFFER (buf));
g_return_if_fail (buf->acquired);
channels = buf->spec.info.channels;
to = buf->spec.info.position;
buf->need_reorder = FALSE;
if (memcmp (position, to, channels * sizeof (to[0])) == 0)
return;
/* Build reorder map and check compatibility */
for (i = 0; i < channels; i++) {
g_return_if_fail (position[i] == GST_AUDIO_CHANNEL_POSITION_NONE
|| to[i] == GST_AUDIO_CHANNEL_POSITION_NONE);
g_return_if_fail (position[i] == GST_AUDIO_CHANNEL_POSITION_INVALID
|| to[i] == GST_AUDIO_CHANNEL_POSITION_INVALID);
g_return_if_fail (position[i] == GST_AUDIO_CHANNEL_POSITION_MONO
|| to[i] == GST_AUDIO_CHANNEL_POSITION_MONO);
for (j = 0; j < channels; j++) {
if (position[i] == to[j]) {
buf->channel_reorder_map[i] = j;
break;
}
}
/* Not all channels present in both */
g_return_if_fail (j == channels);
}
for (i = 0; i < channels; i++) {
if (buf->channel_reorder_map[i] != i) {
buf->need_reorder = TRUE;
break;
}
}
}

View file

@ -123,6 +123,7 @@ struct _GstAudioRingBufferSpec
GstAudioRingBufferFormatType type;
GstAudioInfo info;
guint64 latency_time; /* the required/actual latency time, this is the
* actual the size of one segment and the
* minimum possible latency we can achieve. */
@ -189,6 +190,10 @@ struct _GstAudioRingBuffer {
GstAudioRingBufferCallback callback;
gpointer cb_data;
gboolean need_reorder;
/* gst[channel_reorder_map[i]] = device[i] */
gint channel_reorder_map[64];
gboolean flushing;
/* ATOMIC */
gint may_start;
@ -273,6 +278,9 @@ gboolean gst_audio_ring_buffer_release (GstAudioRingBuffer *buf);
gboolean gst_audio_ring_buffer_is_acquired (GstAudioRingBuffer *buf);
/* set the device channel positions */
void gst_audio_ring_buffer_set_channel_positions (GstAudioRingBuffer *buf, const GstAudioChannelPosition *position);
/* activating */
gboolean gst_audio_ring_buffer_activate (GstAudioRingBuffer *buf, gboolean active);
gboolean gst_audio_ring_buffer_is_active (GstAudioRingBuffer *buf);