зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1395393 - Update libcubeb to 2e5814de. r=achronop
MozReview-Commit-ID: JWDv93mjAAE --HG-- extra : rebase_source : 170547b55d8b04838523a255502c58991da8ffdb extra : amend_source : c7f5e99b69e27c40e5fa6ed9054a57dbdf1c2f1c
This commit is contained in:
Родитель
9ed090fe94
Коммит
54e3972626
|
@ -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 d59010398cee559349ba8f8363a7250b5279aa69 (2017-08-07 22:45:24 +1200)
|
||||
The git commit ID used was 2e5814de4fd9830d201d61b9d35ed24c2bba6d0f (2017-08-31 13:51:29 +0300)
|
||||
|
|
|
@ -633,3 +633,42 @@ TEST(cubeb, DISABLED_stream_destroy_pending_drain)
|
|||
{
|
||||
// This test needs to be implemented.
|
||||
}
|
||||
|
||||
TEST(cubeb, stable_devid)
|
||||
{
|
||||
/* Test that the devid field of cubeb_device_info is stable
|
||||
* (ie. compares equal) over two invocations of
|
||||
* cubeb_enumerate_devices(). */
|
||||
|
||||
int r;
|
||||
cubeb * ctx;
|
||||
cubeb_device_collection first;
|
||||
cubeb_device_collection second;
|
||||
cubeb_device_type all_devices =
|
||||
(cubeb_device_type) (CUBEB_DEVICE_TYPE_INPUT | CUBEB_DEVICE_TYPE_OUTPUT);
|
||||
size_t n;
|
||||
|
||||
r = common_init(&ctx, "test_sanity");
|
||||
ASSERT_EQ(r, CUBEB_OK);
|
||||
ASSERT_NE(ctx, nullptr);
|
||||
|
||||
r = cubeb_enumerate_devices(ctx, all_devices, &first);
|
||||
if (r == CUBEB_ERROR_NOT_SUPPORTED)
|
||||
return;
|
||||
|
||||
ASSERT_EQ(r, CUBEB_OK);
|
||||
|
||||
r = cubeb_enumerate_devices(ctx, all_devices, &second);
|
||||
ASSERT_EQ(r, CUBEB_OK);
|
||||
|
||||
ASSERT_EQ(first.count, second.count);
|
||||
for (n = 0; n < first.count; n++) {
|
||||
ASSERT_EQ(first.device[n].devid, second.device[n].devid);
|
||||
}
|
||||
|
||||
r = cubeb_device_collection_destroy(ctx, &first);
|
||||
ASSERT_EQ(r, CUBEB_OK);
|
||||
r = cubeb_device_collection_destroy(ctx, &second);
|
||||
ASSERT_EQ(r, CUBEB_OK);
|
||||
cubeb_destroy(ctx);
|
||||
}
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
#include <alsa/asoundlib.h>
|
||||
#include "cubeb/cubeb.h"
|
||||
#include "cubeb-internal.h"
|
||||
#include "cubeb_utils.h"
|
||||
|
||||
#define CUBEB_STREAM_MAX 16
|
||||
#define CUBEB_WATCHDOG_MS 10000
|
||||
|
|
|
@ -63,6 +63,8 @@ void audiounit_stream_stop_internal(cubeb_stream * stm);
|
|||
void audiounit_stream_start_internal(cubeb_stream * stm);
|
||||
static void audiounit_close_stream(cubeb_stream *stm);
|
||||
static int audiounit_setup_stream(cubeb_stream *stm);
|
||||
static std::vector<AudioObjectID>
|
||||
audiounit_get_devices_of_type(cubeb_device_type devtype);
|
||||
|
||||
extern cubeb_ops const audiounit_ops;
|
||||
|
||||
|
@ -2874,35 +2876,6 @@ int audiounit_stream_register_device_changed_callback(cubeb_stream * stream,
|
|||
return CUBEB_OK;
|
||||
}
|
||||
|
||||
static OSStatus
|
||||
audiounit_get_devices(std::vector<AudioObjectID> & devices)
|
||||
{
|
||||
OSStatus ret;
|
||||
UInt32 size = 0;
|
||||
AudioObjectPropertyAddress adr = { kAudioHardwarePropertyDevices,
|
||||
kAudioObjectPropertyScopeGlobal,
|
||||
kAudioObjectPropertyElementMaster };
|
||||
|
||||
ret = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &adr, 0, NULL, &size);
|
||||
if (ret != noErr) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint32_t count = static_cast<uint32_t>(size / sizeof(AudioObjectID));
|
||||
if (count == 0) {
|
||||
return -1;
|
||||
}
|
||||
assert(devices.empty());
|
||||
devices.resize(count);
|
||||
|
||||
ret = AudioObjectGetPropertyData(kAudioObjectSystemObject, &adr, 0, NULL, &size, devices.data());
|
||||
if (ret != noErr) {
|
||||
devices.clear();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static char *
|
||||
audiounit_strref_to_cstr_utf8(CFStringRef strref)
|
||||
{
|
||||
|
@ -3109,22 +3082,30 @@ static int
|
|||
audiounit_enumerate_devices(cubeb * /* context */, cubeb_device_type type,
|
||||
cubeb_device_collection * collection)
|
||||
{
|
||||
std::vector<AudioObjectID> hwdevs;
|
||||
uint32_t i;
|
||||
OSStatus err;
|
||||
std::vector<AudioObjectID> input_devs;
|
||||
std::vector<AudioObjectID> output_devs;
|
||||
|
||||
err = audiounit_get_devices(hwdevs);
|
||||
if (err != noErr) {
|
||||
return CUBEB_ERROR;
|
||||
// Count number of input and output devices. This is not
|
||||
// necessarily the same as the count of raw devices supported by the
|
||||
// system since, for example, with Soundflower installed, some
|
||||
// devices may report as being both input *and* output and cubeb
|
||||
// separates those into two different devices.
|
||||
|
||||
if (type & CUBEB_DEVICE_TYPE_OUTPUT) {
|
||||
output_devs = audiounit_get_devices_of_type(CUBEB_DEVICE_TYPE_OUTPUT);
|
||||
}
|
||||
|
||||
auto devices = new cubeb_device_info[hwdevs.size()];
|
||||
if (type & CUBEB_DEVICE_TYPE_INPUT) {
|
||||
input_devs = audiounit_get_devices_of_type(CUBEB_DEVICE_TYPE_INPUT);
|
||||
}
|
||||
|
||||
auto devices = new cubeb_device_info[output_devs.size() + input_devs.size()];
|
||||
collection->count = 0;
|
||||
|
||||
if (type & CUBEB_DEVICE_TYPE_OUTPUT) {
|
||||
for (i = 0; i < hwdevs.size(); i++) {
|
||||
for (auto dev: output_devs) {
|
||||
auto device = &devices[collection->count];
|
||||
auto err = audiounit_create_device_from_hwdev(device, hwdevs[i], CUBEB_DEVICE_TYPE_OUTPUT);
|
||||
auto err = audiounit_create_device_from_hwdev(device, dev, CUBEB_DEVICE_TYPE_OUTPUT);
|
||||
if (err != CUBEB_OK) {
|
||||
continue;
|
||||
}
|
||||
|
@ -3133,9 +3114,9 @@ audiounit_enumerate_devices(cubeb * /* context */, cubeb_device_type type,
|
|||
}
|
||||
|
||||
if (type & CUBEB_DEVICE_TYPE_INPUT) {
|
||||
for (i = 0; i < hwdevs.size(); i++) {
|
||||
for (auto dev: input_devs) {
|
||||
auto device = &devices[collection->count];
|
||||
auto err = audiounit_create_device_from_hwdev(device, hwdevs[i], CUBEB_DEVICE_TYPE_INPUT);
|
||||
auto err = audiounit_create_device_from_hwdev(device, dev, CUBEB_DEVICE_TYPE_INPUT);
|
||||
if (err != CUBEB_OK) {
|
||||
continue;
|
||||
}
|
||||
|
|
|
@ -96,6 +96,8 @@ static int cbjack_stream_device_destroy(cubeb_stream * stream,
|
|||
static int cbjack_stream_get_current_device(cubeb_stream * stm, cubeb_device ** const device);
|
||||
static int cbjack_enumerate_devices(cubeb * context, cubeb_device_type type,
|
||||
cubeb_device_collection * collection);
|
||||
static int cbjack_device_collection_destroy(cubeb * context,
|
||||
cubeb_device_collection * collection);
|
||||
static int cbjack_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_name,
|
||||
cubeb_devid input_device,
|
||||
cubeb_stream_params * input_stream_params,
|
||||
|
@ -119,7 +121,7 @@ static struct cubeb_ops const cbjack_ops = {
|
|||
.get_preferred_sample_rate = cbjack_get_preferred_sample_rate,
|
||||
.get_preferred_channel_layout = NULL,
|
||||
.enumerate_devices = cbjack_enumerate_devices,
|
||||
.device_collection_destroy = cubeb_utils_default_device_collection_destroy,
|
||||
.device_collection_destroy = cbjack_device_collection_destroy,
|
||||
.destroy = cbjack_destroy,
|
||||
.stream_init = cbjack_stream_init,
|
||||
.stream_destroy = cbjack_stream_destroy,
|
||||
|
@ -974,6 +976,9 @@ cbjack_stream_device_destroy(cubeb_stream * /*stream*/,
|
|||
return CUBEB_OK;
|
||||
}
|
||||
|
||||
#define JACK_DEFAULT_IN "JACK capture"
|
||||
#define JACK_DEFAULT_OUT "JACK playback"
|
||||
|
||||
static int
|
||||
cbjack_enumerate_devices(cubeb * context, cubeb_device_type type,
|
||||
cubeb_device_collection * collection)
|
||||
|
@ -983,20 +988,20 @@ cbjack_enumerate_devices(cubeb * context, cubeb_device_type type,
|
|||
|
||||
uint32_t rate;
|
||||
cbjack_get_preferred_sample_rate(context, &rate);
|
||||
const char * j_in = "JACK capture";
|
||||
const char * j_out = "JACK playback";
|
||||
|
||||
cubeb_device_info * devices = new cubeb_device_info[2];
|
||||
reinterpret_cast<cubeb_device_info *>(calloc(2, sizeof(cubeb_device_info)));
|
||||
if (!devices)
|
||||
return CUBEB_ERROR;
|
||||
PodZero(devices, 2);
|
||||
collection->count = 0;
|
||||
|
||||
if (type & CUBEB_DEVICE_TYPE_OUTPUT) {
|
||||
cubeb_device_info * cur = &devices[collection->count];
|
||||
cur->device_id = strdup(j_out);
|
||||
cur->device_id = JACK_DEFAULT_OUT;
|
||||
cur->devid = (cubeb_devid) cur->device_id;
|
||||
cur->friendly_name = strdup(j_out);
|
||||
cur->group_id = strdup(j_out);
|
||||
cur->vendor_name = strdup(j_out);
|
||||
cur->friendly_name = JACK_DEFAULT_OUT;
|
||||
cur->group_id = JACK_DEFAULT_OUT;
|
||||
cur->vendor_name = JACK_DEFAULT_OUT;
|
||||
cur->type = CUBEB_DEVICE_TYPE_OUTPUT;
|
||||
cur->state = CUBEB_DEVICE_STATE_ENABLED;
|
||||
cur->preferred = CUBEB_DEVICE_PREF_ALL;
|
||||
|
@ -1013,11 +1018,11 @@ cbjack_enumerate_devices(cubeb * context, cubeb_device_type type,
|
|||
|
||||
if (type & CUBEB_DEVICE_TYPE_INPUT) {
|
||||
cubeb_device_info * cur = &devices[collection->count];
|
||||
cur->device_id = strdup(j_in);
|
||||
cur->device_id = JACK_DEFAULT_IN;
|
||||
cur->devid = (cubeb_devid) cur->device_id;
|
||||
cur->friendly_name = strdup(j_in);
|
||||
cur->group_id = strdup(j_in);
|
||||
cur->vendor_name = strdup(j_in);
|
||||
cur->friendly_name = JACK_DEFAULT_IN;
|
||||
cur->group_id = JACK_DEFAULT_IN;
|
||||
cur->vendor_name = JACK_DEFAULT_IN;
|
||||
cur->type = CUBEB_DEVICE_TYPE_INPUT;
|
||||
cur->state = CUBEB_DEVICE_STATE_ENABLED;
|
||||
cur->preferred = CUBEB_DEVICE_PREF_ALL;
|
||||
|
@ -1036,3 +1041,12 @@ cbjack_enumerate_devices(cubeb * context, cubeb_device_type type,
|
|||
|
||||
return CUBEB_OK;
|
||||
}
|
||||
|
||||
static int
|
||||
cbjack_device_collection_destroy(cubeb * /*ctx*/,
|
||||
cubeb_device_collection * collection)
|
||||
{
|
||||
XASSERT(collection);
|
||||
delete [] collection->device;
|
||||
return CUBEB_OK;
|
||||
}
|
||||
|
|
|
@ -95,6 +95,12 @@ public:
|
|||
}
|
||||
}).detach();
|
||||
}
|
||||
// Tell the underlying queue the producer thread has changed, so it does not
|
||||
// assert in debug. This should be called with the thread stopped.
|
||||
void reset_producer_thread()
|
||||
{
|
||||
msg_queue.reset_thread_ids();
|
||||
}
|
||||
private:
|
||||
#ifndef _WIN32
|
||||
const struct timespec sleep_for = {
|
||||
|
@ -128,3 +134,11 @@ void cubeb_async_log(char const * fmt, ...)
|
|||
cubeb_async_logger::get().push(msg);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void cubeb_async_log_reset_threads()
|
||||
{
|
||||
if (!g_cubeb_log_callback) {
|
||||
return;
|
||||
}
|
||||
cubeb_async_logger::get().reset_producer_thread();
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ extern "C" {
|
|||
extern cubeb_log_level g_cubeb_log_level;
|
||||
extern cubeb_log_callback g_cubeb_log_callback PRINTF_FORMAT(1, 2);
|
||||
void cubeb_async_log(const char * fmt, ...);
|
||||
void cubeb_async_log_reset_threads();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
#include "cubeb/cubeb.h"
|
||||
#include "cubeb-internal.h"
|
||||
#include "cubeb_mixer.h"
|
||||
#include "cubeb_utils.h"
|
||||
#include "cubeb_strings.h"
|
||||
#include <stdio.h>
|
||||
|
||||
#ifdef DISABLE_LIBPULSE_DLOPEN
|
||||
|
@ -102,6 +102,7 @@ struct cubeb {
|
|||
int error;
|
||||
cubeb_device_collection_changed_callback collection_changed_callback;
|
||||
void * collection_changed_user_ptr;
|
||||
cubeb_strings * device_ids;
|
||||
};
|
||||
|
||||
struct cubeb_stream {
|
||||
|
@ -127,6 +128,24 @@ enum cork_state {
|
|||
NOTIFY = 1 << 1
|
||||
};
|
||||
|
||||
static int
|
||||
intern_device_id(cubeb * ctx, char const ** id)
|
||||
{
|
||||
char const * interned;
|
||||
|
||||
assert(ctx);
|
||||
assert(id);
|
||||
|
||||
interned = cubeb_strings_intern(ctx->device_ids, *id);
|
||||
if (!interned) {
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
|
||||
*id = interned;
|
||||
|
||||
return CUBEB_OK;
|
||||
}
|
||||
|
||||
static void
|
||||
sink_info_callback(pa_context * context, const pa_sink_info * info, int eol, void * u)
|
||||
{
|
||||
|
@ -608,6 +627,10 @@ pulse_init(cubeb ** context, char const * context_name)
|
|||
|
||||
ctx->ops = &pulse_ops;
|
||||
ctx->libpulse = libpulse;
|
||||
if (cubeb_strings_init(&ctx->device_ids) != CUBEB_OK) {
|
||||
pulse_destroy(ctx);
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
|
||||
ctx->mainloop = WRAP(pa_threaded_mainloop_new)();
|
||||
ctx->default_sink_info = NULL;
|
||||
|
@ -717,6 +740,10 @@ pulse_destroy(cubeb * ctx)
|
|||
WRAP(pa_threaded_mainloop_free)(ctx->mainloop);
|
||||
}
|
||||
|
||||
if (ctx->device_ids) {
|
||||
cubeb_strings_destroy(ctx->device_ids);
|
||||
}
|
||||
|
||||
if (ctx->libpulse) {
|
||||
dlclose(ctx->libpulse);
|
||||
}
|
||||
|
@ -752,8 +779,8 @@ create_pa_stream(cubeb_stream * stm,
|
|||
assert(stm && stream_params);
|
||||
assert(&stm->input_stream == pa_stm || (&stm->output_stream == pa_stm &&
|
||||
(stream_params->layout == CUBEB_LAYOUT_UNDEFINED ||
|
||||
stream_params->layout != CUBEB_LAYOUT_UNDEFINED &&
|
||||
CUBEB_CHANNEL_LAYOUT_MAPS[stream_params->layout].channels == stream_params->channels)));
|
||||
(stream_params->layout != CUBEB_LAYOUT_UNDEFINED &&
|
||||
CUBEB_CHANNEL_LAYOUT_MAPS[stream_params->layout].channels == stream_params->channels))));
|
||||
*pa_stm = NULL;
|
||||
pa_sample_spec ss;
|
||||
ss.format = to_pulse_format(stream_params->format);
|
||||
|
@ -1203,18 +1230,25 @@ pulse_sink_info_cb(pa_context * context, const pa_sink_info * info,
|
|||
{
|
||||
pulse_dev_list_data * list_data = user_data;
|
||||
cubeb_device_info * devinfo;
|
||||
const char * prop;
|
||||
char const * prop = NULL;
|
||||
char const * device_id = NULL;
|
||||
|
||||
(void)context;
|
||||
|
||||
if (eol || info == NULL)
|
||||
return;
|
||||
|
||||
device_id = info->name;
|
||||
if (intern_device_id(list_data->context, &device_id) != CUBEB_OK) {
|
||||
assert(false);
|
||||
return;
|
||||
}
|
||||
|
||||
pulse_ensure_dev_list_data_list_size(list_data);
|
||||
devinfo = &list_data->devinfo[list_data->count];
|
||||
memset(devinfo, 0, sizeof(cubeb_device_info));
|
||||
|
||||
devinfo->device_id = strdup(info->name);
|
||||
devinfo->device_id = device_id;
|
||||
devinfo->devid = (cubeb_devid) devinfo->device_id;
|
||||
devinfo->friendly_name = strdup(info->description);
|
||||
prop = WRAP(pa_proplist_gets)(info->proplist, "sysfs.path");
|
||||
|
@ -1265,18 +1299,25 @@ pulse_source_info_cb(pa_context * context, const pa_source_info * info,
|
|||
{
|
||||
pulse_dev_list_data * list_data = user_data;
|
||||
cubeb_device_info * devinfo;
|
||||
const char * prop;
|
||||
char const * prop = NULL;
|
||||
char const * device_id = NULL;
|
||||
|
||||
(void)context;
|
||||
|
||||
if (eol)
|
||||
return;
|
||||
|
||||
device_id = info->name;
|
||||
if (intern_device_id(list_data->context, &device_id) != CUBEB_OK) {
|
||||
assert(false);
|
||||
return;
|
||||
}
|
||||
|
||||
pulse_ensure_dev_list_data_list_size(list_data);
|
||||
devinfo = &list_data->devinfo[list_data->count];
|
||||
memset(devinfo, 0, sizeof(cubeb_device_info));
|
||||
|
||||
devinfo->device_id = strdup(info->name);
|
||||
devinfo->device_id = device_id;
|
||||
devinfo->devid = (cubeb_devid) devinfo->device_id;
|
||||
devinfo->friendly_name = strdup(info->description);
|
||||
prop = WRAP(pa_proplist_gets)(info->proplist, "sysfs.path");
|
||||
|
@ -1364,6 +1405,21 @@ pulse_enumerate_devices(cubeb * context, cubeb_device_type type,
|
|||
return CUBEB_OK;
|
||||
}
|
||||
|
||||
static int
|
||||
pulse_device_collection_destroy(cubeb * ctx, cubeb_device_collection * collection)
|
||||
{
|
||||
size_t n;
|
||||
|
||||
for (n = 0; n < collection->count; n++) {
|
||||
free((void *) collection->device[n].friendly_name);
|
||||
free((void *) collection->device[n].vendor_name);
|
||||
free((void *) collection->device[n].group_id);
|
||||
}
|
||||
|
||||
free(collection->device);
|
||||
return CUBEB_OK;
|
||||
}
|
||||
|
||||
static int
|
||||
pulse_stream_get_current_device(cubeb_stream * stm, cubeb_device ** const device)
|
||||
{
|
||||
|
@ -1493,7 +1549,7 @@ static struct cubeb_ops const pulse_ops = {
|
|||
.get_preferred_sample_rate = pulse_get_preferred_sample_rate,
|
||||
.get_preferred_channel_layout = pulse_get_preferred_channel_layout,
|
||||
.enumerate_devices = pulse_enumerate_devices,
|
||||
.device_collection_destroy = cubeb_utils_default_device_collection_destroy,
|
||||
.device_collection_destroy = pulse_device_collection_destroy,
|
||||
.destroy = pulse_destroy,
|
||||
.stream_init = pulse_stream_init,
|
||||
.stream_destroy = pulse_stream_destroy,
|
||||
|
|
|
@ -226,6 +226,17 @@ public:
|
|||
{
|
||||
return storage_capacity() - 1;
|
||||
}
|
||||
/**
|
||||
* Reset the consumer and producer thread identifier, in case the thread are
|
||||
* being changed. This has to be externally synchronized. This is no-op when
|
||||
* asserts are disabled.
|
||||
*/
|
||||
void reset_thread_ids()
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
consumer_id = producer_id = std::thread::id();
|
||||
#endif
|
||||
}
|
||||
private:
|
||||
/** Return true if the ring buffer is empty.
|
||||
*
|
||||
|
|
|
@ -0,0 +1,155 @@
|
|||
/*
|
||||
* Copyright © 2011 Mozilla Foundation
|
||||
*
|
||||
* This program is made available under an ISC-style license. See the
|
||||
* accompanying file LICENSE for details.
|
||||
*/
|
||||
|
||||
#include "cubeb_strings.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define CUBEB_STRINGS_INLINE_COUNT 4
|
||||
|
||||
struct cubeb_strings {
|
||||
uint32_t size;
|
||||
uint32_t count;
|
||||
char ** data;
|
||||
char * small_store[CUBEB_STRINGS_INLINE_COUNT];
|
||||
};
|
||||
|
||||
int
|
||||
cubeb_strings_init(cubeb_strings ** strings)
|
||||
{
|
||||
cubeb_strings* strs = NULL;
|
||||
|
||||
if (!strings) {
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
|
||||
strs = calloc(1, sizeof(cubeb_strings));
|
||||
assert(strs);
|
||||
|
||||
if (!strs) {
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
|
||||
strs->size = sizeof(strs->small_store) / sizeof(strs->small_store[0]);
|
||||
strs->count = 0;
|
||||
strs->data = strs->small_store;
|
||||
|
||||
*strings = strs;
|
||||
|
||||
return CUBEB_OK;
|
||||
}
|
||||
|
||||
void
|
||||
cubeb_strings_destroy(cubeb_strings * strings)
|
||||
{
|
||||
char ** sp = NULL;
|
||||
char ** se = NULL;
|
||||
|
||||
if (!strings) {
|
||||
return;
|
||||
}
|
||||
|
||||
sp = strings->data;
|
||||
se = sp + strings->count;
|
||||
|
||||
for ( ; sp != se; sp++) {
|
||||
if (*sp) {
|
||||
free(*sp);
|
||||
}
|
||||
}
|
||||
|
||||
if (strings->data != strings->small_store) {
|
||||
free(strings->data);
|
||||
}
|
||||
|
||||
free(strings);
|
||||
}
|
||||
|
||||
/** Look for string in string storage.
|
||||
@param strings Opaque pointer to interned string storage.
|
||||
@param s String to look up.
|
||||
@retval Read-only string or NULL if not found. */
|
||||
static char const *
|
||||
cubeb_strings_lookup(cubeb_strings * strings, char const * s)
|
||||
{
|
||||
char ** sp = NULL;
|
||||
char ** se = NULL;
|
||||
|
||||
if (!strings || !s) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
sp = strings->data;
|
||||
se = sp + strings->count;
|
||||
|
||||
for ( ; sp != se; sp++) {
|
||||
if (*sp && strcmp(*sp, s) == 0) {
|
||||
return *sp;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static char const *
|
||||
cubeb_strings_push(cubeb_strings * strings, char const * s)
|
||||
{
|
||||
char * is = NULL;
|
||||
|
||||
if (strings->count == strings->size) {
|
||||
char ** new_data;
|
||||
uint32_t value_size = sizeof(char const *);
|
||||
uint32_t new_size = strings->size * 2;
|
||||
if (!new_size || value_size > (uint32_t)-1 / new_size) {
|
||||
// overflow
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (strings->small_store == strings->data) {
|
||||
// First time heap allocation.
|
||||
new_data = malloc(new_size * value_size);
|
||||
if (new_data) {
|
||||
memcpy(new_data, strings->small_store, sizeof(strings->small_store));
|
||||
}
|
||||
} else {
|
||||
new_data = realloc(strings->data, new_size * value_size);
|
||||
}
|
||||
|
||||
if (!new_data) {
|
||||
// out of memory
|
||||
return NULL;
|
||||
}
|
||||
|
||||
strings->size = new_size;
|
||||
strings->data = new_data;
|
||||
}
|
||||
|
||||
is = strdup(s);
|
||||
strings->data[strings->count++] = is;
|
||||
|
||||
return is;
|
||||
}
|
||||
|
||||
char const *
|
||||
cubeb_strings_intern(cubeb_strings * strings, char const * s)
|
||||
{
|
||||
char const * is = NULL;
|
||||
|
||||
if (!strings || !s) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
is = cubeb_strings_lookup(strings, s);
|
||||
if (is) {
|
||||
return is;
|
||||
}
|
||||
|
||||
return cubeb_strings_push(strings, s);
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* Copyright © 2011 Mozilla Foundation
|
||||
*
|
||||
* This program is made available under an ISC-style license. See the
|
||||
* accompanying file LICENSE for details.
|
||||
*/
|
||||
|
||||
#ifndef CUBEB_STRINGS_H
|
||||
#define CUBEB_STRINGS_H
|
||||
|
||||
#include "cubeb/cubeb.h"
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** Opaque handle referencing interned string storage. */
|
||||
typedef struct cubeb_strings cubeb_strings;
|
||||
|
||||
/** Initialize an interned string structure.
|
||||
@param strings An out param where an opaque pointer to the
|
||||
interned string storage will be returned.
|
||||
@retval CUBEB_OK in case of success.
|
||||
@retval CUBEB_ERROR in case of error. */
|
||||
CUBEB_EXPORT int cubeb_strings_init(cubeb_strings ** strings);
|
||||
|
||||
/** Destroy an interned string structure freeing all associated memory.
|
||||
@param strings An opaque pointer to the interned string storage to
|
||||
destroy. */
|
||||
CUBEB_EXPORT void cubeb_strings_destroy(cubeb_strings * strings);
|
||||
|
||||
/** Add string to internal storage.
|
||||
@param strings Opaque pointer to interned string storage.
|
||||
@param s String to add to storage.
|
||||
@retval CUBEB_OK
|
||||
@retval CUBEB_ERROR
|
||||
*/
|
||||
CUBEB_EXPORT char const * cubeb_strings_intern(cubeb_strings * strings, char const * s);
|
||||
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // !CUBEB_STRINGS_H
|
|
@ -1,38 +0,0 @@
|
|||
/*
|
||||
* Copyright © 2016 Mozilla Foundation
|
||||
*
|
||||
* This program is made available under an ISC-style license. See the
|
||||
* accompanying file LICENSE for details.
|
||||
*/
|
||||
|
||||
#include "cubeb_utils.h"
|
||||
#include "cubeb_assert.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
static void
|
||||
device_info_destroy(cubeb_device_info * info)
|
||||
{
|
||||
XASSERT(info);
|
||||
|
||||
free((void *) info->device_id);
|
||||
free((void *) info->friendly_name);
|
||||
free((void *) info->group_id);
|
||||
free((void *) info->vendor_name);
|
||||
}
|
||||
|
||||
int
|
||||
cubeb_utils_default_device_collection_destroy(cubeb * context,
|
||||
cubeb_device_collection * collection)
|
||||
{
|
||||
uint32_t i;
|
||||
XASSERT(collection);
|
||||
|
||||
(void) context;
|
||||
|
||||
for (i = 0; i < collection->count; i++)
|
||||
device_info_destroy(&collection->device[i]);
|
||||
|
||||
free(collection->device);
|
||||
return CUBEB_OK;
|
||||
}
|
|
@ -336,17 +336,4 @@ private:
|
|||
using auto_lock = std::lock_guard<owned_critical_section>;
|
||||
#endif // __cplusplus
|
||||
|
||||
// C language helpers
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
int cubeb_utils_default_device_collection_destroy(cubeb * context,
|
||||
cubeb_device_collection * collection);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* CUBEB_UTILS */
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#include "cubeb-internal.h"
|
||||
#include "cubeb_mixer.h"
|
||||
#include "cubeb_resampler.h"
|
||||
#include "cubeb_strings.h"
|
||||
#include "cubeb_utils.h"
|
||||
|
||||
#ifndef PKEY_Device_FriendlyName
|
||||
|
@ -173,6 +174,7 @@ static std::unique_ptr<wchar_t const []> utf8_to_wstr(char const * str);
|
|||
|
||||
struct cubeb {
|
||||
cubeb_ops const * ops = &wasapi_ops;
|
||||
cubeb_strings * device_ids;
|
||||
};
|
||||
|
||||
class wasapi_endpoint_notification_client;
|
||||
|
@ -382,6 +384,23 @@ private:
|
|||
};
|
||||
|
||||
namespace {
|
||||
|
||||
char const *
|
||||
intern_device_id(cubeb * ctx, wchar_t const * id)
|
||||
{
|
||||
XASSERT(id);
|
||||
|
||||
char const * tmp = wstr_to_utf8(id);
|
||||
if (!tmp)
|
||||
return nullptr;
|
||||
|
||||
char const * interned = cubeb_strings_intern(ctx->device_ids, tmp);
|
||||
|
||||
free((void *) tmp);
|
||||
|
||||
return interned;
|
||||
}
|
||||
|
||||
bool has_input(cubeb_stream * stm)
|
||||
{
|
||||
return stm->input_stream_params.rate != 0;
|
||||
|
@ -427,25 +446,25 @@ channel_layout_to_mask(cubeb_channel_layout layout)
|
|||
// allocate it in stack, or it will be created and removed repeatedly.
|
||||
// Use static to allocate this local variable in data space instead of stack.
|
||||
static DWORD map[CUBEB_LAYOUT_MAX] = {
|
||||
0, // CUBEB_LAYOUT_UNDEFINED
|
||||
MASK_DUAL_MONO, // CUBEB_LAYOUT_DUAL_MONO
|
||||
MASK_DUAL_MONO_LFE, // CUBEB_LAYOUT_DUAL_MONO_LFE
|
||||
MASK_MONO, // CUBEB_LAYOUT_MONO
|
||||
MASK_MONO_LFE, // CUBEB_LAYOUT_MONO_LFE
|
||||
MASK_STEREO, // CUBEB_LAYOUT_STEREO
|
||||
MASK_STEREO_LFE, // CUBEB_LAYOUT_STEREO_LFE
|
||||
MASK_3F, // CUBEB_LAYOUT_3F
|
||||
MASK_3F_LFE, // CUBEB_LAYOUT_3F_LFE
|
||||
MASK_2F1, // CUBEB_LAYOUT_2F1
|
||||
MASK_2F1_LFE, // CUBEB_LAYOUT_2F1_LFE
|
||||
MASK_3F1, // CUBEB_LAYOUT_3F1
|
||||
MASK_3F1_LFE, // CUBEB_LAYOUT_3F1_LFE
|
||||
MASK_2F2, // CUBEB_LAYOUT_2F2
|
||||
MASK_2F2_LFE, // CUBEB_LAYOUT_2F2_LFE
|
||||
MASK_3F2, // CUBEB_LAYOUT_3F2
|
||||
MASK_3F2_LFE, // CUBEB_LAYOUT_3F2_LFE
|
||||
MASK_3F3R_LFE, // CUBEB_LAYOUT_3F3R_LFE
|
||||
MASK_3F4_LFE, // CUBEB_LAYOUT_3F4_LFE
|
||||
KSAUDIO_SPEAKER_DIRECTOUT, // CUBEB_LAYOUT_UNDEFINED
|
||||
MASK_DUAL_MONO, // CUBEB_LAYOUT_DUAL_MONO
|
||||
MASK_DUAL_MONO_LFE, // CUBEB_LAYOUT_DUAL_MONO_LFE
|
||||
MASK_MONO, // CUBEB_LAYOUT_MONO
|
||||
MASK_MONO_LFE, // CUBEB_LAYOUT_MONO_LFE
|
||||
MASK_STEREO, // CUBEB_LAYOUT_STEREO
|
||||
MASK_STEREO_LFE, // CUBEB_LAYOUT_STEREO_LFE
|
||||
MASK_3F, // CUBEB_LAYOUT_3F
|
||||
MASK_3F_LFE, // CUBEB_LAYOUT_3F_LFE
|
||||
MASK_2F1, // CUBEB_LAYOUT_2F1
|
||||
MASK_2F1_LFE, // CUBEB_LAYOUT_2F1_LFE
|
||||
MASK_3F1, // CUBEB_LAYOUT_3F1
|
||||
MASK_3F1_LFE, // CUBEB_LAYOUT_3F1_LFE
|
||||
MASK_2F2, // CUBEB_LAYOUT_2F2
|
||||
MASK_2F2_LFE, // CUBEB_LAYOUT_2F2_LFE
|
||||
MASK_3F2, // CUBEB_LAYOUT_3F2
|
||||
MASK_3F2_LFE, // CUBEB_LAYOUT_3F2_LFE
|
||||
MASK_3F3R_LFE, // CUBEB_LAYOUT_3F3R_LFE
|
||||
MASK_3F4_LFE, // CUBEB_LAYOUT_3F4_LFE
|
||||
};
|
||||
return map[layout];
|
||||
}
|
||||
|
@ -1153,6 +1172,10 @@ int wasapi_init(cubeb ** context, char const * context_name)
|
|||
cubeb * ctx = new cubeb();
|
||||
|
||||
ctx->ops = &wasapi_ops;
|
||||
if (cubeb_strings_init(&ctx->device_ids) != CUBEB_OK) {
|
||||
free(ctx);
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
|
||||
*context = ctx;
|
||||
|
||||
|
@ -1220,6 +1243,10 @@ bool stop_and_join_render_thread(cubeb_stream * stm)
|
|||
|
||||
void wasapi_destroy(cubeb * context)
|
||||
{
|
||||
if (context->device_ids) {
|
||||
cubeb_strings_destroy(context->device_ids);
|
||||
}
|
||||
|
||||
delete context;
|
||||
}
|
||||
|
||||
|
@ -1407,8 +1434,6 @@ waveformatex_update_derived_properties(WAVEFORMATEX * format)
|
|||
static void
|
||||
handle_channel_layout(cubeb_stream * stm, EDataFlow direction, com_heap_ptr<WAVEFORMATEX> & mix_format, const cubeb_stream_params * stream_params)
|
||||
{
|
||||
// The CUBEB_LAYOUT_UNDEFINED can be used for input but it's not allowed for output.
|
||||
XASSERT(direction == eCapture || stream_params->layout != CUBEB_LAYOUT_UNDEFINED);
|
||||
com_ptr<IAudioClient> & audio_client = (direction == eRender) ? stm->output_client : stm->input_client;
|
||||
XASSERT(audio_client);
|
||||
/* The docs say that GetMixFormat is always of type WAVEFORMATEXTENSIBLE [1],
|
||||
|
@ -1972,6 +1997,7 @@ int wasapi_stream_start(cubeb_stream * stm)
|
|||
return CUBEB_ERROR;
|
||||
}
|
||||
|
||||
cubeb_async_log_reset_threads();
|
||||
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.");
|
||||
|
@ -2174,8 +2200,8 @@ wasapi_is_default_device(EDataFlow flow, ERole role, LPCWSTR device_id,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
wasapi_create_device(cubeb_device_info * ret, IMMDeviceEnumerator * enumerator, IMMDevice * dev)
|
||||
int
|
||||
wasapi_create_device(cubeb * ctx, cubeb_device_info& ret, IMMDeviceEnumerator * enumerator, IMMDevice * dev)
|
||||
{
|
||||
com_ptr<IMMEndpoint> endpoint;
|
||||
com_ptr<IMMDevice> devnode;
|
||||
|
@ -2204,19 +2230,23 @@ wasapi_create_device(cubeb_device_info * ret, IMMDeviceEnumerator * enumerator,
|
|||
if (FAILED(hr)) return CUBEB_ERROR;
|
||||
com_heap_ptr<wchar_t> device_id(tmp);
|
||||
|
||||
char const * device_id_intern = intern_device_id(ctx, device_id.get());
|
||||
if (!device_id_intern) {
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
|
||||
hr = dev->OpenPropertyStore(STGM_READ, propstore.receive());
|
||||
if (FAILED(hr)) return CUBEB_ERROR;
|
||||
|
||||
hr = dev->GetState(&state);
|
||||
if (FAILED(hr)) return CUBEB_ERROR;
|
||||
|
||||
XASSERT(ret);
|
||||
ret->device_id = wstr_to_utf8(device_id.get());
|
||||
ret->devid = reinterpret_cast<cubeb_devid>(ret->device_id);
|
||||
ret.device_id = device_id_intern;
|
||||
ret.devid = reinterpret_cast<cubeb_devid>(ret.device_id);
|
||||
prop_variant namevar;
|
||||
hr = propstore->GetValue(PKEY_Device_FriendlyName, &namevar);
|
||||
if (SUCCEEDED(hr))
|
||||
ret->friendly_name = wstr_to_utf8(namevar.pwszVal);
|
||||
ret.friendly_name = wstr_to_utf8(namevar.pwszVal);
|
||||
|
||||
devnode = wasapi_get_device_node(enumerator, dev);
|
||||
if (devnode) {
|
||||
|
@ -2227,60 +2257,60 @@ wasapi_create_device(cubeb_device_info * ret, IMMDeviceEnumerator * enumerator,
|
|||
prop_variant instancevar;
|
||||
hr = ps->GetValue(PKEY_Device_InstanceId, &instancevar);
|
||||
if (SUCCEEDED(hr)) {
|
||||
ret->group_id = wstr_to_utf8(instancevar.pwszVal);
|
||||
ret.group_id = wstr_to_utf8(instancevar.pwszVal);
|
||||
}
|
||||
}
|
||||
|
||||
ret->preferred = CUBEB_DEVICE_PREF_NONE;
|
||||
ret.preferred = CUBEB_DEVICE_PREF_NONE;
|
||||
if (wasapi_is_default_device(flow, eConsole, device_id.get(), enumerator))
|
||||
ret->preferred = (cubeb_device_pref)(ret->preferred | CUBEB_DEVICE_PREF_MULTIMEDIA);
|
||||
ret.preferred = (cubeb_device_pref)(ret.preferred | CUBEB_DEVICE_PREF_MULTIMEDIA);
|
||||
if (wasapi_is_default_device(flow, eCommunications, device_id.get(), enumerator))
|
||||
ret->preferred = (cubeb_device_pref)(ret->preferred | CUBEB_DEVICE_PREF_VOICE);
|
||||
ret.preferred = (cubeb_device_pref)(ret.preferred | CUBEB_DEVICE_PREF_VOICE);
|
||||
if (wasapi_is_default_device(flow, eConsole, device_id.get(), enumerator))
|
||||
ret->preferred = (cubeb_device_pref)(ret->preferred | CUBEB_DEVICE_PREF_NOTIFICATION);
|
||||
ret.preferred = (cubeb_device_pref)(ret.preferred | CUBEB_DEVICE_PREF_NOTIFICATION);
|
||||
|
||||
if (flow == eRender) ret->type = CUBEB_DEVICE_TYPE_OUTPUT;
|
||||
else if (flow == eCapture) ret->type = CUBEB_DEVICE_TYPE_INPUT;
|
||||
if (flow == eRender) ret.type = CUBEB_DEVICE_TYPE_OUTPUT;
|
||||
else if (flow == eCapture) ret.type = CUBEB_DEVICE_TYPE_INPUT;
|
||||
switch (state) {
|
||||
case DEVICE_STATE_ACTIVE:
|
||||
ret->state = CUBEB_DEVICE_STATE_ENABLED;
|
||||
ret.state = CUBEB_DEVICE_STATE_ENABLED;
|
||||
break;
|
||||
case DEVICE_STATE_UNPLUGGED:
|
||||
ret->state = CUBEB_DEVICE_STATE_UNPLUGGED;
|
||||
ret.state = CUBEB_DEVICE_STATE_UNPLUGGED;
|
||||
break;
|
||||
default:
|
||||
ret->state = CUBEB_DEVICE_STATE_DISABLED;
|
||||
ret.state = CUBEB_DEVICE_STATE_DISABLED;
|
||||
break;
|
||||
};
|
||||
|
||||
ret->format = static_cast<cubeb_device_fmt>(CUBEB_DEVICE_FMT_F32NE | CUBEB_DEVICE_FMT_S16NE);
|
||||
ret->default_format = CUBEB_DEVICE_FMT_F32NE;
|
||||
ret.format = static_cast<cubeb_device_fmt>(CUBEB_DEVICE_FMT_F32NE | CUBEB_DEVICE_FMT_S16NE);
|
||||
ret.default_format = CUBEB_DEVICE_FMT_F32NE;
|
||||
prop_variant fmtvar;
|
||||
hr = propstore->GetValue(PKEY_AudioEngine_DeviceFormat, &fmtvar);
|
||||
if (SUCCEEDED(hr) && fmtvar.vt == VT_BLOB) {
|
||||
if (fmtvar.blob.cbSize == sizeof(PCMWAVEFORMAT)) {
|
||||
const PCMWAVEFORMAT * pcm = reinterpret_cast<const PCMWAVEFORMAT *>(fmtvar.blob.pBlobData);
|
||||
|
||||
ret->max_rate = ret->min_rate = ret->default_rate = pcm->wf.nSamplesPerSec;
|
||||
ret->max_channels = pcm->wf.nChannels;
|
||||
ret.max_rate = ret.min_rate = ret.default_rate = pcm->wf.nSamplesPerSec;
|
||||
ret.max_channels = pcm->wf.nChannels;
|
||||
} else if (fmtvar.blob.cbSize >= sizeof(WAVEFORMATEX)) {
|
||||
WAVEFORMATEX* wfx = reinterpret_cast<WAVEFORMATEX*>(fmtvar.blob.pBlobData);
|
||||
|
||||
if (fmtvar.blob.cbSize >= sizeof(WAVEFORMATEX) + wfx->cbSize ||
|
||||
wfx->wFormatTag == WAVE_FORMAT_PCM) {
|
||||
ret->max_rate = ret->min_rate = ret->default_rate = wfx->nSamplesPerSec;
|
||||
ret->max_channels = wfx->nChannels;
|
||||
ret.max_rate = ret.min_rate = ret.default_rate = wfx->nSamplesPerSec;
|
||||
ret.max_channels = wfx->nChannels;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (SUCCEEDED(dev->Activate(__uuidof(IAudioClient), CLSCTX_INPROC_SERVER, NULL, client.receive_vpp())) &&
|
||||
SUCCEEDED(client->GetDevicePeriod(&def_period, &min_period))) {
|
||||
ret->latency_lo = hns_to_frames(ret->default_rate, min_period);
|
||||
ret->latency_hi = hns_to_frames(ret->default_rate, def_period);
|
||||
ret.latency_lo = hns_to_frames(ret.default_rate, min_period);
|
||||
ret.latency_hi = hns_to_frames(ret.default_rate, def_period);
|
||||
} else {
|
||||
ret->latency_lo = 0;
|
||||
ret->latency_hi = 0;
|
||||
ret.latency_lo = 0;
|
||||
ret.latency_hi = 0;
|
||||
}
|
||||
|
||||
return CUBEB_OK;
|
||||
|
@ -2323,11 +2353,11 @@ wasapi_enumerate_devices(cubeb * context, cubeb_device_type type,
|
|||
LOG("IMMDeviceCollection::GetCount() failed: %lx", hr);
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
cubeb_device_info * devices =
|
||||
(cubeb_device_info *) calloc(cc, sizeof(cubeb_device_info));
|
||||
if (!devices) {
|
||||
cubeb_device_info * devices = new cubeb_device_info[cc];
|
||||
if (!devices)
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
|
||||
PodZero(devices, cc);
|
||||
out->count = 0;
|
||||
for (i = 0; i < cc; i++) {
|
||||
com_ptr<IMMDevice> dev;
|
||||
|
@ -2336,8 +2366,8 @@ wasapi_enumerate_devices(cubeb * context, cubeb_device_type type,
|
|||
LOG("IMMDeviceCollection::Item(%u) failed: %lx", i-1, hr);
|
||||
continue;
|
||||
}
|
||||
auto cur = &devices[out->count];
|
||||
if (wasapi_create_device(cur, enumerator.get(), dev.get()) == CUBEB_OK) {
|
||||
if (wasapi_create_device(context, devices[out->count],
|
||||
enumerator.get(), dev.get()) == CUBEB_OK) {
|
||||
out->count += 1;
|
||||
}
|
||||
}
|
||||
|
@ -2346,6 +2376,21 @@ wasapi_enumerate_devices(cubeb * context, cubeb_device_type type,
|
|||
return CUBEB_OK;
|
||||
}
|
||||
|
||||
static int
|
||||
wasapi_device_collection_destroy(cubeb * /*ctx*/, cubeb_device_collection * collection)
|
||||
{
|
||||
XASSERT(collection);
|
||||
|
||||
for (size_t n = 0; n < collection->count; n++) {
|
||||
cubeb_device_info& dev = collection->device[n];
|
||||
delete [] dev.friendly_name;
|
||||
delete [] dev.group_id;
|
||||
}
|
||||
|
||||
delete [] collection->device;
|
||||
return CUBEB_OK;
|
||||
}
|
||||
|
||||
cubeb_ops const wasapi_ops = {
|
||||
/*.init =*/ wasapi_init,
|
||||
/*.get_backend_id =*/ wasapi_get_backend_id,
|
||||
|
@ -2354,7 +2399,7 @@ cubeb_ops const wasapi_ops = {
|
|||
/*.get_preferred_sample_rate =*/ wasapi_get_preferred_sample_rate,
|
||||
/*.get_preferred_channel_layout =*/ wasapi_get_preferred_channel_layout,
|
||||
/*.enumerate_devices =*/ wasapi_enumerate_devices,
|
||||
/*.device_collection_destroy =*/ cubeb_utils_default_device_collection_destroy,
|
||||
/*.device_collection_destroy =*/ wasapi_device_collection_destroy,
|
||||
/*.destroy =*/ wasapi_destroy,
|
||||
/*.stream_init =*/ wasapi_stream_init,
|
||||
/*.stream_destroy =*/ wasapi_stream_destroy,
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
#include <math.h>
|
||||
#include "cubeb/cubeb.h"
|
||||
#include "cubeb-internal.h"
|
||||
#include "cubeb_utils.h"
|
||||
|
||||
/* This is missing from the MinGW headers. Use a safe fallback. */
|
||||
#if !defined(MEMORY_ALLOCATION_ALIGNMENT)
|
||||
|
@ -1017,6 +1016,26 @@ winmm_enumerate_devices(cubeb * context, cubeb_device_type type,
|
|||
return CUBEB_OK;
|
||||
}
|
||||
|
||||
static int
|
||||
winmm_device_collection_destroy(cubeb * ctx,
|
||||
cubeb_device_collection * collection)
|
||||
{
|
||||
uint32_t i;
|
||||
XASSERT(collection);
|
||||
|
||||
(void) ctx;
|
||||
|
||||
for (i = 0; i < collection->count; i++) {
|
||||
free((void *) collection->device[i].device_id);
|
||||
free((void *) collection->device[i].friendly_name);
|
||||
free((void *) collection->device[i].group_id);
|
||||
free((void *) collection->device[i].vendor_name);
|
||||
}
|
||||
|
||||
free(collection->device);
|
||||
return CUBEB_OK;
|
||||
}
|
||||
|
||||
static struct cubeb_ops const winmm_ops = {
|
||||
/*.init =*/ winmm_init,
|
||||
/*.get_backend_id =*/ winmm_get_backend_id,
|
||||
|
@ -1025,7 +1044,7 @@ static struct cubeb_ops const winmm_ops = {
|
|||
/*.get_preferred_sample_rate =*/ winmm_get_preferred_sample_rate,
|
||||
/*.get_preferred_channel_layout =*/ NULL,
|
||||
/*.enumerate_devices =*/ winmm_enumerate_devices,
|
||||
/*.device_collection_destroy =*/ cubeb_utils_default_device_collection_destroy,
|
||||
/*.device_collection_destroy =*/ winmm_device_collection_destroy,
|
||||
/*.destroy =*/ winmm_destroy,
|
||||
/*.stream_init =*/ winmm_stream_init,
|
||||
/*.stream_destroy =*/ winmm_stream_destroy,
|
||||
|
|
|
@ -13,6 +13,7 @@ SOURCES += [
|
|||
'cubeb_log.cpp',
|
||||
'cubeb_mixer.cpp',
|
||||
'cubeb_panner.cpp',
|
||||
'cubeb_strings.c',
|
||||
'cubeb_utils.c'
|
||||
]
|
||||
|
||||
|
|
|
@ -30,7 +30,8 @@ cp $1/src/cubeb_resampler_internal.h src
|
|||
cp $1/src/cubeb_ring_array.h src
|
||||
cp $1/src/cubeb_ringbuffer.h src
|
||||
cp $1/src/cubeb_sndio.c src
|
||||
cp $1/src/cubeb_utils.c src
|
||||
cp $1/src/cubeb_strings.c src
|
||||
cp $1/src/cubeb_strings.h src
|
||||
cp $1/src/cubeb_utils.h src
|
||||
cp $1/src/cubeb_utils_unix.h src
|
||||
cp $1/src/cubeb_utils_win.h src
|
||||
|
|
Загрузка…
Ссылка в новой задаче