mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Backed out changeset 3f51676191a6 (bug 1225703) for cpp unittest failures on at least Windows CLOSED TREE
This commit is contained in:
parent
6710bc74bc
commit
757fdbb644
@ -4,5 +4,3 @@ Michael Wu <mwu@mozilla.com>
|
||||
Paul Adenot <paul@paul.cx>
|
||||
David Richards <drichards@mozilla.com>
|
||||
Sebastien Alaiwan <sebastien.alaiwan@gmail.com>
|
||||
KO Myung-Hun <komh@chollian.net>
|
||||
Haakon Sporsheim <haakon.sporsheim@telenordigital.com>
|
||||
|
@ -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 77745f635240a5a85a2464bd3758c231045ac3d7.
|
||||
The git commit ID used was 588c82be50ffee59b7fab71b56e6081a5a89301c.
|
||||
|
@ -152,74 +152,6 @@ enum {
|
||||
CUBEB_ERROR_NOT_SUPPORTED = -4 /**< Optional function not implemented in current backend. */
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
CUBEB_DEVICE_TYPE_UNKNOWN,
|
||||
CUBEB_DEVICE_TYPE_INPUT,
|
||||
CUBEB_DEVICE_TYPE_OUTPUT
|
||||
} cubeb_device_type;
|
||||
|
||||
typedef enum {
|
||||
CUBEB_DEVICE_STATE_DISABLED,
|
||||
CUBEB_DEVICE_STATE_UNPLUGGED,
|
||||
CUBEB_DEVICE_STATE_ENABLED
|
||||
} cubeb_device_state;
|
||||
|
||||
typedef void * cubeb_devid;
|
||||
|
||||
typedef enum {
|
||||
CUBEB_DEVICE_FMT_S16LE = 0x0010,
|
||||
CUBEB_DEVICE_FMT_S16BE = 0x0020,
|
||||
CUBEB_DEVICE_FMT_F32LE = 0x1000,
|
||||
CUBEB_DEVICE_FMT_F32BE = 0x2000
|
||||
} cubeb_device_fmt;
|
||||
|
||||
#if defined(WORDS_BIGENDIAN) || defined(__BIG_ENDIAN__)
|
||||
#define CUBEB_DEVICE_FMT_S16NE CUBEB_DEVICE_FMT_S16BE
|
||||
#define CUBEB_DEVICE_FMT_F32NE CUBEB_DEVICE_FMT_F32BE
|
||||
#else
|
||||
#define CUBEB_DEVICE_FMT_S16NE CUBEB_DEVICE_FMT_S16LE
|
||||
#define CUBEB_DEVICE_FMT_F32NE CUBEB_DEVICE_FMT_F32LE
|
||||
#endif
|
||||
#define CUBEB_DEVICE_FMT_S16_MASK (CUBEB_DEVICE_FMT_S16LE | CUBEB_DEVICE_FMT_S16BE)
|
||||
#define CUBEB_DEVICE_FMT_F32_MASK (CUBEB_DEVICE_FMT_F32LE | CUBEB_DEVICE_FMT_F32BE)
|
||||
#define CUBEB_DEVICE_FMT_ALL (CUBEB_DEVICE_FMT_S16_MASK | CUBEB_DEVICE_FMT_F32_MASK)
|
||||
|
||||
typedef enum {
|
||||
CUBEB_DEVICE_PREF_NONE = 0x00,
|
||||
CUBEB_DEVICE_PREF_MULTIMEDIA = 0x01,
|
||||
CUBEB_DEVICE_PREF_VOICE = 0x02,
|
||||
CUBEB_DEVICE_PREF_NOTIFICATION = 0x04,
|
||||
CUBEB_DEVICE_PREF_ALL = 0x0F
|
||||
} cubeb_device_pref;
|
||||
|
||||
typedef struct {
|
||||
cubeb_devid devid; /* Device identifier handle */
|
||||
char * device_id; /* Device identifier which might be presented in a UI */
|
||||
char * friendly_name; /* Friendly device name which might be presented in a UI */
|
||||
char * group_id; /* Two devices have the same group identifier if they belong to the same physical device; for example a headset and microphone. */
|
||||
char * vendor_name; /* Optional vendor name, may be NULL */
|
||||
|
||||
cubeb_device_type type; /* Type of device (Input/Output) */
|
||||
cubeb_device_state state; /* State of device disabled/enabled/unplugged */
|
||||
cubeb_device_pref preferred;/* Preferred device */
|
||||
|
||||
cubeb_device_fmt format; /* Sample format supported */
|
||||
cubeb_device_fmt default_format;
|
||||
unsigned int max_channels; /* Channels */
|
||||
unsigned int default_rate; /* Default/Preferred sample rate */
|
||||
unsigned int max_rate; /* Maximum sample rate supported */
|
||||
unsigned int min_rate; /* Minimum sample rate supported */
|
||||
|
||||
unsigned int latency_lo_ms; /* Lowest possible latency in milliseconds */
|
||||
unsigned int latency_hi_ms; /* Higest possible latency in milliseconds */
|
||||
} cubeb_device_info;
|
||||
|
||||
/** Device collection. */
|
||||
typedef struct {
|
||||
uint32_t count; /**< Device count in collection. */
|
||||
cubeb_device_info * device[1]; /**< Array of pointers to device info. */
|
||||
} cubeb_device_collection;
|
||||
|
||||
/** User supplied data callback.
|
||||
@param stream
|
||||
@param user_ptr
|
||||
@ -247,12 +179,6 @@ typedef void (* cubeb_state_callback)(cubeb_stream * stream,
|
||||
* @param user */
|
||||
typedef void (* cubeb_device_changed_callback)(void * user_ptr);
|
||||
|
||||
/**
|
||||
* User supplied callback called when the underlying device collection changed.
|
||||
* @param context
|
||||
* @param user_ptr */
|
||||
typedef void (* cubeb_device_collection_changed_callback)(cubeb * context, void * user_ptr);
|
||||
|
||||
/** Initialize an application context. This will perform any library or
|
||||
application scoped initialization.
|
||||
@param context
|
||||
@ -411,41 +337,6 @@ int cubeb_stream_device_destroy(cubeb_stream * stream,
|
||||
int cubeb_stream_register_device_changed_callback(cubeb_stream * stream,
|
||||
cubeb_device_changed_callback device_changed_callback);
|
||||
|
||||
/** Returns enumerated devices.
|
||||
@param context
|
||||
@param devtype device type to include
|
||||
@param collection output collection. Must be destroyed with cubeb_device_collection_destroy
|
||||
@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);
|
||||
|
||||
/** Destroy a cubeb_device_collection.
|
||||
@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);
|
||||
|
||||
/** 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);
|
||||
|
||||
/** Registers a callback which is called when the system detects
|
||||
a new device or a device is removed.
|
||||
@param context
|
||||
@param callback a function called whenever the system device list changes.
|
||||
Passing NULL allow to unregister a function
|
||||
@param user_ptr pointer to user specified data which will be present in
|
||||
subsequent callbacks.
|
||||
@retval CUBEB_ERROR_NOT_SUPPORTED */
|
||||
int cubeb_register_device_collection_changed(cubeb * context,
|
||||
cubeb_device_collection_changed_callback callback,
|
||||
void * user_ptr);
|
||||
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
#endif
|
||||
|
@ -8,8 +8,6 @@
|
||||
#define CUBEB_INTERNAL_0eb56756_4e20_4404_a76d_42bf88cd15a5
|
||||
|
||||
#include "cubeb/cubeb.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
struct cubeb_ops {
|
||||
int (* init)(cubeb ** context, char const * context_name);
|
||||
@ -19,8 +17,6 @@ struct cubeb_ops {
|
||||
cubeb_stream_params params,
|
||||
uint32_t * latency_ms);
|
||||
int (* get_preferred_sample_rate)(cubeb * context, uint32_t * rate);
|
||||
int (* enumerate_devices)(cubeb * context, cubeb_device_type type,
|
||||
cubeb_device_collection ** collection);
|
||||
void (* destroy)(cubeb * context);
|
||||
int (* stream_init)(cubeb * context, cubeb_stream ** stream, char const * stream_name,
|
||||
cubeb_stream_params stream_params, unsigned int latency,
|
||||
@ -40,6 +36,7 @@ struct cubeb_ops {
|
||||
cubeb_device * device);
|
||||
int (* stream_register_device_changed_callback)(cubeb_stream * stream,
|
||||
cubeb_device_changed_callback device_changed_callback);
|
||||
|
||||
};
|
||||
|
||||
#define XASSERT(expr) do { \
|
||||
|
@ -7,7 +7,6 @@
|
||||
#undef NDEBUG
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#if defined(HAVE_CONFIG_H)
|
||||
#include "config.h"
|
||||
#endif
|
||||
@ -57,9 +56,6 @@ int opensl_init(cubeb ** context, char const * context_name);
|
||||
#if defined(USE_AUDIOTRACK)
|
||||
int audiotrack_init(cubeb ** context, char const * context_name);
|
||||
#endif
|
||||
#if defined(USE_KAI)
|
||||
int kai_init(cubeb ** context, char const * context_name);
|
||||
#endif
|
||||
|
||||
int
|
||||
validate_stream_params(cubeb_stream_params stream_params)
|
||||
@ -93,12 +89,12 @@ int
|
||||
cubeb_init(cubeb ** context, char const * context_name)
|
||||
{
|
||||
int (* init[])(cubeb **, char const *) = {
|
||||
#if defined(USE_JACK)
|
||||
jack_init,
|
||||
#endif
|
||||
#if defined(USE_PULSE)
|
||||
pulse_init,
|
||||
#endif
|
||||
#if defined(USE_JACK)
|
||||
jack_init,
|
||||
#endif
|
||||
#if defined(USE_ALSA)
|
||||
alsa_init,
|
||||
#endif
|
||||
@ -125,9 +121,6 @@ cubeb_init(cubeb ** context, char const * context_name)
|
||||
#endif
|
||||
#if defined(USE_AUDIOTRACK)
|
||||
audiotrack_init,
|
||||
#endif
|
||||
#if defined(USE_KAI)
|
||||
kai_init,
|
||||
#endif
|
||||
};
|
||||
int i;
|
||||
@ -363,50 +356,3 @@ int cubeb_stream_register_device_changed_callback(cubeb_stream * stream,
|
||||
|
||||
return stream->context->ops->stream_register_device_changed_callback(stream, device_changed_callback);
|
||||
}
|
||||
|
||||
int cubeb_enumerate_devices(cubeb * context,
|
||||
cubeb_device_type devtype,
|
||||
cubeb_device_collection ** collection)
|
||||
{
|
||||
if ((devtype & (CUBEB_DEVICE_TYPE_INPUT | CUBEB_DEVICE_TYPE_OUTPUT)) == 0)
|
||||
return CUBEB_ERROR_INVALID_PARAMETER;
|
||||
if (collection == NULL)
|
||||
return CUBEB_ERROR_INVALID_PARAMETER;
|
||||
if (!context->ops->enumerate_devices)
|
||||
return CUBEB_ERROR_NOT_SUPPORTED;
|
||||
|
||||
return context->ops->enumerate_devices(context, devtype, collection);
|
||||
}
|
||||
|
||||
int cubeb_device_collection_destroy(cubeb_device_collection * collection)
|
||||
{
|
||||
uint32_t i;
|
||||
|
||||
if (collection == NULL)
|
||||
return CUBEB_ERROR_INVALID_PARAMETER;
|
||||
|
||||
for (i = 0; i < collection->count; i++)
|
||||
cubeb_device_info_destroy(collection->device[i]);
|
||||
|
||||
free(collection);
|
||||
return CUBEB_OK;
|
||||
}
|
||||
|
||||
int cubeb_device_info_destroy(cubeb_device_info * info)
|
||||
{
|
||||
free(info->device_id);
|
||||
free(info->friendly_name);
|
||||
free(info->group_id);
|
||||
free(info->vendor_name);
|
||||
|
||||
free(info);
|
||||
return CUBEB_OK;
|
||||
}
|
||||
|
||||
int cubeb_register_device_collection_changed(cubeb * context,
|
||||
cubeb_device_collection_changed_callback callback,
|
||||
void * user_ptr)
|
||||
{
|
||||
return CUBEB_ERROR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
|
@ -963,7 +963,7 @@ alsa_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate) {
|
||||
|
||||
/* get a pcm, disabling resampling, so we get a rate the
|
||||
* hardware/dmix/pulse/etc. supports. */
|
||||
r = snd_pcm_open(&pcm, CUBEB_ALSA_PCM_NAME, SND_PCM_STREAM_PLAYBACK | SND_PCM_NO_AUTO_RESAMPLE, 0);
|
||||
r = snd_pcm_open(&pcm, "default", SND_PCM_STREAM_PLAYBACK | SND_PCM_NO_AUTO_RESAMPLE, 0);
|
||||
if (r < 0) {
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
@ -1116,7 +1116,6 @@ static struct cubeb_ops const alsa_ops = {
|
||||
.get_max_channel_count = alsa_get_max_channel_count,
|
||||
.get_min_latency = alsa_get_min_latency,
|
||||
.get_preferred_sample_rate = alsa_get_preferred_sample_rate,
|
||||
.enumerate_devices = NULL,
|
||||
.destroy = alsa_destroy,
|
||||
.stream_init = alsa_stream_init,
|
||||
.stream_destroy = alsa_stream_destroy,
|
||||
|
@ -415,7 +415,6 @@ static struct cubeb_ops const audiotrack_ops = {
|
||||
.get_max_channel_count = audiotrack_get_max_channel_count,
|
||||
.get_min_latency = audiotrack_get_min_latency,
|
||||
.get_preferred_sample_rate = audiotrack_get_preferred_sample_rate,
|
||||
.enumerate_devices = NULL,
|
||||
.destroy = audiotrack_destroy,
|
||||
.stream_init = audiotrack_stream_init,
|
||||
.stream_destroy = audiotrack_stream_destroy,
|
||||
|
@ -159,10 +159,7 @@ audiounit_output_callback(void * user_ptr, AudioUnitRenderActionFlags * flags,
|
||||
pthread_mutex_unlock(&stm->mutex);
|
||||
|
||||
if (stm->sample_spec.mChannelsPerFrame == 2) {
|
||||
if (stm->sample_spec.mFormatFlags & kAudioFormatFlagIsFloat)
|
||||
cubeb_pan_stereo_buffer_float((float*)buf, got, panning);
|
||||
else if (stm->sample_spec.mFormatFlags & kAudioFormatFlagIsSignedInteger)
|
||||
cubeb_pan_stereo_buffer_int((short*)buf, got, panning);
|
||||
cubeb_pan_stereo_buffer_float((float*)buf, got, panning);
|
||||
}
|
||||
|
||||
return noErr;
|
||||
@ -403,28 +400,6 @@ audiounit_get_acceptable_latency_range(AudioValueRange * latency_range)
|
||||
}
|
||||
#endif /* !TARGET_OS_IPHONE */
|
||||
|
||||
static AudioObjectID
|
||||
audiounit_get_default_device_id(cubeb_device_type type)
|
||||
{
|
||||
AudioObjectPropertyAddress adr = { 0, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
|
||||
AudioDeviceID devid;
|
||||
UInt32 size;
|
||||
|
||||
if (type == CUBEB_DEVICE_TYPE_OUTPUT)
|
||||
adr.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
|
||||
else if (type == CUBEB_DEVICE_TYPE_INPUT)
|
||||
adr.mSelector = kAudioHardwarePropertyDefaultInputDevice;
|
||||
else
|
||||
return kAudioObjectUnknown;
|
||||
|
||||
size = sizeof(AudioDeviceID);
|
||||
if (AudioObjectGetPropertyData(kAudioObjectSystemObject, &adr, 0, NULL, &size, &devid) != noErr) {
|
||||
return kAudioObjectUnknown;
|
||||
}
|
||||
|
||||
return devid;
|
||||
}
|
||||
|
||||
int
|
||||
audiounit_get_max_channel_count(cubeb * ctx, uint32_t * max_channels)
|
||||
{
|
||||
@ -1021,281 +996,12 @@ int audiounit_stream_register_device_changed_callback(cubeb_stream * stream,
|
||||
return CUBEB_OK;
|
||||
}
|
||||
|
||||
static OSStatus
|
||||
audiounit_get_devices(AudioObjectID ** devices, uint32_t * count)
|
||||
{
|
||||
OSStatus ret;
|
||||
UInt32 size = 0;
|
||||
AudioObjectPropertyAddress adr = { kAudioHardwarePropertyDevices,
|
||||
kAudioObjectPropertyScopeGlobal,
|
||||
kAudioObjectPropertyElementMaster };
|
||||
|
||||
ret = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &adr, 0, NULL, &size);
|
||||
if (ret != noErr)
|
||||
return ret;
|
||||
|
||||
*count = (uint32_t)(size / sizeof(AudioObjectID));
|
||||
if (size >= sizeof(AudioObjectID)) {
|
||||
if (*devices != NULL) free(*devices);
|
||||
*devices = malloc(size);
|
||||
memset(*devices, 0, size);
|
||||
|
||||
ret = AudioObjectGetPropertyData(kAudioObjectSystemObject, &adr, 0, NULL, &size, (void *)*devices);
|
||||
if (ret != noErr) {
|
||||
free(*devices);
|
||||
*devices = NULL;
|
||||
}
|
||||
} else {
|
||||
*devices = NULL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static char *
|
||||
audiounit_strref_to_cstr_utf8(CFStringRef strref) {
|
||||
CFIndex len, size;
|
||||
char * ret;
|
||||
if (strref == NULL)
|
||||
return NULL;
|
||||
|
||||
len = CFStringGetLength(strref);
|
||||
size = CFStringGetMaximumSizeForEncoding(len, kCFStringEncodingUTF8);
|
||||
ret = malloc(size);
|
||||
|
||||
if (!CFStringGetCString(strref, ret, size, kCFStringEncodingUTF8)) {
|
||||
free(ret);
|
||||
ret = NULL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
audiounit_get_channel_count(AudioObjectID devid, AudioObjectPropertyScope scope)
|
||||
{
|
||||
AudioObjectPropertyAddress adr = { 0, scope, kAudioObjectPropertyElementMaster };
|
||||
UInt32 size = 0;
|
||||
uint32_t i, ret = 0;
|
||||
|
||||
adr.mSelector = kAudioDevicePropertyStreamConfiguration;
|
||||
|
||||
if (AudioObjectGetPropertyDataSize(devid, &adr, 0, NULL, &size) == noErr && size > 0) {
|
||||
AudioBufferList * list = alloca(size);
|
||||
if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, list) == noErr) {
|
||||
for (i = 0; i < list->mNumberBuffers; i++)
|
||||
ret += list->mBuffers[i].mNumberChannels;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
audiounit_get_available_samplerate(AudioObjectID devid, AudioObjectPropertyScope scope,
|
||||
uint32_t * min, uint32_t * max, uint32_t * def)
|
||||
{
|
||||
AudioObjectPropertyAddress adr = { 0, scope, kAudioObjectPropertyElementMaster };
|
||||
|
||||
adr.mSelector = kAudioDevicePropertyNominalSampleRate;
|
||||
if (AudioObjectHasProperty(devid, &adr)) {
|
||||
UInt32 size = sizeof(Float64);
|
||||
Float64 fvalue = 0.0;
|
||||
if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &fvalue) == noErr)
|
||||
*def = fvalue;
|
||||
}
|
||||
|
||||
adr.mSelector = kAudioDevicePropertyAvailableNominalSampleRates;
|
||||
if (AudioObjectHasProperty(devid, &adr)) {
|
||||
UInt32 size = 0;
|
||||
AudioValueRange range;
|
||||
if (AudioObjectGetPropertyDataSize(devid, &adr, 0, NULL, &size) == noErr) {
|
||||
uint32_t i, count = size / sizeof(AudioValueRange);
|
||||
AudioValueRange * ranges = malloc(size);
|
||||
range.mMinimum = 9999999999.0;
|
||||
range.mMaximum = 0.0;
|
||||
if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, ranges) == noErr) {
|
||||
for (i = 0; i < count; i++) {
|
||||
if (ranges[i].mMaximum > range.mMaximum)
|
||||
range.mMaximum = ranges[i].mMaximum;
|
||||
if (ranges[i].mMinimum < range.mMinimum)
|
||||
range.mMinimum = ranges[i].mMinimum;
|
||||
}
|
||||
}
|
||||
free(ranges);
|
||||
}
|
||||
*max = (uint32_t)range.mMaximum;
|
||||
*min = (uint32_t)range.mMinimum;
|
||||
} else {
|
||||
*min = *max = 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static UInt32
|
||||
audiounit_get_device_presentation_latency(AudioObjectID devid, AudioObjectPropertyScope scope)
|
||||
{
|
||||
AudioObjectPropertyAddress adr = { 0, scope, kAudioObjectPropertyElementMaster };
|
||||
UInt32 size, dev, stream = 0, offset;
|
||||
AudioStreamID sid[1];
|
||||
|
||||
adr.mSelector = kAudioDevicePropertyLatency;
|
||||
size = sizeof(UInt32);
|
||||
if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &dev) != noErr)
|
||||
dev = 0;
|
||||
|
||||
adr.mSelector = kAudioDevicePropertyStreams;
|
||||
size = sizeof(sid);
|
||||
if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, sid) == noErr) {
|
||||
adr.mSelector = kAudioStreamPropertyLatency;
|
||||
size = sizeof(UInt32);
|
||||
AudioObjectGetPropertyData(sid[0], &adr, 0, NULL, &size, &stream);
|
||||
}
|
||||
|
||||
adr.mSelector = kAudioDevicePropertySafetyOffset;
|
||||
size = sizeof(UInt32);
|
||||
if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &offset) != noErr)
|
||||
offset = 0;
|
||||
|
||||
return dev + stream + offset;
|
||||
}
|
||||
|
||||
static cubeb_device_info *
|
||||
audiounit_create_device_from_hwdev(AudioObjectID devid, cubeb_device_type type)
|
||||
{
|
||||
AudioObjectPropertyAddress adr = { 0, 0, kAudioObjectPropertyElementMaster };
|
||||
UInt32 size, ch, latency;
|
||||
cubeb_device_info * ret;
|
||||
CFStringRef str = NULL;
|
||||
AudioValueRange range;
|
||||
|
||||
if (type == CUBEB_DEVICE_TYPE_OUTPUT) {
|
||||
adr.mScope = kAudioDevicePropertyScopeOutput;
|
||||
} else if (type == CUBEB_DEVICE_TYPE_INPUT) {
|
||||
adr.mScope = kAudioDevicePropertyScopeInput;
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ((ch = audiounit_get_channel_count(devid, adr.mScope)) == 0)
|
||||
return NULL;
|
||||
|
||||
ret = calloc(1, sizeof(cubeb_device_info));
|
||||
|
||||
size = sizeof(CFStringRef);
|
||||
adr.mSelector = kAudioDevicePropertyDeviceUID;
|
||||
if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &str) == noErr && str != NULL) {
|
||||
ret->device_id = audiounit_strref_to_cstr_utf8(str);
|
||||
ret->devid = (cubeb_devid)ret->device_id;
|
||||
ret->group_id = strdup(ret->device_id);
|
||||
CFRelease(str);
|
||||
}
|
||||
|
||||
size = sizeof(CFStringRef);
|
||||
adr.mSelector = kAudioObjectPropertyName;
|
||||
if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &str) == noErr && str != NULL) {
|
||||
UInt32 ds;
|
||||
size = sizeof(UInt32);
|
||||
adr.mSelector = kAudioDevicePropertyDataSource;
|
||||
if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &ds) == noErr) {
|
||||
CFStringRef dsname;
|
||||
AudioValueTranslation trl = { &ds, sizeof(ds), &dsname, sizeof(dsname) };
|
||||
adr.mSelector = kAudioDevicePropertyDataSourceNameForIDCFString;
|
||||
size = sizeof(AudioValueTranslation);
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ret->friendly_name = audiounit_strref_to_cstr_utf8(str);
|
||||
CFRelease(str);
|
||||
}
|
||||
|
||||
size = sizeof(CFStringRef);
|
||||
adr.mSelector = kAudioObjectPropertyManufacturer;
|
||||
if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &str) == noErr && str != NULL) {
|
||||
ret->vendor_name = audiounit_strref_to_cstr_utf8(str);
|
||||
CFRelease(str);
|
||||
}
|
||||
|
||||
ret->type = type;
|
||||
ret->state = CUBEB_DEVICE_STATE_ENABLED;
|
||||
ret->preferred = (devid == audiounit_get_default_device_id(type)) ?
|
||||
CUBEB_DEVICE_PREF_ALL : CUBEB_DEVICE_PREF_NONE;
|
||||
|
||||
ret->max_channels = ch;
|
||||
ret->format = CUBEB_DEVICE_FMT_ALL; /* CoreAudio supports All! */
|
||||
/* kAudioFormatFlagsAudioUnitCanonical is deprecated, prefer floating point */
|
||||
ret->default_format = CUBEB_DEVICE_FMT_F32NE;
|
||||
audiounit_get_available_samplerate(devid, adr.mScope,
|
||||
&ret->min_rate, &ret->max_rate, &ret->default_rate);
|
||||
|
||||
latency = audiounit_get_device_presentation_latency(devid, adr.mScope);
|
||||
|
||||
adr.mSelector = kAudioDevicePropertyBufferFrameSizeRange;
|
||||
size = sizeof(AudioValueRange);
|
||||
if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &range) == noErr) {
|
||||
ret->latency_lo_ms = ((latency + range.mMinimum) * 1000) / ret->default_rate;
|
||||
ret->latency_hi_ms = ((latency + range.mMaximum) * 1000) / ret->default_rate;
|
||||
} else {
|
||||
ret->latency_lo_ms = 10; /* Default to 10ms */
|
||||
ret->latency_hi_ms = 100; /* Default to 100ms */
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
audiounit_enumerate_devices(cubeb * context, cubeb_device_type type,
|
||||
cubeb_device_collection ** collection)
|
||||
{
|
||||
AudioObjectID * hwdevs = NULL;
|
||||
uint32_t i, hwdevcount = 0;
|
||||
OSStatus err;
|
||||
|
||||
if ((err = audiounit_get_devices(&hwdevs, &hwdevcount)) != noErr)
|
||||
return CUBEB_ERROR;
|
||||
|
||||
*collection = malloc(sizeof(cubeb_device_collection) +
|
||||
sizeof(cubeb_device_info*) * (hwdevcount > 0 ? hwdevcount - 1 : 0));
|
||||
(*collection)->count = 0;
|
||||
|
||||
if (hwdevcount > 0) {
|
||||
cubeb_device_info * cur;
|
||||
|
||||
if (type & CUBEB_DEVICE_TYPE_OUTPUT) {
|
||||
for (i = 0; i < hwdevcount; i++) {
|
||||
if ((cur = audiounit_create_device_from_hwdev(hwdevs[i], CUBEB_DEVICE_TYPE_OUTPUT)) != NULL)
|
||||
(*collection)->device[(*collection)->count++] = cur;
|
||||
}
|
||||
}
|
||||
|
||||
if (type & CUBEB_DEVICE_TYPE_INPUT) {
|
||||
for (i = 0; i < hwdevcount; i++) {
|
||||
if ((cur = audiounit_create_device_from_hwdev(hwdevs[i], CUBEB_DEVICE_TYPE_INPUT)) != NULL)
|
||||
(*collection)->device[(*collection)->count++] = cur;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
free(hwdevs);
|
||||
|
||||
return CUBEB_OK;
|
||||
}
|
||||
|
||||
static struct cubeb_ops const audiounit_ops = {
|
||||
.init = audiounit_init,
|
||||
.get_backend_id = audiounit_get_backend_id,
|
||||
.get_max_channel_count = audiounit_get_max_channel_count,
|
||||
.get_min_latency = audiounit_get_min_latency,
|
||||
.get_preferred_sample_rate = audiounit_get_preferred_sample_rate,
|
||||
.enumerate_devices = audiounit_enumerate_devices,
|
||||
.destroy = audiounit_destroy,
|
||||
.stream_init = audiounit_stream_init,
|
||||
.stream_destroy = audiounit_stream_destroy,
|
||||
|
@ -817,7 +817,6 @@ static struct cubeb_ops const opensl_ops = {
|
||||
.get_max_channel_count = opensl_get_max_channel_count,
|
||||
.get_min_latency = opensl_get_min_latency,
|
||||
.get_preferred_sample_rate = opensl_get_preferred_sample_rate,
|
||||
.enumerate_devices = NULL,
|
||||
.destroy = opensl_destroy,
|
||||
.stream_init = opensl_stream_init,
|
||||
.stream_destroy = opensl_stream_destroy,
|
||||
|
@ -26,8 +26,6 @@
|
||||
X(pa_context_drain) \
|
||||
X(pa_context_get_server_info) \
|
||||
X(pa_context_get_sink_info_by_name) \
|
||||
X(pa_context_get_sink_info_list) \
|
||||
X(pa_context_get_source_info_list) \
|
||||
X(pa_context_get_state) \
|
||||
X(pa_context_new) \
|
||||
X(pa_context_rttime_new) \
|
||||
@ -39,7 +37,6 @@
|
||||
X(pa_frame_size) \
|
||||
X(pa_operation_get_state) \
|
||||
X(pa_operation_unref) \
|
||||
X(pa_proplist_gets) \
|
||||
X(pa_rtclock_now) \
|
||||
X(pa_stream_begin_write) \
|
||||
X(pa_stream_cancel_write) \
|
||||
@ -208,7 +205,7 @@ stream_request_callback(pa_stream * s, size_t nbytes, void * u)
|
||||
if (stm->volume != PULSE_NO_GAIN) {
|
||||
uint32_t samples = size * stm->sample_spec.channels / frame_size ;
|
||||
|
||||
if (stm->sample_spec.format == PA_SAMPLE_S16BE ||
|
||||
if (stm->sample_spec.format == PA_SAMPLE_S16LE ||
|
||||
stm->sample_spec.format == PA_SAMPLE_S16LE) {
|
||||
short * b = buffer;
|
||||
for (uint32_t i = 0; i < samples; i++) {
|
||||
@ -727,221 +724,12 @@ pulse_stream_set_panning(cubeb_stream * stream, float panning)
|
||||
return CUBEB_OK;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
char * default_sink_name;
|
||||
char * default_source_name;
|
||||
|
||||
cubeb_device_info ** devinfo;
|
||||
uint32_t max;
|
||||
uint32_t count;
|
||||
} pulse_dev_list_data;
|
||||
|
||||
static cubeb_device_fmt
|
||||
pulse_format_to_cubeb_format(pa_sample_format_t format)
|
||||
{
|
||||
switch (format) {
|
||||
case PA_SAMPLE_S16LE:
|
||||
return CUBEB_DEVICE_FMT_S16LE;
|
||||
case PA_SAMPLE_S16BE:
|
||||
return CUBEB_DEVICE_FMT_S16BE;
|
||||
case PA_SAMPLE_FLOAT32LE:
|
||||
return CUBEB_DEVICE_FMT_F32LE;
|
||||
case PA_SAMPLE_FLOAT32BE:
|
||||
return CUBEB_DEVICE_FMT_F32BE;
|
||||
default:
|
||||
return CUBEB_DEVICE_FMT_F32NE;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
pulse_ensure_dev_list_data_list_size (pulse_dev_list_data * list_data)
|
||||
{
|
||||
if (list_data->count == list_data->max) {
|
||||
list_data->max += 8;
|
||||
list_data->devinfo = realloc(list_data->devinfo,
|
||||
sizeof(cubeb_device_info) * list_data->max);
|
||||
}
|
||||
}
|
||||
|
||||
static cubeb_device_state
|
||||
pulse_get_state_from_sink_port(pa_sink_port_info * info)
|
||||
{
|
||||
if (info != NULL) {
|
||||
#if PA_CHECK_VERSION(2, 0, 0)
|
||||
if (info->available == PA_PORT_AVAILABLE_NO)
|
||||
return CUBEB_DEVICE_STATE_UNPLUGGED;
|
||||
else /*if (info->available == PA_PORT_AVAILABLE_YES) + UNKNOWN */
|
||||
#endif
|
||||
return CUBEB_DEVICE_STATE_ENABLED;
|
||||
}
|
||||
|
||||
return CUBEB_DEVICE_STATE_DISABLED;
|
||||
}
|
||||
|
||||
static void
|
||||
pulse_sink_info_cb(pa_context * context, const pa_sink_info * info,
|
||||
int eol, void * user_data)
|
||||
{
|
||||
pulse_dev_list_data * list_data = user_data;
|
||||
cubeb_device_info * devinfo;
|
||||
const char * prop;
|
||||
|
||||
(void)context;
|
||||
|
||||
if (eol || info == NULL)
|
||||
return;
|
||||
|
||||
devinfo = calloc(1, sizeof(cubeb_device_info));
|
||||
|
||||
devinfo->device_id = strdup(info->name);
|
||||
devinfo->devid = (cubeb_devid)devinfo->device_id;
|
||||
devinfo->friendly_name = strdup(info->description);
|
||||
prop = WRAP(pa_proplist_gets)(info->proplist, "sysfs.path");
|
||||
if (prop)
|
||||
devinfo->group_id = strdup(prop);
|
||||
prop = WRAP(pa_proplist_gets)(info->proplist, "device.vendor.name");
|
||||
if (prop)
|
||||
devinfo->vendor_name = strdup(prop);
|
||||
|
||||
devinfo->type = CUBEB_DEVICE_TYPE_OUTPUT;
|
||||
devinfo->state = pulse_get_state_from_sink_port(info->active_port);
|
||||
devinfo->preferred = strcmp(info->name, list_data->default_sink_name) == 0;
|
||||
|
||||
devinfo->format = CUBEB_DEVICE_FMT_ALL;
|
||||
devinfo->default_format = pulse_format_to_cubeb_format(info->sample_spec.format);
|
||||
devinfo->max_channels = info->channel_map.channels;
|
||||
devinfo->min_rate = 1;
|
||||
devinfo->max_rate = PA_RATE_MAX;
|
||||
devinfo->default_rate = info->sample_spec.rate;
|
||||
|
||||
devinfo->latency_lo_ms = 40;
|
||||
devinfo->latency_hi_ms = 400;
|
||||
|
||||
pulse_ensure_dev_list_data_list_size (list_data);
|
||||
list_data->devinfo[list_data->count++] = devinfo;
|
||||
}
|
||||
|
||||
static cubeb_device_state
|
||||
pulse_get_state_from_source_port(pa_source_port_info * info)
|
||||
{
|
||||
if (info != NULL) {
|
||||
#if PA_CHECK_VERSION(2, 0, 0)
|
||||
if (info->available == PA_PORT_AVAILABLE_NO)
|
||||
return CUBEB_DEVICE_STATE_UNPLUGGED;
|
||||
else /*if (info->available == PA_PORT_AVAILABLE_YES) + UNKNOWN */
|
||||
#endif
|
||||
return CUBEB_DEVICE_STATE_ENABLED;
|
||||
}
|
||||
|
||||
return CUBEB_DEVICE_STATE_DISABLED;
|
||||
}
|
||||
|
||||
static void
|
||||
pulse_source_info_cb(pa_context * context, const pa_source_info * info,
|
||||
int eol, void * user_data)
|
||||
{
|
||||
pulse_dev_list_data * list_data = user_data;
|
||||
cubeb_device_info * devinfo;
|
||||
const char * prop;
|
||||
|
||||
(void)context;
|
||||
|
||||
if (eol)
|
||||
return;
|
||||
|
||||
devinfo = calloc(1, sizeof(cubeb_device_info));
|
||||
|
||||
devinfo->device_id = strdup(info->name);
|
||||
devinfo->devid = (cubeb_devid)devinfo->device_id;
|
||||
devinfo->friendly_name = strdup(info->description);
|
||||
prop = WRAP(pa_proplist_gets)(info->proplist, "sysfs.path");
|
||||
if (prop)
|
||||
devinfo->group_id = strdup(prop);
|
||||
prop = WRAP(pa_proplist_gets)(info->proplist, "device.vendor.name");
|
||||
if (prop)
|
||||
devinfo->vendor_name = strdup(prop);
|
||||
|
||||
devinfo->type = CUBEB_DEVICE_TYPE_INPUT;
|
||||
devinfo->state = pulse_get_state_from_source_port(info->active_port);
|
||||
devinfo->preferred = strcmp(info->name, list_data->default_source_name) == 0;
|
||||
|
||||
devinfo->format = CUBEB_DEVICE_FMT_ALL;
|
||||
devinfo->default_format = pulse_format_to_cubeb_format(info->sample_spec.format);
|
||||
devinfo->max_channels = info->channel_map.channels;
|
||||
devinfo->min_rate = 1;
|
||||
devinfo->max_rate = PA_RATE_MAX;
|
||||
devinfo->default_rate = info->sample_spec.rate;
|
||||
|
||||
devinfo->latency_lo_ms = 1;
|
||||
devinfo->latency_hi_ms = 10;
|
||||
|
||||
pulse_ensure_dev_list_data_list_size (list_data);
|
||||
list_data->devinfo[list_data->count++] = devinfo;
|
||||
}
|
||||
|
||||
static void
|
||||
pulse_server_info_cb(pa_context * c, const pa_server_info * i, void * userdata)
|
||||
{
|
||||
pulse_dev_list_data * list_data = userdata;
|
||||
|
||||
(void)c;
|
||||
|
||||
free(list_data->default_sink_name);
|
||||
free(list_data->default_source_name);
|
||||
list_data->default_sink_name = strdup(i->default_sink_name);
|
||||
list_data->default_source_name = strdup(i->default_source_name);
|
||||
}
|
||||
|
||||
static int
|
||||
pulse_enumerate_devices(cubeb * context, cubeb_device_type type,
|
||||
cubeb_device_collection ** collection)
|
||||
{
|
||||
pulse_dev_list_data user_data = { NULL, NULL, NULL, 0, 0 };
|
||||
pa_operation * o;
|
||||
uint32_t i;
|
||||
|
||||
o = WRAP(pa_context_get_server_info)(context->context,
|
||||
pulse_server_info_cb, &user_data);
|
||||
if (o) {
|
||||
operation_wait(context, NULL, o);
|
||||
WRAP(pa_operation_unref)(o);
|
||||
}
|
||||
|
||||
if (type & CUBEB_DEVICE_TYPE_OUTPUT) {
|
||||
o = WRAP(pa_context_get_sink_info_list)(context->context,
|
||||
pulse_sink_info_cb, &user_data);
|
||||
if (o) {
|
||||
operation_wait(context, NULL, o);
|
||||
WRAP(pa_operation_unref)(o);
|
||||
}
|
||||
}
|
||||
|
||||
if (type & CUBEB_DEVICE_TYPE_INPUT) {
|
||||
o = WRAP(pa_context_get_source_info_list)(context->context,
|
||||
pulse_source_info_cb, &user_data);
|
||||
if (o) {
|
||||
operation_wait(context, NULL, o);
|
||||
WRAP(pa_operation_unref)(o);
|
||||
}
|
||||
}
|
||||
|
||||
*collection = malloc(sizeof(cubeb_device_collection) +
|
||||
sizeof(cubeb_device_info*) * (user_data.count > 0 ? user_data.count - 1 : 0));
|
||||
(*collection)->count = user_data.count;
|
||||
for (i = 0; i < user_data.count; i++)
|
||||
(*collection)->device[i] = user_data.devinfo[i];
|
||||
|
||||
free(user_data.devinfo);
|
||||
return CUBEB_OK;
|
||||
}
|
||||
|
||||
static struct cubeb_ops const pulse_ops = {
|
||||
.init = pulse_init,
|
||||
.get_backend_id = pulse_get_backend_id,
|
||||
.get_max_channel_count = pulse_get_max_channel_count,
|
||||
.get_min_latency = pulse_get_min_latency,
|
||||
.get_preferred_sample_rate = pulse_get_preferred_sample_rate,
|
||||
.enumerate_devices = pulse_enumerate_devices,
|
||||
.destroy = pulse_destroy,
|
||||
.stream_init = pulse_stream_init,
|
||||
.stream_destroy = pulse_stream_destroy,
|
||||
|
@ -356,7 +356,6 @@ static struct cubeb_ops const sndio_ops = {
|
||||
.get_max_channel_count = sndio_get_max_channel_count,
|
||||
.get_min_latency = sndio_get_min_latency,
|
||||
.get_preferred_sample_rate = sndio_get_preferred_sample_rate,
|
||||
.enumerate_devices = NULL,
|
||||
.destroy = sndio_destroy,
|
||||
.stream_init = sndio_stream_init,
|
||||
.stream_destroy = sndio_stream_destroy,
|
||||
|
@ -4,15 +4,14 @@
|
||||
* This program is made available under an ISC-style license. See the
|
||||
* accompanying file LICENSE for details.
|
||||
*/
|
||||
// This enables assert in release, and lets us have debug-only code
|
||||
#if defined(HAVE_CONFIG_H)
|
||||
#include "config.h"
|
||||
#endif
|
||||
#include <initguid.h>
|
||||
#include <windows.h>
|
||||
#include <mmdeviceapi.h>
|
||||
#include <windef.h>
|
||||
#include <audioclient.h>
|
||||
#include <devicetopology.h>
|
||||
#include <process.h>
|
||||
#include <avrt.h>
|
||||
#include "cubeb/cubeb.h"
|
||||
@ -23,23 +22,11 @@
|
||||
#include <stdlib.h>
|
||||
#include <cmath>
|
||||
|
||||
/* devicetopology.h missing in MinGW. */
|
||||
#ifndef __devicetopology_h__
|
||||
#include "cubeb_devicetopology.h"
|
||||
#endif
|
||||
|
||||
/* Taken from winbase.h, Not in MinGW. */
|
||||
/**Taken from winbase.h, Not in MinGW.*/
|
||||
#ifndef STACK_SIZE_PARAM_IS_A_RESERVATION
|
||||
#define STACK_SIZE_PARAM_IS_A_RESERVATION 0x00010000 // Threads only
|
||||
#endif
|
||||
|
||||
#ifndef PKEY_Device_FriendlyName
|
||||
DEFINE_PROPERTYKEY(PKEY_Device_FriendlyName, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 14); // DEVPROP_TYPE_STRING
|
||||
#endif
|
||||
#ifndef PKEY_Device_InstanceId
|
||||
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
|
||||
@ -61,13 +48,13 @@ ms_to_hns(uint32_t ms)
|
||||
}
|
||||
|
||||
uint32_t
|
||||
hns_to_ms(REFERENCE_TIME hns)
|
||||
hns_to_ms(uint32_t hns)
|
||||
{
|
||||
return hns / 10000;
|
||||
}
|
||||
|
||||
double
|
||||
hns_to_s(REFERENCE_TIME hns)
|
||||
hns_to_s(uint32_t hns)
|
||||
{
|
||||
return static_cast<double>(hns) / 10000000;
|
||||
}
|
||||
@ -89,7 +76,7 @@ void SafeRelease(T * ptr)
|
||||
}
|
||||
|
||||
/* This wraps a critical section to track the owner in debug mode, adapted from
|
||||
NSPR and http://blogs.msdn.com/b/oldnewthing/archive/2013/07/12/10433554.aspx */
|
||||
* NSPR and http://blogs.msdn.com/b/oldnewthing/archive/2013/07/12/10433554.aspx */
|
||||
class owned_critical_section
|
||||
{
|
||||
public:
|
||||
@ -125,7 +112,7 @@ public:
|
||||
}
|
||||
|
||||
/* This is guaranteed to have the good behaviour if it succeeds. The behaviour
|
||||
is undefined otherwise. */
|
||||
* is undefined otherwise. */
|
||||
void assert_current_thread_owns()
|
||||
{
|
||||
#ifdef DEBUG
|
||||
@ -163,12 +150,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 already initialized in STA.\n");
|
||||
} 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 already initialized in MTA\n");
|
||||
}
|
||||
if (SUCCEEDED(result)) {
|
||||
CoUninitialize();
|
||||
@ -197,8 +184,8 @@ int setup_wasapi_stream(cubeb_stream * stm);
|
||||
struct cubeb
|
||||
{
|
||||
cubeb_ops const * ops;
|
||||
/* Library dynamically opened to increase the render thread priority, and
|
||||
the two function pointers we need. */
|
||||
/* Library dynamically opened to increase the render
|
||||
* thread priority, and the two function pointers we need. */
|
||||
HMODULE mmcss_module;
|
||||
set_mm_thread_characteristics_function set_mm_thread_characteristics;
|
||||
revert_mm_thread_characteristics_function revert_mm_thread_characteristics;
|
||||
@ -209,9 +196,9 @@ class wasapi_endpoint_notification_client;
|
||||
struct cubeb_stream
|
||||
{
|
||||
cubeb * context;
|
||||
/* Mixer pameters. We need to convert the input stream to this
|
||||
samplerate/channel layout, as WASAPI * does not resample nor upmix
|
||||
itself. */
|
||||
/* Mixer pameters. We need to convert the input
|
||||
* stream to this samplerate/channel layout, as WASAPI
|
||||
* does not resample nor upmix itself. */
|
||||
cubeb_stream_params mix_params;
|
||||
cubeb_stream_params stream_params;
|
||||
/* The latency initially requested for this stream. */
|
||||
@ -221,10 +208,10 @@ struct cubeb_stream
|
||||
void * user_ptr;
|
||||
|
||||
/* Lifetime considerations:
|
||||
- client, render_client, audio_clock and audio_stream_volume are interface
|
||||
pointer to the IAudioClient.
|
||||
- The lifetime for device_enumerator and notification_client, resampler,
|
||||
mix_buffer are the same as the cubeb_stream instance. */
|
||||
* - client, render_client, audio_clock and audio_stream_volume are interface
|
||||
* pointer to the IAudioClient.
|
||||
* - The lifetime for device_enumerator and notification_client, resampler,
|
||||
* mix_buffer are the same as the cubeb_stream instance. */
|
||||
|
||||
/* Main handle on the WASAPI stream. */
|
||||
IAudioClient * client;
|
||||
@ -235,23 +222,23 @@ struct cubeb_stream
|
||||
/* Interface pointer to use the stream audio clock. */
|
||||
IAudioClock * audio_clock;
|
||||
/* Frames written to the stream since it was opened. Reset on device
|
||||
change. Uses mix_params.rate. */
|
||||
* change. Uses mix_params.rate. */
|
||||
UINT64 frames_written;
|
||||
/* Frames written to the (logical) stream since it was first
|
||||
created. Updated on device change. Uses stream_params.rate. */
|
||||
* created. Updated on device change. Uses stream_params.rate. */
|
||||
UINT64 total_frames_written;
|
||||
/* Last valid reported stream position. Used to ensure the position
|
||||
reported by stream_get_position increases monotonically. */
|
||||
* reported by stream_get_position increases monotonically. */
|
||||
UINT64 prev_position;
|
||||
/* Device enumerator to be able to be notified when the default
|
||||
device change. */
|
||||
* device change. */
|
||||
IMMDeviceEnumerator * device_enumerator;
|
||||
/* Device notification client, to be able to be notified when the default
|
||||
audio device changes and route the audio to the new default audio output
|
||||
device */
|
||||
* audio device changes and route the audio to the new default audio output
|
||||
* device */
|
||||
wasapi_endpoint_notification_client * notification_client;
|
||||
/* This event is set by the stream_stop and stream_destroy
|
||||
function, so the render loop can exit properly. */
|
||||
* function, so the render loop can exit properly. */
|
||||
HANDLE shutdown_event;
|
||||
/* Set by OnDefaultDeviceChanged when a stream reconfiguration is required.
|
||||
The reconfiguration is handled by the render loop thread. */
|
||||
@ -269,10 +256,10 @@ struct cubeb_stream
|
||||
/* Resampler instance. Resampling will only happen if necessary. */
|
||||
cubeb_resampler * resampler;
|
||||
/* Buffer used to downmix or upmix to the number of channels the mixer has.
|
||||
its size is |frames_to_bytes_before_mix(buffer_frame_count)|. */
|
||||
* its size is |frames_to_bytes_before_mix(buffer_frame_count)|. */
|
||||
float * mix_buffer;
|
||||
/* Stream volume. Set via stream_set_volume and used to reset volume on
|
||||
device changes. */
|
||||
* device changes. */
|
||||
float volume;
|
||||
/* True if the stream is draining. */
|
||||
bool draining;
|
||||
@ -320,14 +307,9 @@ public:
|
||||
, reconfigure_event(event)
|
||||
{ }
|
||||
|
||||
virtual ~wasapi_endpoint_notification_client()
|
||||
{ }
|
||||
|
||||
HRESULT STDMETHODCALLTYPE
|
||||
OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR device_id)
|
||||
{
|
||||
LOG("Audio device default changed.\n");
|
||||
|
||||
/* we only support a single stream type for now. */
|
||||
if (flow != eRender && role != eMultimedia) {
|
||||
return S_OK;
|
||||
@ -335,14 +317,14 @@ 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;
|
||||
}
|
||||
|
||||
/* The remaining methods are not implemented, they simply log when called (if
|
||||
log is enabled), for debugging. */
|
||||
* log is enabled), for debugging. */
|
||||
HRESULT STDMETHODCALLTYPE OnDeviceAdded(LPCWSTR device_id)
|
||||
{
|
||||
LOG("Audio device added.\n");
|
||||
@ -440,8 +422,8 @@ downmix(T * in, long inframes, T * out, int32_t in_channels, int32_t out_channel
|
||||
{
|
||||
XASSERT(in_channels >= out_channels);
|
||||
/* We could use a downmix matrix here, applying mixing weight based on the
|
||||
channel, but directsound and winmm simply drop the channels that cannot be
|
||||
rendered by the hardware, so we do the same for consistency. */
|
||||
* channel, but directsound and winmm simply drop the channels that cannot be
|
||||
* rendered by the hardware, so we do the same for consistency. */
|
||||
long out_index = 0;
|
||||
for (long i = 0; i < inframes * in_channels; i += in_channels) {
|
||||
for (int j = 0; j < out_channels; ++j) {
|
||||
@ -451,8 +433,8 @@ downmix(T * in, long inframes, T * out, int32_t in_channels, int32_t out_channel
|
||||
}
|
||||
}
|
||||
|
||||
/* This returns the size of a frame in the stream, before the eventual upmix
|
||||
occurs. */
|
||||
/* This returns the size of a frame in the stream,
|
||||
* before the eventual upmix occurs. */
|
||||
static size_t
|
||||
frames_to_bytes_before_mix(cubeb_stream * stm, size_t frames)
|
||||
{
|
||||
@ -464,7 +446,7 @@ long
|
||||
refill(cubeb_stream * stm, float * data, long frames_needed)
|
||||
{
|
||||
/* If we need to upmix after resampling, resample into the mix buffer to
|
||||
avoid a copy. */
|
||||
* avoid a copy. */
|
||||
float * dest;
|
||||
if (should_upmix(stm) || should_downmix(stm)) {
|
||||
dest = stm->mix_buffer;
|
||||
@ -483,12 +465,12 @@ refill(cubeb_stream * stm, float * data, long frames_needed)
|
||||
|
||||
/* Go in draining mode if we got fewer frames than requested. */
|
||||
if (out_frames < frames_needed) {
|
||||
LOG("start draining.\n");
|
||||
LOG("draining.\n");
|
||||
stm->draining = true;
|
||||
}
|
||||
|
||||
/* If this is not true, there will be glitches.
|
||||
It is alright to have produced less frames if we are draining, though. */
|
||||
* It is alright to have produced less frames if we are draining, though. */
|
||||
XASSERT(out_frames == frames_needed || stm->draining);
|
||||
|
||||
if (should_upmix(stm)) {
|
||||
@ -511,16 +493,16 @@ wasapi_stream_render_loop(LPVOID stream)
|
||||
HANDLE wait_array[3] = {stm->shutdown_event, stm->reconfigure_event, stm->refill_event};
|
||||
HANDLE mmcss_handle = NULL;
|
||||
HRESULT hr = 0;
|
||||
bool first = true;
|
||||
DWORD mmcss_task_index = 0;
|
||||
auto_com com;
|
||||
if (!com.ok()) {
|
||||
LOG("COM initialization failed on render_loop thread.\n");
|
||||
stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* We could consider using "Pro Audio" here for WebAudio and
|
||||
maybe WebRTC. */
|
||||
* maybe WebRTC. */
|
||||
mmcss_handle =
|
||||
stm->context->set_mm_thread_characteristics("Audio", &mmcss_task_index);
|
||||
if (!mmcss_handle) {
|
||||
@ -547,7 +529,7 @@ wasapi_stream_render_loop(LPVOID stream)
|
||||
case WAIT_OBJECT_0: { /* shutdown */
|
||||
is_playing = false;
|
||||
/* We don't check if the drain is actually finished here, we just want to
|
||||
shutdown. */
|
||||
* shutdown. */
|
||||
if (stm->draining) {
|
||||
stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED);
|
||||
}
|
||||
@ -560,13 +542,13 @@ wasapi_stream_render_loop(LPVOID stream)
|
||||
auto_lock lock(stm->stream_reset_lock);
|
||||
close_wasapi_stream(stm);
|
||||
/* Reopen a stream and start it immediately. This will automatically pick the
|
||||
new default device for this role. */
|
||||
* new default device for this role. */
|
||||
int r = setup_wasapi_stream(stm);
|
||||
if (r != CUBEB_OK) {
|
||||
/* Don't destroy the stream here, since we expect the caller to do
|
||||
so after the error has propagated via the state callback. */
|
||||
is_playing = false;
|
||||
hr = E_FAIL;
|
||||
hr = -1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@ -578,12 +560,14 @@ wasapi_stream_render_loop(LPVOID stream)
|
||||
|
||||
hr = stm->client->GetCurrentPadding(&padding);
|
||||
if (FAILED(hr)) {
|
||||
LOG("Failed to get padding: %x\n", hr);
|
||||
LOG("Failed to get padding\n");
|
||||
is_playing = false;
|
||||
continue;
|
||||
}
|
||||
XASSERT(padding <= stm->buffer_frame_count);
|
||||
|
||||
long available = stm->buffer_frame_count - padding;
|
||||
|
||||
if (stm->draining) {
|
||||
if (padding == 0) {
|
||||
stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED);
|
||||
@ -592,8 +576,6 @@ wasapi_stream_render_loop(LPVOID stream)
|
||||
continue;
|
||||
}
|
||||
|
||||
long available = stm->buffer_frame_count - padding;
|
||||
|
||||
if (available == 0) {
|
||||
continue;
|
||||
}
|
||||
@ -606,11 +588,11 @@ wasapi_stream_render_loop(LPVOID stream)
|
||||
|
||||
hr = stm->render_client->ReleaseBuffer(wrote, 0);
|
||||
if (FAILED(hr)) {
|
||||
LOG("failed to release buffer: %x\n", hr);
|
||||
LOG("failed to release buffer.\n");
|
||||
is_playing = false;
|
||||
}
|
||||
} else {
|
||||
LOG("failed to get buffer: %x\n", hr);
|
||||
LOG("failed to get buffer.\n");
|
||||
is_playing = false;
|
||||
}
|
||||
}
|
||||
@ -619,7 +601,7 @@ wasapi_stream_render_loop(LPVOID stream)
|
||||
XASSERT(stm->shutdown_event == wait_array[0]);
|
||||
if (++timeout_count >= timeout_limit) {
|
||||
is_playing = false;
|
||||
hr = E_FAIL;
|
||||
hr = -1;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@ -654,6 +636,7 @@ HRESULT register_notification_client(cubeb_stream * stm)
|
||||
HRESULT hr = CoCreateInstance(__uuidof(MMDeviceEnumerator),
|
||||
NULL, CLSCTX_INPROC_SERVER,
|
||||
IID_PPV_ARGS(&stm->device_enumerator));
|
||||
|
||||
if (FAILED(hr)) {
|
||||
LOG("Could not get device enumerator: %x\n", hr);
|
||||
return hr;
|
||||
@ -662,6 +645,7 @@ HRESULT register_notification_client(cubeb_stream * stm)
|
||||
stm->notification_client = new wasapi_endpoint_notification_client(stm->reconfigure_event);
|
||||
|
||||
hr = stm->device_enumerator->RegisterEndpointNotificationCallback(stm->notification_client);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
LOG("Could not register endpoint notification callback: %x\n", hr);
|
||||
return hr;
|
||||
@ -693,16 +677,16 @@ HRESULT get_default_endpoint(IMMDevice ** device)
|
||||
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.\n");
|
||||
return hr;
|
||||
}
|
||||
/* eMultimedia is okay for now ("Music, movies, narration, [...]").
|
||||
We will need to change this when we distinguish streams by use-case, other
|
||||
possible values being eConsole ("Games, system notification sounds [...]")
|
||||
and eCommunication ("Voice communication"). */
|
||||
* We will need to change this when we distinguish streams by use-case, other
|
||||
* possible values being eConsole ("Games, system notification sounds [...]")
|
||||
* and eCommunication ("Voice communication"). */
|
||||
hr = enumerator->GetDefaultAudioEndpoint(eRender, eMultimedia, device);
|
||||
if (FAILED(hr)) {
|
||||
LOG("Could not get default audio endpoint: %x\n", hr);
|
||||
LOG("Could not get default audio endpoint. %d\n", __LINE__);
|
||||
SafeRelease(enumerator);
|
||||
return hr;
|
||||
}
|
||||
@ -797,15 +781,12 @@ int wasapi_init(cubeb ** context, char const * context_name)
|
||||
IMMDevice * device;
|
||||
hr = get_default_endpoint(&device);
|
||||
if (FAILED(hr)) {
|
||||
LOG("Could not get device: %x\n", hr);
|
||||
LOG("Could not get device.\n");
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
SafeRelease(device);
|
||||
|
||||
cubeb * ctx = (cubeb *)calloc(1, sizeof(cubeb));
|
||||
if (!ctx) {
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
|
||||
ctx->ops = &wasapi_ops;
|
||||
|
||||
@ -928,7 +909,7 @@ wasapi_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * laten
|
||||
IMMDevice * device;
|
||||
hr = get_default_endpoint(&device);
|
||||
if (FAILED(hr)) {
|
||||
LOG("Could not get default endpoint: %x\n", hr);
|
||||
LOG("Could not get default endpoint:%x.\n", hr);
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
|
||||
@ -937,7 +918,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.\n", hr);
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
|
||||
@ -945,15 +926,15 @@ 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.\n", hr);
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
|
||||
LOG("default device period: %ld\n", default_period);
|
||||
|
||||
/* According to the docs, the best latency we can achieve is by synchronizing
|
||||
the stream and the engine.
|
||||
http://msdn.microsoft.com/en-us/library/windows/desktop/dd370871%28v=vs.85%29.aspx */
|
||||
* the stream and the engine.
|
||||
* http://msdn.microsoft.com/en-us/library/windows/desktop/dd370871%28v=vs.85%29.aspx */
|
||||
*latency_ms = hns_to_ms(default_period);
|
||||
|
||||
SafeRelease(client);
|
||||
@ -1002,22 +983,22 @@ wasapi_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate)
|
||||
|
||||
void wasapi_stream_destroy(cubeb_stream * stm);
|
||||
|
||||
/* Based on the mix format and the stream format, try to find a way to play
|
||||
what the user requested. */
|
||||
/* Based on the mix format and the stream format, try to find a way to play what
|
||||
* the user requested. */
|
||||
static void
|
||||
handle_channel_layout(cubeb_stream * stm, WAVEFORMATEX ** mix_format, const cubeb_stream_params * stream_params)
|
||||
{
|
||||
/* Common case: the hardware is stereo. Up-mixing and down-mixing will be
|
||||
handled in the callback. */
|
||||
* handled in the callback. */
|
||||
if ((*mix_format)->nChannels <= 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* The docs say that GetMixFormat is always of type WAVEFORMATEXTENSIBLE [1],
|
||||
so the reinterpret_cast below should be safe. In practice, this is not
|
||||
true, and we just want to bail out and let the rest of the code find a good
|
||||
conversion path instead of trying to make WASAPI do it by itself.
|
||||
[1]: http://msdn.microsoft.com/en-us/library/windows/desktop/dd370811%28v=vs.85%29.aspx*/
|
||||
* so the reinterpret_cast below should be safe. In practice, this is not
|
||||
* true, and we just want to bail out and let the rest of the code find a good
|
||||
* conversion path instead of trying to make WASAPI do it by itself.
|
||||
* [1]: http://msdn.microsoft.com/en-us/library/windows/desktop/dd370811%28v=vs.85%29.aspx*/
|
||||
if ((*mix_format)->wFormatTag != WAVE_FORMAT_EXTENSIBLE) {
|
||||
return;
|
||||
}
|
||||
@ -1028,7 +1009,7 @@ handle_channel_layout(cubeb_stream * stm, WAVEFORMATEX ** mix_format, const cub
|
||||
WAVEFORMATEXTENSIBLE hw_mix_format = *format_pcm;
|
||||
|
||||
/* The hardware is in surround mode, we want to only use front left and front
|
||||
right. Try that, and check if it works. */
|
||||
* right. Try that, and check if it works. */
|
||||
switch (stream_params->channels) {
|
||||
case 1: /* Mono */
|
||||
format_pcm->dwChannelMask = KSAUDIO_SPEAKER_MONO;
|
||||
@ -1055,7 +1036,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 */
|
||||
* eventual upmix/downmix ourselves */
|
||||
LOG("Using WASAPI suggested format: channels: %d\n", closest->nChannels);
|
||||
WAVEFORMATEXTENSIBLE * closest_pcm = reinterpret_cast<WAVEFORMATEXTENSIBLE *>(closest);
|
||||
XASSERT(closest_pcm->SubFormat == format_pcm->SubFormat);
|
||||
@ -1063,13 +1044,11 @@ handle_channel_layout(cubeb_stream * stm, WAVEFORMATEX ** mix_format, const cub
|
||||
*mix_format = closest;
|
||||
} else if (hr == AUDCLNT_E_UNSUPPORTED_FORMAT) {
|
||||
/* Not supported, no suggestion. This should not happen, but it does in the
|
||||
field with some sound cards. We restore the mix format, and let the rest
|
||||
of the code figure out the right conversion path. */
|
||||
* field with some sound cards. We restore the mix format, and let the rest
|
||||
* 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");
|
||||
} else {
|
||||
LOG("IsFormatSupported unhandled error: %x\n", hr);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1095,7 +1074,7 @@ int setup_wasapi_stream(cubeb_stream * stm)
|
||||
}
|
||||
|
||||
/* Get a client. We will get all other interfaces we need from
|
||||
this pointer. */
|
||||
* this pointer. */
|
||||
hr = device->Activate(__uuidof(IAudioClient),
|
||||
CLSCTX_INPROC_SERVER,
|
||||
NULL, (void **)&stm->client);
|
||||
@ -1106,7 +1085,7 @@ int setup_wasapi_stream(cubeb_stream * stm)
|
||||
}
|
||||
|
||||
/* We have to distinguish between the format the mixer uses,
|
||||
and the format the stream we want to play uses. */
|
||||
* and the format the stream we want to play uses. */
|
||||
hr = stm->client->GetMixFormat(&mix_format);
|
||||
if (FAILED(hr)) {
|
||||
LOG("Could not fetch current mix format from the audio client: error: %x\n", hr);
|
||||
@ -1116,7 +1095,7 @@ int setup_wasapi_stream(cubeb_stream * stm)
|
||||
handle_channel_layout(stm, &mix_format, &stm->stream_params);
|
||||
|
||||
/* Shared mode WASAPI always supports float32 sample format, so this
|
||||
is safe. */
|
||||
* is safe. */
|
||||
stm->mix_params.format = CUBEB_SAMPLE_FLOAT32NE;
|
||||
stm->mix_params.rate = mix_format->nSamplesPerSec;
|
||||
stm->mix_params.channels = mix_format->nChannels;
|
||||
@ -1128,15 +1107,17 @@ int setup_wasapi_stream(cubeb_stream * stm)
|
||||
0,
|
||||
mix_format,
|
||||
NULL);
|
||||
|
||||
CoTaskMemFree(mix_format);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
LOG("Unable to initialize audio client: %x\n", hr);
|
||||
LOG("Unable to initialize audio client: %x.\n", hr);
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
|
||||
hr = stm->client->GetBufferSize(&stm->buffer_frame_count);
|
||||
if (FAILED(hr)) {
|
||||
LOG("Could not get the buffer size from the client: %x\n", hr);
|
||||
LOG("Could not get the buffer size from the client %x.\n", hr);
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
|
||||
@ -1146,21 +1127,21 @@ int setup_wasapi_stream(cubeb_stream * stm)
|
||||
|
||||
hr = stm->client->SetEventHandle(stm->refill_event);
|
||||
if (FAILED(hr)) {
|
||||
LOG("Could set the event handle for the client: %x\n", hr);
|
||||
LOG("Could set the event handle for the client %x.\n", hr);
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
|
||||
hr = stm->client->GetService(__uuidof(IAudioRenderClient),
|
||||
(void **)&stm->render_client);
|
||||
if (FAILED(hr)) {
|
||||
LOG("Could not get the render client: %x\n", hr);
|
||||
LOG("Could not get the render client %x.\n", hr);
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
|
||||
hr = stm->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.\n", hr);
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
|
||||
@ -1168,7 +1149,7 @@ int setup_wasapi_stream(cubeb_stream * stm)
|
||||
hr = stm->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.\n", hr);
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
|
||||
@ -1178,9 +1159,9 @@ int setup_wasapi_stream(cubeb_stream * stm)
|
||||
}
|
||||
|
||||
/* If we are playing a mono stream, we only resample one channel,
|
||||
and copy it over, so we are always resampling the number
|
||||
of channels of the stream, not the number of channels
|
||||
that WASAPI wants. */
|
||||
* and copy it over, so we are always resampling the number
|
||||
* of channels of the stream, not the number of channels
|
||||
* that WASAPI wants. */
|
||||
stm->resampler = cubeb_resampler_create(stm, stm->stream_params,
|
||||
stm->mix_params.rate,
|
||||
stm->data_callback,
|
||||
@ -1210,10 +1191,6 @@ wasapi_stream_init(cubeb * context, cubeb_stream ** stream,
|
||||
|
||||
XASSERT(context && stream);
|
||||
|
||||
if (stream_params.format != CUBEB_SAMPLE_FLOAT32NE) {
|
||||
return CUBEB_ERROR_INVALID_FORMAT;
|
||||
}
|
||||
|
||||
cubeb_stream * stm = (cubeb_stream *)calloc(1, sizeof(cubeb_stream));
|
||||
|
||||
XASSERT(stm);
|
||||
@ -1258,7 +1235,7 @@ wasapi_stream_init(cubeb * context, cubeb_stream ** stream,
|
||||
hr = register_notification_client(stm);
|
||||
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. */
|
||||
* to keep using the default audio endpoint if it changes. */
|
||||
LOG("failed to register notification client, %x\n", hr);
|
||||
}
|
||||
|
||||
@ -1348,7 +1325,7 @@ int wasapi_stream_start(cubeb_stream * stm)
|
||||
LOG("could not start the stream after reconfig: %x\n", hr);
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
} else if (FAILED(hr)) {
|
||||
} else if (FAILED(hr)) {
|
||||
LOG("could not start the stream.\n");
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
@ -1425,16 +1402,13 @@ int wasapi_stream_get_latency(cubeb_stream * stm, uint32_t * latency)
|
||||
auto_lock lock(stm->stream_reset_lock);
|
||||
|
||||
/* The GetStreamLatency method only works if the
|
||||
AudioClient has been initialized. */
|
||||
* AudioClient has been initialized. */
|
||||
if (!stm->client) {
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
|
||||
REFERENCE_TIME latency_hns;
|
||||
HRESULT hr = stm->client->GetStreamLatency(&latency_hns);
|
||||
if (FAILED(hr)) {
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
stm->client->GetStreamLatency(&latency_hns);
|
||||
double latency_s = hns_to_s(latency_hns);
|
||||
*latency = static_cast<uint32_t>(latency_s * stm->stream_params.rate);
|
||||
|
||||
@ -1454,245 +1428,12 @@ int wasapi_stream_set_volume(cubeb_stream * stm, float volume)
|
||||
return CUBEB_OK;
|
||||
}
|
||||
|
||||
static char *
|
||||
wstr_to_utf8(LPCWSTR str)
|
||||
{
|
||||
char * ret = NULL;
|
||||
int size;
|
||||
|
||||
size = ::WideCharToMultiByte(CP_UTF8, 0, str, -1, ret, 0, NULL, NULL);
|
||||
if (size > 0) {
|
||||
ret = (char *) malloc(size);
|
||||
::WideCharToMultiByte(CP_UTF8, 0, str, -1, ret, size, NULL, NULL);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static IMMDevice *
|
||||
wasapi_get_device_node(IMMDeviceEnumerator * enumerator, IMMDevice * dev)
|
||||
{
|
||||
IMMDevice * ret = NULL;
|
||||
IDeviceTopology * devtopo = NULL;
|
||||
IConnector * connector = NULL;
|
||||
|
||||
if (SUCCEEDED(dev->Activate(__uuidof(IDeviceTopology), CLSCTX_ALL, NULL, (void**)&devtopo)) &&
|
||||
SUCCEEDED(devtopo->GetConnector(0, &connector))) {
|
||||
LPWSTR filterid;
|
||||
if (SUCCEEDED(connector->GetDeviceIdConnectedTo(&filterid))) {
|
||||
if (FAILED(enumerator->GetDevice(filterid, &ret)))
|
||||
ret = NULL;
|
||||
CoTaskMemFree(filterid);
|
||||
}
|
||||
}
|
||||
|
||||
SafeRelease(connector);
|
||||
SafeRelease(devtopo);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static BOOL
|
||||
wasapi_is_default_device(EDataFlow flow, ERole role, LPCWSTR device_id,
|
||||
IMMDeviceEnumerator * enumerator)
|
||||
{
|
||||
BOOL ret = FALSE;
|
||||
IMMDevice * dev;
|
||||
HRESULT hr;
|
||||
|
||||
hr = enumerator->GetDefaultAudioEndpoint(flow, role, &dev);
|
||||
if (SUCCEEDED(hr)) {
|
||||
LPWSTR defdevid = NULL;
|
||||
if (SUCCEEDED(dev->GetId(&defdevid)))
|
||||
ret = (wcscmp(defdevid, device_id) == 0);
|
||||
if (defdevid != NULL)
|
||||
CoTaskMemFree(defdevid);
|
||||
SafeRelease(dev);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static cubeb_device_info *
|
||||
wasapi_create_device(IMMDeviceEnumerator * enumerator, IMMDevice * dev)
|
||||
{
|
||||
IMMEndpoint * endpoint = NULL;
|
||||
IMMDevice * devnode;
|
||||
IAudioClient * client = NULL;
|
||||
cubeb_device_info * ret = NULL;
|
||||
EDataFlow flow;
|
||||
LPWSTR device_id = NULL;
|
||||
DWORD state = DEVICE_STATE_NOTPRESENT;
|
||||
IPropertyStore * propstore = NULL;
|
||||
PROPVARIANT propvar;
|
||||
REFERENCE_TIME def_period, min_period;
|
||||
HRESULT hr;
|
||||
|
||||
PropVariantInit(&propvar);
|
||||
|
||||
hr = dev->QueryInterface(IID_PPV_ARGS(&endpoint));
|
||||
if (FAILED(hr)) goto done;
|
||||
|
||||
hr = endpoint->GetDataFlow(&flow);
|
||||
if (FAILED(hr)) goto done;
|
||||
|
||||
hr = dev->GetId(&device_id);
|
||||
if (FAILED(hr)) goto done;
|
||||
|
||||
hr = dev->OpenPropertyStore(STGM_READ, &propstore);
|
||||
if (FAILED(hr)) goto done;
|
||||
|
||||
hr = dev->GetState(&state);
|
||||
if (FAILED(hr)) goto done;
|
||||
|
||||
ret = (cubeb_device_info *)calloc(1, sizeof(cubeb_device_info));
|
||||
|
||||
ret->devid = ret->device_id = wstr_to_utf8(device_id);
|
||||
hr = propstore->GetValue(PKEY_Device_FriendlyName, &propvar);
|
||||
if (SUCCEEDED(hr))
|
||||
ret->friendly_name = wstr_to_utf8(propvar.pwszVal);
|
||||
|
||||
devnode = wasapi_get_device_node(enumerator, dev);
|
||||
if (devnode != NULL) {
|
||||
IPropertyStore * ps = NULL;
|
||||
hr = devnode->OpenPropertyStore(STGM_READ, &ps);
|
||||
if (FAILED(hr)) goto done;
|
||||
|
||||
PropVariantClear(&propvar);
|
||||
hr = ps->GetValue(PKEY_Device_InstanceId, &propvar);
|
||||
if (SUCCEEDED(hr)) {
|
||||
ret->group_id = wstr_to_utf8(propvar.pwszVal);
|
||||
}
|
||||
SafeRelease(ps);
|
||||
}
|
||||
|
||||
ret->preferred = CUBEB_DEVICE_PREF_NONE;
|
||||
if (wasapi_is_default_device(flow, eMultimedia, device_id, enumerator))
|
||||
ret->preferred = (cubeb_device_pref)(ret->preferred | CUBEB_DEVICE_PREF_MULTIMEDIA);
|
||||
if (wasapi_is_default_device(flow, eCommunications, device_id, enumerator))
|
||||
ret->preferred = (cubeb_device_pref)(ret->preferred | CUBEB_DEVICE_PREF_VOICE);
|
||||
if (wasapi_is_default_device(flow, eConsole, device_id, enumerator))
|
||||
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;
|
||||
switch (state) {
|
||||
case DEVICE_STATE_ACTIVE:
|
||||
ret->state = CUBEB_DEVICE_STATE_ENABLED;
|
||||
break;
|
||||
case DEVICE_STATE_UNPLUGGED:
|
||||
ret->state = CUBEB_DEVICE_STATE_UNPLUGGED;
|
||||
break;
|
||||
default:
|
||||
ret->state = CUBEB_DEVICE_STATE_DISABLED;
|
||||
break;
|
||||
};
|
||||
|
||||
ret->format = CUBEB_DEVICE_FMT_F32NE; /* cubeb only supports 32bit float at the moment */
|
||||
ret->default_format = CUBEB_DEVICE_FMT_F32NE;
|
||||
PropVariantClear(&propvar);
|
||||
hr = propstore->GetValue(PKEY_AudioEngine_DeviceFormat, &propvar);
|
||||
if (SUCCEEDED(hr) && propvar.vt == VT_BLOB) {
|
||||
if (propvar.blob.cbSize == sizeof(PCMWAVEFORMAT)) {
|
||||
const PCMWAVEFORMAT * pcm = reinterpret_cast<const PCMWAVEFORMAT *>(propvar.blob.pBlobData);
|
||||
|
||||
ret->max_rate = ret->min_rate = ret->default_rate = pcm->wf.nSamplesPerSec;
|
||||
ret->max_channels = pcm->wf.nChannels;
|
||||
} else if (propvar.blob.cbSize >= sizeof(WAVEFORMATEX)) {
|
||||
WAVEFORMATEX* wfx = reinterpret_cast<WAVEFORMATEX*>(propvar.blob.pBlobData);
|
||||
|
||||
if (propvar.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (SUCCEEDED(dev->Activate(__uuidof(IAudioClient), CLSCTX_INPROC_SERVER, NULL, (void**)&client)) &&
|
||||
SUCCEEDED(client->GetDevicePeriod(&def_period, &min_period))) {
|
||||
ret->latency_lo_ms = hns_to_ms(min_period);
|
||||
ret->latency_hi_ms = hns_to_ms(def_period);
|
||||
} else {
|
||||
ret->latency_lo_ms = 0;
|
||||
ret->latency_hi_ms = 0;
|
||||
}
|
||||
SafeRelease(client);
|
||||
|
||||
done:
|
||||
SafeRelease(devnode);
|
||||
SafeRelease(endpoint);
|
||||
SafeRelease(propstore);
|
||||
if (device_id != NULL)
|
||||
CoTaskMemFree(device_id);
|
||||
PropVariantClear(&propvar);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
wasapi_enumerate_devices(cubeb * context, cubeb_device_type type,
|
||||
cubeb_device_collection ** out)
|
||||
{
|
||||
auto_com com;
|
||||
IMMDeviceEnumerator * enumerator;
|
||||
IMMDeviceCollection * collection;
|
||||
IMMDevice * dev;
|
||||
cubeb_device_info * cur;
|
||||
HRESULT hr;
|
||||
UINT cc, i;
|
||||
EDataFlow flow;
|
||||
|
||||
*out = NULL;
|
||||
|
||||
if (!com.ok())
|
||||
return CUBEB_ERROR;
|
||||
|
||||
hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL,
|
||||
CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&enumerator));
|
||||
if (FAILED(hr)) {
|
||||
LOG("Could not get device enumerator: %x\n", hr);
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
|
||||
if (type == CUBEB_DEVICE_TYPE_OUTPUT) flow = eRender;
|
||||
else if (type == CUBEB_DEVICE_TYPE_INPUT) flow = eCapture;
|
||||
else if (type & (CUBEB_DEVICE_TYPE_INPUT | CUBEB_DEVICE_TYPE_INPUT)) flow = eAll;
|
||||
else return CUBEB_ERROR;
|
||||
|
||||
hr = enumerator->EnumAudioEndpoints(flow, DEVICE_STATEMASK_ALL, &collection);
|
||||
if (FAILED(hr)) {
|
||||
LOG("Could not enumerate audio endpoints: %x\n", hr);
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
|
||||
hr = collection->GetCount(&cc);
|
||||
if (FAILED(hr)) {
|
||||
LOG("IMMDeviceCollection::GetCount() failed: %x\n", hr);
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
*out = (cubeb_device_collection *) malloc(sizeof(cubeb_device_collection) +
|
||||
sizeof(cubeb_device_info*) * (cc > 0 ? cc - 1 : 0));
|
||||
(*out)->count = 0;
|
||||
for (i = 0; i < cc; i++) {
|
||||
hr = collection->Item(i, &dev);
|
||||
if (FAILED(hr)) {
|
||||
LOG("IMMDeviceCollection::Item(%u) failed: %x\n", i-1, hr);
|
||||
} else if ((cur = wasapi_create_device(enumerator, dev)) != NULL) {
|
||||
(*out)->device[(*out)->count++] = cur;
|
||||
}
|
||||
}
|
||||
|
||||
SafeRelease(collection);
|
||||
SafeRelease(enumerator);
|
||||
return CUBEB_OK;
|
||||
}
|
||||
|
||||
cubeb_ops const wasapi_ops = {
|
||||
/*.init =*/ wasapi_init,
|
||||
/*.get_backend_id =*/ wasapi_get_backend_id,
|
||||
/*.get_max_channel_count =*/ wasapi_get_max_channel_count,
|
||||
/*.get_min_latency =*/ wasapi_get_min_latency,
|
||||
/*.get_preferred_sample_rate =*/ wasapi_get_preferred_sample_rate,
|
||||
/*.enumerate_devices =*/ wasapi_enumerate_devices,
|
||||
/*.destroy =*/ wasapi_destroy,
|
||||
/*.stream_init =*/ wasapi_stream_init,
|
||||
/*.stream_destroy =*/ wasapi_stream_destroy,
|
||||
|
@ -8,6 +8,7 @@
|
||||
#undef WINVER
|
||||
#define WINVER 0x0501
|
||||
#undef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
|
||||
#include <malloc.h>
|
||||
#include <windows.h>
|
||||
@ -26,46 +27,15 @@
|
||||
#endif
|
||||
|
||||
/**This is also missing from the MinGW headers. It also appears to be undocumented by Microsoft.*/
|
||||
#ifndef WAVE_FORMAT_48M08
|
||||
#define WAVE_FORMAT_48M08 0x00001000 /* 48 kHz, Mono, 8-bit */
|
||||
#endif
|
||||
#ifndef WAVE_FORMAT_48M16
|
||||
#define WAVE_FORMAT_48M16 0x00002000 /* 48 kHz, Mono, 16-bit */
|
||||
#endif
|
||||
#ifndef WAVE_FORMAT_48S08
|
||||
#define WAVE_FORMAT_48S08 0x00004000 /* 48 kHz, Stereo, 8-bit */
|
||||
#endif
|
||||
#ifndef WAVE_FORMAT_48S16
|
||||
#define WAVE_FORMAT_48S16 0x00008000 /* 48 kHz, Stereo, 16-bit */
|
||||
#endif
|
||||
#ifndef WAVE_FORMAT_96M08
|
||||
#define WAVE_FORMAT_96M08 0x00010000 /* 96 kHz, Mono, 8-bit */
|
||||
#endif
|
||||
#ifndef WAVE_FORMAT_96M16
|
||||
#define WAVE_FORMAT_96M16 0x00020000 /* 96 kHz, Mono, 16-bit */
|
||||
#endif
|
||||
#ifndef WAVE_FORMAT_96S08
|
||||
#define WAVE_FORMAT_96S08 0x00040000 /* 96 kHz, Stereo, 8-bit */
|
||||
#endif
|
||||
#ifndef WAVE_FORMAT_96S16
|
||||
#define WAVE_FORMAT_96S16 0x00080000 /* 96 kHz, Stereo, 16-bit */
|
||||
#endif
|
||||
|
||||
/**Taken from winbase.h, also not in MinGW.*/
|
||||
#ifndef STACK_SIZE_PARAM_IS_A_RESERVATION
|
||||
#define STACK_SIZE_PARAM_IS_A_RESERVATION 0x00010000 // Threads only
|
||||
#endif
|
||||
|
||||
#ifndef DRVM_MAPPER
|
||||
#define DRVM_MAPPER (0x2000)
|
||||
#endif
|
||||
#ifndef DRVM_MAPPER_PREFERRED_GET
|
||||
#define DRVM_MAPPER_PREFERRED_GET (DRVM_MAPPER+21)
|
||||
#endif
|
||||
#ifndef DRVM_MAPPER_CONSOLEVOICECOM_GET
|
||||
#define DRVM_MAPPER_CONSOLEVOICECOM_GET (DRVM_MAPPER+23)
|
||||
#endif
|
||||
|
||||
#define CUBEB_STREAM_MAX 32
|
||||
#define NBUFS 4
|
||||
|
||||
@ -701,307 +671,12 @@ winmm_stream_set_volume(cubeb_stream * stm, float volume)
|
||||
return CUBEB_OK;
|
||||
}
|
||||
|
||||
#define MM_11025HZ_MASK (WAVE_FORMAT_1M08 | WAVE_FORMAT_1M16 | WAVE_FORMAT_1S08 | WAVE_FORMAT_1S16)
|
||||
#define MM_22050HZ_MASK (WAVE_FORMAT_2M08 | WAVE_FORMAT_2M16 | WAVE_FORMAT_2S08 | WAVE_FORMAT_2S16)
|
||||
#define MM_44100HZ_MASK (WAVE_FORMAT_4M08 | WAVE_FORMAT_4M16 | WAVE_FORMAT_4S08 | WAVE_FORMAT_4S16)
|
||||
#define MM_48000HZ_MASK (WAVE_FORMAT_48M08 | WAVE_FORMAT_48M16 | WAVE_FORMAT_48S08 | WAVE_FORMAT_48S16)
|
||||
#define MM_96000HZ_MASK (WAVE_FORMAT_96M08 | WAVE_FORMAT_96M16 | WAVE_FORMAT_96S08 | WAVE_FORMAT_96S16)
|
||||
static void
|
||||
winmm_calculate_device_rate(cubeb_device_info * info, DWORD formats)
|
||||
{
|
||||
if (formats & MM_11025HZ_MASK) {
|
||||
info->min_rate = 11025;
|
||||
info->default_rate = 11025;
|
||||
info->max_rate = 11025;
|
||||
}
|
||||
if (formats & MM_22050HZ_MASK) {
|
||||
if (info->min_rate == 0) info->min_rate = 22050;
|
||||
info->max_rate = 22050;
|
||||
info->default_rate = 22050;
|
||||
}
|
||||
if (formats & MM_44100HZ_MASK) {
|
||||
if (info->min_rate == 0) info->min_rate = 44100;
|
||||
info->max_rate = 44100;
|
||||
info->default_rate = 44100;
|
||||
}
|
||||
if (formats & MM_48000HZ_MASK) {
|
||||
if (info->min_rate == 0) info->min_rate = 48000;
|
||||
info->max_rate = 48000;
|
||||
info->default_rate = 48000;
|
||||
}
|
||||
if (formats & MM_96000HZ_MASK) {
|
||||
if (info->min_rate == 0) {
|
||||
info->min_rate = 96000;
|
||||
info->default_rate = 96000;
|
||||
}
|
||||
info->max_rate = 96000;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#define MM_S16_MASK (WAVE_FORMAT_1M16 | WAVE_FORMAT_1S16 | WAVE_FORMAT_2M16 | WAVE_FORMAT_2S16 | WAVE_FORMAT_4M16 | \
|
||||
WAVE_FORMAT_4S16 | WAVE_FORMAT_48M16 | WAVE_FORMAT_48S16 | WAVE_FORMAT_96M16 | WAVE_FORMAT_96S16)
|
||||
static int
|
||||
winmm_query_supported_formats(UINT devid, DWORD formats,
|
||||
cubeb_device_fmt * supfmt, cubeb_device_fmt * deffmt)
|
||||
{
|
||||
WAVEFORMATEXTENSIBLE wfx;
|
||||
|
||||
if (formats & MM_S16_MASK)
|
||||
*deffmt = *supfmt = CUBEB_DEVICE_FMT_S16LE;
|
||||
else
|
||||
*deffmt = *supfmt = 0;
|
||||
|
||||
ZeroMemory(&wfx, sizeof(WAVEFORMATEXTENSIBLE));
|
||||
wfx.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
|
||||
wfx.Format.nChannels = 2;
|
||||
wfx.Format.nSamplesPerSec = 44100;
|
||||
wfx.Format.wBitsPerSample = 32;
|
||||
wfx.Format.nBlockAlign = (wfx.Format.wBitsPerSample * wfx.Format.nChannels) / 8;
|
||||
wfx.Format.nAvgBytesPerSec = wfx.Format.nSamplesPerSec * wfx.Format.nBlockAlign;
|
||||
wfx.Format.cbSize = 22;
|
||||
wfx.Samples.wValidBitsPerSample = wfx.Format.wBitsPerSample;
|
||||
wfx.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
|
||||
wfx.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
|
||||
if (waveOutOpen(NULL, devid, &wfx.Format, 0, 0, WAVE_FORMAT_QUERY) == MMSYSERR_NOERROR)
|
||||
*supfmt = (cubeb_device_fmt)(*supfmt | CUBEB_DEVICE_FMT_F32LE);
|
||||
|
||||
return (*deffmt != 0) ? CUBEB_OK : CUBEB_ERROR;
|
||||
}
|
||||
|
||||
static char *
|
||||
guid_to_cstr(LPGUID guid)
|
||||
{
|
||||
char * ret = malloc(sizeof(char) * 40);
|
||||
_snprintf(ret, sizeof(char) * 40,
|
||||
"{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}",
|
||||
guid->Data1, guid->Data2, guid->Data3,
|
||||
guid->Data4[0], guid->Data4[1], guid->Data4[2], guid->Data4[3],
|
||||
guid->Data4[4], guid->Data4[5], guid->Data4[6], guid->Data4[7]);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static cubeb_device_pref
|
||||
winmm_query_preferred_out_device(UINT devid)
|
||||
{
|
||||
DWORD mmpref = WAVE_MAPPER, compref = WAVE_MAPPER, status;
|
||||
cubeb_device_pref ret = CUBEB_DEVICE_PREF_NONE;
|
||||
|
||||
if (waveOutMessage((HWAVEOUT)(size_t)WAVE_MAPPER, DRVM_MAPPER_PREFERRED_GET,
|
||||
(DWORD_PTR)&mmpref, (DWORD_PTR)&status) == MMSYSERR_NOERROR &&
|
||||
devid == mmpref)
|
||||
ret |= CUBEB_DEVICE_PREF_MULTIMEDIA | CUBEB_DEVICE_PREF_NOTIFICATION;
|
||||
|
||||
if (waveOutMessage((HWAVEOUT)(size_t)WAVE_MAPPER, DRVM_MAPPER_CONSOLEVOICECOM_GET,
|
||||
(DWORD_PTR)&compref, (DWORD_PTR)&status) == MMSYSERR_NOERROR &&
|
||||
devid == compref)
|
||||
ret |= CUBEB_DEVICE_PREF_VOICE;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static char *
|
||||
device_id_idx(UINT devid)
|
||||
{
|
||||
char * ret = (char *)malloc(sizeof(char)*16);
|
||||
_snprintf(ret, 16, "%u", devid);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static cubeb_device_info *
|
||||
winmm_create_device_from_outcaps2(LPWAVEOUTCAPS2A caps, UINT devid)
|
||||
{
|
||||
cubeb_device_info * ret;
|
||||
|
||||
ret = calloc(1, sizeof(cubeb_device_info));
|
||||
ret->devid = (cubeb_devid)(size_t)devid;
|
||||
ret->device_id = device_id_idx(devid);
|
||||
ret->friendly_name = _strdup(caps->szPname);
|
||||
ret->group_id = guid_to_cstr(&caps->ProductGuid);
|
||||
ret->vendor_name = guid_to_cstr(&caps->ManufacturerGuid);
|
||||
|
||||
ret->type = CUBEB_DEVICE_TYPE_OUTPUT;
|
||||
ret->state = CUBEB_DEVICE_STATE_ENABLED;
|
||||
ret->preferred = winmm_query_preferred_out_device(devid);
|
||||
|
||||
ret->max_channels = caps->wChannels;
|
||||
winmm_calculate_device_rate(ret, caps->dwFormats);
|
||||
winmm_query_supported_formats(devid, caps->dwFormats,
|
||||
&ret->format, &ret->default_format);
|
||||
|
||||
/* Hardcoed latency estimates... */
|
||||
ret->latency_lo_ms = 100;
|
||||
ret->latency_hi_ms = 200;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static cubeb_device_info *
|
||||
winmm_create_device_from_outcaps(LPWAVEOUTCAPSA caps, UINT devid)
|
||||
{
|
||||
cubeb_device_info * ret;
|
||||
|
||||
ret = calloc(1, sizeof(cubeb_device_info));
|
||||
ret->devid = (cubeb_devid)(size_t)devid;
|
||||
ret->device_id = device_id_idx(devid);
|
||||
ret->friendly_name = _strdup(caps->szPname);
|
||||
ret->group_id = NULL;
|
||||
ret->vendor_name = NULL;
|
||||
|
||||
ret->type = CUBEB_DEVICE_TYPE_OUTPUT;
|
||||
ret->state = CUBEB_DEVICE_STATE_ENABLED;
|
||||
ret->preferred = winmm_query_preferred_out_device(devid);
|
||||
|
||||
ret->max_channels = caps->wChannels;
|
||||
winmm_calculate_device_rate(ret, caps->dwFormats);
|
||||
winmm_query_supported_formats(devid, caps->dwFormats,
|
||||
&ret->format, &ret->default_format);
|
||||
|
||||
/* Hardcoed latency estimates... */
|
||||
ret->latency_lo_ms = 100;
|
||||
ret->latency_hi_ms = 200;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static cubeb_device_pref
|
||||
winmm_query_preferred_in_device(UINT devid)
|
||||
{
|
||||
DWORD mmpref = WAVE_MAPPER, compref = WAVE_MAPPER, status;
|
||||
cubeb_device_pref ret = CUBEB_DEVICE_PREF_NONE;
|
||||
|
||||
if (waveInMessage((HWAVEIN)(size_t)WAVE_MAPPER, DRVM_MAPPER_PREFERRED_GET,
|
||||
(DWORD_PTR)&mmpref, (DWORD_PTR)&status) == MMSYSERR_NOERROR &&
|
||||
devid == mmpref)
|
||||
ret |= CUBEB_DEVICE_PREF_MULTIMEDIA | CUBEB_DEVICE_PREF_NOTIFICATION;
|
||||
|
||||
if (waveInMessage((HWAVEIN)(size_t)WAVE_MAPPER, DRVM_MAPPER_CONSOLEVOICECOM_GET,
|
||||
(DWORD_PTR)&compref, (DWORD_PTR)&status) == MMSYSERR_NOERROR &&
|
||||
devid == compref)
|
||||
ret |= CUBEB_DEVICE_PREF_VOICE;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static cubeb_device_info *
|
||||
winmm_create_device_from_incaps2(LPWAVEINCAPS2A caps, UINT devid)
|
||||
{
|
||||
cubeb_device_info * ret;
|
||||
|
||||
ret = calloc(1, sizeof(cubeb_device_info));
|
||||
ret->devid = (cubeb_devid)(size_t)devid;
|
||||
ret->device_id = device_id_idx(devid);
|
||||
ret->friendly_name = _strdup(caps->szPname);
|
||||
ret->group_id = guid_to_cstr(&caps->ProductGuid);
|
||||
ret->vendor_name = guid_to_cstr(&caps->ManufacturerGuid);
|
||||
|
||||
ret->type = CUBEB_DEVICE_TYPE_INPUT;
|
||||
ret->state = CUBEB_DEVICE_STATE_ENABLED;
|
||||
ret->preferred = winmm_query_preferred_in_device(devid);
|
||||
|
||||
ret->max_channels = caps->wChannels;
|
||||
winmm_calculate_device_rate(ret, caps->dwFormats);
|
||||
winmm_query_supported_formats(devid, caps->dwFormats,
|
||||
&ret->format, &ret->default_format);
|
||||
|
||||
/* Hardcoed latency estimates... */
|
||||
ret->latency_lo_ms = 100;
|
||||
ret->latency_hi_ms = 200;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static cubeb_device_info *
|
||||
winmm_create_device_from_incaps(LPWAVEINCAPSA caps, UINT devid)
|
||||
{
|
||||
cubeb_device_info * ret;
|
||||
|
||||
ret = calloc(1, sizeof(cubeb_device_info));
|
||||
ret->devid = (cubeb_devid)(size_t)devid;
|
||||
ret->device_id = device_id_idx(devid);
|
||||
ret->friendly_name = _strdup(caps->szPname);
|
||||
ret->group_id = NULL;
|
||||
ret->vendor_name = NULL;
|
||||
|
||||
ret->type = CUBEB_DEVICE_TYPE_INPUT;
|
||||
ret->state = CUBEB_DEVICE_STATE_ENABLED;
|
||||
ret->preferred = winmm_query_preferred_in_device(devid);
|
||||
|
||||
ret->max_channels = caps->wChannels;
|
||||
winmm_calculate_device_rate(ret, caps->dwFormats);
|
||||
winmm_query_supported_formats(devid, caps->dwFormats,
|
||||
&ret->format, &ret->default_format);
|
||||
|
||||
/* Hardcoed latency estimates... */
|
||||
ret->latency_lo_ms = 100;
|
||||
ret->latency_hi_ms = 200;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
winmm_enumerate_devices(cubeb * context, cubeb_device_type type,
|
||||
cubeb_device_collection ** collection)
|
||||
{
|
||||
UINT i, incount, outcount, total;
|
||||
cubeb_device_info * cur;
|
||||
|
||||
outcount = waveOutGetNumDevs();
|
||||
incount = waveInGetNumDevs();
|
||||
total = outcount + incount;
|
||||
if (total > 0) {
|
||||
total -= 1;
|
||||
}
|
||||
*collection = malloc(sizeof(cubeb_device_collection) +
|
||||
sizeof(cubeb_device_info*) * total);
|
||||
(*collection)->count = 0;
|
||||
|
||||
if (type & CUBEB_DEVICE_TYPE_OUTPUT) {
|
||||
WAVEOUTCAPSA woc;
|
||||
WAVEOUTCAPS2A woc2;
|
||||
|
||||
ZeroMemory(&woc, sizeof(woc));
|
||||
ZeroMemory(&woc2, sizeof(woc2));
|
||||
|
||||
for (i = 0; i < outcount; i++) {
|
||||
if ((waveOutGetDevCapsA(i, (LPWAVEOUTCAPSA)&woc2, sizeof(woc2)) == MMSYSERR_NOERROR &&
|
||||
(cur = winmm_create_device_from_outcaps2(&woc2, i)) != NULL) ||
|
||||
(waveOutGetDevCapsA(i, &woc, sizeof(woc)) == MMSYSERR_NOERROR &&
|
||||
(cur = winmm_create_device_from_outcaps(&woc, i)) != NULL)
|
||||
) {
|
||||
(*collection)->device[(*collection)->count++] = cur;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (type & CUBEB_DEVICE_TYPE_INPUT) {
|
||||
WAVEINCAPSA wic;
|
||||
WAVEINCAPS2A wic2;
|
||||
|
||||
ZeroMemory(&wic, sizeof(wic));
|
||||
ZeroMemory(&wic2, sizeof(wic2));
|
||||
|
||||
for (i = 0; i < incount; i++) {
|
||||
if ((waveInGetDevCapsA(i, (LPWAVEINCAPSA)&wic2, sizeof(wic2)) == MMSYSERR_NOERROR &&
|
||||
(cur = winmm_create_device_from_incaps2(&wic2, i)) != NULL) ||
|
||||
(waveInGetDevCapsA(i, &wic, sizeof(wic)) == MMSYSERR_NOERROR &&
|
||||
(cur = winmm_create_device_from_incaps(&wic, i)) != NULL)
|
||||
) {
|
||||
(*collection)->device[(*collection)->count++] = cur;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return CUBEB_OK;
|
||||
}
|
||||
|
||||
static struct cubeb_ops const winmm_ops = {
|
||||
/*.init =*/ winmm_init,
|
||||
/*.get_backend_id =*/ winmm_get_backend_id,
|
||||
/*.get_max_channel_count=*/ winmm_get_max_channel_count,
|
||||
/*.get_min_latency=*/ winmm_get_min_latency,
|
||||
/*.get_preferred_sample_rate =*/ winmm_get_preferred_sample_rate,
|
||||
/*.enumerate_devices =*/ winmm_enumerate_devices,
|
||||
/*.destroy =*/ winmm_destroy,
|
||||
/*.stream_init =*/ winmm_stream_init,
|
||||
/*.stream_destroy =*/ winmm_stream_destroy,
|
||||
|
@ -45,8 +45,6 @@ typedef struct {
|
||||
synth_state* synth_create(int num_channels, float sample_rate)
|
||||
{
|
||||
synth_state* synth = (synth_state *) malloc(sizeof(synth_state));
|
||||
if (!synth)
|
||||
return NULL;
|
||||
for(int i=0;i < MAX_NUM_CHANNELS;++i)
|
||||
synth->phase[i] = 0.0f;
|
||||
synth->num_channels = num_channels;
|
||||
|
Loading…
Reference in New Issue
Block a user