Bug 1311346 - Update libcubeb to revision 9eacd3144. r=kinetik

--HG--
extra : rebase_source : efeb55082e9a1a0a60d96b717dbb3767e9a54280
This commit is contained in:
Paul Adenot 2016-10-20 16:01:01 +02:00
Родитель 30a3044073
Коммит 527bc745cd
18 изменённых файлов: 659 добавлений и 450 удалений

Просмотреть файл

@ -1,5 +1,5 @@
[![Build Status](https://travis-ci.org/kinetiknz/cubeb.svg?branch=master)](https://travis-ci.org/kinetiknz/cubeb)
See INSTALL for build instructions.
See INSTALL.md for build instructions.
Licensed under an ISC-style license. See LICENSE for details.

Просмотреть файл

@ -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 a317ba01a7b9afa0ba31b77ab9419244ebc4cf23.
The git commit ID used was 9eacd3144b62ce2356c66c3260c9bc4151274192.

Просмотреть файл

@ -8,6 +8,7 @@
#define CUBEB_c2f983e9_c96f_e71c_72c3_bbf62992a382
#include <stdint.h>
#include "cubeb_export.h"
#if defined(__cplusplus)
extern "C" {
@ -168,6 +169,13 @@ typedef enum {
* across calls. */
typedef void * cubeb_devid;
/** Level (verbosity) of logging for a particular cubeb context. */
typedef enum {
CUBEB_LOG_DISABLED = 0, /** < Logging disabled */
CUBEB_LOG_NORMAL = 1, /**< Logging lifetime operation (creation/destruction). */
CUBEB_LOG_VERBOSE = 2, /**< Verbose logging of callbacks, can have performance implications. */
} cubeb_log_level;
/** Stream format initialization parameters. */
typedef struct {
cubeb_sample_format format; /**< Requested sample format. One of
@ -338,6 +346,9 @@ typedef void (* cubeb_device_changed_callback)(void * user_ptr);
typedef void (* cubeb_device_collection_changed_callback)(cubeb * context,
void * user_ptr);
/** User supplied callback called when a message needs logging. */
typedef void (* cubeb_log_callback)(const char * fmt, ...);
/** Initialize an application context. This will perform any library or
application scoped initialization.
@param context A out param where an opaque pointer to the application
@ -347,12 +358,12 @@ typedef void (* cubeb_device_collection_changed_callback)(cubeb * context,
@retval CUBEB_OK in case of success.
@retval CUBEB_ERROR in case of error, for example because the host
has no audio hardware. */
int cubeb_init(cubeb ** context, char const * context_name);
CUBEB_EXPORT int cubeb_init(cubeb ** context, char const * context_name);
/** Get a read-only string identifying this context's current backend.
@param context A pointer to the cubeb context.
@retval Read-only string identifying current backend. */
char const * cubeb_get_backend_id(cubeb * context);
CUBEB_EXPORT char const * cubeb_get_backend_id(cubeb * context);
/** Get the maximum possible number of channels.
@param context A pointer to the cubeb context.
@ -361,7 +372,7 @@ char const * cubeb_get_backend_id(cubeb * context);
@retval CUBEB_ERROR_INVALID_PARAMETER
@retval CUBEB_ERROR_NOT_SUPPORTED
@retval CUBEB_ERROR */
int cubeb_get_max_channel_count(cubeb * context, uint32_t * max_channels);
CUBEB_EXPORT int cubeb_get_max_channel_count(cubeb * context, uint32_t * max_channels);
/** Get the minimal latency value, in frames, that is guaranteed to work
when creating a stream for the specified sample rate. This is platform,
@ -374,9 +385,9 @@ int cubeb_get_max_channel_count(cubeb * context, uint32_t * max_channels);
@retval CUBEB_OK
@retval CUBEB_ERROR_INVALID_PARAMETER
@retval CUBEB_ERROR_NOT_SUPPORTED */
int cubeb_get_min_latency(cubeb * context,
cubeb_stream_params params,
uint32_t * latency_frames);
CUBEB_EXPORT int cubeb_get_min_latency(cubeb * context,
cubeb_stream_params params,
uint32_t * latency_frames);
/** Get the preferred sample rate for this backend: this is hardware and
platform dependant, and can avoid resampling, and/or trigger fastpaths.
@ -385,12 +396,12 @@ int cubeb_get_min_latency(cubeb * context,
@retval CUBEB_OK
@retval CUBEB_ERROR_INVALID_PARAMETER
@retval CUBEB_ERROR_NOT_SUPPORTED */
int cubeb_get_preferred_sample_rate(cubeb * context, uint32_t * rate);
CUBEB_EXPORT int cubeb_get_preferred_sample_rate(cubeb * context, uint32_t * rate);
/** Destroy an application context. This must be called after all stream have
* been destroyed.
@param context A pointer to the cubeb context.*/
void cubeb_destroy(cubeb * context);
CUBEB_EXPORT void cubeb_destroy(cubeb * context);
/** Initialize a stream associated with the supplied application context.
@param context A pointer to the cubeb context.
@ -416,41 +427,41 @@ void cubeb_destroy(cubeb * context);
@retval CUBEB_ERROR
@retval CUBEB_ERROR_INVALID_FORMAT
@retval CUBEB_ERROR_DEVICE_UNAVAILABLE */
int cubeb_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_frames,
cubeb_data_callback data_callback,
cubeb_state_callback state_callback,
void * user_ptr);
CUBEB_EXPORT int cubeb_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_frames,
cubeb_data_callback data_callback,
cubeb_state_callback state_callback,
void * user_ptr);
/** Destroy a stream. `cubeb_stream_stop` MUST be called before destroying a
stream.
@param stream The stream to destroy. */
void cubeb_stream_destroy(cubeb_stream * stream);
CUBEB_EXPORT void cubeb_stream_destroy(cubeb_stream * stream);
/** Start playback.
@param stream
@retval CUBEB_OK
@retval CUBEB_ERROR */
int cubeb_stream_start(cubeb_stream * stream);
CUBEB_EXPORT int cubeb_stream_start(cubeb_stream * stream);
/** Stop playback.
@param stream
@retval CUBEB_OK
@retval CUBEB_ERROR */
int cubeb_stream_stop(cubeb_stream * stream);
CUBEB_EXPORT int cubeb_stream_stop(cubeb_stream * stream);
/** Get the current stream playback position.
@param stream
@param position Playback position in frames.
@retval CUBEB_OK
@retval CUBEB_ERROR */
int cubeb_stream_get_position(cubeb_stream * stream, uint64_t * position);
CUBEB_EXPORT int cubeb_stream_get_position(cubeb_stream * stream, uint64_t * position);
/** Get the latency for this stream, in frames. This is the number of frames
between the time cubeb acquires the data in the callback and the listener
@ -460,7 +471,7 @@ int cubeb_stream_get_position(cubeb_stream * stream, uint64_t * position);
@retval CUBEB_OK
@retval CUBEB_ERROR_NOT_SUPPORTED
@retval CUBEB_ERROR */
int cubeb_stream_get_latency(cubeb_stream * stream, uint32_t * latency);
CUBEB_EXPORT int cubeb_stream_get_latency(cubeb_stream * stream, uint32_t * latency);
/** Set the volume for a stream.
@param stream the stream for which to adjust the volume.
@ -469,7 +480,7 @@ int cubeb_stream_get_latency(cubeb_stream * stream, uint32_t * latency);
@retval CUBEB_ERROR_INVALID_PARAMETER volume is outside [0.0, 1.0] or
stream is an invalid pointer
@retval CUBEB_ERROR_NOT_SUPPORTED */
int cubeb_stream_set_volume(cubeb_stream * stream, float volume);
CUBEB_EXPORT int cubeb_stream_set_volume(cubeb_stream * stream, float volume);
/** If the stream is stereo, set the left/right panning. If the stream is mono,
this has no effect.
@ -483,7 +494,7 @@ int cubeb_stream_set_volume(cubeb_stream * stream, float volume);
outside the [-1.0, 1.0] range.
@retval CUBEB_ERROR_NOT_SUPPORTED
@retval CUBEB_ERROR stream is not mono nor stereo */
int cubeb_stream_set_panning(cubeb_stream * stream, float panning);
CUBEB_EXPORT int cubeb_stream_set_panning(cubeb_stream * stream, float panning);
/** Get the current output device for this stream.
@param stm the stream for which to query the current output device
@ -492,8 +503,8 @@ int cubeb_stream_set_panning(cubeb_stream * stream, float panning);
@retval CUBEB_ERROR_INVALID_PARAMETER if either stm, device or count are
invalid pointers
@retval CUBEB_ERROR_NOT_SUPPORTED */
int cubeb_stream_get_current_device(cubeb_stream * stm,
cubeb_device ** const device);
CUBEB_EXPORT int cubeb_stream_get_current_device(cubeb_stream * stm,
cubeb_device ** const device);
/** Destroy a cubeb_device structure.
@param stream the stream passed in cubeb_stream_get_current_device
@ -501,8 +512,8 @@ int cubeb_stream_get_current_device(cubeb_stream * stm,
@retval CUBEB_OK in case of success
@retval CUBEB_ERROR_INVALID_PARAMETER if devices is an invalid pointer
@retval CUBEB_ERROR_NOT_SUPPORTED */
int cubeb_stream_device_destroy(cubeb_stream * stream,
cubeb_device * devices);
CUBEB_EXPORT int cubeb_stream_device_destroy(cubeb_stream * stream,
cubeb_device * devices);
/** Set a callback to be notified when the output device changes.
@param stream the stream for which to set the callback.
@ -512,8 +523,8 @@ int cubeb_stream_device_destroy(cubeb_stream * stream,
@retval CUBEB_ERROR_INVALID_PARAMETER if either stream or
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_EXPORT int cubeb_stream_register_device_changed_callback(cubeb_stream * stream,
cubeb_device_changed_callback device_changed_callback);
/** Returns enumerated devices.
@param context
@ -522,21 +533,21 @@ int cubeb_stream_register_device_changed_callback(cubeb_stream * stream,
@retval CUBEB_OK in case of success
@retval CUBEB_ERROR_INVALID_PARAMETER if collection is an invalid pointer
@retval CUBEB_ERROR_NOT_SUPPORTED */
int cubeb_enumerate_devices(cubeb * context,
cubeb_device_type devtype,
cubeb_device_collection ** collection);
CUBEB_EXPORT int cubeb_enumerate_devices(cubeb * context,
cubeb_device_type devtype,
cubeb_device_collection ** 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 */
int cubeb_device_collection_destroy(cubeb_device_collection * collection);
CUBEB_EXPORT int cubeb_device_collection_destroy(cubeb_device_collection * collection);
/** Destroy a cubeb_device_info structure.
@param info pointer to device info structure
@retval CUBEB_OK
@retval CUBEB_ERROR_INVALID_PARAMETER if info is an invalid pointer */
int cubeb_device_info_destroy(cubeb_device_info * info);
CUBEB_EXPORT 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.
@ -547,10 +558,21 @@ int cubeb_device_info_destroy(cubeb_device_info * info);
@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);
CUBEB_EXPORT int cubeb_register_device_collection_changed(cubeb * context,
cubeb_device_type devtype,
cubeb_device_collection_changed_callback callback,
void * user_ptr);
/** Set a callback to be called with a message.
@param log_level CUBEB_LOG_VERBOSE, CUBEB_LOG_NORMAL.
@param log_callback A function called with a message when there is
something to log. Pass NULL to unregister.
@retval CUBEB_OK in case of success.
@retval CUBEB_ERROR_INVALID_PARAMETER if either context or log_callback are
invalid pointers, or if level is not
in cubeb_log_level. */
CUBEB_EXPORT int cubeb_set_log_callback(cubeb_log_level log_level,
cubeb_log_callback log_callback);
#if defined(__cplusplus)
}

Просмотреть файл

@ -6,5 +6,6 @@
EXPORTS.cubeb += [
'cubeb.h',
'cubeb_export.h'
]

Просмотреть файл

@ -8,6 +8,7 @@
#define CUBEB_INTERNAL_0eb56756_4e20_4404_a76d_42bf88cd15a5
#include "cubeb/cubeb.h"
#include "cubeb_log.h"
#include <stdio.h>
#include <string.h>

Просмотреть файл

@ -8,14 +8,14 @@
#include <assert.h>
#include <stddef.h>
#include <stdlib.h>
#if defined(HAVE_CONFIG_H)
#include "config.h"
#endif
#include "cubeb/cubeb.h"
#include "cubeb-internal.h"
#define NELEMS(x) ((int) (sizeof(x) / sizeof(x[0])))
cubeb_log_level g_log_level;
cubeb_log_callback g_log_callback;
struct cubeb {
struct cubeb_ops * ops;
};
@ -56,7 +56,7 @@ int kai_init(cubeb ** context, char const * context_name);
#endif
int
static int
validate_stream_params(cubeb_stream_params * input_stream_params,
cubeb_stream_params * output_stream_params)
{
@ -98,7 +98,7 @@ validate_stream_params(cubeb_stream_params * input_stream_params,
int
static int
validate_latency(int latency)
{
if (latency < 1 || latency > 96000) {
@ -442,10 +442,31 @@ int cubeb_register_device_collection_changed(cubeb * context,
return context->ops->register_device_collection_changed(context, devtype, callback, user_ptr);
}
void cubeb_crash()
int cubeb_set_log_callback(cubeb_log_level log_level,
cubeb_log_callback log_callback)
{
if (log_level < CUBEB_LOG_DISABLED || log_level > CUBEB_LOG_VERBOSE) {
return CUBEB_ERROR_INVALID_FORMAT;
}
if (!log_callback && log_level != CUBEB_LOG_DISABLED) {
return CUBEB_ERROR_INVALID_PARAMETER;
}
if (g_log_callback && log_callback) {
return CUBEB_ERROR_NOT_SUPPORTED;
}
g_log_callback = log_callback;
g_log_level = log_level;
return CUBEB_OK;
}
void
cubeb_crash()
{
abort();
*((volatile int *) NULL) = 0;
}

Просмотреть файл

@ -642,11 +642,17 @@ static void
silent_error_handler(char const * file, int line, char const * function,
int err, char const * fmt, ...)
{
(void)file;
(void)line;
(void)function;
(void)err;
(void)fmt;
}
/*static*/ int
alsa_init(cubeb ** context, char const * context_name)
{
(void)context_name;
cubeb * ctx;
int r;
int i;
@ -732,6 +738,7 @@ alsa_init(cubeb ** context, char const * context_name)
static char const *
alsa_get_backend_id(cubeb * ctx)
{
(void)ctx;
return "alsa";
}
@ -776,6 +783,7 @@ alsa_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_name,
cubeb_data_callback data_callback, cubeb_state_callback state_callback,
void * user_ptr)
{
(void)stream_name;
cubeb_stream * stm;
int r;
snd_pcm_format_t format;
@ -961,6 +969,7 @@ alsa_get_max_channel_count(cubeb * ctx, uint32_t * max_channels)
static int
alsa_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate) {
(void)ctx;
int r, dir;
snd_pcm_t * pcm;
snd_pcm_hw_params_t * hw_params;
@ -1004,6 +1013,7 @@ alsa_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate) {
static int
alsa_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_frames)
{
(void)ctx;
/* 40ms is found to be an acceptable minimum, even on a super low-end
* machine. */
*latency_frames = 40 * params.rate / 1000;
@ -1090,7 +1100,7 @@ alsa_stream_get_position(cubeb_stream * stm, uint64_t * position)
return CUBEB_OK;
}
int
static int
alsa_stream_get_latency(cubeb_stream * stm, uint32_t * latency)
{
snd_pcm_sframes_t delay;
@ -1105,7 +1115,7 @@ alsa_stream_get_latency(cubeb_stream * stm, uint32_t * latency)
return CUBEB_OK;
}
int
static int
alsa_stream_set_volume(cubeb_stream * stm, float volume)
{
/* setting the volume using an API call does not seem very stable/supported */

Просмотреть файл

@ -54,23 +54,9 @@ typedef UInt32 AudioFormatFlags;
#define AU_OUT_BUS 0
#define AU_IN_BUS 1
//#define LOGGING_ENABLED
#ifdef LOGGING_ENABLED
#define LOG(...) do { \
fprintf(stderr, __VA_ARGS__); \
fprintf(stderr, "(line: %d)\n", __LINE__); \
} while(0)
#else
#define LOG(...)
#endif
#ifdef LOGGING_ENABLED
#define PRINT_ERROR_CODE(str, r) do { \
fprintf(stderr, "Error %s (line: %d) (%d)\n", str, __LINE__, r); \
} while (0)
#else
#define PRINT_ERROR_CODE(str, r)
#endif
LOG("System call failed: %s (rv: %d)", str, r); \
} while(0)
/* Testing empirically, some headsets report a minimal latency that is very
* low, but this does not work in practice. Lie and say the minimum is 256
@ -78,12 +64,17 @@ typedef UInt32 AudioFormatFlags;
const uint32_t SAFE_MIN_LATENCY_FRAMES = 256;
const uint32_t SAFE_MAX_LATENCY_FRAMES = 512;
void audiounit_stream_stop_internal(cubeb_stream * stm);
void audiounit_stream_start_internal(cubeb_stream * stm);
static void close_audiounit_stream(cubeb_stream * stm);
static int setup_audiounit_stream(cubeb_stream * stm);
extern cubeb_ops const audiounit_ops;
struct cubeb {
cubeb_ops const * ops;
owned_critical_section mutex;
int active_streams;
std::atomic<int> active_streams;
int limit_streams;
cubeb_device_collection_changed_callback collection_changed_callback;
void * collection_changed_user_ptr;
@ -107,6 +98,7 @@ public:
{assert((float_ar && !short_ar) || (!float_ar && short_ar));}
~auto_array_wrapper() {
auto_lock l(lock);
assert((float_ar && !short_ar) || (!float_ar && short_ar));
delete float_ar;
delete short_ar;
@ -114,13 +106,15 @@ public:
void push(void * elements, size_t length){
assert((float_ar && !short_ar) || (!float_ar && short_ar));
auto_lock l(lock);
if (float_ar)
return float_ar->push(static_cast<float*>(elements), length);
return short_ar->push(static_cast<short*>(elements), length);
}
size_t length() const {
size_t length() {
assert((float_ar && !short_ar) || (!float_ar && short_ar));
auto_lock l(lock);
if (float_ar)
return float_ar->length();
return short_ar->length();
@ -128,6 +122,7 @@ public:
void push_silence(size_t length) {
assert((float_ar && !short_ar) || (!float_ar && short_ar));
auto_lock l(lock);
if (float_ar)
return float_ar->push_silence(length);
return short_ar->push_silence(length);
@ -135,21 +130,34 @@ public:
bool pop(void * elements, size_t length) {
assert((float_ar && !short_ar) || (!float_ar && short_ar));
auto_lock l(lock);
if (float_ar)
return float_ar->pop(static_cast<float*>(elements), length);
return short_ar->pop(static_cast<short*>(elements), length);
}
void * data() const {
void * data() {
assert((float_ar && !short_ar) || (!float_ar && short_ar));
auto_lock l(lock);
if (float_ar)
return float_ar->data();
return short_ar->data();
}
void clear() {
assert((float_ar && !short_ar) || (!float_ar && short_ar));
auto_lock l(lock);
if (float_ar) {
float_ar->clear();
} else {
short_ar->clear();
}
}
private:
auto_array<float> * float_ar;
auto_array<short> * short_ar;
owned_critical_section lock;
};
struct cubeb_stream {
@ -157,6 +165,11 @@ struct cubeb_stream {
cubeb_data_callback data_callback;
cubeb_state_callback state_callback;
cubeb_device_changed_callback device_changed_callback;
/* Stream creation parameters */
cubeb_stream_params input_stream_params;
cubeb_stream_params output_stream_params;
cubeb_devid input_device;
cubeb_devid output_device;
/* User pointer of data_callback */
void * user_ptr;
/* Format descriptions */
@ -172,19 +185,38 @@ struct cubeb_stream {
* input callback iteration */
auto_array_wrapper * input_linear_buffer;
/* Frames on input buffer */
uint32_t input_buffer_frames;
std::atomic<uint32_t> input_buffer_frames;
/* Frame counters */
uint64_t frames_played;
uint64_t frames_queued;
uint64_t frames_read;
int shutdown;
int draining;
uint64_t current_latency_frames;
std::atomic<int64_t> frames_read;
std::atomic<bool> shutdown;
std::atomic<bool> draining;
/* Latency requested by the user. */
uint64_t latency_frames;
std::atomic<uint64_t> current_latency_frames;
uint64_t hw_latency_frames;
std::atomic<float> panning;
cubeb_resampler * resampler;
/* This is the number of output callback we got in a row. This is usually one,
* but can be two when the input and output rate are different, and more when
* a device has been plugged or unplugged, as there can be some time before
* the device is ready. */
std::atomic<int> output_callback_in_a_row;
/* This is true if a device change callback is currently running. */
std::atomic<bool> switching_device;
};
bool has_input(cubeb_stream * stm)
{
return stm->input_stream_params.rate != 0;
}
bool has_output(cubeb_stream * stm)
{
return stm->output_stream_params.rate != 0;
}
#if TARGET_OS_IPHONE
typedef UInt32 AudioDeviceID;
typedef UInt32 AudioObjectID;
@ -262,11 +294,11 @@ audiounit_render_input(cubeb_stream * stm,
stm->input_linear_buffer->push(input_buffer_list.mBuffers[0].mData,
input_frames * stm->input_desc.mChannelsPerFrame);
LOG("- input: buffers %d, size %d, channels %d, frames %d.",
input_buffer_list.mNumberBuffers,
input_buffer_list.mBuffers[0].mDataByteSize,
input_buffer_list.mBuffers[0].mNumberChannels,
input_frames);
LOGV("input: buffers %d, size %d, channels %d, frames %d.",
input_buffer_list.mNumberBuffers,
input_buffer_list.mBuffers[0].mDataByteSize,
input_buffer_list.mBuffers[0].mNumberChannels,
input_frames);
/* Advance input frame counter. */
assert(input_frames > 0);
@ -286,13 +318,11 @@ audiounit_input_callback(void * user_ptr,
cubeb_stream * stm = static_cast<cubeb_stream *>(user_ptr);
long outframes, frames;
auto_lock lock(stm->mutex);
assert(stm->input_unit != NULL);
assert(AU_IN_BUS == bus);
if (stm->shutdown) {
LOG("- input shutdown");
LOG("input shutdown");
return noErr;
}
@ -303,24 +333,35 @@ audiounit_input_callback(void * user_ptr,
// Full Duplex. We'll call data_callback in the AudioUnit output callback.
if (stm->output_unit != NULL) {
// User callback will be called by output callback
// This happens when we're finally getting a new input callback after having
// switched device, we can clear the input buffer now, only keeping the data
// we just got.
if (stm->output_callback_in_a_row > 2) {
stm->input_linear_buffer->pop(
nullptr,
stm->input_linear_buffer->length() -
input_frames * stm->input_stream_params.channels);
}
stm->output_callback_in_a_row = 0;
return noErr;
}
/* Input only. Call the user callback through resampler.
Resampler will deliver input buffer in the correct rate. */
frames = input_frames;
assert(input_frames <= stm->input_linear_buffer->length() / stm->input_desc.mChannelsPerFrame);
long total_input_frames = stm->input_linear_buffer->length() / stm->input_desc.mChannelsPerFrame;
outframes = cubeb_resampler_fill(stm->resampler,
stm->input_linear_buffer->data(),
&frames,
&total_input_frames,
NULL,
0);
// Reset input buffer
stm->input_linear_buffer->pop(nullptr, frames * stm->input_desc.mChannelsPerFrame);
stm->input_linear_buffer->clear();
if (outframes < 0 || outframes != input_frames) {
stm->shutdown = 1;
stm->shutdown = true;
return noErr;
}
@ -340,17 +381,17 @@ audiounit_output_callback(void * user_ptr,
cubeb_stream * stm = static_cast<cubeb_stream *>(user_ptr);
LOG("- output(%p): buffers %d, size %d, channels %d, frames %d.", stm,
outBufferList->mNumberBuffers, outBufferList->mBuffers[0].mDataByteSize,
outBufferList->mBuffers[0].mNumberChannels, output_frames);
stm->output_callback_in_a_row++;
LOGV("output(%p): buffers %d, size %d, channels %d, frames %d.", stm,
outBufferList->mNumberBuffers, outBufferList->mBuffers[0].mDataByteSize,
outBufferList->mBuffers[0].mNumberChannels, output_frames);
long outframes = 0, input_frames = 0;
void * output_buffer = NULL, * input_buffer = NULL;
auto_lock lock(stm->mutex);
if (stm->shutdown) {
LOG("- output shutdown.");
LOG("output shutdown.");
audiounit_make_silent(&outBufferList->mBuffers[0]);
return noErr;
}
@ -371,16 +412,17 @@ audiounit_output_callback(void * user_ptr,
output_buffer = outBufferList->mBuffers[0].mData;
/* If Full duplex get also input buffer */
if (stm->input_unit != NULL) {
/* Output callback came first */
if (stm->frames_read == 0) {
/* If the output callback came first and this is a duplex stream, we need to
* fill in some additional silence in the resampler.
* Otherwise, if we had more than two callback in a row, or we're currently
* switching, we add some silence as well to compensate for the fact that
* we're lacking some input data. */
if (stm->frames_read == 0 ||
(stm->input_linear_buffer->length() == 0 &&
(stm->output_callback_in_a_row > 2 || stm->switching_device))) {
LOG("Output callback came first send silent.");
stm->input_linear_buffer->push_silence(stm->input_buffer_frames *
stm->input_desc.mChannelsPerFrame);
}
/* Input samples stored previously in input callback. */
if (stm->input_linear_buffer->length() == 0) {
/* Do nothing, there should be enough pre-buffered data to consume. */
LOG("Input hole. Requested more input than ouput.");
stm->input_desc.mChannelsPerFrame);
}
// The input buffer
input_buffer = stm->input_linear_buffer->data();
@ -400,7 +442,7 @@ audiounit_output_callback(void * user_ptr,
}
if (outframes < 0) {
stm->shutdown = 1;
stm->shutdown = true;
return noErr;
}
@ -523,6 +565,30 @@ audiounit_property_listener_callback(AudioObjectID id, UInt32 address_count,
void * user)
{
cubeb_stream * stm = (cubeb_stream*) user;
int rv;
bool was_running = false;
stm->switching_device = true;
// Note if the stream was running or not
was_running = !stm->shutdown;
LOG("Audio device changed, %d events.", address_count);
if (g_log_level) {
for (UInt32 i = 0; i < address_count; i++) {
switch(addresses[i].mSelector) {
case kAudioHardwarePropertyDefaultOutputDevice:
LOG("%d mSelector == kAudioHardwarePropertyDefaultOutputDevice", i);
break;
case kAudioHardwarePropertyDefaultInputDevice:
LOG("%d mSelector == kAudioHardwarePropertyDefaultInputDevice", i);
break;
case kAudioDevicePropertyDataSource:
LOG("%d mSelector == kAudioHardwarePropertyDataSource", i);
break;
}
}
}
for (UInt32 i = 0; i < address_count; i++) {
switch(addresses[i].mSelector) {
@ -539,6 +605,29 @@ audiounit_property_listener_callback(AudioObjectID id, UInt32 address_count,
}
}
// This means the callback won't be called again.
audiounit_stream_stop_internal(stm);
{
auto_lock lock(stm->mutex);
close_audiounit_stream(stm);
rv = setup_audiounit_stream(stm);
if (rv != CUBEB_OK) {
LOG("Could not reopen a stream after switching.");
stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STOPPED);
return noErr;
}
stm->frames_read = 0;
// If the stream was running, start it again.
if (was_running) {
audiounit_stream_start_internal(stm);
}
}
stm->switching_device = false;
return noErr;
}
@ -1079,19 +1168,10 @@ audiounit_clamp_latency(cubeb_stream * stm,
}
static int
audiounit_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_frames,
cubeb_data_callback data_callback,
cubeb_state_callback state_callback,
void * user_ptr)
setup_audiounit_stream(cubeb_stream * stm)
{
cubeb_stream * stm;
stm->mutex.assert_current_thread_owns();
AudioUnit input_unit;
AudioUnit output_unit;
int r;
@ -1099,64 +1179,28 @@ audiounit_stream_init(cubeb * context,
AURenderCallbackStruct aurcbs_out;
UInt32 size;
assert(context);
*stream = NULL;
{
auto_lock lock(context->mutex);
if (context->limit_streams && context->active_streams >= CUBEB_STREAM_MAX) {
LOG("Reached the stream limit of %d", CUBEB_STREAM_MAX);
return CUBEB_ERROR;
}
context->active_streams += 1;
}
if (input_stream_params != NULL) {
r = audiounit_create_unit(&input_unit, true,
input_stream_params,
input_device);
if (has_input(stm)) {
r = audiounit_create_unit(&stm->input_unit, true,
&stm->input_stream_params,
stm->input_device);
if (r != CUBEB_OK) {
LOG("AudioUnit creation for input failed.");
return r;
}
}
if (output_stream_params != NULL) {
r = audiounit_create_unit(&output_unit, false,
output_stream_params,
output_device);
if (has_output(stm)) {
r = audiounit_create_unit(&stm->output_unit, false,
&stm->output_stream_params,
stm->output_device);
if (r != CUBEB_OK) {
LOG("AudioUnit creation for output failed.");
return r;
}
}
stm = (cubeb_stream *) calloc(1, sizeof(cubeb_stream));
assert(stm);
// Placement new to call the ctors of cubeb_stream members.
new (stm) cubeb_stream();
/* These could be different in the future if we have both
* full-duplex stream and different devices for input vs output. */
stm->input_unit = (input_stream_params != NULL) ? input_unit : NULL;
stm->output_unit = (output_stream_params != NULL) ? output_unit : NULL;
stm->context = context;
stm->data_callback = data_callback;
stm->state_callback = state_callback;
stm->user_ptr = user_ptr;
stm->device_changed_callback = NULL;
/* Init data members where necessary */
stm->hw_latency_frames = UINT64_MAX;
/* Silently clamp the latency down to the platform default, because we
* synthetize the clock from the callbacks, and we want the clock to update
* often. */
latency_frames = audiounit_clamp_latency(stm, latency_frames);
assert(latency_frames > 0);
/* Setup Input Stream! */
if (input_stream_params != NULL) {
if (has_input(stm)) {
/* Get input device sample rate. */
AudioStreamBasicDescription input_hw_desc;
size = sizeof(AudioStreamBasicDescription);
@ -1168,22 +1212,20 @@ audiounit_stream_init(cubeb * context,
&size);
if (r != noErr) {
PRINT_ERROR_CODE("AudioUnitGetProperty/input/kAudioUnitProperty_StreamFormat", r);
audiounit_stream_destroy(stm);
return CUBEB_ERROR;
}
stm->input_hw_rate = input_hw_desc.mSampleRate;
/* Set format description according to the input params. */
r = audio_stream_desc_init(&stm->input_desc, input_stream_params);
r = audio_stream_desc_init(&stm->input_desc, &stm->input_stream_params);
if (r != CUBEB_OK) {
LOG("Setting format description for input failed.");
audiounit_stream_destroy(stm);
return r;
}
// Use latency to set buffer size
stm->input_buffer_frames = latency_frames;
LOG("Input buffer frame count %u.", stm->input_buffer_frames);
stm->input_buffer_frames = stm->latency_frames;
LOG("Input buffer frame count %u.", unsigned(stm->input_buffer_frames));
r = AudioUnitSetProperty(stm->input_unit,
kAudioDevicePropertyBufferFrameSize,
kAudioUnitScope_Output,
@ -1192,7 +1234,6 @@ audiounit_stream_init(cubeb * context,
sizeof(UInt32));
if (r != noErr) {
PRINT_ERROR_CODE("AudioUnitSetProperty/input/kAudioDevicePropertyBufferFrameSize", r);
audiounit_stream_destroy(stm);
return CUBEB_ERROR;
}
@ -1209,7 +1250,6 @@ audiounit_stream_init(cubeb * context,
sizeof(AudioStreamBasicDescription));
if (r != noErr) {
PRINT_ERROR_CODE("AudioUnitSetProperty/input/kAudioUnitProperty_StreamFormat", r);
audiounit_stream_destroy(stm);
return CUBEB_ERROR;
}
@ -1222,18 +1262,16 @@ audiounit_stream_init(cubeb * context,
sizeof(UInt32));
if (r != noErr) {
PRINT_ERROR_CODE("AudioUnitSetProperty/input/kAudioUnitProperty_MaximumFramesPerSlice", r);
audiounit_stream_destroy(stm);
return CUBEB_ERROR;
}
// Input only capacity
unsigned int array_capacity = 1;
if (output_stream_params) {
if (has_output(stm)) {
// Full-duplex increase capacity
array_capacity = 8;
}
if (audiounit_init_input_linear_buffer(stm, array_capacity) != CUBEB_OK) {
audiounit_stream_destroy(stm);
return CUBEB_ERROR;
}
@ -1249,18 +1287,16 @@ audiounit_stream_init(cubeb * context,
sizeof(aurcbs_in));
if (r != noErr) {
PRINT_ERROR_CODE("AudioUnitSetProperty/input/kAudioOutputUnitProperty_SetInputCallback", r);
audiounit_stream_destroy(stm);
return CUBEB_ERROR;
}
LOG("Input audiounit init successfully.");
}
/* Setup Output Stream! */
if (output_stream_params != NULL) {
r = audio_stream_desc_init(&stm->output_desc, output_stream_params);
if (has_output(stm)) {
r = audio_stream_desc_init(&stm->output_desc, &stm->output_stream_params);
if (r != CUBEB_OK) {
LOG("Could not initialize the audio stream description.");
audiounit_stream_destroy(stm);
return r;
}
@ -1276,7 +1312,6 @@ audiounit_stream_init(cubeb * context,
&size);
if (r != noErr) {
PRINT_ERROR_CODE("AudioUnitGetProperty/output/tkAudioUnitProperty_StreamFormat", r);
audiounit_stream_destroy(stm);
return CUBEB_ERROR;
}
@ -1288,12 +1323,11 @@ audiounit_stream_init(cubeb * context,
sizeof(AudioStreamBasicDescription));
if (r != noErr) {
PRINT_ERROR_CODE("AudioUnitSetProperty/output/kAudioUnitProperty_StreamFormat", r);
audiounit_stream_destroy(stm);
return CUBEB_ERROR;
}
// Use latency to calculate buffer size
uint32_t output_buffer_frames = latency_frames;
uint32_t output_buffer_frames = stm->latency_frames;
LOG("Output buffer frame count %u.", output_buffer_frames);
r = AudioUnitSetProperty(stm->output_unit,
kAudioDevicePropertyBufferFrameSize,
@ -1303,7 +1337,6 @@ audiounit_stream_init(cubeb * context,
sizeof(output_buffer_frames));
if (r != noErr) {
PRINT_ERROR_CODE("AudioUnitSetProperty/output/kAudioDevicePropertyBufferFrameSize", r);
audiounit_stream_destroy(stm);
return CUBEB_ERROR;
}
@ -1317,7 +1350,6 @@ audiounit_stream_init(cubeb * context,
&aurcbs_out,
sizeof(aurcbs_out));
if (r != noErr) {
audiounit_stream_destroy(stm);
PRINT_ERROR_CODE("AudioUnitSetProperty/output/kAudioUnitProperty_SetRenderCallback", r);
return CUBEB_ERROR;
}
@ -1333,7 +1365,6 @@ audiounit_stream_init(cubeb * context,
* the requested latency to this acceptable range. */
#if !TARGET_OS_IPHONE
if (audiounit_get_acceptable_latency_range(&latency_range) != CUBEB_OK) {
audiounit_stream_destroy(stm);
return CUBEB_ERROR;
}
@ -1350,7 +1381,6 @@ audiounit_stream_init(cubeb * context,
size = sizeof(default_buffer_size);
if (AudioUnitGetProperty(stm->output_unit, kAudioDevicePropertyBufferFrameSize,
kAudioUnitScope_Output, 0, &default_buffer_size, &size) != 0) {
audiounit_stream_destroy(stm);
return CUBEB_ERROR;
}
@ -1359,7 +1389,6 @@ audiounit_stream_init(cubeb * context,
* effectively setting the latency of the stream. This is process-wide. */
if (AudioUnitSetProperty(stm->output_unit, kAudioDevicePropertyBufferFrameSize,
kAudioUnitScope_Output, 0, &buffer_size, sizeof(buffer_size)) != 0) {
audiounit_stream_destroy(stm);
return CUBEB_ERROR;
}
}
@ -1374,16 +1403,16 @@ audiounit_stream_init(cubeb * context,
* Resampler will convert it to the user sample rate
* and deliver it to the callback. */
uint32_t target_sample_rate;
if (input_stream_params) {
target_sample_rate = input_stream_params->rate;
if (has_input(stm)) {
target_sample_rate = stm->input_stream_params.rate;
} else {
assert(output_stream_params);
target_sample_rate = output_stream_params->rate;
assert(has_output(stm));
target_sample_rate = stm->output_stream_params.rate;
}
cubeb_stream_params input_unconverted_params;
if (input_stream_params) {
input_unconverted_params = *input_stream_params;
if (has_input(stm)) {
input_unconverted_params = stm->input_stream_params;
/* Use the rate of the input device. */
input_unconverted_params.rate = stm->input_hw_rate;
}
@ -1391,15 +1420,14 @@ audiounit_stream_init(cubeb * context,
/* Create resampler. Output params are unchanged
* because we do not need conversion on the output. */
stm->resampler = cubeb_resampler_create(stm,
input_stream_params ? &input_unconverted_params : NULL,
output_stream_params,
has_input(stm) ? &input_unconverted_params : NULL,
has_output(stm) ? &stm->output_stream_params : NULL,
target_sample_rate,
stm->data_callback,
stm->user_ptr,
CUBEB_RESAMPLER_QUALITY_DESKTOP);
if (!stm->resampler) {
LOG("Could not create resampler.");
audiounit_stream_destroy(stm);
return CUBEB_ERROR;
}
@ -1407,7 +1435,6 @@ audiounit_stream_init(cubeb * context,
r = AudioUnitInitialize(stm->input_unit);
if (r != noErr) {
PRINT_ERROR_CODE("AudioUnitInitialize/input", r);
audiounit_stream_destroy(stm);
return CUBEB_ERROR;
}
}
@ -1416,10 +1443,88 @@ audiounit_stream_init(cubeb * context,
r = AudioUnitInitialize(stm->output_unit);
if (r != noErr) {
PRINT_ERROR_CODE("AudioUnitInitialize/output", r);
audiounit_stream_destroy(stm);
return CUBEB_ERROR;
}
}
return CUBEB_OK;
}
static int
audiounit_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_frames,
cubeb_data_callback data_callback,
cubeb_state_callback state_callback,
void * user_ptr)
{
cubeb_stream * stm;
int r;
assert(context);
*stream = NULL;
if (context->limit_streams && context->active_streams >= CUBEB_STREAM_MAX) {
LOG("Reached the stream limit of %d", CUBEB_STREAM_MAX);
return CUBEB_ERROR;
}
context->active_streams += 1;
stm = (cubeb_stream *) calloc(1, sizeof(cubeb_stream));
assert(stm);
// Placement new to call the ctors of cubeb_stream members.
new (stm) cubeb_stream();
/* These could be different in the future if we have both
* full-duplex stream and different devices for input vs output. */
stm->context = context;
stm->data_callback = data_callback;
stm->state_callback = state_callback;
stm->user_ptr = user_ptr;
stm->device_changed_callback = NULL;
if (input_stream_params) {
stm->input_stream_params = *input_stream_params;
stm->input_device = input_device;
}
if (output_stream_params) {
stm->output_stream_params = *output_stream_params;
stm->output_device = output_device;
}
/* Init data members where necessary */
stm->hw_latency_frames = UINT64_MAX;
/* Silently clamp the latency down to the platform default, because we
* synthetize the clock from the callbacks, and we want the clock to update
* often. */
stm->latency_frames = audiounit_clamp_latency(stm, latency_frames);
assert(latency_frames > 0);
stm->switching_device = false;
{
// It's not critical to lock here, because no other thread has been started
// yet, but it allows to assert that the lock has been taken in
// `setup_audiounit_stream`.
auto_lock lock(stm->mutex);
r = setup_audiounit_stream(stm);
}
if (r != CUBEB_OK) {
LOG("Could not setup the audiounit stream.");
audiounit_stream_destroy(stm);
return r;
}
r = audiounit_install_device_changed_callback(stm);
if (r != CUBEB_OK) {
LOG("Could not install the device change callback.");
return r;
}
*stream = stm;
LOG("Cubeb stream (%p) init successful.", stm);
@ -1427,49 +1532,53 @@ audiounit_stream_init(cubeb * context,
}
static void
audiounit_stream_destroy(cubeb_stream * stm)
close_audiounit_stream(cubeb_stream * stm)
{
stm->shutdown = 1;
if (stm->input_unit != NULL) {
AudioOutputUnitStop(stm->input_unit);
stm->mutex.assert_current_thread_owns();
if (stm->input_unit) {
AudioUnitUninitialize(stm->input_unit);
AudioComponentInstanceDispose(stm->input_unit);
}
audiounit_destroy_input_linear_buffer(stm);
if (stm->output_unit != NULL) {
AudioOutputUnitStop(stm->output_unit);
if (stm->output_unit) {
AudioUnitUninitialize(stm->output_unit);
AudioComponentInstanceDispose(stm->output_unit);
}
cubeb_resampler_destroy(stm->resampler);
}
#if !TARGET_OS_IPHONE
audiounit_uninstall_device_changed_callback(stm);
#endif
static void
audiounit_stream_destroy(cubeb_stream * stm)
{
stm->shutdown = true;
audiounit_stream_stop_internal(stm);
{
auto_lock lock(stm->context->mutex);
assert(stm->context->active_streams >= 1);
stm->context->active_streams -= 1;
auto_lock lock(stm->mutex);
close_audiounit_stream(stm);
}
#if !TARGET_OS_IPHONE
int r = audiounit_uninstall_device_changed_callback(stm);
if (r != CUBEB_OK) {
LOG("Could not uninstall the device changed callback");
}
#endif
assert(stm->context->active_streams >= 1);
stm->context->active_streams -= 1;
stm->~cubeb_stream();
free(stm);
}
static int
audiounit_stream_start(cubeb_stream * stm)
void
audiounit_stream_start_internal(cubeb_stream * stm)
{
{
auto_lock lock(stm->mutex);
stm->shutdown = 0;
stm->draining = 0;
}
OSStatus r;
if (stm->input_unit != NULL) {
r = AudioOutputUnitStart(stm->input_unit);
@ -1479,19 +1588,25 @@ audiounit_stream_start(cubeb_stream * stm)
r = AudioOutputUnitStart(stm->output_unit);
assert(r == 0);
}
}
static int
audiounit_stream_start(cubeb_stream * stm)
{
stm->shutdown = false;
stm->draining = false;
audiounit_stream_start_internal(stm);
stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STARTED);
LOG("Cubeb stream (%p) started successfully.", stm);
return CUBEB_OK;
}
static int
audiounit_stream_stop(cubeb_stream * stm)
void
audiounit_stream_stop_internal(cubeb_stream * stm)
{
{
auto_lock lock(stm->mutex);
stm->shutdown = 1;
}
OSStatus r;
if (stm->input_unit != NULL) {
r = AudioOutputUnitStop(stm->input_unit);
@ -1501,7 +1616,17 @@ audiounit_stream_stop(cubeb_stream * stm)
r = AudioOutputUnitStop(stm->output_unit);
assert(r == 0);
}
}
static int
audiounit_stream_stop(cubeb_stream * stm)
{
stm->shutdown = true;
audiounit_stream_stop_internal(stm);
stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STOPPED);
LOG("Cubeb stream (%p) stopped successfully.", stm);
return CUBEB_OK;
}
@ -1722,7 +1847,7 @@ int audiounit_stream_device_destroy(cubeb_stream * stream,
}
int audiounit_stream_register_device_changed_callback(cubeb_stream * stream,
cubeb_device_changed_callback device_changed_callback)
cubeb_device_changed_callback device_changed_callback)
{
/* Note: second register without unregister first causes 'nope' error.
* Current implementation requires unregister before register a new cb. */
@ -1731,16 +1856,8 @@ int audiounit_stream_register_device_changed_callback(cubeb_stream * stream,
auto_lock lock(stream->mutex);
stream->device_changed_callback = device_changed_callback;
int r = CUBEB_OK;
#if !TARGET_OS_IPHONE
if (device_changed_callback) {
r = audiounit_install_device_changed_callback(stream);
} else {
r = audiounit_uninstall_device_changed_callback(stream);
}
#endif
return r;
return CUBEB_OK;
}
static OSStatus
@ -1935,14 +2052,11 @@ audiounit_create_device_from_hwdev(AudioObjectID devid, cubeb_device_type type)
AudioValueTranslation trl = { &ds, sizeof(ds), &dsname, sizeof(dsname) };
adr.mSelector = kAudioDevicePropertyDataSourceNameForIDCFString;
size = sizeof(AudioValueTranslation);
// If there is a datasource for this device, use it instead of the device
// name.
if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &trl) == noErr) {
CFStringRef fullstr = CFStringCreateWithFormat(NULL, NULL,
CFSTR("%@ (%@)"), str, dsname);
CFRelease(dsname);
if (fullstr != NULL) {
CFRelease(str);
str = fullstr;
}
CFRelease(str);
str = dsname;
}
}

Просмотреть файл

@ -29,20 +29,20 @@ void cubeb_pan_stereo_buffer(T * buf, uint32_t frames, float pan)
/* rescale in [0; 1] */
pan += 1;
pan /= 2;
float left_gain = cos(pan * M_PI * 0.5);
float right_gain = sin(pan * M_PI * 0.5);
float left_gain = float(cos(pan * M_PI * 0.5));
float right_gain = float(sin(pan * M_PI * 0.5));
/* In we are panning on the left, pan the right channel into the left one and
* vice-versa. */
if (pan < 0.5) {
for (uint32_t i = 0; i < frames * 2; i+=2) {
buf[i] = buf[i] + buf[i + 1] * left_gain;
buf[i + 1] = buf[i + 1] * right_gain;
buf[i] = T(buf[i] + buf[i + 1] * left_gain);
buf[i + 1] = T(buf[i + 1] * right_gain);
}
} else {
for (uint32_t i = 0; i < frames * 2; i+=2) {
buf[i] = buf[i] * left_gain;
buf[i + 1] = buf[i + 1] + buf[i] * right_gain;
buf[i] = T(buf[i] * left_gain);
buf[i + 1] = T(buf[i + 1] + buf[i] * right_gain);
}
}
}
@ -50,11 +50,11 @@ void cubeb_pan_stereo_buffer(T * buf, uint32_t frames, float pan)
void cubeb_pan_stereo_buffer_float(float * buf, uint32_t frames, float pan)
{
cubeb_pan_stereo_buffer(reinterpret_cast<float*>(buf), frames, pan);
cubeb_pan_stereo_buffer(buf, frames, pan);
}
void cubeb_pan_stereo_buffer_int(short * buf, uint32_t frames, float pan)
{
cubeb_pan_stereo_buffer(reinterpret_cast<short*>(buf), frames, pan);
cubeb_pan_stereo_buffer(buf, frames, pan);
}

Просмотреть файл

@ -87,15 +87,6 @@ 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 {
@ -125,7 +116,7 @@ struct cubeb_stream {
cubeb_state state;
};
const float PULSE_NO_GAIN = -1.0;
static const float PULSE_NO_GAIN = -1.0;
enum cork_state {
UNCORK = 0,
@ -136,6 +127,7 @@ enum cork_state {
static void
sink_info_callback(pa_context * context, const pa_sink_info * info, int eol, void * u)
{
(void)context;
cubeb * ctx = u;
if (!eol) {
free(ctx->default_sink_info);
@ -164,6 +156,7 @@ context_state_callback(pa_context * c, void * u)
static void
context_notify_callback(pa_context * c, void * u)
{
(void)c;
cubeb * ctx = u;
WRAP(pa_threaded_mainloop_signal)(ctx->mainloop, 0);
}
@ -171,6 +164,8 @@ context_notify_callback(pa_context * c, void * u)
static void
stream_success_callback(pa_stream * s, int success, void * u)
{
(void)s;
(void)success;
cubeb_stream * stm = u;
WRAP(pa_threaded_mainloop_signal)(stm->context->mainloop, 0);
}
@ -185,6 +180,9 @@ stream_state_change_callback(cubeb_stream * stm, cubeb_state s)
static void
stream_drain_callback(pa_mainloop_api * a, pa_time_event * e, struct timeval const * tv, void * u)
{
(void)a;
(void)e;
(void)tv;
cubeb_stream * stm = u;
stream_state_change_callback(stm, CUBEB_STATE_DRAINED);
/* there's no pa_rttime_free, so use this instead. */
@ -226,7 +224,7 @@ trigger_user_callback(pa_stream * s, void const * input_data, size_t nbytes, cub
assert(size > 0);
assert(size % frame_size == 0);
LOG("Trigger user callback with output buffer size=%zd, read_offset=%zd\n", size, read_offset);
LOGV("Trigger user callback with output buffer size=%zd, read_offset=%zd", 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);
@ -295,7 +293,7 @@ read_from_input(pa_stream * s, void const ** buffer, size_t * 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);
LOGV("Output callback to be written buffer size %zd", nbytes);
cubeb_stream * stm = u;
if (stm->shutdown ||
stm->state != CUBEB_STATE_STARTED) {
@ -313,7 +311,7 @@ stream_write_callback(pa_stream * s, size_t nbytes, void * u)
static void
stream_read_callback(pa_stream * s, size_t nbytes, void * u)
{
LOG("Input callback buffer size %zd\n", nbytes);
LOGV("Input callback buffer size %zd", nbytes);
cubeb_stream * stm = u;
if (stm->shutdown) {
return;
@ -558,12 +556,14 @@ pulse_init(cubeb ** context, char const * context_name)
static char const *
pulse_get_backend_id(cubeb * ctx)
{
(void)ctx;
return "pulse";
}
static int
pulse_get_max_channel_count(cubeb * ctx, uint32_t * max_channels)
{
(void)ctx;
assert(ctx && max_channels);
WRAP(pa_threaded_mainloop_lock)(ctx->mainloop);
@ -581,6 +581,7 @@ static int
pulse_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate)
{
assert(ctx && rate);
(void)ctx;
WRAP(pa_threaded_mainloop_lock)(ctx->mainloop);
while (!ctx->default_sink_info) {
@ -596,6 +597,7 @@ pulse_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate)
static int
pulse_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_frames)
{
(void)ctx;
// According to PulseAudio developers, this is a safe minimum.
*latency_frames = 25 * params.rate / 1000;
@ -645,8 +647,8 @@ pulse_destroy(cubeb * ctx)
static void pulse_stream_destroy(cubeb_stream * stm);
pa_sample_format_t
cubeb_to_pulse_format(cubeb_sample_format format)
static pa_sample_format_t
to_pulse_format(cubeb_sample_format format)
{
switch (format) {
case CUBEB_SAMPLE_S16LE:
@ -671,7 +673,7 @@ create_pa_stream(cubeb_stream * stm,
assert(stm && stream_params);
*pa_stm = NULL;
pa_sample_spec ss;
ss.format = cubeb_to_pulse_format(stream_params->format);
ss.format = to_pulse_format(stream_params->format);
if (ss.format == PA_SAMPLE_INVALID)
return CUBEB_ERROR_INVALID_FORMAT;
ss.rate = stream_params->rate;
@ -691,7 +693,7 @@ set_buffering_attribute(unsigned int latency_frames, pa_sample_spec * sample_spe
battr.minreq = battr.tlength / 4;
battr.fragsize = battr.minreq;
LOG("Requested buffer attributes maxlength %u, tlength %u, prebuf %u, minreq %u, fragsize %u\n",
LOG("Requested buffer attributes maxlength %u, tlength %u, prebuf %u, minreq %u, fragsize %u",
battr.maxlength, battr.tlength, battr.prebuf, battr.minreq, battr.fragsize);
return battr;
@ -793,21 +795,21 @@ pulse_stream_init(cubeb * context,
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 (g_log_level) {
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",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);
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",input_att->maxlength, input_att->tlength,
input_att->prebuf, input_att->minreq, input_att->fragsize);
}
}
#endif
*stream = stm;
@ -844,9 +846,10 @@ pulse_stream_destroy(cubeb_stream * stm)
free(stm);
}
void
static void
pulse_defer_event_cb(pa_mainloop_api * a, void * userdata)
{
(void)a;
cubeb_stream * stm = userdata;
size_t writable_size = WRAP(pa_stream_writable_size)(stm->output_stream);
trigger_user_callback(stm->output_stream, NULL, writable_size, stm);
@ -917,7 +920,7 @@ pulse_stream_get_position(cubeb_stream * stm, uint64_t * position)
return CUBEB_OK;
}
int
static int
pulse_stream_get_latency(cubeb_stream * stm, uint32_t * latency)
{
pa_usec_t r_usec;
@ -937,14 +940,17 @@ pulse_stream_get_latency(cubeb_stream * stm, uint32_t * latency)
return CUBEB_OK;
}
void volume_success(pa_context *c, int success, void *userdata)
static void
volume_success(pa_context *c, int success, void *userdata)
{
(void)success;
(void)c;
cubeb_stream * stream = userdata;
assert(success);
WRAP(pa_threaded_mainloop_signal)(stream->context->mainloop, 0);
}
int
static int
pulse_stream_set_volume(cubeb_stream * stm, float volume)
{
uint32_t index;
@ -989,7 +995,7 @@ pulse_stream_set_volume(cubeb_stream * stm, float volume)
return CUBEB_OK;
}
int
static int
pulse_stream_set_panning(cubeb_stream * stream, float panning)
{
const pa_channel_map * map;
@ -1231,7 +1237,8 @@ pulse_enumerate_devices(cubeb * context, cubeb_device_type type,
return CUBEB_OK;
}
int pulse_stream_get_current_device(cubeb_stream * stm, cubeb_device ** const device)
static int
pulse_stream_get_current_device(cubeb_stream * stm, cubeb_device ** const device)
{
#if PA_CHECK_VERSION(0, 9, 8)
*device = calloc(1, sizeof(cubeb_device));
@ -1254,39 +1261,45 @@ int pulse_stream_get_current_device(cubeb_stream * stm, cubeb_device ** const de
#endif
}
int pulse_stream_device_destroy(cubeb_stream * stream,
cubeb_device * device)
static int
pulse_stream_device_destroy(cubeb_stream * stream,
cubeb_device * device)
{
(void)stream;
free(device->input_name);
free(device->output_name);
free(device);
return CUBEB_OK;
}
void pulse_subscribe_callback(pa_context * ctx,
pa_subscription_event_type_t t,
uint32_t index, void * userdata)
static void
pulse_subscribe_callback(pa_context * ctx,
pa_subscription_event_type_t t,
uint32_t index, void * userdata)
{
(void)ctx;
cubeb * context = userdata;
switch (t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) {
case PA_SUBSCRIPTION_EVENT_SOURCE:
case PA_SUBSCRIPTION_EVENT_SINK:
#ifdef LOGGING_ENABLED
if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE &&
(t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE)
LOG("Removing sink index %d\n", index);
else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE &&
(t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW)
LOG("Adding sink index %d\n", index);
if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK &&
(t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE)
LOG("Removing source index %d\n", index);
else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK &&
(t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW)
LOG("Adding source index %d\n", index);
#endif
if (g_log_level) {
if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE &&
(t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
LOG("Removing sink index %d", index);
} else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE &&
(t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) {
LOG("Adding sink index %d", index);
}
if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK &&
(t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
LOG("Removing source index %d", index);
} else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK &&
(t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) {
LOG("Adding source index %d", index);
}
}
if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE ||
(t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) {
@ -1296,17 +1309,20 @@ void pulse_subscribe_callback(pa_context * ctx,
}
}
void subscribe_success(pa_context *c, int success, void *userdata)
static void
subscribe_success(pa_context *c, int success, void *userdata)
{
(void)c;
cubeb * context = userdata;
assert(success);
WRAP(pa_threaded_mainloop_signal)(context->mainloop, 0);
}
int pulse_register_device_collection_changed(cubeb * context,
cubeb_device_type devtype,
cubeb_device_collection_changed_callback collection_changed_callback,
void * user_ptr)
static int
pulse_register_device_collection_changed(cubeb * context,
cubeb_device_type devtype,
cubeb_device_collection_changed_callback collection_changed_callback,
void * user_ptr)
{
context->collection_changed_callback = collection_changed_callback;
context->collection_changed_user_ptr = user_ptr;
@ -1331,7 +1347,7 @@ int pulse_register_device_collection_changed(cubeb * context,
pa_operation * o;
o = WRAP(pa_context_subscribe)(context->context, mask, subscribe_success, context);
if (o == NULL) {
LOG("Context subscribe failed\n");
LOG("Context subscribe failed");
return CUBEB_ERROR;
}
operation_wait(context, NULL, o);

Просмотреть файл

@ -4,7 +4,6 @@
* This program is made available under an ISC-style license. See the
* accompanying file LICENSE for details.
*/
#ifndef NOMINMAX
#define NOMINMAX
#endif // NOMINMAX
@ -15,9 +14,6 @@
#include <cstring>
#include <cstddef>
#include <cstdio>
#if defined(HAVE_CONFIG_H)
#include "config.h"
#endif
#include "cubeb_resampler.h"
#include "cubeb-speex-resampler.h"
#include "cubeb_resampler_internal.h"

Просмотреть файл

@ -11,6 +11,7 @@
#include <stdint.h>
#include <string.h>
#include <assert.h>
#include <type_traits>
#if defined(WIN32)
#include "cubeb_utils_win.h"
#else
@ -21,6 +22,7 @@
template<typename T>
void PodCopy(T * destination, const T * source, size_t count)
{
static_assert(std::is_trivial<T>::value, "Requires trivial type");
memcpy(destination, source, count * sizeof(T));
}
@ -28,6 +30,7 @@ void PodCopy(T * destination, const T * source, size_t count)
template<typename T>
void PodMove(T * destination, const T * source, size_t count)
{
static_assert(std::is_trivial<T>::value, "Requires trivial type");
memmove(destination, source, count * sizeof(T));
}
@ -35,6 +38,7 @@ void PodMove(T * destination, const T * source, size_t count)
template<typename T>
void PodZero(T * destination, size_t count)
{
static_assert(std::is_trivial<T>::value, "Requires trivial type");
memset(destination, 0, count * sizeof(T));
}
@ -208,4 +212,18 @@ private:
owned_critical_section & lock;
};
struct auto_unlock {
explicit auto_unlock(owned_critical_section & lock)
: lock(lock)
{
lock.leave();
}
~auto_unlock()
{
lock.enter();
}
private:
owned_critical_section & lock;
};
#endif /* CUBEB_UTILS */

Просмотреть файл

@ -20,17 +20,17 @@ public:
{
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
#ifdef DEBUG
#ifndef NDEBUG
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK);
#else
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL);
#endif
#ifdef DEBUG
#ifndef NDEBUG
int r =
#endif
pthread_mutex_init(&mutex, &attr);
#ifdef DEBUG
#ifndef NDEBUG
assert(r == 0);
#endif
@ -39,40 +39,40 @@ public:
~owned_critical_section()
{
#ifdef DEBUG
#ifndef NDEBUG
int r =
#endif
pthread_mutex_destroy(&mutex);
#ifdef DEBUG
#ifndef NDEBUG
assert(r == 0);
#endif
}
void enter()
{
#ifdef DEBUG
#ifndef NDEBUG
int r =
#endif
pthread_mutex_lock(&mutex);
#ifdef DEBUG
#ifndef NDEBUG
assert(r == 0 && "Deadlock");
#endif
}
void leave()
{
#ifdef DEBUG
#ifndef NDEBUG
int r =
#endif
pthread_mutex_unlock(&mutex);
#ifdef DEBUG
#ifndef NDEBUG
assert(r == 0 && "Unlocking unlocked mutex");
#endif
}
void assert_current_thread_owns()
{
#ifdef DEBUG
#ifndef NDEBUG
int r = pthread_mutex_lock(&mutex);
assert(r == EDEADLK);
#endif

Просмотреть файл

@ -17,7 +17,7 @@ class owned_critical_section
{
public:
owned_critical_section()
#ifdef DEBUG
#ifndef NDEBUG
: owner(0)
#endif
{
@ -32,7 +32,7 @@ public:
void enter()
{
EnterCriticalSection(&critical_section);
#ifdef DEBUG
#ifndef NDEBUG
XASSERT(owner != GetCurrentThreadId() && "recursive locking");
owner = GetCurrentThreadId();
#endif
@ -40,7 +40,7 @@ public:
void leave()
{
#ifdef DEBUG
#ifndef NDEBUG
/* GetCurrentThreadId cannot return 0: it is not a the valid thread id */
owner = 0;
#endif
@ -51,7 +51,7 @@ public:
is undefined otherwise. */
void assert_current_thread_owns()
{
#ifdef DEBUG
#ifndef NDEBUG
/* This implies owner != 0, because GetCurrentThreadId cannot return 0. */
XASSERT(owner == GetCurrentThreadId());
#endif
@ -59,7 +59,7 @@ public:
private:
CRITICAL_SECTION critical_section;
#ifdef DEBUG
#ifndef NDEBUG
DWORD owner;
#endif

Просмотреть файл

@ -6,9 +6,6 @@
*/
#define NOMINMAX
#if defined(HAVE_CONFIG_H)
#include "config.h"
#endif
#include <initguid.h>
#include <windows.h>
#include <mmdeviceapi.h>
@ -48,20 +45,13 @@ DEFINE_PROPERTYKEY(PKEY_Device_FriendlyName, 0xa45c254e, 0xdf1c, 0x4efd, 0x80
DEFINE_PROPERTYKEY(PKEY_Device_InstanceId, 0x78c34fc8, 0x104a, 0x4aca, 0x9e, 0xa4, 0x52, 0x4d, 0x52, 0x99, 0x6e, 0x57, 0x00000100); // VT_LPWSTR
#endif
// #define LOGGING_ENABLED
#ifdef LOGGING_ENABLED
#define LOG(...) do { \
fprintf(stderr, __VA_ARGS__); \
} while(0)
#else
#define LOG(...)
#endif
#define ARRAY_LENGTH(array_) \
(sizeof(array_) / sizeof(array_[0]))
namespace {
template<typename T, size_t N>
constexpr size_t
ARRAY_LENGTH(T(&)[N])
{
return N;
}
void
SafeRelease(HANDLE handle)
@ -87,12 +77,12 @@ struct auto_com {
if (result == RPC_E_CHANGED_MODE) {
// This is not an error, COM was not initialized by this function, so it is
// not necessary to uninit it.
LOG("COM was already initialized in STA.\n");
LOG("COM was already initialized in STA.");
} else if (result == S_FALSE) {
// This is not an error. We are allowed to call CoInitializeEx more than
// once, as long as it is matches by an CoUninitialize call.
// We do that in the dtor which is guaranteed to be called.
LOG("COM was already initialized in MTA\n");
LOG("COM was already initialized in MTA");
}
if (SUCCEEDED(result)) {
CoUninitialize();
@ -280,7 +270,7 @@ public:
HRESULT STDMETHODCALLTYPE
OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR device_id)
{
LOG("Audio device default changed.\n");
LOG("Audio device default changed.");
/* we only support a single stream type for now. */
if (flow != eRender && role != eConsole) {
@ -289,7 +279,7 @@ public:
BOOL ok = SetEvent(reconfigure_event);
if (!ok) {
LOG("SetEvent on reconfigure_event failed: %x\n", GetLastError());
LOG("SetEvent on reconfigure_event failed: %x", GetLastError());
}
return S_OK;
@ -299,27 +289,27 @@ public:
log is enabled), for debugging. */
HRESULT STDMETHODCALLTYPE OnDeviceAdded(LPCWSTR device_id)
{
LOG("Audio device added.\n");
LOG("Audio device added.");
return S_OK;
};
HRESULT STDMETHODCALLTYPE OnDeviceRemoved(LPCWSTR device_id)
{
LOG("Audio device removed.\n");
LOG("Audio device removed.");
return S_OK;
}
HRESULT STDMETHODCALLTYPE
OnDeviceStateChanged(LPCWSTR device_id, DWORD new_state)
{
LOG("Audio device state changed.\n");
LOG("Audio device state changed.");
return S_OK;
}
HRESULT STDMETHODCALLTYPE
OnPropertyValueChanged(LPCWSTR device_id, const PROPERTYKEY key)
{
LOG("Audio device property value changed.\n");
LOG("Audio device property value changed.");
return S_OK;
}
private:
@ -501,7 +491,7 @@ refill(cubeb_stream * stm, float * input_buffer, long input_frames_count,
/* Go in draining mode if we got fewer frames than requested. */
if (out_frames < output_frames_needed) {
LOG("start draining.\n");
LOG("start draining.");
stm->draining = true;
}
@ -534,7 +524,7 @@ bool get_input_buffer(cubeb_stream * stm)
hr = stm->input_client->GetCurrentPadding(&padding_in);
if (FAILED(hr)) {
LOG("Failed to get padding\n");
LOG("Failed to get padding");
return false;
}
XASSERT(padding_in <= stm->input_buffer_frame_count);
@ -550,7 +540,7 @@ bool get_input_buffer(cubeb_stream * stm)
while (offset != total_available_input) {
hr = stm->capture_client->GetNextPacketSize(&next);
if (FAILED(hr)) {
LOG("cannot get next packet size: %x\n", hr);
LOG("cannot get next packet size: %x", hr);
return false;
}
/* This can happen if the capture stream has stopped. Just return in this
@ -566,12 +556,12 @@ bool get_input_buffer(cubeb_stream * stm)
&dev_pos,
NULL);
if (FAILED(hr)) {
LOG("GetBuffer failed for capture: %x\n", hr);
LOG("GetBuffer failed for capture: %x", hr);
return false;
}
XASSERT(packet_size == next);
if (flags & AUDCLNT_BUFFERFLAGS_SILENT) {
LOG("insert silence: ps=%u\n", packet_size);
LOG("insert silence: ps=%u", packet_size);
stm->linear_input_buffer.push_silence(packet_size * stm->input_stream_params.channels);
} else {
if (should_upmix(stm->input_mix_params, stm->input_stream_params)) {
@ -622,7 +612,7 @@ bool get_output_buffer(cubeb_stream * stm, float *& buffer, size_t & frame_count
hr = stm->output_client->GetCurrentPadding(&padding_out);
if (FAILED(hr)) {
LOG("Failed to get padding: %x\n", hr);
LOG("Failed to get padding: %x", hr);
return false;
}
XASSERT(padding_out <= stm->output_buffer_frame_count);
@ -640,7 +630,7 @@ bool get_output_buffer(cubeb_stream * stm, float *& buffer, size_t & frame_count
hr = stm->render_client->GetBuffer(frame_count, &output_buffer);
if (FAILED(hr)) {
LOG("cannot get render buffer\n");
LOG("cannot get render buffer");
return false;
}
@ -690,7 +680,7 @@ refill_callback_duplex(cubeb_stream * stm)
double input_duration = double(stm->linear_input_buffer.length() / stm->input_stream_params.channels) / stm->input_mix_params.rate;
if (input_duration < output_duration) {
size_t padding = size_t(round((output_duration - input_duration) * stm->input_mix_params.rate));
LOG("padding silence: out=%f in=%f pad=%u\n", output_duration, input_duration, padding);
LOG("padding silence: out=%f in=%f pad=%u", output_duration, input_duration, padding);
stm->linear_input_buffer.push_front_silence(padding * stm->input_stream_params.channels);
}
@ -704,7 +694,7 @@ refill_callback_duplex(cubeb_stream * stm)
hr = stm->render_client->ReleaseBuffer(output_frames, 0);
if (FAILED(hr)) {
LOG("failed to release buffer: %x\n", hr);
LOG("failed to release buffer: %x", hr);
return false;
}
return true;
@ -722,6 +712,11 @@ refill_callback_input(cubeb_stream * stm)
return rv;
}
// This can happen at the very beginning of the stream.
if (!stm->linear_input_buffer.length()) {
return true;
}
long read = refill(stm,
stm->linear_input_buffer.data(),
stm->linear_input_buffer.length(),
@ -764,7 +759,7 @@ refill_callback_output(cubeb_stream * stm)
hr = stm->render_client->ReleaseBuffer(got, 0);
if (FAILED(hr)) {
LOG("failed to release buffer: %x\n", hr);
LOG("failed to release buffer: %x", hr);
return false;
}
@ -788,7 +783,7 @@ wasapi_stream_render_loop(LPVOID stream)
DWORD mmcss_task_index = 0;
auto_com com;
if (!com.ok()) {
LOG("COM initialization failed on render_loop thread.\n");
LOG("COM initialization failed on render_loop thread.");
stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR);
return 0;
}
@ -799,7 +794,7 @@ wasapi_stream_render_loop(LPVOID stream)
stm->context->set_mm_thread_characteristics("Audio", &mmcss_task_index);
if (!mmcss_handle) {
/* This is not fatal, but we might glitch under heavy load. */
LOG("Unable to use mmcss to bump the render thread priority: %x\n", GetLastError());
LOG("Unable to use mmcss to bump the render thread priority: %x", GetLastError());
}
@ -908,7 +903,7 @@ HRESULT register_notification_client(cubeb_stream * stm)
NULL, CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&stm->device_enumerator));
if (FAILED(hr)) {
LOG("Could not get device enumerator: %x\n", hr);
LOG("Could not get device enumerator: %x", hr);
return hr;
}
@ -916,7 +911,7 @@ HRESULT register_notification_client(cubeb_stream * stm)
hr = stm->device_enumerator->RegisterEndpointNotificationCallback(stm->notification_client);
if (FAILED(hr)) {
LOG("Could not register endpoint notification callback: %x\n", hr);
LOG("Could not register endpoint notification callback: %x", hr);
SafeRelease(stm->notification_client);
stm->notification_client = nullptr;
SafeRelease(stm->device_enumerator);
@ -956,13 +951,13 @@ HRESULT get_endpoint(IMMDevice ** device, LPCWSTR devid)
NULL, CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&enumerator));
if (FAILED(hr)) {
LOG("Could not get device enumerator: %x\n", hr);
LOG("Could not get device enumerator: %x", hr);
return hr;
}
hr = enumerator->GetDevice(devid, device);
if (FAILED(hr)) {
LOG("Could not get device: %x\n", hr);
LOG("Could not get device: %x", hr);
SafeRelease(enumerator);
return hr;
}
@ -979,12 +974,12 @@ HRESULT get_default_endpoint(IMMDevice ** device, EDataFlow direction)
NULL, CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&enumerator));
if (FAILED(hr)) {
LOG("Could not get device enumerator: %x\n", hr);
LOG("Could not get device enumerator: %x", hr);
return hr;
}
hr = enumerator->GetDefaultAudioEndpoint(direction, eConsole, device);
if (FAILED(hr)) {
LOG("Could not get default audio endpoint: %x\n", hr);
LOG("Could not get default audio endpoint: %x", hr);
SafeRelease(enumerator);
return hr;
}
@ -1009,14 +1004,14 @@ current_stream_delay(cubeb_stream * stm)
UINT64 freq;
HRESULT hr = stm->audio_clock->GetFrequency(&freq);
if (FAILED(hr)) {
LOG("GetFrequency failed: %x\n", hr);
LOG("GetFrequency failed: %x", hr);
return 0;
}
UINT64 pos;
hr = stm->audio_clock->GetPosition(&pos, NULL);
if (FAILED(hr)) {
LOG("GetPosition failed: %x\n", hr);
LOG("GetPosition failed: %x", hr);
return 0;
}
@ -1040,7 +1035,7 @@ stream_set_volume(cubeb_stream * stm, float volume)
uint32_t channels;
HRESULT hr = stm->audio_stream_volume->GetChannelCount(&channels);
if (hr != S_OK) {
LOG("could not get the channel count: %x\n", hr);
LOG("could not get the channel count: %x", hr);
return CUBEB_ERROR;
}
@ -1056,7 +1051,7 @@ stream_set_volume(cubeb_stream * stm, float volume)
hr = stm->audio_stream_volume->SetAllVolumes(channels, volumes);
if (hr != S_OK) {
LOG("could not set the channels volume: %x\n", hr);
LOG("could not set the channels volume: %x", hr);
return CUBEB_ERROR;
}
@ -1079,7 +1074,7 @@ int wasapi_init(cubeb ** context, char const * context_name)
IMMDevice * device;
hr = get_default_endpoint(&device, eRender);
if (FAILED(hr)) {
LOG("Could not get device: %x\n", hr);
LOG("Could not get device: %x", hr);
return CUBEB_ERROR;
}
SafeRelease(device);
@ -1101,13 +1096,13 @@ int wasapi_init(cubeb ** context, char const * context_name)
(revert_mm_thread_characteristics_function) GetProcAddress(
ctx->mmcss_module, "AvRevertMmThreadCharacteristics");
if (!(ctx->set_mm_thread_characteristics && ctx->revert_mm_thread_characteristics)) {
LOG("Could not load AvSetMmThreadCharacteristics or AvRevertMmThreadCharacteristics: %x\n", GetLastError());
LOG("Could not load AvSetMmThreadCharacteristics or AvRevertMmThreadCharacteristics: %x", GetLastError());
FreeLibrary(ctx->mmcss_module);
}
} else {
// This is not a fatal error, but we might end up glitching when
// the system is under high load.
LOG("Could not load Avrt.dll\n");
LOG("Could not load Avrt.dll");
ctx->set_mm_thread_characteristics = &set_mm_thread_characteristics_noop;
ctx->revert_mm_thread_characteristics = &revert_mm_thread_characteristics_noop;
}
@ -1127,7 +1122,7 @@ void stop_and_join_render_thread(cubeb_stream * stm)
BOOL ok = SetEvent(stm->shutdown_event);
if (!ok) {
LOG("Destroy SetEvent failed: %d\n", GetLastError());
LOG("Destroy SetEvent failed: %d", GetLastError());
}
/* Wait five seconds for the rendering thread to return. It's supposed to
@ -1137,10 +1132,10 @@ void stop_and_join_render_thread(cubeb_stream * stm)
/* Something weird happened, leak the thread and continue the shutdown
* process. */
LOG("Destroy WaitForSingleObject on thread timed out,"
" leaking the thread: %d\n", GetLastError());
" leaking the thread: %d", GetLastError());
}
if (r == WAIT_FAILED) {
LOG("Destroy WaitForSingleObject on thread failed: %d\n", GetLastError());
LOG("Destroy WaitForSingleObject on thread failed: %d", GetLastError());
}
CloseHandle(stm->thread);
@ -1222,7 +1217,7 @@ wasapi_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * laten
IMMDevice * device;
hr = get_default_endpoint(&device, eRender);
if (FAILED(hr)) {
LOG("Could not get default endpoint: %x\n", hr);
LOG("Could not get default endpoint: %x", hr);
return CUBEB_ERROR;
}
@ -1231,7 +1226,7 @@ wasapi_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * laten
NULL, (void **)&client);
SafeRelease(device);
if (FAILED(hr)) {
LOG("Could not activate device for latency: %x\n", hr);
LOG("Could not activate device for latency: %x", hr);
return CUBEB_ERROR;
}
@ -1239,11 +1234,11 @@ wasapi_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * laten
hr = client->GetDevicePeriod(&default_period, NULL);
if (FAILED(hr)) {
SafeRelease(client);
LOG("Could not get device period: %x\n", hr);
LOG("Could not get device period: %x", hr);
return CUBEB_ERROR;
}
LOG("default device period: %lld\n", default_period);
LOG("default device period: %lld", default_period);
/* According to the docs, the best latency we can achieve is by synchronizing
the stream and the engine.
@ -1350,7 +1345,7 @@ handle_channel_layout(cubeb_stream * stm, WAVEFORMATEX ** mix_format, const cub
if (hr == S_FALSE) {
/* Not supported, but WASAPI gives us a suggestion. Use it, and handle the
eventual upmix/downmix ourselves */
LOG("Using WASAPI suggested format: channels: %d\n", closest->nChannels);
LOG("Using WASAPI suggested format: channels: %d", closest->nChannels);
WAVEFORMATEXTENSIBLE * closest_pcm = reinterpret_cast<WAVEFORMATEXTENSIBLE *>(closest);
XASSERT(closest_pcm->SubFormat == format_pcm->SubFormat);
CoTaskMemFree(*mix_format);
@ -1361,9 +1356,9 @@ handle_channel_layout(cubeb_stream * stm, WAVEFORMATEX ** mix_format, const cub
of the code figure out the right conversion path. */
*reinterpret_cast<WAVEFORMATEXTENSIBLE *>(*mix_format) = hw_mix_format;
} else if (hr == S_OK) {
LOG("Requested format accepted by WASAPI.\n");
LOG("Requested format accepted by WASAPI.");
} else {
LOG("IsFormatSupported unhandled error: %x\n", hr);
LOG("IsFormatSupported unhandled error: %x", hr);
}
}
@ -1386,41 +1381,56 @@ int setup_wasapi_stream_one_side(cubeb_stream * stm,
HRESULT hr;
stm->stream_reset_lock.assert_current_thread_owns();
if (devid) {
std::unique_ptr<const wchar_t> id;
id.reset(utf8_to_wstr(reinterpret_cast<char*>(devid)));
hr = get_endpoint(&device, id.get());
if (FAILED(hr)) {
LOG("Could not get %s endpoint, error: %x\n", DIRECTION_NAME, hr);
return CUBEB_ERROR;
bool try_again = false;
// This loops until we find a device that works, or we've exhausted all
// possibilities.
do {
if (devid) {
std::unique_ptr<const wchar_t> id;
id.reset(utf8_to_wstr(reinterpret_cast<char*>(devid)));
hr = get_endpoint(&device, id.get());
if (FAILED(hr)) {
LOG("Could not get %s endpoint, error: %x\n", DIRECTION_NAME, hr);
return CUBEB_ERROR;
}
}
} else {
hr = get_default_endpoint(&device, direction);
if (FAILED(hr)) {
LOG("Could not get default %s endpoint, error: %x\n", DIRECTION_NAME, hr);
return CUBEB_ERROR;
else {
hr = get_default_endpoint(&device, direction);
if (FAILED(hr)) {
LOG("Could not get default %s endpoint, error: %x\n", DIRECTION_NAME, hr);
return CUBEB_ERROR;
}
}
}
/* Get a client. We will get all other interfaces we need from
* this pointer. */
hr = device->Activate(__uuidof(IAudioClient),
CLSCTX_INPROC_SERVER,
NULL, (void **)audio_client);
SafeRelease(device);
if (FAILED(hr)) {
LOG("Could not activate the device to get an audio"
" client for %s: error: %x\n", DIRECTION_NAME, hr);
return CUBEB_ERROR;
}
/* Get a client. We will get all other interfaces we need from
* this pointer. */
hr = device->Activate(__uuidof(IAudioClient),
CLSCTX_INPROC_SERVER,
NULL, (void **)audio_client);
SafeRelease(device);
if (FAILED(hr)) {
LOG("Could not activate the device to get an audio"
" client for %s: error: %x\n", DIRECTION_NAME, hr);
// A particular device can't be activated because it has been
// unplugged, try fall back to the default audio device.
if (devid && hr == AUDCLNT_E_DEVICE_INVALIDATED) {
LOG("Trying again with the default %s audio device.", DIRECTION_NAME);
devid = nullptr;
try_again = true;
} else {
return CUBEB_ERROR;
}
} else {
try_again = false;
}
} while (try_again);
/* We have to distinguish between the format the mixer uses,
* and the format the stream we want to play uses. */
hr = (*audio_client)->GetMixFormat(&mix_format);
if (FAILED(hr)) {
LOG("Could not fetch current mix format from the audio"
" client for %s: error: %x\n", DIRECTION_NAME, hr);
" client for %s: error: %x", DIRECTION_NAME, hr);
return CUBEB_ERROR;
}
@ -1431,7 +1441,7 @@ int setup_wasapi_stream_one_side(cubeb_stream * stm,
mix_params->format = CUBEB_SAMPLE_FLOAT32NE;
mix_params->rate = mix_format->nSamplesPerSec;
mix_params->channels = mix_format->nChannels;
LOG("Setup requested=[f=%d r=%u c=%u] mix=[f=%d r=%u c=%u]\n",
LOG("Setup requested=[f=%d r=%u c=%u] mix=[f=%d r=%u c=%u]",
stream_params->format, stream_params->rate, stream_params->channels,
mix_params->format, mix_params->rate, mix_params->channels);
@ -1443,7 +1453,7 @@ int setup_wasapi_stream_one_side(cubeb_stream * stm,
mix_format,
NULL);
if (FAILED(hr)) {
LOG("Unable to initialize audio client for %s: %x.\n", DIRECTION_NAME, hr);
LOG("Unable to initialize audio client for %s: %x.", DIRECTION_NAME, hr);
return CUBEB_ERROR;
}
@ -1452,7 +1462,7 @@ int setup_wasapi_stream_one_side(cubeb_stream * stm,
hr = (*audio_client)->GetBufferSize(buffer_frame_count);
if (FAILED(hr)) {
LOG("Could not get the buffer size from the client"
" for %s %x.\n", DIRECTION_NAME, hr);
" for %s %x.", DIRECTION_NAME, hr);
return CUBEB_ERROR;
}
@ -1465,14 +1475,14 @@ int setup_wasapi_stream_one_side(cubeb_stream * stm,
hr = (*audio_client)->SetEventHandle(event);
if (FAILED(hr)) {
LOG("Could set the event handle for the %s client %x.\n",
LOG("Could set the event handle for the %s client %x.",
DIRECTION_NAME, hr);
return CUBEB_ERROR;
}
hr = (*audio_client)->GetService(riid, (void **)render_or_capture_client);
if (FAILED(hr)) {
LOG("Could not get the %s client %x.\n", DIRECTION_NAME, hr);
LOG("Could not get the %s client %x.", DIRECTION_NAME, hr);
return CUBEB_ERROR;
}
@ -1496,7 +1506,7 @@ int setup_wasapi_stream(cubeb_stream * stm)
XASSERT((!stm->output_client || !stm->input_client) && "WASAPI stream already setup, close it first.");
if (has_input(stm)) {
LOG("Setup capture: device=%x\n", (int)stm->input_device);
LOG("Setup capture: device=%x", (int)stm->input_device);
rv = setup_wasapi_stream_one_side(stm,
&stm->input_stream_params,
stm->input_device,
@ -1513,7 +1523,7 @@ int setup_wasapi_stream(cubeb_stream * stm)
}
if (has_output(stm)) {
LOG("Setup render: device=%x\n", (int)stm->output_device);
LOG("Setup render: device=%x", (int)stm->output_device);
rv = setup_wasapi_stream_one_side(stm,
&stm->output_stream_params,
stm->output_device,
@ -1531,7 +1541,7 @@ int setup_wasapi_stream(cubeb_stream * stm)
hr = stm->output_client->GetService(__uuidof(IAudioStreamVolume),
(void **)&stm->audio_stream_volume);
if (FAILED(hr)) {
LOG("Could not get the IAudioStreamVolume: %x\n", hr);
LOG("Could not get the IAudioStreamVolume: %x", hr);
return CUBEB_ERROR;
}
@ -1539,7 +1549,7 @@ int setup_wasapi_stream(cubeb_stream * stm)
hr = stm->output_client->GetService(__uuidof(IAudioClock),
(void **)&stm->audio_clock);
if (FAILED(hr)) {
LOG("Could not get the IAudioClock: %x\n", hr);
LOG("Could not get the IAudioClock: %x", hr);
return CUBEB_ERROR;
}
@ -1580,7 +1590,7 @@ int setup_wasapi_stream(cubeb_stream * stm)
stm->user_ptr,
CUBEB_RESAMPLER_QUALITY_DESKTOP);
if (!stm->resampler) {
LOG("Could not get a resampler\n");
LOG("Could not get a resampler");
return CUBEB_ERROR;
}
@ -1647,7 +1657,7 @@ wasapi_stream_init(cubeb * context, cubeb_stream ** stream,
stm->reconfigure_event = CreateEvent(NULL, 0, 0, NULL);
if (!stm->reconfigure_event) {
LOG("Can't create the reconfigure event, error: %x\n", GetLastError());
LOG("Can't create the reconfigure event, error: %x", GetLastError());
wasapi_stream_destroy(stm);
return CUBEB_ERROR;
}
@ -1655,14 +1665,14 @@ wasapi_stream_init(cubeb * context, cubeb_stream ** stream,
/* Unconditionally create the two events so that the wait logic is simpler. */
stm->refill_event = CreateEvent(NULL, 0, 0, NULL);
if (!stm->refill_event) {
LOG("Can't create the refill event, error: %x\n", GetLastError());
LOG("Can't create the refill event, error: %x", GetLastError());
wasapi_stream_destroy(stm);
return CUBEB_ERROR;
}
stm->input_available_event = CreateEvent(NULL, 0, 0, NULL);
if (!stm->input_available_event) {
LOG("Can't create the input available event , error: %x\n", GetLastError());
LOG("Can't create the input available event , error: %x", GetLastError());
wasapi_stream_destroy(stm);
return CUBEB_ERROR;
}
@ -1684,7 +1694,7 @@ wasapi_stream_init(cubeb * context, cubeb_stream ** stream,
if (FAILED(hr)) {
/* this is not fatal, we can still play audio, but we won't be able
to keep using the default audio endpoint if it changes. */
LOG("failed to register notification client, %x\n", hr);
LOG("failed to register notification client, %x", hr);
}
*stream = stm;
@ -1758,30 +1768,30 @@ int stream_start_one_side(cubeb_stream * stm, StreamDirection dir)
HRESULT hr = dir == OUTPUT ? stm->output_client->Start() : stm->input_client->Start();
if (hr == AUDCLNT_E_DEVICE_INVALIDATED) {
LOG("audioclient invalidated for %s device, reconfiguring\n",
LOG("audioclient invalidated for %s device, reconfiguring",
dir == OUTPUT ? "output" : "input");
BOOL ok = ResetEvent(stm->reconfigure_event);
if (!ok) {
LOG("resetting reconfig event failed for %s stream: %x\n",
LOG("resetting reconfig event failed for %s stream: %x",
dir == OUTPUT ? "output" : "input", GetLastError());
}
close_wasapi_stream(stm);
int r = setup_wasapi_stream(stm);
if (r != CUBEB_OK) {
LOG("reconfigure failed\n");
LOG("reconfigure failed");
return r;
}
HRESULT hr2 = dir == OUTPUT ? stm->output_client->Start() : stm->input_client->Start();
if (FAILED(hr2)) {
LOG("could not start the %s stream after reconfig: %x\n",
LOG("could not start the %s stream after reconfig: %x",
dir == OUTPUT ? "output" : "input", hr);
return CUBEB_ERROR;
}
} else if (FAILED(hr)) {
LOG("could not start the %s stream: %x.\n",
LOG("could not start the %s stream: %x.",
dir == OUTPUT ? "output" : "input", hr);
return CUBEB_ERROR;
}
@ -1812,13 +1822,13 @@ int wasapi_stream_start(cubeb_stream * stm)
stm->shutdown_event = CreateEvent(NULL, 0, 0, NULL);
if (!stm->shutdown_event) {
LOG("Can't create the shutdown event, error: %x\n", GetLastError());
LOG("Can't create the shutdown event, error: %x", GetLastError());
return CUBEB_ERROR;
}
stm->thread = (HANDLE) _beginthreadex(NULL, 512 * 1024, wasapi_stream_render_loop, stm, STACK_SIZE_PARAM_IS_A_RESERVATION, NULL);
if (stm->thread == NULL) {
LOG("could not create WASAPI render thread.\n");
LOG("could not create WASAPI render thread.");
return CUBEB_ERROR;
}
@ -1838,7 +1848,7 @@ int wasapi_stream_stop(cubeb_stream * stm)
if (stm->output_client) {
hr = stm->output_client->Stop();
if (FAILED(hr)) {
LOG("could not stop AudioClient (output)\n");
LOG("could not stop AudioClient (output)");
return CUBEB_ERROR;
}
}
@ -1846,7 +1856,7 @@ int wasapi_stream_stop(cubeb_stream * stm)
if (stm->input_client) {
hr = stm->input_client->Stop();
if (FAILED(hr)) {
LOG("could not stop AudioClient (input)\n");
LOG("could not stop AudioClient (input)");
return CUBEB_ERROR;
}
}
@ -2142,7 +2152,7 @@ wasapi_enumerate_devices(cubeb * context, cubeb_device_type type,
hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL,
CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&enumerator));
if (FAILED(hr)) {
LOG("Could not get device enumerator: %x\n", hr);
LOG("Could not get device enumerator: %x", hr);
return CUBEB_ERROR;
}
@ -2153,13 +2163,13 @@ wasapi_enumerate_devices(cubeb * context, cubeb_device_type type,
hr = enumerator->EnumAudioEndpoints(flow, DEVICE_STATEMASK_ALL, &collection);
if (FAILED(hr)) {
LOG("Could not enumerate audio endpoints: %x\n", hr);
LOG("Could not enumerate audio endpoints: %x", hr);
return CUBEB_ERROR;
}
hr = collection->GetCount(&cc);
if (FAILED(hr)) {
LOG("IMMDeviceCollection::GetCount() failed: %x\n", hr);
LOG("IMMDeviceCollection::GetCount() failed: %x", hr);
return CUBEB_ERROR;
}
*out = (cubeb_device_collection *) malloc(sizeof(cubeb_device_collection) +
@ -2171,7 +2181,7 @@ wasapi_enumerate_devices(cubeb * context, cubeb_device_type type,
for (i = 0; i < cc; i++) {
hr = collection->Item(i, &dev);
if (FAILED(hr)) {
LOG("IMMDeviceCollection::Item(%u) failed: %x\n", i-1, hr);
LOG("IMMDeviceCollection::Item(%u) failed: %x", i-1, hr);
} else if ((cur = wasapi_create_device(enumerator, dev)) != NULL) {
(*out)->device[(*out)->count++] = cur;
}

Просмотреть файл

@ -4,13 +4,13 @@
* This program is made available under an ISC-style license. See the
* accompanying file LICENSE for details.
*/
#ifndef NOMINMAX
#define NOMINMAX
#endif // NOMINMAX
#ifdef NDEBUG
#undef NDEBUG
#endif
#ifndef CUBEB_GECKO_BUILD
#include "config.h"
#endif
#include "cubeb_resampler_internal.h"
#include <assert.h>
#include <stdio.h>

Просмотреть файл

@ -18,7 +18,6 @@
#include "TestHarness.h"
#endif
#define ARRAY_LENGTH(_x) (sizeof(_x) / sizeof(_x[0]))
#define BEGIN_TEST fprintf(stderr, "START %s\n", __func__)
#define END_TEST fprintf(stderr, "END %s\n", __func__)
@ -31,6 +30,13 @@
#define STREAM_FORMAT CUBEB_SAMPLE_S16LE
#endif
template<typename T, size_t N>
constexpr size_t
ARRAY_LENGTH(T(&)[N])
{
return N;
}
static int dummy;
static uint64_t total_frames_written;
static int delay_callback;

Просмотреть файл

@ -11,6 +11,7 @@ cp $1/src/cubeb-internal.h src
cp $1/src/cubeb-speex-resampler.h src
cp $1/src/cubeb.c src
cp $1/src/cubeb_alsa.c src
cp $1/src/cubeb_log.h src
cp $1/src/cubeb_audiotrack.c src
cp $1/src/cubeb_audiounit.cpp src
cp $1/src/cubeb_osx_run_loop.h src
@ -56,10 +57,3 @@ if [ -n "$rev" ]; then
else
echo "Remember to update README_MOZILLA with the version details."
fi
echo "Applying a patch on top of $version"
patch -p1 < ./wasapi-drift.patch
echo "Applying another patch on top of $version"
patch -p1 < ./wasapi-stereo-mic.patch
echo "Applying another patch on top of $version"
patch -p1 < ./bug1308418-mutex-copy-ctor.patch