summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenny Prijono <bennylp@teluu.com>2008-02-29 17:19:42 +0000
committerBenny Prijono <bennylp@teluu.com>2008-02-29 17:19:42 +0000
commit364a4cee9f53d438b67fa048e0d7f717ed7b1ab4 (patch)
treee9647a2a0a17b1252d0b36c6a8ef6d93a7f2a92f
parente773051f40d4e27afc1aa3f7ac15c0ee1e2c66f6 (diff)
Ticket #438: Workaround for frame bursts from audio devices: added wsola in delaybuf, and put delaybuf in the bridge
git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@1833 74dad513-b988-da41-8d7b-12977e46ad98
-rw-r--r--pjmedia/include/pjmedia/delaybuf.h29
-rw-r--r--pjmedia/src/pjmedia/conference.c123
-rw-r--r--pjmedia/src/pjmedia/delaybuf.c403
-rw-r--r--pjmedia/src/pjmedia/sound_port.c24
-rw-r--r--pjmedia/src/test/wsola_test.c5
5 files changed, 448 insertions, 136 deletions
diff --git a/pjmedia/include/pjmedia/delaybuf.h b/pjmedia/include/pjmedia/delaybuf.h
index bdb0dd1d..2141c481 100644
--- a/pjmedia/include/pjmedia/delaybuf.h
+++ b/pjmedia/include/pjmedia/delaybuf.h
@@ -66,15 +66,17 @@ typedef struct pjmedia_delay_buf pjmedia_delay_buf;
* from.
* @param name Optional name for the buffer for log
* identification.
+ * @param clock_rate Number of samples processed per second.
* @param samples_per_frame Number of samples per frame.
- * @param max_cnt Maximum number of delay to be accommodated,
+ * @param max_frames Maximum number of delay to be accommodated,
* in number of frames.
* @param delay The delay to be applied, in number of frames.
- * If the value is -1, the delay buffer will
+ * If the value is -1 or 0, the delay buffer will
* learn about the delay automatically. If
* the value is greater than zero, then this
* value will be used and no learning will be
* performed.
+ * @param option Option flags, must be zero for now.
* @param p_b Pointer to receive the delay buffer instance.
*
* @return PJ_SUCCESS if the delay buffer has been
@@ -83,9 +85,11 @@ typedef struct pjmedia_delay_buf pjmedia_delay_buf;
*/
PJ_DECL(pj_status_t) pjmedia_delay_buf_create(pj_pool_t *pool,
const char *name,
+ unsigned clock_rate,
unsigned samples_per_frame,
- unsigned max_cnt,
+ unsigned max_frames,
int delay,
+ unsigned options,
pjmedia_delay_buf **p_b);
/**
@@ -131,6 +135,25 @@ PJ_DECL(pj_status_t) pjmedia_delay_buf_get(pjmedia_delay_buf *b,
*/
PJ_DECL(pj_status_t) pjmedia_delay_buf_learn(pjmedia_delay_buf *b);
+/**
+ * Reset delay buffer. This will clear the buffer's content. But keep
+ * the learning result.
+ *
+ * @param b The delay buffer.
+ *
+ * @return PJ_SUCCESS on success or the appropriate error.
+ */
+PJ_DECL(pj_status_t) pjmedia_delay_buf_reset(pjmedia_delay_buf *b);
+
+/**
+ * Destroy delay buffer.
+ *
+ * @param b Delay buffer session.
+ *
+ * @return PJ_SUCCESS normally.
+ */
+PJ_DECL(pj_status_t) pjmedia_delay_buf_destroy(pjmedia_delay_buf *b);
+
PJ_END_DECL
diff --git a/pjmedia/src/pjmedia/conference.c b/pjmedia/src/pjmedia/conference.c
index 4999b7fe..185d08bc 100644
--- a/pjmedia/src/pjmedia/conference.c
+++ b/pjmedia/src/pjmedia/conference.c
@@ -18,6 +18,7 @@
*/
#include <pjmedia/conference.h>
#include <pjmedia/alaw_ulaw.h>
+#include <pjmedia/delaybuf.h>
#include <pjmedia/errno.h>
#include <pjmedia/port.h>
#include <pjmedia/resample.h>
@@ -56,12 +57,7 @@ static FILE *fhnd_rec;
#define THIS_FILE "conference.c"
-/* When delay buffer is used, we only need 1 frame buffering */
-#if defined(PJMEDIA_SOUND_USE_DELAYBUF) && PJMEDIA_SOUND_USE_DELAYBUF!=0
-# define RX_BUF_COUNT 1
-#else
-# define RX_BUF_COUNT PJMEDIA_SOUND_BUFFER_COUNT
-#endif
+#define RX_BUF_COUNT PJMEDIA_SOUND_BUFFER_COUNT
#define BYTES_PER_SAMPLE 2
@@ -183,17 +179,22 @@ struct conf_port
unsigned tx_buf_cap; /**< Max size, in samples. */
unsigned tx_buf_count; /**< # of samples in the buffer. */
- /* Snd buffers is a special buffer for sound device port (port 0, master
- * port). It's not used by other ports.
+ /* Delay buffer is a special buffer for sound device port (port 0, master
+ * port) and other passive ports (sound device port is also passive port).
+ *
+ * We need the delay buffer because we can not expect the mic and speaker
+ * thread to run equally after one another. In most systems, each thread
+ * will run multiple times before the other thread gains execution time.
+ * For example, in my system, mic thread is called three times, then
+ * speaker thread is called three times, and so on. This we call burst.
*
- * There are multiple numbers of this buffer, because we can not expect
- * the mic and speaker thread to run equally after one another. In most
- * systems, each thread will run multiple times before the other thread
- * gains execution time. For example, in my system, mic thread is called
- * three times, then speaker thread is called three times, and so on.
+ * There is also possibility of drift, unbalanced rate between put_frame
+ * and get_frame operation, in passive ports. If drift happens, snd_buf
+ * needs to be expanded or shrinked.
+ *
+ * Burst and drift are handled by delay buffer.
*/
- int snd_write_pos, snd_read_pos;
- pj_int16_t *snd_buf[RX_BUF_COUNT]; /**< Buffer */
+ pjmedia_delay_buf *delay_buf;
};
@@ -226,6 +227,7 @@ static pj_status_t get_frame(pjmedia_port *this_port,
static pj_status_t get_frame_pasv(pjmedia_port *this_port,
pjmedia_frame *frame);
static pj_status_t destroy_port(pjmedia_port *this_port);
+static pj_status_t destroy_port_pasv(pjmedia_port *this_port);
/*
@@ -377,7 +379,6 @@ static pj_status_t create_pasv_port( pjmedia_conf *conf,
struct conf_port **p_conf_port)
{
struct conf_port *conf_port;
- unsigned i;
pj_status_t status;
/* Create port */
@@ -385,17 +386,16 @@ static pj_status_t create_pasv_port( pjmedia_conf *conf,
if (status != PJ_SUCCESS)
return status;
- /* Passive port has rx buffers. */
- for (i=0; i<RX_BUF_COUNT; ++i) {
- conf_port->snd_buf[i] = (pj_int16_t*)
- pj_pool_zalloc(pool, conf->samples_per_frame *
- sizeof(conf_port->snd_buf[0][0]));
- if (conf_port->snd_buf[i] == NULL) {
- return PJ_ENOMEM;
- }
- }
- conf_port->snd_write_pos = 0;
- conf_port->snd_read_pos = 0;
+ /* Passive port has delay buf. */
+ status = pjmedia_delay_buf_create(pool, name->ptr,
+ conf->clock_rate,
+ conf->samples_per_frame,
+ RX_BUF_COUNT, /* max */
+ -1, /* delay */
+ 0, /* options */
+ &conf_port->delay_buf);
+ if (status != PJ_SUCCESS)
+ return status;
*p_conf_port = conf_port;
@@ -444,6 +444,7 @@ static pj_status_t create_sound_port( pj_pool_t *pool,
conf->bits_per_sample,
0, /* Options */
&conf->snd_dev_port);
+
}
if (status != PJ_SUCCESS)
@@ -606,6 +607,17 @@ static pj_status_t destroy_port(pjmedia_port *this_port)
return pjmedia_conf_destroy(conf);
}
+static pj_status_t destroy_port_pasv(pjmedia_port *this_port) {
+ pjmedia_conf *conf = (pjmedia_conf*) this_port->port_data.pdata;
+ struct conf_port *port = conf->ports[this_port->port_data.ldata];
+ pj_status_t status;
+
+ status = pjmedia_delay_buf_destroy(port->delay_buf);
+ if (status == PJ_SUCCESS)
+ port->delay_buf = NULL;
+
+ return status;
+}
/*
* Get port zero interface.
@@ -785,7 +797,7 @@ PJ_DEF(pj_status_t) pjmedia_conf_add_passive_port( pjmedia_conf *conf,
port->get_frame = &get_frame_pasv;
port->put_frame = &put_frame;
- port->on_destroy = NULL;
+ port->on_destroy = &destroy_port_pasv;
/* Create conf port structure. */
@@ -954,7 +966,9 @@ PJ_DEF(pj_status_t) pjmedia_conf_disconnect_port( pjmedia_conf *conf,
(int)dst_port->name.slen,
dst_port->name.ptr));
-
+ /* if source port is passive port and has no listener, reset delaybuf */
+ if (src_port->delay_buf && src_port->listener_cnt == 0)
+ pjmedia_delay_buf_reset(src_port->delay_buf);
}
pj_mutex_unlock(conf->mutex);
@@ -1616,29 +1630,17 @@ static pj_status_t get_frame(pjmedia_port *this_port,
continue;
}
- /* Get frame from this port.
- * For port zero (sound port) and passive ports, get the frame from
- * the rx_buffer instead.
+ /* Get frame from this port.
+ * For passive ports, get the frame from the delay_buf.
+ * For other ports, get the frame from the port.
*/
- if (conf_port->port == NULL) {
- pj_int16_t *snd_buf;
-
- if (conf_port->snd_read_pos == conf_port->snd_write_pos) {
- conf_port->snd_read_pos =
- (conf_port->snd_write_pos+RX_BUF_COUNT-RX_BUF_COUNT/2) %
- RX_BUF_COUNT;
- }
-
- /* Skip if this port is muted/disabled. */
- if (conf_port->rx_setting != PJMEDIA_PORT_ENABLE) {
- conf_port->rx_level = 0;
+ if (conf_port->delay_buf != NULL) {
+ pj_status_t status;
+
+ status = pjmedia_delay_buf_get(conf_port->delay_buf,
+ (pj_int16_t*)frame->buf);
+ if (status != PJ_SUCCESS)
continue;
- }
-
- snd_buf = conf_port->snd_buf[conf_port->snd_read_pos];
- pjmedia_copy_samples((pj_int16_t*)frame->buf, snd_buf,
- conf->samples_per_frame);
- conf_port->snd_read_pos = (conf_port->snd_read_pos+1) % RX_BUF_COUNT;
} else {
@@ -1830,21 +1832,23 @@ static pj_status_t get_frame_pasv(pjmedia_port *this_port,
/*
- * Recorder callback.
+ * Recorder (or passive port) callback.
*/
static pj_status_t put_frame(pjmedia_port *this_port,
const pjmedia_frame *frame)
{
pjmedia_conf *conf = (pjmedia_conf*) this_port->port_data.pdata;
struct conf_port *port = conf->ports[this_port->port_data.ldata];
- const pj_int16_t *input = (const pj_int16_t*) frame->buf;
- pj_int16_t *target_snd_buf;
+ pj_status_t status;
/* Check for correct size. */
PJ_ASSERT_RETURN( frame->size == conf->samples_per_frame *
conf->bits_per_sample / 8,
PJMEDIA_ENCSAMPLESPFRAME);
+ /* Check existance of delay_buf instance */
+ PJ_ASSERT_RETURN( port->delay_buf, PJ_EBUG );
+
/* Skip if this port is muted/disabled. */
if (port->rx_setting != PJMEDIA_PORT_ENABLE) {
return PJ_SUCCESS;
@@ -1855,17 +1859,8 @@ static pj_status_t put_frame(pjmedia_port *this_port,
return PJ_SUCCESS;
}
+ status = pjmedia_delay_buf_put(port->delay_buf, (pj_int16_t*)frame->buf);
- /* Determine which rx_buffer to fill in */
- target_snd_buf = port->snd_buf[port->snd_write_pos];
-
- /* Copy samples from audio device to target rx_buffer */
- pjmedia_copy_samples(target_snd_buf, input, conf->samples_per_frame);
-
- /* Switch buffer */
- port->snd_write_pos = (port->snd_write_pos+1)%RX_BUF_COUNT;
-
-
- return PJ_SUCCESS;
+ return status;
}
diff --git a/pjmedia/src/pjmedia/delaybuf.c b/pjmedia/src/pjmedia/delaybuf.c
index 6133f7bf..69c1e45b 100644
--- a/pjmedia/src/pjmedia/delaybuf.c
+++ b/pjmedia/src/pjmedia/delaybuf.c
@@ -19,10 +19,21 @@
#include <pjmedia/delaybuf.h>
#include <pjmedia/errno.h>
+#include <pjmedia/wsola.h>
#include <pj/assert.h>
+#include <pj/lock.h>
#include <pj/log.h>
#include <pj/pool.h>
+
+#if 0
+# define TRACE__(x) PJ_LOG(3,x)
+#else
+# define TRACE__(x)
+#endif
+
+
+/* Type of states of delay buffer */
enum state
{
STATE_WAITING,
@@ -30,6 +41,7 @@ enum state
STATE_RUNNING
};
+/* Type of operation of delay buffer */
enum OP
{
OP_PUT,
@@ -37,13 +49,18 @@ enum OP
OP_UNDEFINED
};
-/* The following macros represent cycles of test. */
-/* Since there are two operations performed (get & put), */
-/* these macros value must be minimum 2 and should be even number */
+/* The following macros represent cycles of test. Since there are two
+ * operations performed (get & put), these macros minimum value must be 2
+ * and should be even number.
+ */
#define WAITING_COUNT 4
#define LEARN_COUNT 16
-/* Number of buffers to add to learnt level for additional stability */
+/* Number of buffers to add to learnt level for additional stability
+ * Please note that wsola_discard() needs minimum 3 frames, so max buffer
+ * count should be minimally 3, setting SAFE_MARGIN to 2 will guarantees
+ * this.
+ */
#define SAFE_MARGIN 2
/*
@@ -66,36 +83,48 @@ enum OP
struct pjmedia_delay_buf
{
- char obj_name[PJ_MAX_OBJ_NAME];
+ char obj_name[PJ_MAX_OBJ_NAME];
+
+ pj_lock_t *lock; /**< Lock object. */
- pj_int16_t *frame_buf;
- unsigned put_pos;
- unsigned get_pos;
- unsigned buf_cnt;
+ pj_int16_t *frame_buf;
+ enum state state; /**< State of delay buffer */
+ unsigned samples_per_frame; /**< Number of samples in one frame */
+ unsigned max_frames; /**< Buffer allocated, in frames */
- unsigned samples_per_frame;
- unsigned max_cnt;
- enum state state;
+ unsigned put_pos; /**< Position for put op, in samples */
+ unsigned get_pos; /**< Position for get op, in samples */
+ unsigned buf_cnt; /**< Number of buffered samples */
+ unsigned max_cnt; /**< Max number of buffered samples */
struct {
- unsigned level;
+ unsigned level; /**< Burst level storage on learning */
} op[2];
+ enum OP last_op; /**< Last op (GET or PUT) of learning*/
+ unsigned state_count; /**< Counter of op cycles of learning*/
- enum OP last_op;
- unsigned max_level;
- unsigned state_count;
+ unsigned max_level; /**< Learning result: burst level */
+
+ pjmedia_wsola *wsola; /**< Drift handler */
};
PJ_DEF(pj_status_t) pjmedia_delay_buf_create( pj_pool_t *pool,
const char *name,
+ unsigned clock_rate,
unsigned samples_per_frame,
- unsigned max_cnt,
+ unsigned max_frames,
int delay,
+ unsigned options,
pjmedia_delay_buf **p_b)
{
pjmedia_delay_buf *b;
+ pj_status_t status;
+
+ PJ_ASSERT_RETURN(pool && samples_per_frame && max_frames && p_b, PJ_EINVAL);
+ PJ_ASSERT_RETURN((int)max_frames >= delay, PJ_EINVAL);
+ PJ_ASSERT_RETURN(options==0, PJ_EINVAL);
- PJ_ASSERT_RETURN(pool && samples_per_frame && max_cnt && p_b, PJ_EINVAL);
+ PJ_UNUSED_ARG(options);
if (!name) {
name = "delaybuf";
@@ -104,29 +133,184 @@ PJ_DEF(pj_status_t) pjmedia_delay_buf_create( pj_pool_t *pool,
b = PJ_POOL_ZALLOC_T(pool, pjmedia_delay_buf);
pj_ansi_strncpy(b->obj_name, name, PJ_MAX_OBJ_NAME-1);
- b->frame_buf = (pj_int16_t*)
- pj_pool_zalloc(pool, samples_per_frame * max_cnt *
- sizeof(pj_int16_t));
b->samples_per_frame = samples_per_frame;
- b->max_cnt = max_cnt;
+ b->max_frames = max_frames;
+
+ status = pj_lock_create_recursive_mutex(pool, b->obj_name,
+ &b->lock);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ b->frame_buf = (pj_int16_t*)
+ pj_pool_zalloc(pool, samples_per_frame * max_frames *
+ sizeof(pj_int16_t));
if (delay >= 0) {
- PJ_ASSERT_RETURN(delay <= (int)max_cnt, PJ_EINVAL);
+ if (delay == 0)
+ delay = 1;
b->max_level = delay;
+ b->max_cnt = delay * samples_per_frame;
b->state = STATE_RUNNING;
} else {
+ b->max_cnt = max_frames * samples_per_frame;
b->last_op = OP_UNDEFINED;
b->state = STATE_WAITING;
- b->buf_cnt = 0;
}
+ status = pjmedia_wsola_create(pool, clock_rate, samples_per_frame,
+ PJMEDIA_WSOLA_NO_PLC, &b->wsola);
+ if (status != PJ_SUCCESS)
+ return status;
+
*p_b = b;
- PJ_LOG(5,(b->obj_name,"Delay buffer created"));
+ TRACE__((b->obj_name,"Delay buffer created"));
return PJ_SUCCESS;
}
+PJ_DEF(pj_status_t) pjmedia_delay_buf_destroy(pjmedia_delay_buf *b)
+{
+ pj_status_t status;
+
+ PJ_ASSERT_RETURN(b, PJ_EINVAL);
+
+ pj_lock_acquire(b->lock);
+
+ status = pjmedia_wsola_destroy(b->wsola);
+ if (status == PJ_SUCCESS)
+ b->wsola = NULL;
+
+ pj_lock_release(b->lock);
+ pj_lock_destroy(b->lock);
+ b->lock = NULL;
+
+ return status;
+}
+
+/* This function will erase samples from delay buffer.
+ * The number of erased samples is guaranteed to be >= erase_cnt.
+ */
+static void shrink_buffer(pjmedia_delay_buf *b, unsigned erase_cnt)
+{
+ unsigned buf1len;
+ unsigned buf2len;
+ pj_status_t status;
+
+ pj_assert(b && erase_cnt);
+
+ if (b->get_pos < b->put_pos) {
+ /* sssss .. sssss
+ * ^ ^
+ * G P
+ */
+ buf1len = b->put_pos - b->get_pos;
+ buf2len = 0;
+ } else {
+ /* sssss .. sssss
+ * ^ ^
+ * P G
+ */
+ buf1len = b->max_cnt - b->get_pos;
+ buf2len = b->put_pos;
+ }
+
+ /* Consistency checking */
+ pj_assert((buf1len + buf2len) == b->buf_cnt);
+
+ if (buf1len != 0)
+ status = pjmedia_wsola_discard(b->wsola,
+ &b->frame_buf[b->get_pos], buf1len,
+ b->frame_buf, buf2len,
+ &erase_cnt);
+ else
+ status = pjmedia_wsola_discard(b->wsola,
+ b->frame_buf, buf2len,
+ NULL, 0,
+ &erase_cnt);
+
+ if ((status == PJ_SUCCESS) && (erase_cnt > 0)) {
+ /* WSOLA discard will shrink only the second buffer, but it may
+ * also shrink first buffer if second buffer is 'destroyed', so
+ * it is safe to just set the new put_pos.
+ */
+ if (b->put_pos >= erase_cnt)
+ b->put_pos -= erase_cnt;
+ else
+ b->put_pos = b->max_cnt - (erase_cnt - b->put_pos);
+
+ b->buf_cnt -= erase_cnt;
+
+ PJ_LOG(5,(b->obj_name,"Successfully shrinking %d samples, "
+ "buf_cnt=%d", erase_cnt, b->buf_cnt));
+ }
+
+ /* Shrinking failed or erased count is less than requested,
+ * delaybuf needs to drop eldest samples, this is bad since the voice
+ * samples may not have smooth transition.
+ */
+ if (b->buf_cnt + b->samples_per_frame > b->max_cnt) {
+ erase_cnt = b->buf_cnt + b->samples_per_frame - b->max_cnt;
+
+ b->buf_cnt -= erase_cnt;
+
+ /* Shift get_pos forward */
+ b->get_pos = (b->get_pos + erase_cnt) % b->max_cnt;
+
+ PJ_LOG(4,(b->obj_name,"Shrinking failed or insufficient, dropping"
+ " %d eldest samples, buf_cnt=%d", erase_cnt, b->buf_cnt));
+ }
+}
+
+static void set_max_cnt(pjmedia_delay_buf *b, unsigned new_max_cnt)
+{
+ unsigned old_max_cnt = b->max_cnt;
+
+ /* nothing to adjust */
+ if (old_max_cnt == new_max_cnt)
+ return;
+
+ /* For now, only support shrinking */
+ pj_assert(old_max_cnt > new_max_cnt);
+
+ shrink_buffer(b, old_max_cnt - new_max_cnt);
+
+ /* Adjust buffer to accomodate the new max_cnt so the samples is secured.
+ * This is done by make get_pos = 0
+ */
+ if (b->get_pos <= b->put_pos) {
+ /* sssss .. sssss
+ * ^ ^
+ * G P
+ */
+ /* Consistency checking */
+ pj_assert((b->put_pos - b->get_pos) <= new_max_cnt);
+ pj_assert((b->put_pos - b->get_pos) == b->buf_cnt);
+
+ pjmedia_move_samples(b->frame_buf, &b->frame_buf[b->get_pos],
+ b->get_pos);
+ b->put_pos -= b->get_pos;
+ b->get_pos = 0;
+ } else {
+ /* sssss .. sssss
+ * ^ ^
+ * P G
+ */
+ unsigned d = old_max_cnt - b->get_pos;
+
+ /* Consistency checking */
+ pj_assert((b->get_pos - b->put_pos) >= (old_max_cnt - new_max_cnt));
+
+ /* Make get_pos = 0, shift right the leftmost block first */
+ pjmedia_move_samples(&b->frame_buf[d], b->frame_buf, d);
+ pjmedia_copy_samples(b->frame_buf, &b->frame_buf[b->get_pos], d);
+ b->put_pos += d;
+ b->get_pos = 0;
+ }
+
+ b->max_cnt = new_max_cnt;
+}
+
static void update(pjmedia_delay_buf *b, enum OP op)
{
enum OP other = (enum OP) !op;
@@ -157,18 +341,23 @@ static void update(pjmedia_delay_buf *b, enum OP op)
/* give SAFE_MARGIN compensation for added stability */
b->max_level += SAFE_MARGIN;
- PJ_LOG(5,(b->obj_name,"Delay buffer start running, level=%u",
- b->max_level));
-
/* buffer not enough! */
- if (b->max_level > b->max_cnt) {
- b->max_level = b->max_cnt;
- PJ_LOG(2,(b->obj_name,"Delay buffer %s learning result " \
- "exceeds the maximum delay allowed",
- b->max_level));
+ if (b->max_level > b->max_frames) {
+ PJ_LOG(4,(b->obj_name,"Delay buffer learning result (%d)"
+ " exceeds the maximum delay allowed (%d)",
+ b->max_level,
+ b->max_frames));
+
+ b->max_level = b->max_frames;
}
+ /* we need to set new max_cnt & adjust buffer */
+ set_max_cnt(b, b->max_level * b->samples_per_frame);
+
b->state = STATE_RUNNING;
+
+ PJ_LOG(4,(b->obj_name,"Delay buffer start running, level=%u",
+ b->max_level));
}
}
b->last_op = op;
@@ -181,73 +370,163 @@ static void update(pjmedia_delay_buf *b, enum OP op)
PJ_DEF(pj_status_t) pjmedia_delay_buf_put(pjmedia_delay_buf *b,
pj_int16_t frame[])
{
- update(b, OP_PUT);
-
- if (b->state != STATE_RUNNING)
- return PJ_EPENDING;
+ pj_status_t status;
- pj_memcpy(&b->frame_buf[b->put_pos * b->samples_per_frame], frame,
- b->samples_per_frame*sizeof(pj_int16_t));
+ PJ_ASSERT_RETURN(b && frame, PJ_EINVAL);
- /* overflow case */
- if (b->put_pos == b->get_pos && b->buf_cnt) {
- if (++b->get_pos == b->max_level)
- b->get_pos = 0;
+ pj_lock_acquire(b->lock);
- b->put_pos = b->get_pos;
+ update(b, OP_PUT);
+
+ status = pjmedia_wsola_save(b->wsola, frame, PJ_FALSE);
+ if (status != PJ_SUCCESS) {
+ pj_lock_release(b->lock);
+ return status;
+ }
- PJ_LOG(5,(b->obj_name,"Warning: buffer overflow"));
+ /* Overflow checking */
+ if (b->buf_cnt + b->samples_per_frame > b->max_cnt)
+ {
+ /* shrink one frame or just the diff? */
+ //unsigned erase_cnt = b->samples_per_frame;
+ unsigned erase_cnt = b->buf_cnt + b->samples_per_frame - b->max_cnt;
- return PJ_ETOOMANY;
+ shrink_buffer(b, erase_cnt);
}
- ++b->buf_cnt;
+ /* put the frame on put_pos */
+ if (b->put_pos + b->samples_per_frame <= b->max_cnt) {
+ pjmedia_copy_samples(&b->frame_buf[b->put_pos], frame,
+ b->samples_per_frame);
+ } else {
+ int remainder = b->put_pos + b->samples_per_frame - b->max_cnt;
- if (++b->put_pos == b->max_level)
- b->put_pos = 0;
+ pjmedia_copy_samples(&b->frame_buf[b->put_pos], frame,
+ b->samples_per_frame - remainder);
+ pjmedia_copy_samples(&b->frame_buf[0],
+ &frame[b->samples_per_frame - remainder],
+ remainder);
+ }
+ /* Update put_pos & buf_cnt */
+ b->put_pos = (b->put_pos + b->samples_per_frame) % b->max_cnt;
+ b->buf_cnt += b->samples_per_frame;
+
+ pj_lock_release(b->lock);
return PJ_SUCCESS;
}
-PJ_DEF(pj_status_t) pjmedia_delay_buf_get(pjmedia_delay_buf *b,
+PJ_DEF(pj_status_t) pjmedia_delay_buf_get( pjmedia_delay_buf *b,
pj_int16_t frame[])
{
+ pj_status_t status;
+
+ PJ_ASSERT_RETURN(b && frame, PJ_EINVAL);
+
+ pj_lock_acquire(b->lock);
+
update(b, OP_GET);
- if (b->state != STATE_RUNNING || !b->buf_cnt) {
- if (b->state == STATE_RUNNING)
- PJ_LOG(5,(b->obj_name,"Warning: delay buffer empty"));
+ /* Starvation checking */
+ if (b->buf_cnt < b->samples_per_frame) {
+
+ PJ_LOG(5,(b->obj_name,"Underflow, buf_cnt=%d, will generate 1 frame",
+ b->buf_cnt));
+
+ status = pjmedia_wsola_generate(b->wsola, frame);
+
+ if (status == PJ_SUCCESS) {
+ TRACE__((b->obj_name,"Successfully generate 1 frame"));
+ if (b->buf_cnt == 0) {
+ pj_lock_release(b->lock);
+ return PJ_SUCCESS;
+ }
+
+ /* Put generated frame into buffer */
+ if (b->put_pos + b->samples_per_frame <= b->max_cnt) {
+ pjmedia_copy_samples(&b->frame_buf[b->put_pos], frame,
+ b->samples_per_frame);
+ } else {
+ int remainder = b->put_pos + b->samples_per_frame - b->max_cnt;
+
+ pjmedia_copy_samples(&b->frame_buf[b->put_pos], &frame[0],
+ b->samples_per_frame - remainder);
+ pjmedia_copy_samples(&b->frame_buf[0],
+ &frame[b->samples_per_frame - remainder],
+ remainder);
+ }
+
+ b->put_pos = (b->put_pos + b->samples_per_frame) % b->max_cnt;
+ b->buf_cnt += b->samples_per_frame;
+
+ } else {
+ /* Give all what delay buffer has, then pad zeroes */
+ PJ_LOG(4,(b->obj_name,"Error generating frame, status=%d",
+ status));
+
+ pjmedia_copy_samples(frame, &b->frame_buf[b->get_pos], b->buf_cnt);
+ pjmedia_zero_samples(&frame[b->buf_cnt],
+ b->samples_per_frame - b->buf_cnt);
+ b->get_pos += b->buf_cnt;
+ b->buf_cnt = 0;
+
+ /* Consistency checking */
+ pj_assert(b->get_pos == b->put_pos);
- pj_bzero(frame, b->samples_per_frame*sizeof(pj_int16_t));
- return PJ_EPENDING;
+ pj_lock_release(b->lock);
+
+ return PJ_SUCCESS;
+ }
}
- pj_memcpy(frame, &b->frame_buf[b->get_pos * b->samples_per_frame],
- b->samples_per_frame*sizeof(pj_int16_t));
+ pjmedia_copy_samples(frame, &b->frame_buf[b->get_pos],
+ b->samples_per_frame);
- if (++b->get_pos == b->max_level)
- b->get_pos = 0;
+ b->get_pos = (b->get_pos + b->samples_per_frame) % b->max_cnt;
+ b->buf_cnt -= b->samples_per_frame;
- --b->buf_cnt;
+ pj_lock_release(b->lock);
return PJ_SUCCESS;
}
-PJ_DECL(pj_status_t) pjmedia_delay_buf_learn(pjmedia_delay_buf *b)
+PJ_DEF(pj_status_t) pjmedia_delay_buf_learn(pjmedia_delay_buf *b)
{
PJ_ASSERT_RETURN(b, PJ_EINVAL);
+ pj_lock_acquire(b->lock);
+
b->last_op = OP_UNDEFINED;
b->op[OP_GET].level = b->op[OP_PUT].level = 0;
b->state = STATE_LEARNING;
b->state_count = 0;
b->max_level = 0;
+ b->max_cnt = b->max_frames * b->samples_per_frame;
+
+ pjmedia_delay_buf_reset(b);
+
+ pj_lock_release(b->lock);
+
+ PJ_LOG(5,(b->obj_name,"Delay buffer start learning"));
+
+ return PJ_SUCCESS;
+}
+
+PJ_DEF(pj_status_t) pjmedia_delay_buf_reset(pjmedia_delay_buf *b)
+{
+ PJ_ASSERT_RETURN(b, PJ_EINVAL);
+
+ pj_lock_acquire(b->lock);
/* clean up buffer */
b->buf_cnt = 0;
b->put_pos = b->get_pos = 0;
+ pjmedia_wsola_reset(b->wsola, 0);
- PJ_LOG(5,(b->obj_name,"Delay buffer start learning"));
+ pj_lock_release(b->lock);
+
+ PJ_LOG(5,(b->obj_name,"Delay buffer resetted"));
return PJ_SUCCESS;
}
+
diff --git a/pjmedia/src/pjmedia/sound_port.c b/pjmedia/src/pjmedia/sound_port.c
index 79dbd7cc..8d21e666 100644
--- a/pjmedia/src/pjmedia/sound_port.c
+++ b/pjmedia/src/pjmedia/sound_port.c
@@ -32,6 +32,8 @@
#define THIS_FILE "sound_port.c"
+#define TEST_OVERFLOW_UNDERFLOW
+
enum
{
PJMEDIA_PLC_ENABLED = 1,
@@ -101,12 +103,27 @@ static pj_status_t play_cb(/* in */ void *user_data,
#if PJMEDIA_SOUND_USE_DELAYBUF
if (snd_port->delay_buf) {
status = pjmedia_delay_buf_get(snd_port->delay_buf, (pj_int16_t*)output);
- if (status != PJ_SUCCESS) {
+ if (status != PJ_SUCCESS)
pj_bzero(output, size);
- }
frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
pjmedia_port_put_frame(port, &frame);
+
+#ifdef TEST_OVERFLOW_UNDERFLOW
+ {
+ static int count = 1;
+ if (++count % 10 == 0) {
+ status = pjmedia_delay_buf_get(snd_port->delay_buf,
+ (pj_int16_t*)output);
+ if (status != PJ_SUCCESS)
+ pj_bzero(output, size);
+
+ frame.type = PJMEDIA_FRAME_TYPE_AUDIO;
+ pjmedia_port_put_frame(port, &frame);
+ }
+ }
+#endif
+
}
#endif
@@ -371,7 +388,8 @@ PJ_DEF(pj_status_t) pjmedia_snd_port_create( pj_pool_t *pool,
snd_port->bits_per_sample = bits_per_sample;
#if PJMEDIA_SOUND_USE_DELAYBUF
- status = pjmedia_delay_buf_create(pool, "snd_buff", samples_per_frame,
+ status = pjmedia_delay_buf_create(pool, "snd_buff",
+ clock_rate, samples_per_frame,
PJMEDIA_SOUND_BUFFER_COUNT, -1,
&snd_port->delay_buf);
PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
diff --git a/pjmedia/src/test/wsola_test.c b/pjmedia/src/test/wsola_test.c
index 92b74322..1258ff77 100644
--- a/pjmedia/src/test/wsola_test.c
+++ b/pjmedia/src/test/wsola_test.c
@@ -327,12 +327,9 @@ int main()
pj_caching_pool_init(&cp, NULL, 0);
pool = pj_pool_create(&cp.factory, "", 1000, 1000, NULL);
- mem_test(pool);
- return 0;
-
srand(2);
- rc = expand(pool, "beet44.pcm", "output.pcm", 1, 0, 0);
+ rc = expand(pool, "beet44.pcm", "output.pcm", 0, 0, 0);
//rc = compress(pool, "beet44.pcm", "output.pcm", 2);
if (rc != 0) {