blob: de23e6cc61ec4acf8e3271ba1a4db12ce6d1cb21 [file] [log] [blame]
Alexandre Lision8af73cb2013-12-10 14:11:20 -05001/* $Id$ */
2/*
3 * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21#ifndef __PJMEDIA_CIRC_BUF_H__
22#define __PJMEDIA_CIRC_BUF_H__
23
24/**
25 * @file circbuf.h
26 * @brief Circular Buffer.
27 */
28
29#include <pj/assert.h>
30#include <pj/errno.h>
31#include <pj/pool.h>
32#include <pjmedia/frame.h>
33
34/**
35 * @defgroup PJMED_CIRCBUF Circular Buffer
36 * @ingroup PJMEDIA_FRAME_OP
37 * @brief Circular buffer manages read and write contiguous audio samples in a
38 * non-contiguous buffer as if the buffer were contiguous. This should give
39 * better performance than keeping contiguous samples in a contiguous buffer,
40 * since read/write operations will only update the pointers, instead of
41 * shifting audio samples.
42 *
43 * @{
44 *
45 * This section describes PJMEDIA's implementation of circular buffer.
46 */
47
48/* Algorithm checkings, for development purpose only */
49#if 0
50# define PJMEDIA_CIRC_BUF_CHECK(x) pj_assert(x)
51#else
52# define PJMEDIA_CIRC_BUF_CHECK(x)
53#endif
54
55PJ_BEGIN_DECL
56
57/**
58 * Circular buffer structure
59 */
60typedef struct pjmedia_circ_buf {
61 pj_int16_t *buf; /**< The buffer */
62 unsigned capacity; /**< Buffer capacity, in samples */
63
64 pj_int16_t *start; /**< Pointer to the first sample */
65 unsigned len; /**< Audio samples length,
66 in samples */
67} pjmedia_circ_buf;
68
69
70/**
71 * Create the circular buffer.
72 *
73 * @param pool Pool where the circular buffer will be allocated
74 * from.
75 * @param capacity Capacity of the buffer, in samples.
76 * @param p_cb Pointer to receive the circular buffer instance.
77 *
78 * @return PJ_SUCCESS if the circular buffer has been
79 * created successfully, otherwise the appropriate
80 * error will be returned.
81 */
82PJ_INLINE(pj_status_t) pjmedia_circ_buf_create(pj_pool_t *pool,
83 unsigned capacity,
84 pjmedia_circ_buf **p_cb)
85{
86 pjmedia_circ_buf *cbuf;
87
88 cbuf = PJ_POOL_ZALLOC_T(pool, pjmedia_circ_buf);
89 cbuf->buf = (pj_int16_t*) pj_pool_calloc(pool, capacity,
90 sizeof(pj_int16_t));
91 cbuf->capacity = capacity;
92 cbuf->start = cbuf->buf;
93 cbuf->len = 0;
94
95 *p_cb = cbuf;
96
97 return PJ_SUCCESS;
98}
99
100
101/**
102 * Reset the circular buffer.
103 *
104 * @param circbuf The circular buffer.
105 *
106 * @return PJ_SUCCESS when successful.
107 */
108PJ_INLINE(pj_status_t) pjmedia_circ_buf_reset(pjmedia_circ_buf *circbuf)
109{
110 circbuf->start = circbuf->buf;
111 circbuf->len = 0;
112
113 return PJ_SUCCESS;
114}
115
116
117/**
118 * Get the circular buffer length, it is number of samples buffered in the
119 * circular buffer.
120 *
121 * @param circbuf The circular buffer.
122 *
123 * @return The buffer length.
124 */
125PJ_INLINE(unsigned) pjmedia_circ_buf_get_len(pjmedia_circ_buf *circbuf)
126{
127 return circbuf->len;
128}
129
130
131/**
132 * Set circular buffer length. This is useful when audio buffer is manually
133 * manipulated by the user, e.g: shrinked, expanded.
134 *
135 * @param circbuf The circular buffer.
136 * @param len The new buffer length.
137 */
138PJ_INLINE(void) pjmedia_circ_buf_set_len(pjmedia_circ_buf *circbuf,
139 unsigned len)
140{
141 PJMEDIA_CIRC_BUF_CHECK(len <= circbuf->capacity);
142 circbuf->len = len;
143}
144
145
146/**
147 * Advance the read pointer of circular buffer. This function will discard
148 * the skipped samples while advancing the read pointer, thus reducing
149 * the buffer length.
150 *
151 * @param circbuf The circular buffer.
152 * @param count Distance from current read pointer, can only be
153 * possitive number, in samples.
154 *
155 * @return PJ_SUCCESS when successful, otherwise
156 * the appropriate error will be returned.
157 */
158PJ_INLINE(pj_status_t) pjmedia_circ_buf_adv_read_ptr(pjmedia_circ_buf *circbuf,
159 unsigned count)
160{
161 if (count >= circbuf->len)
162 return pjmedia_circ_buf_reset(circbuf);
163
164 PJMEDIA_CIRC_BUF_CHECK(count <= circbuf->len);
165
166 circbuf->start += count;
167 if (circbuf->start >= circbuf->buf + circbuf->capacity)
168 circbuf->start -= circbuf->capacity;
169 circbuf->len -= count;
170
171 return PJ_SUCCESS;
172}
173
174
175/**
176 * Advance the write pointer of circular buffer. Since write pointer is always
177 * pointing to a sample after the end of sample, so this function also means
178 * increasing the buffer length.
179 *
180 * @param circbuf The circular buffer.
181 * @param count Distance from current write pointer, can only be
182 * possitive number, in samples.
183 *
184 * @return PJ_SUCCESS when successful, otherwise
185 * the appropriate error will be returned.
186 */
187PJ_INLINE(pj_status_t) pjmedia_circ_buf_adv_write_ptr(pjmedia_circ_buf *circbuf,
188 unsigned count)
189{
190 if (count + circbuf->len > circbuf->capacity)
191 return PJ_ETOOBIG;
192
193 circbuf->len += count;
194
195 return PJ_SUCCESS;
196}
197
198
199/**
200 * Get the real buffer addresses containing the audio samples.
201 *
202 * @param circbuf The circular buffer.
203 * @param reg1 Pointer to store the first buffer address.
204 * @param reg1_len Pointer to store the length of the first buffer,
205 * in samples.
206 * @param reg2 Pointer to store the second buffer address.
207 * @param reg2_len Pointer to store the length of the second buffer,
208 * in samples.
209 */
210PJ_INLINE(void) pjmedia_circ_buf_get_read_regions(pjmedia_circ_buf *circbuf,
211 pj_int16_t **reg1,
212 unsigned *reg1_len,
213 pj_int16_t **reg2,
214 unsigned *reg2_len)
215{
216 *reg1 = circbuf->start;
217 *reg1_len = circbuf->len;
218 if (*reg1 + *reg1_len > circbuf->buf + circbuf->capacity) {
219 *reg1_len = (unsigned)(circbuf->buf + circbuf->capacity -
220 circbuf->start);
221 *reg2 = circbuf->buf;
222 *reg2_len = circbuf->len - *reg1_len;
223 } else {
224 *reg2 = NULL;
225 *reg2_len = 0;
226 }
227
228 PJMEDIA_CIRC_BUF_CHECK(*reg1_len != 0 || (*reg1_len == 0 &&
229 circbuf->len == 0));
230 PJMEDIA_CIRC_BUF_CHECK(*reg1_len + *reg2_len == circbuf->len);
231}
232
233
234/**
235 * Get the real buffer addresses that is empty or writeable.
236 *
237 * @param circbuf The circular buffer.
238 * @param reg1 Pointer to store the first buffer address.
239 * @param reg1_len Pointer to store the length of the first buffer,
240 * in samples.
241 * @param reg2 Pointer to store the second buffer address.
242 * @param reg2_len Pointer to store the length of the second buffer,
243 * in samples.
244 */
245PJ_INLINE(void) pjmedia_circ_buf_get_write_regions(pjmedia_circ_buf *circbuf,
246 pj_int16_t **reg1,
247 unsigned *reg1_len,
248 pj_int16_t **reg2,
249 unsigned *reg2_len)
250{
251 *reg1 = circbuf->start + circbuf->len;
252 if (*reg1 >= circbuf->buf + circbuf->capacity)
253 *reg1 -= circbuf->capacity;
254 *reg1_len = circbuf->capacity - circbuf->len;
255 if (*reg1 + *reg1_len > circbuf->buf + circbuf->capacity) {
256 *reg1_len = (unsigned)(circbuf->buf + circbuf->capacity - *reg1);
257 *reg2 = circbuf->buf;
258 *reg2_len = (unsigned)(circbuf->start - circbuf->buf);
259 } else {
260 *reg2 = NULL;
261 *reg2_len = 0;
262 }
263
264 PJMEDIA_CIRC_BUF_CHECK(*reg1_len != 0 || (*reg1_len == 0 &&
265 circbuf->len == 0));
266 PJMEDIA_CIRC_BUF_CHECK(*reg1_len + *reg2_len == circbuf->capacity -
267 circbuf->len);
268}
269
270
271/**
272 * Read audio samples from the circular buffer.
273 *
274 * @param circbuf The circular buffer.
275 * @param data Buffer to store the read audio samples.
276 * @param count Number of samples being read.
277 *
278 * @return PJ_SUCCESS when successful, otherwise
279 * the appropriate error will be returned.
280 */
281PJ_INLINE(pj_status_t) pjmedia_circ_buf_read(pjmedia_circ_buf *circbuf,
282 pj_int16_t *data,
283 unsigned count)
284{
285 pj_int16_t *reg1, *reg2;
286 unsigned reg1cnt, reg2cnt;
287
288 /* Data in the buffer is less than requested */
289 if (count > circbuf->len)
290 return PJ_ETOOBIG;
291
292 pjmedia_circ_buf_get_read_regions(circbuf, &reg1, &reg1cnt,
293 &reg2, &reg2cnt);
294 if (reg1cnt >= count) {
295 pjmedia_copy_samples(data, reg1, count);
296 } else {
297 pjmedia_copy_samples(data, reg1, reg1cnt);
298 pjmedia_copy_samples(data + reg1cnt, reg2, count - reg1cnt);
299 }
300
301 return pjmedia_circ_buf_adv_read_ptr(circbuf, count);
302}
303
304
305/**
306 * Write audio samples to the circular buffer.
307 *
308 * @param circbuf The circular buffer.
309 * @param data Audio samples to be written.
310 * @param count Number of samples being written.
311 *
312 * @return PJ_SUCCESS when successful, otherwise
313 * the appropriate error will be returned.
314 */
315PJ_INLINE(pj_status_t) pjmedia_circ_buf_write(pjmedia_circ_buf *circbuf,
316 pj_int16_t *data,
317 unsigned count)
318{
319 pj_int16_t *reg1, *reg2;
320 unsigned reg1cnt, reg2cnt;
321
322 /* Data to write is larger than buffer can store */
323 if (count > circbuf->capacity - circbuf->len)
324 return PJ_ETOOBIG;
325
326 pjmedia_circ_buf_get_write_regions(circbuf, &reg1, &reg1cnt,
327 &reg2, &reg2cnt);
328 if (reg1cnt >= count) {
329 pjmedia_copy_samples(reg1, data, count);
330 } else {
331 pjmedia_copy_samples(reg1, data, reg1cnt);
332 pjmedia_copy_samples(reg2, data + reg1cnt, count - reg1cnt);
333 }
334
335 return pjmedia_circ_buf_adv_write_ptr(circbuf, count);
336}
337
338
339/**
340 * Copy audio samples from the circular buffer without changing its state.
341 *
342 * @param circbuf The circular buffer.
343 * @param start_idx Starting sample index to be copied.
344 * @param data Buffer to store the read audio samples.
345 * @param count Number of samples being read.
346 *
347 * @return PJ_SUCCESS when successful, otherwise
348 * the appropriate error will be returned.
349 */
350PJ_INLINE(pj_status_t) pjmedia_circ_buf_copy(pjmedia_circ_buf *circbuf,
351 unsigned start_idx,
352 pj_int16_t *data,
353 unsigned count)
354{
355 pj_int16_t *reg1, *reg2;
356 unsigned reg1cnt, reg2cnt;
357
358 /* Data in the buffer is less than requested */
359 if (count + start_idx > circbuf->len)
360 return PJ_ETOOBIG;
361
362 pjmedia_circ_buf_get_read_regions(circbuf, &reg1, &reg1cnt,
363 &reg2, &reg2cnt);
364 if (reg1cnt > start_idx) {
365 unsigned tmp_len;
366 tmp_len = reg1cnt - start_idx;
367 if (tmp_len > count)
368 tmp_len = count;
369 pjmedia_copy_samples(data, reg1 + start_idx, tmp_len);
370 if (tmp_len < count)
371 pjmedia_copy_samples(data + tmp_len, reg2, count - tmp_len);
372 } else {
373 pjmedia_copy_samples(data, reg2 + start_idx - reg1cnt, count);
374 }
375
376 return PJ_SUCCESS;
377}
378
379
380/**
381 * Pack the buffer so the first sample will be in the beginning of the buffer.
382 * This will also make the buffer contiguous.
383 *
384 * @param circbuf The circular buffer.
385 *
386 * @return PJ_SUCCESS when successful, otherwise
387 * the appropriate error will be returned.
388 */
389PJ_INLINE(pj_status_t) pjmedia_circ_buf_pack_buffer(pjmedia_circ_buf *circbuf)
390{
391 pj_int16_t *reg1, *reg2;
392 unsigned reg1cnt, reg2cnt;
393 unsigned gap;
394
395 pjmedia_circ_buf_get_read_regions(circbuf, &reg1, &reg1cnt,
396 &reg2, &reg2cnt);
397
398 /* Check if not contigue */
399 if (reg2cnt != 0) {
400 /* Check if no space left to roll the buffer
401 * (or should this function provide temporary buffer?)
402 */
403 gap = circbuf->capacity - pjmedia_circ_buf_get_len(circbuf);
404 if (gap == 0)
405 return PJ_ETOOBIG;
406
407 /* Roll buffer left using the gap until reg2cnt == 0 */
408 do {
409 if (gap > reg2cnt)
410 gap = reg2cnt;
411 pjmedia_move_samples(reg1 - gap, reg1, reg1cnt);
412 pjmedia_copy_samples(reg1 + reg1cnt - gap, reg2, gap);
413 if (gap < reg2cnt)
414 pjmedia_move_samples(reg2, reg2 + gap, reg2cnt - gap);
415 reg1 -= gap;
416 reg1cnt += gap;
417 reg2cnt -= gap;
418 } while (reg2cnt > 0);
419 }
420
421 /* Finally, Shift samples to the left edge */
422 if (reg1 != circbuf->buf)
423 pjmedia_move_samples(circbuf->buf, reg1,
424 pjmedia_circ_buf_get_len(circbuf));
425 circbuf->start = circbuf->buf;
426
427 return PJ_SUCCESS;
428}
429
430
431PJ_END_DECL
432
433/**
434 * @}
435 */
436
437#endif