Merge pull request #78 from achronop/audiounit-full-duplex

Implement full-duplex for audiounit backend.
This commit is contained in:
Matthew Gregan 2016-03-10 13:39:48 +13:00
Родитель 019be81152 45e379229d
Коммит d06f84ced4
7 изменённых файлов: 864 добавлений и 198 удалений

2
.gitignore поставляемый
Просмотреть файл

@ -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)

163
src/cubeb_ring_array.h Normal file
Просмотреть файл

@ -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

86
test/test_ring_array.c Normal file
Просмотреть файл

@ -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