зеркало из https://github.com/mozilla/cubeb.git
Merge pull request #78 from achronop/audiounit-full-duplex
Implement full-duplex for audiounit backend.
This commit is contained in:
Коммит
d06f84ced4
|
@ -63,3 +63,5 @@ include/cubeb/cubeb-stdint.h
|
|||
test-suite.log
|
||||
test/test_sanity.log
|
||||
test/test_sanity.trs
|
||||
test/test_ring_array
|
||||
test/test_ring_array.exe
|
||||
|
|
|
@ -83,6 +83,7 @@ check_PROGRAMS = test/test_sanity \
|
|||
test/test_resampler \
|
||||
test/test_record \
|
||||
test/test_utils \
|
||||
test/test_ring_array\
|
||||
$(NULL)
|
||||
|
||||
test_test_sanity_SOURCES = test/test_sanity.cpp
|
||||
|
@ -101,7 +102,7 @@ test_test_devices_SOURCES = test/test_devices.cpp
|
|||
test_test_devices_LDADD = -lm src/libcubeb.la $(platform_lib)
|
||||
|
||||
test_test_resampler_SOURCES = test/test_resampler.cpp
|
||||
test_test_resampler_LDADD = -lm src/libcubeb.la $(platform_lib) src/cubeb_resampler.o
|
||||
test_test_resampler_LDADD = -lm src/libcubeb.la $(platform_lib) src/cubeb_resampler.o src/speex/resample.lo
|
||||
|
||||
test_test_duplex_SOURCES = test/test_duplex.cpp
|
||||
test_test_duplex_LDADD = -lm src/libcubeb.la $(platform_lib)
|
||||
|
@ -111,6 +112,8 @@ test_test_record_LDADD = -lm src/libcubeb.la $(platform_lib)
|
|||
|
||||
test_test_utils_SOURCES = test/test_utils.cpp
|
||||
|
||||
test_test_ring_array_SOURCES = test/test_ring_array.c
|
||||
|
||||
TESTS = $(check_PROGRAMS)
|
||||
|
||||
dist-hook:
|
||||
|
|
|
@ -425,6 +425,10 @@ int cubeb_device_collection_destroy(cubeb_device_collection * collection)
|
|||
|
||||
int cubeb_device_info_destroy(cubeb_device_info * info)
|
||||
{
|
||||
if (info == NULL) {
|
||||
return CUBEB_ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
free(info->device_id);
|
||||
free(info->friendly_name);
|
||||
free(info->group_id);
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1036,7 +1036,7 @@ pulse_sink_info_cb(pa_context * context, const pa_sink_info * info,
|
|||
devinfo = calloc(1, sizeof(cubeb_device_info));
|
||||
|
||||
devinfo->device_id = strdup(info->name);
|
||||
devinfo->devid = (cubeb_devid)devinfo->device_id;
|
||||
devinfo->devid = devinfo->device_id;
|
||||
devinfo->friendly_name = strdup(info->description);
|
||||
prop = WRAP(pa_proplist_gets)(info->proplist, "sysfs.path");
|
||||
if (prop)
|
||||
|
@ -1096,7 +1096,7 @@ pulse_source_info_cb(pa_context * context, const pa_source_info * info,
|
|||
devinfo = calloc(1, sizeof(cubeb_device_info));
|
||||
|
||||
devinfo->device_id = strdup(info->name);
|
||||
devinfo->devid = (cubeb_devid)devinfo->device_id;
|
||||
devinfo->devid = devinfo->device_id;
|
||||
devinfo->friendly_name = strdup(info->description);
|
||||
prop = WRAP(pa_proplist_gets)(info->proplist, "sysfs.path");
|
||||
if (prop)
|
||||
|
|
|
@ -0,0 +1,163 @@
|
|||
/*
|
||||
* Copyright © 2016 Mozilla Foundation
|
||||
*
|
||||
* This program is made available under an ISC-style license. See the
|
||||
* accompanying file LICENSE for details.
|
||||
*/
|
||||
|
||||
#ifndef CUBEB_RING_ARRAY_H
|
||||
#define CUBEB_RING_ARRAY_H
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** Ring array of pointers is used to hold buffers. In case that
|
||||
asynchronous producer/consumer callbacks do not arrive in a
|
||||
repeated order the ring array stores the buffers and fetch
|
||||
them in the correct order. */
|
||||
|
||||
typedef struct {
|
||||
AudioBuffer * buffer_array; /**< Array that hold pointers of the allocated space for the buffers. */
|
||||
unsigned int tail; /**< Index of the last element (first to deliver). */
|
||||
unsigned int count; /**< Number of elements in the array. */
|
||||
unsigned int capacity; /**< Total length of the array. */
|
||||
} ring_array;
|
||||
|
||||
static int
|
||||
single_audiobuffer_init(AudioBuffer * buffer,
|
||||
uint32_t bytesPerFrame,
|
||||
uint32_t channelsPerFrame,
|
||||
uint32_t frames)
|
||||
{
|
||||
assert(buffer);
|
||||
assert(bytesPerFrame > 0 && channelsPerFrame && frames > 0);
|
||||
|
||||
size_t size = bytesPerFrame * frames;
|
||||
buffer->mData = calloc(1, size);
|
||||
if (buffer->mData == NULL) {
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
|
||||
buffer->mNumberChannels = channelsPerFrame;
|
||||
buffer->mDataByteSize = size;
|
||||
|
||||
return CUBEB_OK;
|
||||
}
|
||||
|
||||
/** Initialize the ring array.
|
||||
@param ra The ring_array pointer of allocated structure.
|
||||
@retval 0 on success. */
|
||||
int
|
||||
ring_array_init(ring_array * ra,
|
||||
uint32_t capacity,
|
||||
uint32_t bytesPerFrame,
|
||||
uint32_t channelsPerFrame,
|
||||
uint32_t framesPerBuffer)
|
||||
{
|
||||
assert(ra);
|
||||
if (capacity == 0 || bytesPerFrame == 0 ||
|
||||
channelsPerFrame == 0 || framesPerBuffer == 0) {
|
||||
return CUBEB_ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
ra->capacity = capacity;
|
||||
ra->tail = 0;
|
||||
ra->count = 0;
|
||||
|
||||
ra->buffer_array = calloc(ra->capacity, sizeof(AudioBuffer));
|
||||
if (ra->buffer_array == NULL) {
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < ra->capacity; ++i) {
|
||||
if (single_audiobuffer_init(&ra->buffer_array[i],
|
||||
bytesPerFrame,
|
||||
channelsPerFrame,
|
||||
framesPerBuffer) != CUBEB_OK) {
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
return CUBEB_OK;
|
||||
}
|
||||
|
||||
/** Destroy the ring array.
|
||||
@param ra The ring_array pointer.*/
|
||||
void
|
||||
ring_array_destroy(ring_array * ra)
|
||||
{
|
||||
assert(ra);
|
||||
if (ra->buffer_array == NULL){
|
||||
return;
|
||||
}
|
||||
for (unsigned int i = 0; i < ra->capacity; ++i) {
|
||||
if (ra->buffer_array[i].mData) {
|
||||
free(ra->buffer_array[i].mData);
|
||||
}
|
||||
}
|
||||
free(ra->buffer_array);
|
||||
}
|
||||
|
||||
/** Get the allocated buffer to be stored with fresh data.
|
||||
@param ra The ring_array pointer.
|
||||
@retval Pointer of the allocated space to be stored with fresh data or NULL if full. */
|
||||
AudioBuffer *
|
||||
ring_array_get_free_buffer(ring_array * ra)
|
||||
{
|
||||
assert(ra && ra->buffer_array);
|
||||
assert(ra->buffer_array[0].mData != NULL);
|
||||
if (ra->count == ra->capacity) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
assert(ra->count == 0 || (ra->tail + ra->count) % ra->capacity != ra->tail);
|
||||
void * ret = &ra->buffer_array[(ra->tail + ra->count) % ra->capacity];
|
||||
|
||||
++ra->count;
|
||||
assert(ra->count <= ra->capacity);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** Get the next available buffer with data.
|
||||
@param ra The ring_array pointer.
|
||||
@retval Pointer of the next in order data buffer or NULL if empty. */
|
||||
AudioBuffer *
|
||||
ring_array_get_data_buffer(ring_array * ra)
|
||||
{
|
||||
assert(ra && ra->buffer_array);
|
||||
assert(ra->buffer_array[0].mData != NULL);
|
||||
|
||||
if (ra->count == 0) {
|
||||
return NULL;
|
||||
}
|
||||
void * ret = &ra->buffer_array[ra->tail];
|
||||
|
||||
ra->tail = (ra->tail + 1) % ra->capacity;
|
||||
assert(ra->tail < ra->capacity);
|
||||
|
||||
assert(ra->count > 0);
|
||||
--ra->count;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** When array is empty get the first allocated buffer in the array.
|
||||
@param ra The ring_array pointer.
|
||||
@retval If arrays is empty, pointer of the allocated space else NULL. */
|
||||
AudioBuffer *
|
||||
ring_array_get_dummy_buffer(ring_array * ra)
|
||||
{
|
||||
assert(ra && ra->buffer_array);
|
||||
assert(ra->capacity > 0);
|
||||
if (ra->count > 0) {
|
||||
return NULL;
|
||||
}
|
||||
return &ra->buffer_array[0];
|
||||
}
|
||||
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif //CUBEB_RING_ARRAY_H
|
|
@ -0,0 +1,86 @@
|
|||
#ifdef __APPLE__
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <CoreAudio/CoreAudioTypes.h>
|
||||
|
||||
#include "cubeb/cubeb.h"
|
||||
#include "cubeb_ring_array.h"
|
||||
|
||||
int test_ring_array()
|
||||
{
|
||||
ring_array ra;
|
||||
|
||||
assert(ring_array_init(&ra, 0, 0, 1, 1) == CUBEB_ERROR_INVALID_PARAMETER);
|
||||
assert(ring_array_init(&ra, 1, 0, 0, 1) == CUBEB_ERROR_INVALID_PARAMETER);
|
||||
|
||||
unsigned int capacity = 8;
|
||||
ring_array_init(&ra, capacity, sizeof(int), 1, 1);
|
||||
int verify_data[capacity] ;// {1,2,3,4,5,6,7,8};
|
||||
AudioBuffer * p_data = NULL;
|
||||
|
||||
for (unsigned int i = 0; i < capacity; ++i) {
|
||||
verify_data[i] = i; // in case capacity change value
|
||||
*(int*)ra.buffer_array[i].mData = i;
|
||||
assert(ra.buffer_array[i].mDataByteSize == 1 * sizeof(int));
|
||||
assert(ra.buffer_array[i].mNumberChannels == 1);
|
||||
}
|
||||
|
||||
/* Get store buffers*/
|
||||
for (unsigned int i = 0; i < capacity; ++i) {
|
||||
p_data = ring_array_get_free_buffer(&ra);
|
||||
assert(p_data && *(int*)p_data->mData == verify_data[i]);
|
||||
}
|
||||
/*Now array is full extra store should give NULL*/
|
||||
assert(NULL == ring_array_get_free_buffer(&ra));
|
||||
/* Get fetch buffers*/
|
||||
for (unsigned int i = 0; i < capacity; ++i) {
|
||||
p_data = ring_array_get_data_buffer(&ra);
|
||||
assert(p_data && *(int*)p_data->mData == verify_data[i]);
|
||||
}
|
||||
/*Now array is empty extra fetch should give NULL*/
|
||||
assert(NULL == ring_array_get_data_buffer(&ra));
|
||||
|
||||
p_data = NULL;
|
||||
/* Repeated store fetch should can go for ever*/
|
||||
for (unsigned int i = 0; i < 2*capacity; ++i) {
|
||||
p_data = ring_array_get_free_buffer(&ra);
|
||||
assert(p_data);
|
||||
assert(ring_array_get_data_buffer(&ra) == p_data);
|
||||
}
|
||||
|
||||
p_data = NULL;
|
||||
/* Verify/modify buffer data*/
|
||||
for (unsigned int i = 0; i < capacity; ++i) {
|
||||
p_data = ring_array_get_free_buffer(&ra);
|
||||
assert(p_data);
|
||||
assert(*((int*)p_data->mData) == verify_data[i]);
|
||||
(*((int*)p_data->mData))++; // Modify data
|
||||
}
|
||||
for (unsigned int i = 0; i < capacity; ++i) {
|
||||
p_data = ring_array_get_data_buffer(&ra);
|
||||
assert(p_data);
|
||||
assert(*((int*)p_data->mData) == verify_data[i]+1); // Verify modified data
|
||||
}
|
||||
|
||||
ring_array_destroy(&ra);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int main()
|
||||
{
|
||||
test_ring_array();
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
int main()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
Загрузка…
Ссылка в новой задаче