зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1311346 - Update libcubeb to revision 9eacd3144. r=kinetik
--HG-- extra : rebase_source : efeb55082e9a1a0a60d96b717dbb3767e9a54280
This commit is contained in:
Родитель
30a3044073
Коммит
527bc745cd
|
@ -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
|
||||
|
|
Загрузка…
Ссылка в новой задаче