Bug 1241476: Update libcubeb from upstream rs=kinetik

Import of upstream git rev 2de1ab50e16763fd88ec1b5bae30422de598eed0
Updated update.sh script as well
This commit is contained in:
Randell Jesup 2016-01-21 22:17:46 -05:00
parent f11aea5c84
commit ebe394665e
19 changed files with 635 additions and 227 deletions

View File

@ -6,3 +6,10 @@ David Richards <drichards@mozilla.com>
Sebastien Alaiwan <sebastien.alaiwan@gmail.com>
KO Myung-Hun <komh@chollian.net>
Haakon Sporsheim <haakon.sporsheim@telenordigital.com>
Alex Chronopoulos <achronop@gmail.com>
Jan Beich <jbeich@FreeBSD.org>
Vito Caputo <vito.caputo@coreos.com>
Landry Breuil <landry@openbsd.org>
Jacek Caban <jacek@codeweavers.com>
Paul Hancock <Paul.Hancock.17041993@live.com>
Ted Mielczarek <ted@mielczarek.org>

View File

@ -5,4 +5,4 @@ Makefile.in build files for the Mozilla build system.
The cubeb git repository is: git://github.com/kinetiknz/cubeb.git
The git commit ID used was 23a17cb4f9ed2de78a0df5ecdfefbbe47dc83c35.
The git commit ID used was 2de1ab50e16763fd88ec1b5bae30422de598eed0.

View File

@ -118,6 +118,10 @@ typedef enum {
} cubeb_stream_type;
#endif
/** An opaque handle used to refer a particular input or output device
* across calls. */
typedef void * cubeb_devid;
/** Stream format initialization parameters. */
typedef struct {
cubeb_sample_format format; /**< Requested sample format. One of
@ -149,41 +153,61 @@ enum {
CUBEB_ERROR = -1, /**< Unclassified error. */
CUBEB_ERROR_INVALID_FORMAT = -2, /**< Unsupported #cubeb_stream_params requested. */
CUBEB_ERROR_INVALID_PARAMETER = -3, /**< Invalid parameter specified. */
CUBEB_ERROR_NOT_SUPPORTED = -4 /**< Optional function not implemented in current backend. */
CUBEB_ERROR_NOT_SUPPORTED = -4, /**< Optional function not implemented in current backend. */
CUBEB_ERROR_DEVICE_UNAVAILABLE = -5 /**< Device specified by #cubeb_devid not available. */
};
/**
* Whether a particular device is an input device (e.g. a microphone), or an
* output device (e.g. headphones). */
typedef enum {
CUBEB_DEVICE_TYPE_UNKNOWN,
CUBEB_DEVICE_TYPE_INPUT,
CUBEB_DEVICE_TYPE_OUTPUT
} cubeb_device_type;
/**
* The state of a device.
*/
typedef enum {
CUBEB_DEVICE_STATE_DISABLED,
CUBEB_DEVICE_STATE_UNPLUGGED,
CUBEB_DEVICE_STATE_ENABLED
CUBEB_DEVICE_STATE_DISABLED, /**< The device has been disabled at the system level. */
CUBEB_DEVICE_STATE_UNPLUGGED, /**< The device is enabled, but nothing is plugged into it. */
CUBEB_DEVICE_STATE_ENABLED /**< The device is enabled. */
} cubeb_device_state;
typedef void * cubeb_devid;
/**
* Architecture specific sample type.
*/
typedef enum {
CUBEB_DEVICE_FMT_S16LE = 0x0010,
CUBEB_DEVICE_FMT_S16BE = 0x0020,
CUBEB_DEVICE_FMT_F32LE = 0x1000,
CUBEB_DEVICE_FMT_F32BE = 0x2000
CUBEB_DEVICE_FMT_S16LE = 0x0010, /**< 16-bit integers, Little Endian. */
CUBEB_DEVICE_FMT_S16BE = 0x0020, /**< 16-bit integers, Big Endian. */
CUBEB_DEVICE_FMT_F32LE = 0x1000, /**< 32-bit floating point, Little Endian. */
CUBEB_DEVICE_FMT_F32BE = 0x2000 /**< 32-bit floating point, Big Endian. */
} cubeb_device_fmt;
#if defined(WORDS_BIGENDIAN) || defined(__BIG_ENDIAN__)
/** 16-bit integers, native endianess, when on a Big Endian environment. */
#define CUBEB_DEVICE_FMT_S16NE CUBEB_DEVICE_FMT_S16BE
/** 32-bit floating points, native endianess, when on a Big Endian environment. */
#define CUBEB_DEVICE_FMT_F32NE CUBEB_DEVICE_FMT_F32BE
#else
/** 16-bit integers, native endianess, when on a Little Endian environment. */
#define CUBEB_DEVICE_FMT_S16NE CUBEB_DEVICE_FMT_S16LE
/** 32-bit floating points, native endianess, when on a Little Endian
* environment. */
#define CUBEB_DEVICE_FMT_F32NE CUBEB_DEVICE_FMT_F32LE
#endif
/** All the 16-bit integers types. */
#define CUBEB_DEVICE_FMT_S16_MASK (CUBEB_DEVICE_FMT_S16LE | CUBEB_DEVICE_FMT_S16BE)
/** All the 32-bit floating points types. */
#define CUBEB_DEVICE_FMT_F32_MASK (CUBEB_DEVICE_FMT_F32LE | CUBEB_DEVICE_FMT_F32BE)
/** All the device formats types. */
#define CUBEB_DEVICE_FMT_ALL (CUBEB_DEVICE_FMT_S16_MASK | CUBEB_DEVICE_FMT_F32_MASK)
/** Channel type for a `cubeb_stream`. Depending on the backend and platform
* used, this can control inter-stream interruption, ducking, and volume
* control.
*/
typedef enum {
CUBEB_DEVICE_PREF_NONE = 0x00,
CUBEB_DEVICE_PREF_MULTIMEDIA = 0x01,
@ -192,26 +216,30 @@ typedef enum {
CUBEB_DEVICE_PREF_ALL = 0x0F
} cubeb_device_pref;
/** This structure holds the characteristics
* of an input or output audio device. It can be obtained using
* `cubeb_enumerate_devices`, and must be destroyed using
* `cubeb_device_info_destroy`. */
typedef struct {
cubeb_devid devid; /* Device identifier handle */
char * device_id; /* Device identifier which might be presented in a UI */
char * friendly_name; /* Friendly device name which might be presented in a UI */
char * group_id; /* Two devices have the same group identifier if they belong to the same physical device; for example a headset and microphone. */
char * vendor_name; /* Optional vendor name, may be NULL */
cubeb_devid devid; /**< Device identifier handle. */
char * device_id; /**< Device identifier which might be presented in a UI. */
char * friendly_name; /**< Friendly device name which might be presented in a UI. */
char * group_id; /**< Two devices have the same group identifier if they belong to the same physical device; for example a headset and microphone. */
char * vendor_name; /**< Optional vendor name, may be NULL. */
cubeb_device_type type; /* Type of device (Input/Output) */
cubeb_device_state state; /* State of device disabled/enabled/unplugged */
cubeb_device_pref preferred;/* Preferred device */
cubeb_device_type type; /**< Type of device (Input/Output). */
cubeb_device_state state; /**< State of device disabled/enabled/unplugged. */
cubeb_device_pref preferred;/**< Preferred device. */
cubeb_device_fmt format; /* Sample format supported */
cubeb_device_fmt default_format;
unsigned int max_channels; /* Channels */
unsigned int default_rate; /* Default/Preferred sample rate */
unsigned int max_rate; /* Maximum sample rate supported */
unsigned int min_rate; /* Minimum sample rate supported */
cubeb_device_fmt format; /**< Sample format supported. */
cubeb_device_fmt default_format; /**< The default sample format for this device. */
unsigned int max_channels; /**< Channels. */
unsigned int default_rate; /**< Default/Preferred sample rate. */
unsigned int max_rate; /**< Maximum sample rate supported. */
unsigned int min_rate; /**< Minimum sample rate supported. */
unsigned int latency_lo_ms; /* Lowest possible latency in milliseconds */
unsigned int latency_hi_ms; /* Higest possible latency in milliseconds */
unsigned int latency_lo_ms; /**< Lowest possible latency in milliseconds. */
unsigned int latency_hi_ms; /**< Higest possible latency in milliseconds. */
} cubeb_device_info;
/** Device collection. */
@ -221,17 +249,21 @@ typedef struct {
} cubeb_device_collection;
/** User supplied data callback.
@param stream
@param user_ptr
@param buffer
@param nframes
@retval Number of frames written to buffer, which must equal nframes except
at end of stream.
@param stream The stream for which this callback fired
@param user_ptr The pointer passed to cubeb_stream_create
@param input_buffer A pointer containing the input data, or nullptr
if this is an output-only stream.
@param output_buffer A pointer containing the output data, or nullptr
if this is an input -only stream.
@param nframes The number of frames of the two buffer.
@retval Number of frames written to the output buffer, which must equal
nframes except at end of stream.
@retval CUBEB_ERROR on error, in which case the data callback will stop
and the stream will enter a shutdown state. */
typedef long (* cubeb_data_callback)(cubeb_stream * stream,
void * user_ptr,
void * buffer,
const void * input_buffer,
void * output_buffer,
long nframes);
/** User supplied state callback.
@ -306,7 +338,14 @@ void cubeb_destroy(cubeb * context);
@param context
@param stream
@param stream_name
@param stream_params
@param input_device Device for the input side of the stream. If NULL
default input device is used.
@param input_stream_params Parameters for the input side of the stream, or
NULL if this stream is output only.
@param output_device Device for the output side of the stream. If NULL
default output device is used.
@param output_stream_params Parameters for the output side of the stream, or
NULL if this stream is input only.
@param latency Approximate stream latency in milliseconds. Valid range
is [1, 2000].
@param data_callback Will be called to preroll data before playback is
@ -315,11 +354,15 @@ void cubeb_destroy(cubeb * context);
@param user_ptr
@retval CUBEB_OK
@retval CUBEB_ERROR
@retval CUBEB_ERROR_INVALID_FORMAT */
@retval CUBEB_ERROR_INVALID_FORMAT
@retval CUBEB_ERROR_DEVICE_UNAVAILABLE */
int cubeb_stream_init(cubeb * context,
cubeb_stream ** stream,
char const * stream_name,
cubeb_stream_params stream_params,
cubeb_devid input_device,
cubeb_stream_params * input_stream_params,
cubeb_devid output_device,
cubeb_stream_params * output_stream_params,
unsigned int latency,
cubeb_data_callback data_callback,
cubeb_state_callback state_callback,
@ -409,7 +452,7 @@ int cubeb_stream_device_destroy(cubeb_stream * stream,
device_changed_callback are invalid pointers.
@retval CUBEB_ERROR_NOT_SUPPORTED */
int cubeb_stream_register_device_changed_callback(cubeb_stream * stream,
cubeb_device_changed_callback device_changed_callback);
cubeb_device_changed_callback device_changed_callback);
/** Returns enumerated devices.
@param context
@ -422,7 +465,7 @@ int cubeb_enumerate_devices(cubeb * context,
cubeb_device_type devtype,
cubeb_device_collection ** collection);
/** Destroy a cubeb_device_collection.
/** Destroy a cubeb_device_collection, and its `cubeb_device_info`.
@param collection collection to destroy
@retval CUBEB_OK
@retval CUBEB_ERROR_INVALID_PARAMETER if collection is an invalid pointer */
@ -437,12 +480,14 @@ int cubeb_device_info_destroy(cubeb_device_info * info);
/** Registers a callback which is called when the system detects
a new device or a device is removed.
@param context
@param devtype device type to include
@param callback a function called whenever the system device list changes.
Passing NULL allow to unregister a function
@param user_ptr pointer to user specified data which will be present in
subsequent callbacks.
@retval CUBEB_ERROR_NOT_SUPPORTED */
int cubeb_register_device_collection_changed(cubeb * context,
cubeb_device_type devtype,
cubeb_device_collection_changed_callback callback,
void * user_ptr);

View File

@ -22,8 +22,14 @@ struct cubeb_ops {
int (* enumerate_devices)(cubeb * context, cubeb_device_type type,
cubeb_device_collection ** collection);
void (* destroy)(cubeb * context);
int (* stream_init)(cubeb * context, cubeb_stream ** stream, char const * stream_name,
cubeb_stream_params stream_params, unsigned int latency,
int (* stream_init)(cubeb * context,
cubeb_stream ** stream,
char const * stream_name,
cubeb_devid input_device,
cubeb_stream_params * input_stream_params,
cubeb_devid output_device,
cubeb_stream_params * output_stream_params,
unsigned int latency,
cubeb_data_callback data_callback,
cubeb_state_callback state_callback,
void * user_ptr);

View File

@ -61,15 +61,36 @@ int audiotrack_init(cubeb ** context, char const * context_name);
int kai_init(cubeb ** context, char const * context_name);
#endif
int
validate_stream_params(cubeb_stream_params stream_params)
validate_stream_params(cubeb_stream_params * input_stream_params,
cubeb_stream_params * output_stream_params)
{
if (stream_params.rate < 1000 || stream_params.rate > 192000 ||
stream_params.channels < 1 || stream_params.channels > 8) {
return CUBEB_ERROR_INVALID_FORMAT;
if (output_stream_params) {
if (output_stream_params->rate < 1000 || output_stream_params->rate > 192000 ||
output_stream_params->channels < 1 || output_stream_params->channels > 8) {
return CUBEB_ERROR_INVALID_FORMAT;
}
}
if (input_stream_params) {
if (input_stream_params->rate < 1000 || input_stream_params->rate > 192000 ||
input_stream_params->channels < 1 || input_stream_params->channels > 8) {
return CUBEB_ERROR_INVALID_FORMAT;
}
}
// Rate and sample format must be the same for input and output, if using a
// duplex stream
if (input_stream_params && output_stream_params) {
if (input_stream_params->rate != output_stream_params->rate ||
input_stream_params->format != output_stream_params->format) {
return CUBEB_ERROR_INVALID_FORMAT;
}
}
switch (stream_params.format) {
cubeb_stream_params * params = input_stream_params ?
input_stream_params : output_stream_params;
switch (params->format) {
case CUBEB_SAMPLE_S16LE:
case CUBEB_SAMPLE_S16BE:
case CUBEB_SAMPLE_FLOAT32LE:
@ -80,6 +101,8 @@ validate_stream_params(cubeb_stream_params stream_params)
return CUBEB_ERROR_INVALID_FORMAT;
}
int
validate_latency(int latency)
{
@ -218,7 +241,11 @@ cubeb_destroy(cubeb * context)
int
cubeb_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_name,
cubeb_stream_params stream_params, unsigned int latency,
cubeb_devid input_device,
cubeb_stream_params * input_stream_params,
cubeb_devid output_device,
cubeb_stream_params * output_stream_params,
unsigned int latency,
cubeb_data_callback data_callback,
cubeb_state_callback state_callback,
void * user_ptr)
@ -229,13 +256,17 @@ cubeb_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_n
return CUBEB_ERROR_INVALID_PARAMETER;
}
if ((r = validate_stream_params(stream_params)) != CUBEB_OK ||
if ((r = validate_stream_params(input_stream_params, output_stream_params)) != CUBEB_OK ||
(r = validate_latency(latency)) != CUBEB_OK) {
return r;
}
return context->ops->stream_init(context, stream, stream_name,
stream_params, latency,
input_device,
input_stream_params,
output_device,
output_stream_params,
latency,
data_callback,
state_callback,
user_ptr);
@ -404,9 +435,12 @@ int cubeb_device_info_destroy(cubeb_device_info * info)
}
int cubeb_register_device_collection_changed(cubeb * context,
cubeb_device_type devtype,
cubeb_device_collection_changed_callback callback,
void * user_ptr)
{
if ((devtype & (CUBEB_DEVICE_TYPE_INPUT | CUBEB_DEVICE_TYPE_OUTPUT)) == 0)
return CUBEB_ERROR_INVALID_PARAMETER;
return CUBEB_ERROR_NOT_SUPPORTED;
}

View File

@ -306,7 +306,7 @@ alsa_refill_stream(cubeb_stream * stm)
assert(p);
pthread_mutex_unlock(&stm->mutex);
got = stm->data_callback(stm, stm->user_ptr, p, avail);
got = stm->data_callback(stm, stm->user_ptr, NULL, p, avail);
pthread_mutex_lock(&stm->mutex);
if (got < 0) {
pthread_mutex_unlock(&stm->mutex);
@ -781,7 +781,11 @@ static void alsa_stream_destroy(cubeb_stream * stm);
static int
alsa_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_name,
cubeb_stream_params stream_params, unsigned int latency,
cubeb_devid input_device,
cubeb_stream_params * input_stream_params,
cubeb_devid output_device,
cubeb_stream_params * output_stream_params,
unsigned int latency,
cubeb_data_callback data_callback, cubeb_state_callback state_callback,
void * user_ptr)
{
@ -792,9 +796,15 @@ alsa_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_name,
assert(ctx && stream);
assert(!input_stream_params && "not supported.");
if (input_device || output_device) {
/* Device selection not yet implemented. */
return CUBEB_ERROR_DEVICE_UNAVAILABLE;
}
*stream = NULL;
switch (stream_params.format) {
switch (output_stream_params->format) {
case CUBEB_SAMPLE_S16LE:
format = SND_PCM_FORMAT_S16_LE;
break;
@ -826,7 +836,7 @@ alsa_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_name,
stm->data_callback = data_callback;
stm->state_callback = state_callback;
stm->user_ptr = user_ptr;
stm->params = stream_params;
stm->params = *output_stream_params;
stm->state = INACTIVE;
stm->volume = 1.0;
@ -933,7 +943,7 @@ alsa_get_max_channel_count(cubeb * ctx, uint32_t * max_channels)
assert(ctx);
r = alsa_stream_init(ctx, &stm, "", params, 100, NULL, NULL, NULL);
r = alsa_stream_init(ctx, &stm, "", NULL, NULL, NULL, &params, 100, NULL, NULL, NULL);
if (r != CUBEB_OK) {
return CUBEB_ERROR;
}

View File

@ -99,12 +99,11 @@ audiotrack_refill(int event, void* user, void* info)
return;
}
got = stream->data_callback(stream, stream->user_ptr, b->raw, b->frameCount);
got = stream->data_callback(stream, stream->user_ptr, NULL, b->raw, b->frameCount);
stream->written += got;
if (got != (long)b->frameCount) {
uint32_t p;
stream->draining = 1;
/* set a marker so we are notified when the are done draining, that is,
* when every frame has been played by android. */
@ -279,7 +278,11 @@ audiotrack_destroy(cubeb * context)
int
audiotrack_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_name,
cubeb_stream_params stream_params, unsigned int latency,
cubeb_devid input_device,
cubeb_stream_params * input_stream_params,
cubeb_devid output_device,
cubeb_stream_params * output_stream_params,
unsigned int latency,
cubeb_data_callback data_callback,
cubeb_state_callback state_callback,
void * user_ptr)
@ -290,12 +293,18 @@ audiotrack_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_
assert(ctx && stream);
if (stream_params.format == CUBEB_SAMPLE_FLOAT32LE ||
stream_params.format == CUBEB_SAMPLE_FLOAT32BE) {
assert(!input_stream_params && "not supported");
if (input_device || output_device) {
/* Device selection not yet implemented. */
return CUBEB_ERROR_DEVICE_UNAVAILABLE;
}
if (output_stream_params->format == CUBEB_SAMPLE_FLOAT32LE ||
output_stream_params->format == CUBEB_SAMPLE_FLOAT32BE) {
return CUBEB_ERROR_INVALID_FORMAT;
}
if (audiotrack_get_min_frame_count(ctx, &stream_params, (int *)&min_frame_count)) {
if (audiotrack_get_min_frame_count(ctx, output_stream_params, (int *)&min_frame_count)) {
return CUBEB_ERROR;
}
@ -306,7 +315,7 @@ audiotrack_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_
stm->data_callback = data_callback;
stm->state_callback = state_callback;
stm->user_ptr = user_ptr;
stm->params = stream_params;
stm->params = *output_stream_params;
stm->instance = calloc(SIZE_AUDIOTRACK_INSTANCE, 1);
(*(uint32_t*)((intptr_t)stm->instance + SIZE_AUDIOTRACK_INSTANCE - 4)) = 0xbaadbaad;

View File

@ -136,7 +136,7 @@ audiounit_output_callback(void * user_ptr, AudioUnitRenderActionFlags * flags,
}
pthread_mutex_unlock(&stm->mutex);
got = stm->data_callback(stm, stm->user_ptr, buf, nframes);
got = stm->data_callback(stm, stm->user_ptr, NULL, buf, nframes);
pthread_mutex_lock(&stm->mutex);
if (got < 0) {
/* XXX handle this case. */
@ -539,7 +539,11 @@ static void audiounit_stream_destroy(cubeb_stream * stm);
static int
audiounit_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_name,
cubeb_stream_params stream_params, unsigned int latency,
cubeb_devid input_device,
cubeb_stream_params * input_stream_params,
cubeb_devid output_device,
cubeb_stream_params * output_stream_params,
unsigned int latency,
cubeb_data_callback data_callback, cubeb_state_callback state_callback,
void * user_ptr)
{
@ -558,13 +562,19 @@ audiounit_stream_init(cubeb * context, cubeb_stream ** stream, char const * stre
UInt32 size;
AudioValueRange latency_range;
assert(!input_stream_params && "not supported");
if (input_device || output_device) {
/* Device selection not yet implemented. */
return CUBEB_ERROR_DEVICE_UNAVAILABLE;
}
assert(context);
*stream = NULL;
memset(&ss, 0, sizeof(ss));
ss.mFormatFlags = 0;
switch (stream_params.format) {
switch (output_stream_params->format) {
case CUBEB_SAMPLE_S16LE:
ss.mBitsPerChannel = 16;
ss.mFormatFlags |= kAudioFormatFlagIsSignedInteger;
@ -589,8 +599,8 @@ audiounit_stream_init(cubeb * context, cubeb_stream ** stream, char const * stre
ss.mFormatID = kAudioFormatLinearPCM;
ss.mFormatFlags |= kLinearPCMFormatFlagIsPacked;
ss.mSampleRate = stream_params.rate;
ss.mChannelsPerFrame = stream_params.channels;
ss.mSampleRate = output_stream_params->rate;
ss.mChannelsPerFrame = output_stream_params->channels;
ss.mBytesPerFrame = (ss.mBitsPerChannel / 8) * ss.mChannelsPerFrame;
ss.mFramesPerPacket = 1;

View File

@ -120,7 +120,7 @@ bufferqueue_callback(SLBufferQueueItf caller, void * user_ptr)
pthread_mutex_unlock(&stm->mutex);
if (!draining) {
written = cubeb_resampler_fill(stm->resampler, buf,
written = cubeb_resampler_fill(stm->resampler, NULL, buf,
stm->queuebuf_len / stm->framesize);
if (written < 0 || written * stm->framesize > stm->queuebuf_len) {
(*stm->play)->SetPlayState(stm->play, SL_PLAYSTATE_PAUSED);
@ -465,17 +465,26 @@ static void opensl_stream_destroy(cubeb_stream * stm);
static int
opensl_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_name,
cubeb_stream_params stream_params, unsigned int latency,
cubeb_devid input_device,
cubeb_stream_params * input_stream_params,
cubeb_devid output_device,
cubeb_stream_params * output_stream_params,
unsigned int latency,
cubeb_data_callback data_callback, cubeb_state_callback state_callback,
void * user_ptr)
{
cubeb_stream * stm;
assert(ctx);
assert(!input_stream_params && "not supported");
if (input_device || output_device) {
/* Device selection not yet implemented. */
return CUBEB_ERROR_DEVICE_UNAVAILABLE;
}
*stream = NULL;
if (stream_params.channels < 1 || stream_params.channels > 32 ||
if (output_stream_params->channels < 1 || output_stream_params->channels > 32 ||
latency < 1 || latency > 2000) {
return CUBEB_ERROR_INVALID_FORMAT;
}
@ -483,16 +492,16 @@ opensl_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_name
SLDataFormat_PCM format;
format.formatType = SL_DATAFORMAT_PCM;
format.numChannels = stream_params.channels;
format.numChannels = output_stream_params->channels;
// samplesPerSec is in milliHertz
format.samplesPerSec = stream_params.rate * 1000;
format.samplesPerSec = output_stream_params->rate * 1000;
format.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_16;
format.containerSize = SL_PCMSAMPLEFORMAT_FIXED_16;
format.channelMask = stream_params.channels == 1 ?
format.channelMask = output_stream_params->channels == 1 ?
SL_SPEAKER_FRONT_CENTER :
SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
switch (stream_params.format) {
switch (output_stream_params->format) {
case CUBEB_SAMPLE_S16LE:
format.endianness = SL_BYTEORDER_LITTLEENDIAN;
break;
@ -511,10 +520,10 @@ opensl_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_name
stm->state_callback = state_callback;
stm->user_ptr = user_ptr;
stm->inputrate = stream_params.rate;
stm->inputrate = output_stream_params->rate;
stm->latency = latency;
stm->stream_type = stream_params.stream_type;
stm->framesize = stream_params.channels * sizeof(int16_t);
stm->stream_type = output_stream_params->stream_type;
stm->framesize = output_stream_params->channels * sizeof(int16_t);
stm->lastPosition = -1;
stm->lastPositionTimeStamp = 0;
stm->lastCompensativePosition = -1;
@ -575,7 +584,7 @@ opensl_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_name
stm->queuebuf_len += stm->framesize - (stm->queuebuf_len % stm->framesize);
}
stm->resampler = cubeb_resampler_create(stm, stream_params,
stm->resampler = cubeb_resampler_create(stm, *output_stream_params,
preferred_sampling_rate,
data_callback,
stm->queuebuf_len / stm->framesize,
@ -594,7 +603,7 @@ opensl_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_name
}
#if defined(__ANDROID__)
SLuint32 stream_type = convert_stream_type_to_sl_stream(stream_params.stream_type);
SLuint32 stream_type = convert_stream_type_to_sl_stream(output_stream_params->stream_type);
if (stream_type != 0xFFFFFFFF) {
SLAndroidConfigurationItf playerConfig;
res = (*stm->playerObj)->GetInterface(stm->playerObj,

View File

@ -70,12 +70,27 @@
X(pa_threaded_mainloop_unlock) \
X(pa_threaded_mainloop_wait) \
X(pa_usec_to_bytes) \
X(pa_stream_set_read_callback) \
X(pa_stream_connect_record) \
X(pa_stream_readable_size) \
X(pa_stream_peek) \
X(pa_stream_drop) \
X(pa_stream_get_buffer_attr) \
#define MAKE_TYPEDEF(x) static typeof(x) * cubeb_##x;
LIBPULSE_API_VISIT(MAKE_TYPEDEF);
#undef MAKE_TYPEDEF
#endif
//#define LOGGING_ENABLED
#ifdef LOGGING_ENABLED
#define LOG(...) do { \
fprintf(stderr, __VA_ARGS__); \
} while(0)
#else
#define LOG(...)
#endif
static struct cubeb_ops const pulse_ops;
struct cubeb {
@ -90,12 +105,14 @@ struct cubeb {
struct cubeb_stream {
cubeb * context;
pa_stream * stream;
pa_stream * output_stream;
pa_stream * input_stream;
cubeb_data_callback data_callback;
cubeb_state_callback state_callback;
void * user_ptr;
pa_time_event * drain_timer;
pa_sample_spec sample_spec;
pa_sample_spec output_sample_spec;
pa_sample_spec input_sample_spec;
int shutdown;
float volume;
};
@ -113,6 +130,7 @@ sink_info_callback(pa_context * context, const pa_sink_info * info, int eol, voi
{
cubeb * ctx = u;
if (!eol) {
free(ctx->default_sink_info);
ctx->default_sink_info = malloc(sizeof(pa_sink_info));
memcpy(ctx->default_sink_info, info, sizeof(pa_sink_info));
}
@ -170,46 +188,46 @@ stream_state_callback(pa_stream * s, void * u)
}
static void
stream_request_callback(pa_stream * s, size_t nbytes, void * u)
trigger_user_callback(pa_stream * s, void const * input_data, size_t nbytes, cubeb_stream * stm)
{
cubeb_stream * stm;
void * buffer;
size_t size;
int r;
long got;
size_t towrite;
size_t towrite, read_offset;
size_t frame_size;
stm = u;
if (stm->shutdown)
return;
frame_size = WRAP(pa_frame_size)(&stm->sample_spec);
frame_size = WRAP(pa_frame_size)(&stm->output_sample_spec);
assert(nbytes % frame_size == 0);
towrite = nbytes;
read_offset = 0;
while (towrite) {
size = towrite;
r = WRAP(pa_stream_begin_write)(s, &buffer, &size);
// Note: this has failed running under rr on occassion - needs investigation.
assert(r == 0);
assert(size > 0);
assert(size % frame_size == 0);
got = stm->data_callback(stm, stm->user_ptr, buffer, size / frame_size);
LOG("Trigger user callback with output buffer size=%zd, read_offset=%zd\n", size, read_offset);
got = stm->data_callback(stm, stm->user_ptr, (uint8_t const *)input_data + read_offset, buffer, size / frame_size);
if (got < 0) {
WRAP(pa_stream_cancel_write)(s);
stm->shutdown = 1;
return;
}
// If more iterations move offset of read buffer
if (input_data) {
size_t in_frame_size = WRAP(pa_frame_size)(&stm->input_sample_spec);
read_offset += (size / frame_size) * in_frame_size;
}
if (stm->volume != PULSE_NO_GAIN) {
uint32_t samples = size * stm->sample_spec.channels / frame_size ;
uint32_t samples = size * stm->output_sample_spec.channels / frame_size ;
if (stm->sample_spec.format == PA_SAMPLE_S16BE ||
stm->sample_spec.format == PA_SAMPLE_S16LE) {
if (stm->output_sample_spec.format == PA_SAMPLE_S16BE ||
stm->output_sample_spec.format == PA_SAMPLE_S16LE) {
short * b = buffer;
for (uint32_t i = 0; i < samples; i++) {
b[i] *= stm->volume;
@ -246,6 +264,81 @@ stream_request_callback(pa_stream * s, size_t nbytes, void * u)
assert(towrite == 0);
}
static int
read_from_input(pa_stream * s, void const ** buffer, size_t * size)
{
size_t readable_size = WRAP(pa_stream_readable_size)(s);
if (readable_size > 0) {
if (WRAP(pa_stream_peek)(s, buffer, size) < 0) {
return -1;
}
}
return readable_size;
}
static void
stream_write_callback(pa_stream * s, size_t nbytes, void * u)
{
LOG("Output callback to be written buffer size %zd\n", nbytes);
cubeb_stream * stm = u;
if (stm->shutdown) {
return;
}
if (!stm->input_stream){
// Output/playback only operation.
// Write directly to output
assert(!stm->input_stream && stm->output_stream);
trigger_user_callback(s, NULL, nbytes, stm);
}
}
static void
stream_read_callback(pa_stream * s, size_t nbytes, void * u)
{
LOG("Input callback buffer size %zd\n", nbytes);
cubeb_stream * stm = u;
if (stm->shutdown) {
return;
}
// Note: this has failed running under rr on occassion - needs investigation.
assert(stm->input_stream && stm->input_sample_spec.rate != 0);
void const * read_data = NULL;
size_t read_size;
while (read_from_input(s, &read_data, &read_size) > 0) {
/* read_data can be NULL in case of a hole. */
if (read_data) {
size_t in_frame_size = WRAP(pa_frame_size)(&stm->input_sample_spec);
size_t read_frames = read_size / in_frame_size;
if (stm->output_stream) {
// input/capture + output/playback operation
size_t out_frame_size = WRAP(pa_frame_size)(&stm->output_sample_spec);
size_t write_size = read_frames * out_frame_size;
// Offer full duplex data for writing
trigger_user_callback(stm->output_stream, read_data, write_size, stm);
} else {
// input/capture only operation. Call callback directly
long got = stm->data_callback(stm, stm->user_ptr, read_data, NULL, read_frames);
if (got < 0 || (size_t) got != read_frames) {
WRAP(pa_stream_cancel_write)(s);
stm->shutdown = 1;
break;
}
}
}
if (read_size > 0) {
WRAP(pa_stream_drop)(s);
}
if (stm->shutdown) {
return;
}
}
}
static int
wait_until_context_ready(cubeb * ctx)
{
@ -261,15 +354,32 @@ wait_until_context_ready(cubeb * ctx)
}
static int
wait_until_stream_ready(cubeb_stream * stm)
wait_until_io_stream_ready(pa_stream * stream, pa_threaded_mainloop * mainloop)
{
if (!stream || !mainloop){
return -1;
}
for (;;) {
pa_stream_state_t state = WRAP(pa_stream_get_state)(stm->stream);
pa_stream_state_t state = WRAP(pa_stream_get_state)(stream);
if (!PA_STREAM_IS_GOOD(state))
return -1;
if (state == PA_STREAM_READY)
break;
WRAP(pa_threaded_mainloop_wait)(stm->context->mainloop);
WRAP(pa_threaded_mainloop_wait)(mainloop);
}
return 0;
}
static int
wait_until_stream_ready(cubeb_stream * stm)
{
if (stm->output_stream &&
wait_until_io_stream_ready(stm->output_stream, stm->context->mainloop) == -1) {
return -1;
}
if(stm->input_stream &&
wait_until_io_stream_ready(stm->input_stream, stm->context->mainloop) == -1) {
return -1;
}
return 0;
}
@ -290,16 +400,25 @@ operation_wait(cubeb * ctx, pa_stream * stream, pa_operation * o)
}
static void
stream_cork(cubeb_stream * stm, enum cork_state state)
cork_io_stream(cubeb_stream * stm, pa_stream * io_stream, enum cork_state state)
{
pa_operation * o;
WRAP(pa_threaded_mainloop_lock)(stm->context->mainloop);
o = WRAP(pa_stream_cork)(stm->stream, state & CORK, stream_success_callback, stm);
if (!io_stream) {
return;
}
o = WRAP(pa_stream_cork)(io_stream, state & CORK, stream_success_callback, stm);
if (o) {
operation_wait(stm->context, stm->stream, o);
operation_wait(stm->context, io_stream, o);
WRAP(pa_operation_unref)(o);
}
}
static void
stream_cork(cubeb_stream * stm, enum cork_state state)
{
WRAP(pa_threaded_mainloop_lock)(stm->context->mainloop);
cork_io_stream(stm, stm->output_stream, state);
cork_io_stream(stm, stm->input_stream, state);
WRAP(pa_threaded_mainloop_unlock)(stm->context->mainloop);
if (state & NOTIFY) {
@ -308,6 +427,33 @@ stream_cork(cubeb_stream * stm, enum cork_state state)
}
}
static int
stream_update_timing_info(cubeb_stream * stm)
{
int r = -1;
pa_operation * o = NULL;
if (stm->output_stream) {
o = WRAP(pa_stream_update_timing_info)(stm->output_stream, stream_success_callback, stm);
if (o) {
r = operation_wait(stm->context, stm->output_stream, o);
WRAP(pa_operation_unref)(o);
}
if (r != 0) {
return r;
}
}
if (stm->input_stream) {
o = WRAP(pa_stream_update_timing_info)(stm->input_stream, stream_success_callback, stm);
if (o) {
r = operation_wait(stm->context, stm->input_stream, o);
WRAP(pa_operation_unref)(o);
}
}
return r;
}
static void pulse_context_destroy(cubeb * ctx);
static void pulse_destroy(cubeb * ctx);
@ -485,88 +631,145 @@ pulse_destroy(cubeb * ctx)
static void pulse_stream_destroy(cubeb_stream * stm);
pa_sample_format_t
cubeb_to_pulse_format(cubeb_sample_format format)
{
switch (format) {
case CUBEB_SAMPLE_S16LE:
return PA_SAMPLE_S16LE;
case CUBEB_SAMPLE_S16BE:
return PA_SAMPLE_S16BE;
case CUBEB_SAMPLE_FLOAT32LE:
return PA_SAMPLE_FLOAT32LE;
case CUBEB_SAMPLE_FLOAT32BE:
return PA_SAMPLE_FLOAT32BE;
default:
return PA_SAMPLE_INVALID;
}
}
static int
pulse_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_name,
cubeb_stream_params stream_params, unsigned int latency,
cubeb_data_callback data_callback, cubeb_state_callback state_callback,
create_pa_stream(cubeb_stream * stm,
pa_stream ** pa_stm,
cubeb_stream_params * stream_params,
char const * stream_name)
{
assert(stm && stream_params);
*pa_stm = NULL;
pa_sample_spec ss;
ss.format = cubeb_to_pulse_format(stream_params->format);
if (ss.format == PA_SAMPLE_INVALID)
return CUBEB_ERROR_INVALID_FORMAT;
ss.rate = stream_params->rate;
ss.channels = stream_params->channels;
*pa_stm = WRAP(pa_stream_new)(stm->context->context, stream_name, &ss, NULL);
return (*pa_stm == NULL) ? CUBEB_ERROR : CUBEB_OK;
}
static pa_buffer_attr
set_buffering_attribute(unsigned int latency, pa_sample_spec * sample_spec)
{
pa_buffer_attr battr;
battr.maxlength = -1;
battr.prebuf = -1;
battr.tlength = WRAP(pa_usec_to_bytes)(latency * PA_USEC_PER_MSEC, sample_spec);
battr.minreq = battr.tlength / 4;
battr.fragsize = battr.minreq;
LOG("Requested buffer attributes maxlength %u, tlength %u, prebuf %u, minreq %u, fragsize %u\n",
battr.maxlength, battr.tlength, battr.prebuf, battr.minreq, battr.fragsize);
return battr;
}
static int
pulse_stream_init(cubeb * context,
cubeb_stream ** stream,
char const * stream_name,
cubeb_devid input_device,
cubeb_stream_params * input_stream_params,
cubeb_devid output_device,
cubeb_stream_params * output_stream_params,
unsigned int latency,
cubeb_data_callback data_callback,
cubeb_state_callback state_callback,
void * user_ptr)
{
pa_sample_spec ss;
cubeb_stream * stm;
pa_operation * o;
pa_buffer_attr battr;
int r;
assert(context);
*stream = NULL;
switch (stream_params.format) {
case CUBEB_SAMPLE_S16LE:
ss.format = PA_SAMPLE_S16LE;
break;
case CUBEB_SAMPLE_S16BE:
ss.format = PA_SAMPLE_S16BE;
break;
case CUBEB_SAMPLE_FLOAT32LE:
ss.format = PA_SAMPLE_FLOAT32LE;
break;
case CUBEB_SAMPLE_FLOAT32BE:
ss.format = PA_SAMPLE_FLOAT32BE;
break;
default:
return CUBEB_ERROR_INVALID_FORMAT;
}
// If the connection failed for some reason, try to reconnect
if (context->error == 1 && pulse_context_init(context) != 0) {
return CUBEB_ERROR;
}
ss.rate = stream_params.rate;
ss.channels = stream_params.channels;
*stream = NULL;
stm = calloc(1, sizeof(*stm));
assert(stm);
stm->context = context;
stm->data_callback = data_callback;
stm->state_callback = state_callback;
stm->user_ptr = user_ptr;
stm->sample_spec = ss;
stm->volume = PULSE_NO_GAIN;
battr.maxlength = -1;
battr.tlength = WRAP(pa_usec_to_bytes)(latency * PA_USEC_PER_MSEC, &stm->sample_spec);
battr.prebuf = -1;
battr.minreq = battr.tlength / 4;
battr.fragsize = -1;
WRAP(pa_threaded_mainloop_lock)(stm->context->mainloop);
stm->stream = WRAP(pa_stream_new)(stm->context->context, stream_name, &ss, NULL);
if (!stm->stream) {
pulse_stream_destroy(stm);
return CUBEB_ERROR;
if (output_stream_params) {
r = create_pa_stream(stm, &stm->output_stream, output_stream_params, stream_name);
if (r != CUBEB_OK) {
WRAP(pa_threaded_mainloop_unlock)(stm->context->mainloop);
pulse_stream_destroy(stm);
return r;
}
stm->output_sample_spec = *(WRAP(pa_stream_get_sample_spec)(stm->output_stream));
WRAP(pa_stream_set_state_callback)(stm->output_stream, stream_state_callback, stm);
WRAP(pa_stream_set_write_callback)(stm->output_stream, stream_write_callback, stm);
battr = set_buffering_attribute(latency, &stm->output_sample_spec);
WRAP(pa_stream_connect_playback)(stm->output_stream,
output_device,
&battr,
PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_INTERPOLATE_TIMING |
PA_STREAM_START_CORKED | PA_STREAM_ADJUST_LATENCY,
NULL, NULL);
}
WRAP(pa_stream_set_state_callback)(stm->stream, stream_state_callback, stm);
WRAP(pa_stream_set_write_callback)(stm->stream, stream_request_callback, stm);
WRAP(pa_stream_connect_playback)(stm->stream, NULL, &battr,
// Set up input stream
if (input_stream_params) {
r = create_pa_stream(stm, &stm->input_stream, input_stream_params, stream_name);
if (r != CUBEB_OK) {
WRAP(pa_threaded_mainloop_unlock)(stm->context->mainloop);
pulse_stream_destroy(stm);
return r;
}
stm->input_sample_spec = *(WRAP(pa_stream_get_sample_spec)(stm->input_stream));
WRAP(pa_stream_set_state_callback)(stm->input_stream, stream_state_callback, stm);
WRAP(pa_stream_set_read_callback)(stm->input_stream, stream_read_callback, stm);
battr = set_buffering_attribute(latency, &stm->input_sample_spec);
WRAP(pa_stream_connect_record)(stm->input_stream,
input_device,
&battr,
PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_INTERPOLATE_TIMING |
PA_STREAM_START_CORKED,
NULL, NULL);
PA_STREAM_START_CORKED | PA_STREAM_ADJUST_LATENCY);
}
r = wait_until_stream_ready(stm);
if (r == 0) {
/* force a timing update now, otherwise timing info does not become valid
until some point after initialization has completed. */
o = WRAP(pa_stream_update_timing_info)(stm->stream, stream_success_callback, stm);
if (o) {
r = operation_wait(stm->context, stm->stream, o);
WRAP(pa_operation_unref)(o);
}
r = stream_update_timing_info(stm);
}
WRAP(pa_threaded_mainloop_unlock)(stm->context->mainloop);
if (r != 0) {
@ -574,6 +777,22 @@ pulse_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_n
return CUBEB_ERROR;
}
#ifdef LOGGING_ENABLED
if (output_stream_params){
const pa_buffer_attr * output_att;
output_att = WRAP(pa_stream_get_buffer_attr)(stm->output_stream);
LOG("Output buffer attributes maxlength %u, tlength %u, prebuf %u, minreq %u, fragsize %u\n",output_att->maxlength, output_att->tlength,
output_att->prebuf, output_att->minreq, output_att->fragsize);
}
if (input_stream_params){
const pa_buffer_attr * input_att;
input_att = WRAP(pa_stream_get_buffer_attr)(stm->input_stream);
LOG("Input buffer attributes maxlength %u, tlength %u, prebuf %u, minreq %u, fragsize %u\n",input_att->maxlength, input_att->tlength,
input_att->prebuf, input_att->minreq, input_att->fragsize);
}
#endif
*stream = stm;
return CUBEB_OK;
@ -582,22 +801,28 @@ pulse_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_n
static void
pulse_stream_destroy(cubeb_stream * stm)
{
if (stm->stream) {
stream_cork(stm, CORK);
stream_cork(stm, CORK);
WRAP(pa_threaded_mainloop_lock)(stm->context->mainloop);
WRAP(pa_threaded_mainloop_lock)(stm->context->mainloop);
if (stm->output_stream) {
if (stm->drain_timer) {
/* there's no pa_rttime_free, so use this instead. */
WRAP(pa_threaded_mainloop_get_api)(stm->context->mainloop)->time_free(stm->drain_timer);
}
WRAP(pa_stream_set_state_callback)(stm->stream, NULL, NULL);
WRAP(pa_stream_disconnect)(stm->stream);
WRAP(pa_stream_unref)(stm->stream);
WRAP(pa_threaded_mainloop_unlock)(stm->context->mainloop);
WRAP(pa_stream_set_state_callback)(stm->output_stream, NULL, NULL);
WRAP(pa_stream_disconnect)(stm->output_stream);
WRAP(pa_stream_unref)(stm->output_stream);
}
if (stm->input_stream) {
WRAP(pa_stream_set_state_callback)(stm->input_stream, NULL, NULL);
WRAP(pa_stream_disconnect)(stm->input_stream);
WRAP(pa_stream_unref)(stm->input_stream);
}
WRAP(pa_threaded_mainloop_unlock)(stm->context->mainloop);
free(stm);
}
@ -622,12 +847,16 @@ pulse_stream_get_position(cubeb_stream * stm, uint64_t * position)
pa_usec_t r_usec;
uint64_t bytes;
if (!stm || !stm->output_stream) {
return CUBEB_ERROR;
}
in_thread = WRAP(pa_threaded_mainloop_in_thread)(stm->context->mainloop);
if (!in_thread) {
WRAP(pa_threaded_mainloop_lock)(stm->context->mainloop);
}
r = WRAP(pa_stream_get_time)(stm->stream, &r_usec);
r = WRAP(pa_stream_get_time)(stm->output_stream, &r_usec);
if (!in_thread) {
WRAP(pa_threaded_mainloop_unlock)(stm->context->mainloop);
}
@ -636,8 +865,8 @@ pulse_stream_get_position(cubeb_stream * stm, uint64_t * position)
return CUBEB_ERROR;
}
bytes = WRAP(pa_usec_to_bytes)(r_usec, &stm->sample_spec);
*position = bytes / WRAP(pa_frame_size)(&stm->sample_spec);
bytes = WRAP(pa_usec_to_bytes)(r_usec, &stm->output_sample_spec);
*position = bytes / WRAP(pa_frame_size)(&stm->output_sample_spec);
return CUBEB_OK;
}
@ -648,17 +877,17 @@ pulse_stream_get_latency(cubeb_stream * stm, uint32_t * latency)
pa_usec_t r_usec;
int negative, r;
if (!stm) {
if (!stm || !stm->output_stream) {
return CUBEB_ERROR;
}
r = WRAP(pa_stream_get_latency)(stm->stream, &r_usec, &negative);
r = WRAP(pa_stream_get_latency)(stm->output_stream, &r_usec, &negative);
assert(!negative);
if (r) {
return CUBEB_ERROR;
}
*latency = r_usec * stm->sample_spec.rate / PA_USEC_PER_SEC;
*latency = r_usec * stm->output_sample_spec.rate / PA_USEC_PER_SEC;
return CUBEB_OK;
}
@ -678,6 +907,10 @@ pulse_stream_set_volume(cubeb_stream * stm, float volume)
pa_cvolume cvol;
const pa_sample_spec * ss;
if (!stm->output_stream) {
return CUBEB_ERROR;
}
WRAP(pa_threaded_mainloop_lock)(stm->context->mainloop);
while (!stm->context->default_sink_info) {
@ -689,18 +922,18 @@ pulse_stream_set_volume(cubeb_stream * stm, float volume)
if (stm->context->default_sink_info->flags & PA_SINK_FLAT_VOLUME) {
stm->volume = volume;
} else {
ss = WRAP(pa_stream_get_sample_spec)(stm->stream);
ss = WRAP(pa_stream_get_sample_spec)(stm->output_stream);
vol = WRAP(pa_sw_volume_from_linear)(volume);
WRAP(pa_cvolume_set)(&cvol, ss->channels, vol);
index = WRAP(pa_stream_get_index)(stm->stream);
index = WRAP(pa_stream_get_index)(stm->output_stream);
op = WRAP(pa_context_set_sink_input_volume)(stm->context->context,
index, &cvol, volume_success,
stm);
if (op) {
operation_wait(stm->context, stm->stream, op);
operation_wait(stm->context, stm->output_stream, op);
WRAP(pa_operation_unref)(op);
}
}
@ -716,7 +949,11 @@ pulse_stream_set_panning(cubeb_stream * stream, float panning)
const pa_channel_map * map;
pa_cvolume vol;
map = WRAP(pa_stream_get_channel_map)(stream->stream);
if (!stream->output_stream) {
return CUBEB_ERROR;
}
map = WRAP(pa_stream_get_channel_map)(stream->output_stream);
if (!WRAP(pa_channel_map_can_balance)(map)) {
return CUBEB_ERROR;
@ -926,11 +1163,13 @@ pulse_enumerate_devices(cubeb * context, cubeb_device_type type,
}
*collection = malloc(sizeof(cubeb_device_collection) +
sizeof(cubeb_device_info*) * (user_data.count > 0 ? user_data.count - 1 : 0));
sizeof(cubeb_device_info *) * (user_data.count > 0 ? user_data.count - 1 : 0));
(*collection)->count = user_data.count;
for (i = 0; i < user_data.count; i++)
(*collection)->device[i] = user_data.devinfo[i];
free(user_data.default_sink_name);
free(user_data.default_source_name);
free(user_data.devinfo);
return CUBEB_OK;
}

View File

@ -9,6 +9,9 @@
#include <cstring>
#include <cstddef>
#include <cstdio>
#if defined(HAVE_CONFIG_H)
#include "config.h"
#endif
#include "cubeb_resampler.h"
#include "cubeb-speex-resampler.h"
@ -69,7 +72,7 @@ to_speex_quality(cubeb_resampler_quality q)
} // end of anonymous namespace
struct cubeb_resampler {
virtual long fill(void * buffer, long frames_needed) = 0;
virtual long fill(void * input_buffer, void * output_buffer, long frames_needed) = 0;
virtual ~cubeb_resampler() {}
};
@ -84,9 +87,9 @@ public:
{
}
virtual long fill(void * buffer, long frames_needed)
virtual long fill(void * input_buffer, void * output_buffer, long frames_needed)
{
long got = data_callback(stream, user_ptr, buffer, frames_needed);
long got = data_callback(stream, user_ptr, input_buffer, output_buffer, frames_needed);
assert(got <= frames_needed);
return got;
}
@ -106,7 +109,7 @@ public:
virtual ~cubeb_resampler_speex();
virtual long fill(void * buffer, long frames_needed);
virtual long fill(void * input_buffer, void * output_buffer, long frames_needed);
private:
SpeexResamplerState * const speex_resampler;
@ -161,7 +164,7 @@ cubeb_resampler_speex::~cubeb_resampler_speex()
}
long
cubeb_resampler_speex::fill(void * buffer, long frames_needed)
cubeb_resampler_speex::fill(void * input_buffer, void * output_buffer, long frames_needed)
{
// Use more input frames than strictly necessary, so in the worst case,
// we have leftover unresampled frames at the end, that we can use
@ -175,7 +178,7 @@ cubeb_resampler_speex::fill(void * buffer, long frames_needed)
memcpy(resampling_src_buffer.get(), leftover_frames_buffer.get(), leftover_bytes);
uint8_t * buffer_start = resampling_src_buffer.get() + leftover_bytes;
long got = data_callback(stream, user_ptr, buffer_start, frames_requested);
long got = data_callback(stream, user_ptr, NULL, buffer_start, frames_requested);
assert(got <= frames_requested);
if (got < 0) {
@ -188,12 +191,12 @@ cubeb_resampler_speex::fill(void * buffer, long frames_needed)
if (stream_params.format == CUBEB_SAMPLE_FLOAT32NE) {
float * in_buffer = reinterpret_cast<float *>(resampling_src_buffer.get());
float * out_buffer = reinterpret_cast<float *>(buffer);
float * out_buffer = reinterpret_cast<float *>(output_buffer);
speex_resampler_process_interleaved_float(speex_resampler, in_buffer, &in_frames,
out_buffer, &out_frames);
} else {
short * in_buffer = reinterpret_cast<short *>(resampling_src_buffer.get());
short * out_buffer = reinterpret_cast<short *>(buffer);
short * out_buffer = reinterpret_cast<short *>(output_buffer);
speex_resampler_process_interleaved_int(speex_resampler, in_buffer, &in_frames,
out_buffer, &out_frames);
}
@ -239,9 +242,11 @@ cubeb_resampler_create(cubeb_stream * stream,
long
cubeb_resampler_fill(cubeb_resampler * resampler,
void * buffer, long frames_needed)
void * input_buffer,
void * output_buffer,
long frames_needed)
{
return resampler->fill(buffer, frames_needed);
return resampler->fill(input_buffer, output_buffer, frames_needed);
}
void

View File

@ -53,7 +53,8 @@ cubeb_resampler * cubeb_resampler_create(cubeb_stream * stream,
* @retval CUBEB_ERROR on error.
*/
long cubeb_resampler_fill(cubeb_resampler * resampler,
void * buffer, long frames_needed);
void * input_buffer,
void * output_buffer, long frames_needed);
/**
* Destroy a cubeb_resampler.

View File

@ -170,10 +170,14 @@ sndio_destroy(cubeb *context)
}
static int
sndio_stream_init(cubeb *context,
cubeb_stream **stream,
char const *stream_name,
cubeb_stream_params stream_params, unsigned int latency,
sndio_stream_init(cubeb * context,
cubeb_stream ** stream,
char const * stream_name,
cubeb_devid input_device,
cubeb_stream_params * input_stream_params,
cubeb_devid output_device,
cubeb_stream_params * output_stream_params,
unsigned int latency,
cubeb_data_callback data_callback,
cubeb_state_callback state_callback,
void *user_ptr)
@ -183,6 +187,12 @@ sndio_stream_init(cubeb *context,
DPR("sndio_stream_init(%s)\n", stream_name);
size_t size;
assert(!input_stream_params && "not supported.");
if (input_device || output_device) {
/* Device selection not yet implemented. */
return CUBEB_ERROR_DEVICE_UNAVAILABLE;
}
s = malloc(sizeof(cubeb_stream));
if (s == NULL)
return CUBEB_ERROR;
@ -196,7 +206,7 @@ sndio_stream_init(cubeb *context,
sio_initpar(&wpar);
wpar.sig = 1;
wpar.bits = 16;
switch (stream_params.format) {
switch (output_stream_params->format) {
case CUBEB_SAMPLE_S16LE:
wpar.le = 1;
break;
@ -210,8 +220,8 @@ sndio_stream_init(cubeb *context,
DPR("sndio_stream_init() unsupported format\n");
return CUBEB_ERROR_INVALID_FORMAT;
}
wpar.rate = stream_params.rate;
wpar.pchan = stream_params.channels;
wpar.rate = output_stream_params->rate;
wpar.pchan = output_stream_params->channels;
wpar.appbufsz = latency * wpar.rate / 1000;
if (!sio_setpar(s->hdl, &wpar) || !sio_getpar(s->hdl, &rpar)) {
sio_close(s->hdl);
@ -237,7 +247,7 @@ sndio_stream_init(cubeb *context,
s->arg = user_ptr;
s->mtx = PTHREAD_MUTEX_INITIALIZER;
s->rdpos = s->wrpos = 0;
if (stream_params.format == CUBEB_SAMPLE_FLOAT32LE) {
if (output_stream_params->format == CUBEB_SAMPLE_FLOAT32LE) {
s->conv = 1;
size = rpar.round * rpar.pchan * sizeof(float);
} else {

View File

@ -1,5 +1,5 @@
/*
* Copyright © 2013 Mozilla Foundation
* Copyright © 2013 Mozilla Foundation
*
* This program is made available under an ISC-style license. See the
* accompanying file LICENSE for details.
@ -472,7 +472,7 @@ refill(cubeb_stream * stm, float * data, long frames_needed)
dest = data;
}
long out_frames = cubeb_resampler_fill(stm->resampler, dest, frames_needed);
long out_frames = cubeb_resampler_fill(stm->resampler, NULL, dest, frames_needed);
/* TODO: Report out_frames < 0 as an error via the API. */
XASSERT(out_frames >= 0);
@ -1201,7 +1201,11 @@ int setup_wasapi_stream(cubeb_stream * stm)
int
wasapi_stream_init(cubeb * context, cubeb_stream ** stream,
char const * stream_name, cubeb_stream_params stream_params,
char const * stream_name,
cubeb_devid input_device,
cubeb_stream_params * input_stream_params,
cubeb_devid output_device,
cubeb_stream_params * output_stream_params,
unsigned int latency, cubeb_data_callback data_callback,
cubeb_state_callback state_callback, void * user_ptr)
{
@ -1212,9 +1216,15 @@ wasapi_stream_init(cubeb * context, cubeb_stream ** stream,
return CUBEB_ERROR;
}
XASSERT(!input_stream_params && "not supported.");
if (input_device || output_device) {
/* Device selection not yet implemented. */
return CUBEB_ERROR_DEVICE_UNAVAILABLE;
}
XASSERT(context && stream);
if (stream_params.format != CUBEB_SAMPLE_FLOAT32NE) {
if (output_stream_params->format != CUBEB_SAMPLE_FLOAT32NE) {
return CUBEB_ERROR_INVALID_FORMAT;
}
@ -1226,7 +1236,7 @@ wasapi_stream_init(cubeb * context, cubeb_stream ** stream,
stm->data_callback = data_callback;
stm->state_callback = state_callback;
stm->user_ptr = user_ptr;
stm->stream_params = stream_params;
stm->stream_params = *output_stream_params;
stm->draining = false;
stm->latency = latency;
stm->volume = 1.0;

View File

@ -179,7 +179,7 @@ winmm_refill_stream(cubeb_stream * stm)
/* It is assumed that the caller is holding this lock. It must be dropped
during the callback to avoid deadlocks. */
LeaveCriticalSection(&stm->lock);
got = stm->data_callback(stm, stm->user_ptr, hdr->lpData, wanted);
got = stm->data_callback(stm, stm->user_ptr, NULL, hdr->lpData, wanted);
EnterCriticalSection(&stm->lock);
if (got < 0) {
LeaveCriticalSection(&stm->lock);
@ -380,7 +380,11 @@ static void winmm_stream_destroy(cubeb_stream * stm);
static int
winmm_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_name,
cubeb_stream_params stream_params, unsigned int latency,
cubeb_devid input_device,
cubeb_stream_params * input_stream_params,
cubeb_devid output_device,
cubeb_stream_params * output_stream_params,
unsigned int latency,
cubeb_data_callback data_callback,
cubeb_state_callback state_callback,
void * user_ptr)
@ -394,26 +398,32 @@ winmm_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_n
XASSERT(context);
XASSERT(stream);
XASSERT(!input_stream_params && "not supported.");
if (input_device || output_device) {
/* Device selection not yet implemented. */
return CUBEB_ERROR_DEVICE_UNAVAILABLE;
}
*stream = NULL;
memset(&wfx, 0, sizeof(wfx));
if (stream_params.channels > 2) {
if (output_stream_params->channels > 2) {
wfx.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
wfx.Format.cbSize = sizeof(wfx) - sizeof(wfx.Format);
} else {
wfx.Format.wFormatTag = WAVE_FORMAT_PCM;
if (stream_params.format == CUBEB_SAMPLE_FLOAT32LE) {
if (output_stream_params->format == CUBEB_SAMPLE_FLOAT32LE) {
wfx.Format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
}
wfx.Format.cbSize = 0;
}
wfx.Format.nChannels = stream_params.channels;
wfx.Format.nSamplesPerSec = stream_params.rate;
wfx.Format.nChannels = output_stream_params->channels;
wfx.Format.nSamplesPerSec = output_stream_params->rate;
/* XXX fix channel mappings */
wfx.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
switch (stream_params.format) {
switch (output_stream_params->format) {
case CUBEB_SAMPLE_S16LE:
wfx.Format.wBitsPerSample = 16;
wfx.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
@ -446,7 +456,7 @@ winmm_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_n
stm->context = context;
stm->params = stream_params;
stm->params = *output_stream_params;
stm->data_callback = data_callback;
stm->state_callback = state_callback;

View File

@ -73,10 +73,10 @@ void synth_run_float(synth_state* synth, float* audiobuffer, long nframes)
}
}
long data_cb_float(cubeb_stream *stream, void *user, void *buffer, long nframes)
long data_cb_float(cubeb_stream *stream, void *user, const void * inputbuffer, void *outputbuffer, long nframes)
{
synth_state *synth = (synth_state *)user;
synth_run_float(synth, (float*)buffer, nframes);
synth_run_float(synth, (float*)outputbuffer, nframes);
return nframes;
}
@ -92,10 +92,10 @@ void synth_run_16bit(synth_state* synth, short* audiobuffer, long nframes)
}
}
long data_cb_short(cubeb_stream *stream, void *user, void *buffer, long nframes)
long data_cb_short(cubeb_stream *stream, void *user, const void * inputbuffer, void *outputbuffer, long nframes)
{
synth_state *synth = (synth_state *)user;
synth_run_16bit(synth, (short*)buffer, nframes);
synth_run_16bit(synth, (short*)outputbuffer, nframes);
return nframes;
}
@ -160,7 +160,7 @@ int run_test(int num_channels, int sampling_rate, int is_float)
goto cleanup;
}
r = cubeb_stream_init(ctx, &stream, "test tone", params,
r = cubeb_stream_init(ctx, &stream, "test tone", NULL, NULL, NULL, &params,
100, is_float ? data_cb_float : data_cb_short, state_cb, synth);
if (r != CUBEB_OK) {
fprintf(stderr, "Error initializing cubeb stream: %d\n", r);
@ -212,7 +212,7 @@ int run_panning_volume_test(int is_float)
goto cleanup;
}
r = cubeb_stream_init(ctx, &stream, "test tone", params,
r = cubeb_stream_init(ctx, &stream, "test tone", NULL, NULL, NULL, &params,
100, is_float ? data_cb_float : data_cb_short, state_cb, synth);
if (r != CUBEB_OK) {
fprintf(stderr, "Error initializing cubeb stream: %d\n", r);

View File

@ -40,13 +40,14 @@ static uint64_t total_frames_written;
static int delay_callback;
static long
test_data_callback(cubeb_stream * stm, void * user_ptr, void * p, long nframes)
test_data_callback(cubeb_stream * stm, void * user_ptr, const void * inputbuffer, void * outputbuffer, long nframes)
{
assert(stm && user_ptr == &dummy && p && nframes > 0);
assert(stm && user_ptr == &dummy && outputbuffer && nframes > 0);
memset(outputbuffer, 0, nframes * sizeof(short));
#if (defined(_WIN32) || defined(__WIN32__))
memset(p, 0, nframes * sizeof(float));
memset(outputbuffer, 0, nframes * sizeof(float));
#else
memset(p, 0, nframes * sizeof(short));
memset(outputbuffer, 0, nframes * sizeof(short));
#endif
total_frames_written += nframes;
@ -158,7 +159,7 @@ test_init_destroy_stream(void)
params.rate = STREAM_RATE;
params.channels = STREAM_CHANNELS;
r = cubeb_stream_init(ctx, &stream, "test", params, STREAM_LATENCY,
r = cubeb_stream_init(ctx, &stream, "test", NULL, NULL, NULL, &params, STREAM_LATENCY,
test_data_callback, test_state_callback, &dummy);
assert(r == 0 && stream);
@ -187,7 +188,7 @@ test_init_destroy_multiple_streams(void)
params.channels = STREAM_CHANNELS;
for (i = 0; i < ARRAY_LENGTH(stream); ++i) {
r = cubeb_stream_init(ctx, &stream[i], "test", params, STREAM_LATENCY,
r = cubeb_stream_init(ctx, &stream[i], "test", NULL, NULL, NULL, &params, STREAM_LATENCY,
test_data_callback, test_state_callback, &dummy);
assert(r == 0);
assert(stream[i]);
@ -219,7 +220,7 @@ test_configure_stream(void)
params.rate = STREAM_RATE;
params.channels = 2; // panning
r = cubeb_stream_init(ctx, &stream, "test", params, STREAM_LATENCY,
r = cubeb_stream_init(ctx, &stream, "test", NULL, NULL, NULL, &params, STREAM_LATENCY,
test_data_callback, test_state_callback, &dummy);
assert(r == 0 && stream);
@ -253,7 +254,7 @@ test_init_start_stop_destroy_multiple_streams(int early, int delay_ms)
params.channels = STREAM_CHANNELS;
for (i = 0; i < ARRAY_LENGTH(stream); ++i) {
r = cubeb_stream_init(ctx, &stream[i], "test", params, STREAM_LATENCY,
r = cubeb_stream_init(ctx, &stream[i], "test", NULL, NULL, NULL, &params, STREAM_LATENCY,
test_data_callback, test_state_callback, &dummy);
assert(r == 0);
assert(stream[i]);
@ -317,7 +318,7 @@ test_init_destroy_multiple_contexts_and_streams(void)
assert(r == 0 && ctx[i]);
for (j = 0; j < streams_per_ctx; ++j) {
r = cubeb_stream_init(ctx[i], &stream[i * streams_per_ctx + j], "test", params, STREAM_LATENCY,
r = cubeb_stream_init(ctx[i], &stream[i * streams_per_ctx + j], "test", NULL, NULL, NULL, &params, STREAM_LATENCY,
test_data_callback, test_state_callback, &dummy);
assert(r == 0);
assert(stream[i * streams_per_ctx + j]);
@ -352,7 +353,7 @@ test_basic_stream_operations(void)
params.rate = STREAM_RATE;
params.channels = STREAM_CHANNELS;
r = cubeb_stream_init(ctx, &stream, "test", params, STREAM_LATENCY,
r = cubeb_stream_init(ctx, &stream, "test", NULL, NULL, NULL, &params, STREAM_LATENCY,
test_data_callback, test_state_callback, &dummy);
assert(r == 0 && stream);
@ -401,7 +402,7 @@ test_stream_position(void)
params.rate = STREAM_RATE;
params.channels = STREAM_CHANNELS;
r = cubeb_stream_init(ctx, &stream, "test", params, STREAM_LATENCY,
r = cubeb_stream_init(ctx, &stream, "test", NULL, NULL, NULL, &params, STREAM_LATENCY,
test_data_callback, test_state_callback, &dummy);
assert(r == 0 && stream);
@ -474,16 +475,16 @@ static int do_drain;
static int got_drain;
static long
test_drain_data_callback(cubeb_stream * stm, void * user_ptr, void * p, long nframes)
test_drain_data_callback(cubeb_stream * stm, void * user_ptr, const void * inputbuffer, void * outputbuffer, long nframes)
{
assert(stm && user_ptr == &dummy && p && nframes > 0);
assert(stm && user_ptr == &dummy && outputbuffer && nframes > 0);
if (do_drain == 1) {
do_drain = 2;
return 0;
}
/* once drain has started, callback must never be called again */
assert(do_drain != 2);
memset(p, 0, nframes * sizeof(short));
memset(outputbuffer, 0, nframes * sizeof(short));
total_frames_written += nframes;
return nframes;
}
@ -517,7 +518,7 @@ test_drain(void)
params.rate = STREAM_RATE;
params.channels = STREAM_CHANNELS;
r = cubeb_stream_init(ctx, &stream, "test", params, STREAM_LATENCY,
r = cubeb_stream_init(ctx, &stream, "test", NULL, NULL, NULL, &params, STREAM_LATENCY,
test_drain_data_callback, test_drain_state_callback, &dummy);
assert(r == 0 && stream);

View File

@ -34,13 +34,13 @@ struct cb_user_data {
long position;
};
long data_cb(cubeb_stream *stream, void *user, void *buffer, long nframes)
long data_cb(cubeb_stream *stream, void *user, const void* inputbuffer, void *outputbuffer, long nframes)
{
struct cb_user_data *u = (struct cb_user_data *)user;
#if (defined(_WIN32) || defined(__WIN32__))
float *b = (float *)buffer;
float *b = (float *)outputbuffer;
#else
short *b = (short *)buffer;
short *b = (short *)outputbuffer;
#endif
float t1, t2;
int i;
@ -127,7 +127,7 @@ int main(int argc, char *argv[])
}
user_data->position = 0;
r = cubeb_stream_init(ctx, &stream, "Cubeb tone (mono)", params,
r = cubeb_stream_init(ctx, &stream, "Cubeb tone (mono)", NULL, NULL, NULL, &params,
250, data_cb, state_cb, user_data);
if (r != CUBEB_OK) {
fprintf(stderr, "Error initializing cubeb stream\n");

View File

@ -12,6 +12,8 @@ cp $1/src/cubeb_sndio.c src
cp $1/src/cubeb_opensl.c src
cp $1/src/cubeb_audiotrack.c src
cp $1/src/cubeb_wasapi.cpp src
cp $1/src/cubeb_resampler.h src
cp $1/src/cubeb_resampler.cpp src
cp $1/src/cubeb-speex-resampler.h src
cp $1/src/cubeb_panner.h src
cp $1/src/cubeb_panner.cpp src