Merge pull request #6132 from ligfx/updatecubeb
Externals: update cubeb to kinetiknz/cubeb@c2bd582
This commit is contained in:
commit
b002d9d94f
|
@ -43,9 +43,11 @@ add_library(cubeb
|
|||
src/cubeb_resampler.cpp
|
||||
src/cubeb_panner.cpp
|
||||
src/cubeb_log.cpp
|
||||
src/cubeb_utils.c
|
||||
src/cubeb_strings.c
|
||||
$<TARGET_OBJECTS:speex>)
|
||||
target_include_directories(cubeb PUBLIC include)
|
||||
target_include_directories(cubeb
|
||||
PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include> $<INSTALL_INTERFACE:include>
|
||||
)
|
||||
target_include_directories(cubeb PRIVATE src)
|
||||
target_compile_definitions(cubeb PRIVATE OUTSIDE_SPEEX)
|
||||
target_compile_definitions(cubeb PRIVATE FLOATING_POINT)
|
||||
|
@ -56,7 +58,42 @@ add_sanitizers(cubeb)
|
|||
|
||||
include(GenerateExportHeader)
|
||||
generate_export_header(cubeb EXPORT_FILE_NAME ${CMAKE_BINARY_DIR}/exports/cubeb_export.h)
|
||||
target_include_directories(cubeb PUBLIC ${CMAKE_BINARY_DIR}/exports)
|
||||
target_include_directories(cubeb
|
||||
PUBLIC $<BUILD_INTERFACE:${CMAKE_BINARY_DIR}/exports>
|
||||
)
|
||||
|
||||
install(DIRECTORY ${CMAKE_SOURCE_DIR}/include DESTINATION ${CMAKE_INSTALL_PREFIX})
|
||||
install(DIRECTORY ${CMAKE_BINARY_DIR}/exports/ DESTINATION ${CMAKE_INSTALL_PREFIX}/include/cubeb)
|
||||
|
||||
include(CMakePackageConfigHelpers)
|
||||
write_basic_package_version_file(
|
||||
"${PROJECT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake"
|
||||
COMPATIBILITY SameMajorVersion
|
||||
)
|
||||
|
||||
configure_package_config_file(
|
||||
"Config.cmake.in"
|
||||
"${PROJECT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
|
||||
INSTALL_DESTINATION "lib/cmake/${PROJECT_NAME}"
|
||||
)
|
||||
|
||||
install(TARGETS cubeb
|
||||
EXPORT "${PROJECT_NAME}Targets"
|
||||
DESTINATION ${CMAKE_INSTALL_PREFIX}
|
||||
LIBRARY DESTINATION "lib"
|
||||
ARCHIVE DESTINATION "lib"
|
||||
RUNTIME DESTINATION "bin"
|
||||
INCLUDES DESTINATION "include"
|
||||
)
|
||||
install(
|
||||
FILES "${PROJECT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" "${PROJECT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake"
|
||||
DESTINATION "lib/cmake/${PROJECT_NAME}"
|
||||
)
|
||||
install(
|
||||
EXPORT "${PROJECT_NAME}Targets"
|
||||
NAMESPACE "${PROJECT_NAME}::"
|
||||
DESTINATION "lib/cmake/${PROJECT_NAME}"
|
||||
)
|
||||
|
||||
add_library(speex OBJECT
|
||||
src/speex/resample.c)
|
||||
|
@ -82,6 +119,7 @@ if(USE_PULSE)
|
|||
target_sources(cubeb PRIVATE
|
||||
src/cubeb_pulse.c)
|
||||
target_compile_definitions(cubeb PRIVATE USE_PULSE)
|
||||
target_link_libraries(cubeb PRIVATE pulse)
|
||||
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||
target_link_libraries(cubeb PRIVATE dl)
|
||||
endif()
|
||||
|
@ -93,6 +131,9 @@ if(USE_ALSA)
|
|||
src/cubeb_alsa.c)
|
||||
target_compile_definitions(cubeb PRIVATE USE_ALSA)
|
||||
target_link_libraries(cubeb PRIVATE asound pthread)
|
||||
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||
target_link_libraries(cubeb PRIVATE dl)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
check_include_files(jack/jack.h USE_JACK)
|
||||
|
@ -100,10 +141,10 @@ if(USE_JACK)
|
|||
target_sources(cubeb PRIVATE
|
||||
src/cubeb_jack.cpp)
|
||||
target_compile_definitions(cubeb PRIVATE USE_JACK)
|
||||
target_link_libraries(cubeb PRIVATE jack pthread)
|
||||
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||
target_link_libraries(cubeb PRIVATE dl)
|
||||
endif()
|
||||
target_link_libraries(cubeb PRIVATE pthread)
|
||||
endif()
|
||||
|
||||
check_include_files(audioclient.h USE_WASAPI)
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
@PACKAGE_INIT@
|
||||
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/cubebTargets.cmake")
|
||||
check_required_components(cubeb)
|
|
@ -33,11 +33,11 @@ extern "C" {
|
|||
cubeb * app_ctx;
|
||||
cubeb_init(&app_ctx, "Example Application");
|
||||
int rv;
|
||||
int rate;
|
||||
int latency_frames;
|
||||
uint32_t rate;
|
||||
uint32_t latency_frames;
|
||||
uint64_t ts;
|
||||
|
||||
rv = cubeb_get_min_latency(app_ctx, output_params, &latency_frames);
|
||||
rv = cubeb_get_min_latency(app_ctx, &output_params, &latency_frames);
|
||||
if (rv != CUBEB_OK) {
|
||||
fprintf(stderr, "Could not get minimum latency");
|
||||
return rv;
|
||||
|
@ -144,28 +144,6 @@ typedef enum {
|
|||
#endif
|
||||
} cubeb_sample_format;
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
/**
|
||||
* This maps to the underlying stream types on supported platforms, e.g.
|
||||
* Android.
|
||||
*/
|
||||
typedef enum {
|
||||
CUBEB_STREAM_TYPE_VOICE_CALL = 0,
|
||||
CUBEB_STREAM_TYPE_SYSTEM = 1,
|
||||
CUBEB_STREAM_TYPE_RING = 2,
|
||||
CUBEB_STREAM_TYPE_MUSIC = 3,
|
||||
CUBEB_STREAM_TYPE_ALARM = 4,
|
||||
CUBEB_STREAM_TYPE_NOTIFICATION = 5,
|
||||
CUBEB_STREAM_TYPE_BLUETOOTH_SCO = 6,
|
||||
CUBEB_STREAM_TYPE_SYSTEM_ENFORCED = 7,
|
||||
CUBEB_STREAM_TYPE_DTMF = 8,
|
||||
CUBEB_STREAM_TYPE_TTS = 9,
|
||||
CUBEB_STREAM_TYPE_FM = 10,
|
||||
|
||||
CUBEB_STREAM_TYPE_MAX
|
||||
} cubeb_stream_type;
|
||||
#endif
|
||||
|
||||
/** An opaque handle used to refer a particular input or output device
|
||||
* across calls. */
|
||||
typedef void const * cubeb_devid;
|
||||
|
@ -239,12 +217,9 @@ typedef enum {
|
|||
typedef struct {
|
||||
cubeb_sample_format format; /**< Requested sample format. One of
|
||||
#cubeb_sample_format. */
|
||||
unsigned int rate; /**< Requested sample rate. Valid range is [1000, 192000]. */
|
||||
unsigned int channels; /**< Requested channel count. Valid range is [1, 8]. */
|
||||
uint32_t rate; /**< Requested sample rate. Valid range is [1000, 192000]. */
|
||||
uint32_t channels; /**< Requested channel count. Valid range is [1, 8]. */
|
||||
cubeb_channel_layout layout; /**< Requested channel layout. This must be consistent with the provided channels. */
|
||||
#if defined(__ANDROID__)
|
||||
cubeb_stream_type stream_type; /**< Used to map Android audio stream types */
|
||||
#endif
|
||||
} cubeb_stream_params;
|
||||
|
||||
/** Audio device description */
|
||||
|
@ -348,13 +323,13 @@ typedef struct {
|
|||
|
||||
cubeb_device_fmt format; /**< Sample format supported. */
|
||||
cubeb_device_fmt default_format; /**< The default sample format for this device. */
|
||||
unsigned int max_channels; /**< Channels. */
|
||||
unsigned int default_rate; /**< Default/Preferred sample rate. */
|
||||
unsigned int max_rate; /**< Maximum sample rate supported. */
|
||||
unsigned int min_rate; /**< Minimum sample rate supported. */
|
||||
uint32_t max_channels; /**< Channels. */
|
||||
uint32_t default_rate; /**< Default/Preferred sample rate. */
|
||||
uint32_t max_rate; /**< Maximum sample rate supported. */
|
||||
uint32_t min_rate; /**< Minimum sample rate supported. */
|
||||
|
||||
unsigned int latency_lo; /**< Lowest possible latency in frames. */
|
||||
unsigned int latency_hi; /**< Higest possible latency in frames. */
|
||||
uint32_t latency_lo; /**< Lowest possible latency in frames. */
|
||||
uint32_t latency_hi; /**< Higest possible latency in frames. */
|
||||
} cubeb_device_info;
|
||||
|
||||
/** Device collection.
|
||||
|
@ -405,7 +380,7 @@ typedef void (* cubeb_device_changed_callback)(void * user_ptr);
|
|||
/**
|
||||
* User supplied callback called when the underlying device collection changed.
|
||||
* @param context A pointer to the cubeb context.
|
||||
* @param user_ptr The pointer passed to cubeb_stream_init. */
|
||||
* @param user_ptr The pointer passed to cubeb_register_device_collection_changed. */
|
||||
typedef void (* cubeb_device_collection_changed_callback)(cubeb * context,
|
||||
void * user_ptr);
|
||||
|
||||
|
@ -455,7 +430,7 @@ CUBEB_EXPORT int cubeb_get_max_channel_count(cubeb * context, uint32_t * max_cha
|
|||
@retval CUBEB_ERROR_INVALID_PARAMETER
|
||||
@retval CUBEB_ERROR_NOT_SUPPORTED */
|
||||
CUBEB_EXPORT int cubeb_get_min_latency(cubeb * context,
|
||||
cubeb_stream_params params,
|
||||
cubeb_stream_params * params,
|
||||
uint32_t * latency_frames);
|
||||
|
||||
/** Get the preferred sample rate for this backend: this is hardware and
|
||||
|
@ -512,7 +487,7 @@ CUBEB_EXPORT int cubeb_stream_init(cubeb * context,
|
|||
cubeb_stream_params * input_stream_params,
|
||||
cubeb_devid output_device,
|
||||
cubeb_stream_params * output_stream_params,
|
||||
unsigned int latency_frames,
|
||||
uint32_t latency_frames,
|
||||
cubeb_data_callback data_callback,
|
||||
cubeb_state_callback state_callback,
|
||||
void * user_ptr);
|
||||
|
@ -534,6 +509,14 @@ CUBEB_EXPORT int cubeb_stream_start(cubeb_stream * stream);
|
|||
@retval CUBEB_ERROR */
|
||||
CUBEB_EXPORT int cubeb_stream_stop(cubeb_stream * stream);
|
||||
|
||||
/** Reset stream to the default device.
|
||||
@param stream
|
||||
@retval CUBEB_OK
|
||||
@retval CUBEB_ERROR_INVALID_PARAMETER
|
||||
@retval CUBEB_ERROR_NOT_SUPPORTED
|
||||
@retval CUBEB_ERROR */
|
||||
CUBEB_EXPORT int cubeb_stream_reset_default_device(cubeb_stream * stream);
|
||||
|
||||
/** Get the current stream playback position.
|
||||
@param stream
|
||||
@param position Playback position in frames.
|
||||
|
|
|
@ -57,6 +57,7 @@
|
|||
<ClInclude Include="..\src\cubeb_resampler_internal.h" />
|
||||
<ClInclude Include="..\src\cubeb_ringbuffer.h" />
|
||||
<ClInclude Include="..\src\cubeb_ring_array.h" />
|
||||
<ClInclude Include="..\src\cubeb_strings.h" />
|
||||
<ClInclude Include="..\src\cubeb_utils.h" />
|
||||
<ClInclude Include="..\src\cubeb_utils_unix.h" />
|
||||
<ClInclude Include="..\src\cubeb_utils_win.h" />
|
||||
|
@ -74,7 +75,7 @@
|
|||
<ClCompile Include="..\src\cubeb_mixer.cpp" />
|
||||
<ClCompile Include="..\src\cubeb_panner.cpp" />
|
||||
<ClCompile Include="..\src\cubeb_resampler.cpp" />
|
||||
<ClCompile Include="..\src\cubeb_utils.c" />
|
||||
<ClCompile Include="..\src\cubeb_strings.c" />
|
||||
<ClCompile Include="..\src\cubeb_wasapi.cpp" />
|
||||
<ClCompile Include="..\src\cubeb_winmm.c" />
|
||||
<ClCompile Include="..\src\speex\resample.c" />
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
<ClInclude Include="..\src\cubeb_resampler_internal.h" />
|
||||
<ClInclude Include="..\src\cubeb_ring_array.h" />
|
||||
<ClInclude Include="..\src\cubeb_ringbuffer.h" />
|
||||
<ClInclude Include="..\src\cubeb_strings.h" />
|
||||
<ClInclude Include="..\src\cubeb_utils.h" />
|
||||
<ClInclude Include="..\src\cubeb_utils_unix.h" />
|
||||
<ClInclude Include="..\src\cubeb_utils_win.h" />
|
||||
|
@ -45,7 +46,7 @@
|
|||
<ClCompile Include="..\src\cubeb_mixer.cpp" />
|
||||
<ClCompile Include="..\src\cubeb_panner.cpp" />
|
||||
<ClCompile Include="..\src\cubeb_resampler.cpp" />
|
||||
<ClCompile Include="..\src\cubeb_utils.c" />
|
||||
<ClCompile Include="..\src\cubeb_strings.c" />
|
||||
<ClCompile Include="..\src\cubeb_wasapi.cpp" />
|
||||
<ClCompile Include="..\src\cubeb_winmm.c" />
|
||||
<ClCompile Include="..\src\speex\resample.c">
|
||||
|
@ -57,4 +58,4 @@
|
|||
<UniqueIdentifier>{caf7c7d0-0918-4299-8423-b287285d6fd0}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
|
|
@ -69,6 +69,7 @@ struct cubeb_ops {
|
|||
void (* stream_destroy)(cubeb_stream * stream);
|
||||
int (* stream_start)(cubeb_stream * stream);
|
||||
int (* stream_stop)(cubeb_stream * stream);
|
||||
int (* stream_reset_default_device)(cubeb_stream * stream);
|
||||
int (* stream_get_position)(cubeb_stream * stream, uint64_t * position);
|
||||
int (* stream_get_latency)(cubeb_stream * stream, uint32_t * latency);
|
||||
int (* stream_set_volume)(cubeb_stream * stream, float volumes);
|
||||
|
|
|
@ -245,9 +245,9 @@ cubeb_get_max_channel_count(cubeb * context, uint32_t * max_channels)
|
|||
}
|
||||
|
||||
int
|
||||
cubeb_get_min_latency(cubeb * context, cubeb_stream_params params, uint32_t * latency_ms)
|
||||
cubeb_get_min_latency(cubeb * context, cubeb_stream_params * params, uint32_t * latency_ms)
|
||||
{
|
||||
if (!context || !latency_ms) {
|
||||
if (!context || !params || !latency_ms) {
|
||||
return CUBEB_ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
|
@ -255,7 +255,7 @@ cubeb_get_min_latency(cubeb * context, cubeb_stream_params params, uint32_t * la
|
|||
return CUBEB_ERROR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
return context->ops->get_min_latency(context, params, latency_ms);
|
||||
return context->ops->get_min_latency(context, *params, latency_ms);
|
||||
}
|
||||
|
||||
int
|
||||
|
@ -368,6 +368,20 @@ cubeb_stream_stop(cubeb_stream * stream)
|
|||
return stream->context->ops->stream_stop(stream);
|
||||
}
|
||||
|
||||
int
|
||||
cubeb_stream_reset_default_device(cubeb_stream * stream)
|
||||
{
|
||||
if (!stream) {
|
||||
return CUBEB_ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (!stream->context->ops->stream_reset_default_device) {
|
||||
return CUBEB_ERROR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
return stream->context->ops->stream_reset_default_device(stream);
|
||||
}
|
||||
|
||||
int
|
||||
cubeb_stream_get_position(cubeb_stream * stream, uint64_t * position)
|
||||
{
|
||||
|
@ -574,13 +588,24 @@ int cubeb_enumerate_devices(cubeb * context,
|
|||
int cubeb_device_collection_destroy(cubeb * context,
|
||||
cubeb_device_collection * collection)
|
||||
{
|
||||
int r;
|
||||
|
||||
if (context == NULL || collection == NULL)
|
||||
return CUBEB_ERROR_INVALID_PARAMETER;
|
||||
|
||||
if (!context->ops->device_collection_destroy)
|
||||
return CUBEB_ERROR_NOT_SUPPORTED;
|
||||
|
||||
return context->ops->device_collection_destroy(context, collection);
|
||||
if (!collection->device)
|
||||
return CUBEB_OK;
|
||||
|
||||
r = context->ops->device_collection_destroy(context, collection);
|
||||
if (r == CUBEB_OK) {
|
||||
collection->device = NULL;
|
||||
collection->count = 0;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int cubeb_register_device_collection_changed(cubeb * context,
|
||||
|
|
|
@ -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
|
||||
|
@ -1361,6 +1360,7 @@ static struct cubeb_ops const alsa_ops = {
|
|||
.stream_destroy = alsa_stream_destroy,
|
||||
.stream_start = alsa_stream_start,
|
||||
.stream_stop = alsa_stream_stop,
|
||||
.stream_reset_default_device = NULL,
|
||||
.stream_get_position = alsa_stream_get_position,
|
||||
.stream_get_latency = alsa_stream_get_latency,
|
||||
.stream_set_volume = alsa_stream_set_volume,
|
||||
|
|
|
@ -145,9 +145,9 @@ audiotrack_get_min_frame_count(cubeb * ctx, cubeb_stream_params * params, int *
|
|||
status_t status;
|
||||
/* Recent Android have a getMinFrameCount method. */
|
||||
if (!audiotrack_version_is_gingerbread(ctx)) {
|
||||
status = ctx->klass.get_min_frame_count(min_frame_count, params->stream_type, params->rate);
|
||||
status = ctx->klass.get_min_frame_count(min_frame_count, AUDIO_STREAM_TYPE_MUSIC, params->rate);
|
||||
} else {
|
||||
status = ctx->klass.get_min_frame_count_gingerbread(min_frame_count, params->stream_type, params->rate);
|
||||
status = ctx->klass.get_min_frame_count_gingerbread(min_frame_count, AUDIO_STREAM_TYPE_MUSIC, params->rate);
|
||||
}
|
||||
if (status != 0) {
|
||||
ALOG("error getting the min frame count");
|
||||
|
@ -325,7 +325,7 @@ audiotrack_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_
|
|||
channels = stm->params.channels == 2 ? AUDIO_CHANNEL_OUT_STEREO_ICS : AUDIO_CHANNEL_OUT_MONO_ICS;
|
||||
}
|
||||
|
||||
ctx->klass.ctor(stm->instance, stm->params.stream_type, stm->params.rate,
|
||||
ctx->klass.ctor(stm->instance, AUDIO_STREAM_TYPE_MUSIC, stm->params.rate,
|
||||
AUDIO_FORMAT_PCM_16_BIT, channels, min_frame_count, 0,
|
||||
audiotrack_refill, stm, 0, 0);
|
||||
|
||||
|
@ -429,6 +429,7 @@ static struct cubeb_ops const audiotrack_ops = {
|
|||
.stream_destroy = audiotrack_stream_destroy,
|
||||
.stream_start = audiotrack_stream_start,
|
||||
.stream_stop = audiotrack_stream_stop,
|
||||
.stream_reset_default_device = NULL,
|
||||
.stream_get_position = audiotrack_stream_get_position,
|
||||
.stream_get_latency = audiotrack_stream_get_latency,
|
||||
.stream_set_volume = audiotrack_stream_set_volume,
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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,12 +121,13 @@ 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,
|
||||
.stream_start = cbjack_stream_start,
|
||||
.stream_stop = cbjack_stream_stop,
|
||||
.stream_reset_default_device = NULL,
|
||||
.stream_get_position = cbjack_stream_get_position,
|
||||
.stream_get_latency = cbjack_get_latency,
|
||||
.stream_set_volume = cbjack_stream_set_volume,
|
||||
|
@ -973,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)
|
||||
|
@ -982,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;
|
||||
|
@ -1012,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;
|
||||
|
@ -1035,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;
|
||||
}
|
||||
|
|
|
@ -351,6 +351,7 @@ static struct cubeb_ops const kai_ops = {
|
|||
/*.stream_destroy =*/ kai_stream_destroy,
|
||||
/*.stream_start =*/ kai_stream_start,
|
||||
/*.stream_stop =*/ kai_stream_stop,
|
||||
/*.stream_reset_default_device =*/ NULL,
|
||||
/*.stream_get_position =*/ kai_stream_get_position,
|
||||
/*.stream_get_latency = */ kai_stream_get_latency,
|
||||
/*.stream_set_volume =*/ kai_stream_set_volume,
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -299,7 +299,7 @@ mix_remap(long inframes,
|
|||
T * out, unsigned long out_len,
|
||||
cubeb_channel_layout in_layout, cubeb_channel_layout out_layout)
|
||||
{
|
||||
assert(in_layout != out_layout);
|
||||
assert(in_layout != out_layout && inframes >= 0);
|
||||
|
||||
// We might overwrite the data before we copied them to the mapped index
|
||||
// (e.g. upmixing from stereo to 2F1 or mapping [L, R] to [R, L])
|
||||
|
@ -325,15 +325,16 @@ mix_remap(long inframes,
|
|||
return false;
|
||||
}
|
||||
|
||||
for (unsigned long i = 0, out_index = 0; i < inframes * in_channels; i += in_channels, out_index += out_channels) {
|
||||
for (unsigned long i = 0, out_index = 0; i < (unsigned long)inframes * in_channels; i += in_channels, out_index += out_channels) {
|
||||
for (unsigned int j = 0; j < out_channels; ++j) {
|
||||
cubeb_channel channel = CHANNEL_INDEX_TO_ORDER[out_layout][j];
|
||||
uint32_t channel_mask = 1 << channel;
|
||||
int channel_index = CHANNEL_ORDER_TO_INDEX[in_layout][channel];
|
||||
assert(channel_index >= -1);
|
||||
assert(out_index + j < out_len);
|
||||
if (in_layout_mask & channel_mask) {
|
||||
assert(i + channel_index < in_len);
|
||||
assert(channel_index != -1);
|
||||
assert(i + channel_index < in_len);
|
||||
out[out_index + j] = in[i + channel_index];
|
||||
} else {
|
||||
assert(channel_index == -1);
|
||||
|
@ -353,13 +354,13 @@ downmix_fallback(long inframes,
|
|||
T * out, unsigned long out_len,
|
||||
unsigned int in_channels, unsigned int out_channels)
|
||||
{
|
||||
assert(in_channels >= out_channels);
|
||||
assert(in_channels >= out_channels && inframes >= 0);
|
||||
|
||||
if (in_channels == out_channels && in == out) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (unsigned long i = 0, out_index = 0; i < inframes * in_channels; i += in_channels, out_index += out_channels) {
|
||||
for (unsigned long i = 0, out_index = 0; i < (unsigned long)inframes * in_channels; i += in_channels, out_index += out_channels) {
|
||||
for (unsigned int j = 0; j < out_channels; ++j) {
|
||||
assert(i + j < in_len && out_index + j < out_len);
|
||||
out[out_index + j] = in[i + j];
|
||||
|
@ -416,7 +417,7 @@ mono_to_stereo(long insamples, T const * in, unsigned long in_len,
|
|||
T * out, unsigned long out_len, unsigned int out_channels)
|
||||
{
|
||||
for (long i = 0, j = 0; i < insamples; ++i, j += out_channels) {
|
||||
assert(i < in_len && j + 1 < out_len);
|
||||
assert((unsigned long)i < in_len && (unsigned long)j + 1 < out_len);
|
||||
out[j] = out[j + 1] = in[i];
|
||||
}
|
||||
}
|
||||
|
@ -460,7 +461,7 @@ cubeb_upmix(long inframes,
|
|||
/* Put silence in remaining channels. */
|
||||
for (long i = 0, o = 0; i < inframes; ++i, o += out_channels) {
|
||||
for (unsigned int j = 2; j < out_channels; ++j) {
|
||||
assert(o + j < out_len);
|
||||
assert((unsigned long)o + j < out_len);
|
||||
out[o + j] = 0.0;
|
||||
}
|
||||
}
|
||||
|
@ -490,7 +491,8 @@ cubeb_should_downmix(cubeb_stream_params const * stream, cubeb_stream_params con
|
|||
bool
|
||||
cubeb_should_mix(cubeb_stream_params const * stream, cubeb_stream_params const * mixer)
|
||||
{
|
||||
return cubeb_should_upmix(stream, mixer) || cubeb_should_downmix(stream, mixer);
|
||||
return stream->layout != CUBEB_LAYOUT_UNDEFINED &&
|
||||
(cubeb_should_upmix(stream, mixer) || cubeb_should_downmix(stream, mixer));
|
||||
}
|
||||
|
||||
struct cubeb_mixer {
|
||||
|
@ -559,11 +561,11 @@ void cubeb_mixer_destroy(cubeb_mixer * mixer)
|
|||
|
||||
void cubeb_mixer_mix(cubeb_mixer * mixer, long frames,
|
||||
void * input_buffer, unsigned long input_buffer_length,
|
||||
void * output_buffer, unsigned long outputput_buffer_length,
|
||||
void * output_buffer, unsigned long output_buffer_length,
|
||||
cubeb_stream_params const * stream_params,
|
||||
cubeb_stream_params const * mixer_params)
|
||||
{
|
||||
assert(mixer);
|
||||
mixer->mix(frames, input_buffer, input_buffer_length, output_buffer, outputput_buffer_length,
|
||||
mixer->mix(frames, input_buffer, input_buffer_length, output_buffer, output_buffer_length,
|
||||
stream_params, mixer_params);
|
||||
}
|
||||
|
|
|
@ -79,7 +79,7 @@ cubeb_mixer * cubeb_mixer_create(cubeb_sample_format format,
|
|||
void cubeb_mixer_destroy(cubeb_mixer * mixer);
|
||||
void cubeb_mixer_mix(cubeb_mixer * mixer, long frames,
|
||||
void * input_buffer, unsigned long input_buffer_length,
|
||||
void * output_buffer, unsigned long outputput_buffer_length,
|
||||
void * output_buffer, unsigned long output_buffer_length,
|
||||
cubeb_stream_params const * stream_params,
|
||||
cubeb_stream_params const * mixer_params);
|
||||
|
||||
|
|
|
@ -105,7 +105,6 @@ struct cubeb_stream {
|
|||
/* Flag indicating draining. Synchronized
|
||||
* by stream::mutex lock. */
|
||||
int draining;
|
||||
cubeb_stream_type stream_type;
|
||||
/* Flags to determine in/out.*/
|
||||
uint32_t input_enabled;
|
||||
uint32_t output_enabled;
|
||||
|
@ -187,6 +186,29 @@ opensl_set_draining(cubeb_stream * stm, int value)
|
|||
stm->draining = value;
|
||||
}
|
||||
|
||||
static void
|
||||
opensl_notify_drained(cubeb_stream * stm)
|
||||
{
|
||||
assert(stm);
|
||||
int r = pthread_mutex_lock(&stm->mutex);
|
||||
assert(r == 0);
|
||||
int draining = opensl_get_draining(stm);
|
||||
r = pthread_mutex_unlock(&stm->mutex);
|
||||
assert(r == 0);
|
||||
if (draining) {
|
||||
stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED);
|
||||
if (stm->play) {
|
||||
LOG("stop player in play_callback");
|
||||
r = opensl_stop_player(stm);
|
||||
assert(r == CUBEB_OK);
|
||||
}
|
||||
if (stm->recorderItf) {
|
||||
r = opensl_stop_recorder(stm);
|
||||
assert(r == CUBEB_OK);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
opensl_get_shutdown(cubeb_stream * stm)
|
||||
{
|
||||
|
@ -217,24 +239,7 @@ play_callback(SLPlayItf caller, void * user_ptr, SLuint32 event)
|
|||
assert(stm);
|
||||
switch (event) {
|
||||
case SL_PLAYEVENT_HEADATMARKER:
|
||||
{
|
||||
int r = pthread_mutex_lock(&stm->mutex);
|
||||
assert(r == 0);
|
||||
draining = opensl_get_draining(stm);
|
||||
r = pthread_mutex_unlock(&stm->mutex);
|
||||
assert(r == 0);
|
||||
if (draining) {
|
||||
stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED);
|
||||
if (stm->play) {
|
||||
r = opensl_stop_player(stm);
|
||||
assert(r == CUBEB_OK);
|
||||
}
|
||||
if (stm->recorderItf) {
|
||||
r = opensl_stop_recorder(stm);
|
||||
assert(r == CUBEB_OK);
|
||||
}
|
||||
}
|
||||
}
|
||||
opensl_notify_drained(stm);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
@ -330,9 +335,16 @@ bufferqueue_callback(SLBufferQueueItf caller, void * user_ptr)
|
|||
opensl_set_draining(stm, 1);
|
||||
r = pthread_mutex_unlock(&stm->mutex);
|
||||
assert(r == 0);
|
||||
// Use SL_PLAYEVENT_HEADATMARKER event from slPlayCallback of SLPlayItf
|
||||
// to make sure all the data has been processed.
|
||||
(*stm->play)->SetMarkerPosition(stm->play, (SLmillisecond)written_duration);
|
||||
|
||||
if (written_duration == 0) {
|
||||
// since we didn't write any sample, it's not possible to reach the marker
|
||||
// time and trigger the callback. We should initiative notify drained.
|
||||
opensl_notify_drained(stm);
|
||||
} else {
|
||||
// Use SL_PLAYEVENT_HEADATMARKER event from slPlayCallback of SLPlayItf
|
||||
// to make sure all the data has been processed.
|
||||
(*stm->play)->SetMarkerPosition(stm->play, (SLmillisecond)written_duration);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -586,31 +598,6 @@ player_fullduplex_callback(SLBufferQueueItf caller, void * user_ptr)
|
|||
TIMESTAMP("EXIT");
|
||||
}
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
static SLuint32
|
||||
convert_stream_type_to_sl_stream(cubeb_stream_type stream_type)
|
||||
{
|
||||
switch(stream_type) {
|
||||
case CUBEB_STREAM_TYPE_SYSTEM:
|
||||
return SL_ANDROID_STREAM_SYSTEM;
|
||||
case CUBEB_STREAM_TYPE_MUSIC:
|
||||
return SL_ANDROID_STREAM_MEDIA;
|
||||
case CUBEB_STREAM_TYPE_NOTIFICATION:
|
||||
return SL_ANDROID_STREAM_NOTIFICATION;
|
||||
case CUBEB_STREAM_TYPE_ALARM:
|
||||
return SL_ANDROID_STREAM_ALARM;
|
||||
case CUBEB_STREAM_TYPE_VOICE_CALL:
|
||||
return SL_ANDROID_STREAM_VOICE;
|
||||
case CUBEB_STREAM_TYPE_RING:
|
||||
return SL_ANDROID_STREAM_RING;
|
||||
case CUBEB_STREAM_TYPE_SYSTEM_ENFORCED:
|
||||
return SL_ANDROID_STREAM_SYSTEM_ENFORCED;
|
||||
default:
|
||||
return 0xFFFFFFFF;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static void opensl_destroy(cubeb * ctx);
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
|
@ -899,7 +886,7 @@ opensl_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * laten
|
|||
if (get_primary_output_frame_count) {
|
||||
primary_buffer_size = get_primary_output_frame_count();
|
||||
} else {
|
||||
if (get_output_frame_count(&primary_buffer_size, params.stream_type) != 0) {
|
||||
if (get_output_frame_count(&primary_buffer_size, AUDIO_STREAM_TYPE_MUSIC) != 0) {
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
}
|
||||
|
@ -1127,7 +1114,6 @@ opensl_configure_playback(cubeb_stream * stm, cubeb_stream_params * params) {
|
|||
assert(params);
|
||||
|
||||
stm->inputrate = params->rate;
|
||||
stm->stream_type = params->stream_type;
|
||||
stm->framesize = params->channels * sizeof(int16_t);
|
||||
stm->lastPosition = -1;
|
||||
stm->lastPositionTimeStamp = 0;
|
||||
|
@ -1228,29 +1214,6 @@ opensl_configure_playback(cubeb_stream * stm, cubeb_stream_params * params) {
|
|||
assert(stm->queuebuf[i]);
|
||||
}
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
SLuint32 stream_type = convert_stream_type_to_sl_stream(params->stream_type);
|
||||
if (stream_type != 0xFFFFFFFF) {
|
||||
SLAndroidConfigurationItf playerConfig;
|
||||
res = (*stm->playerObj)->GetInterface(stm->playerObj,
|
||||
stm->context->SL_IID_ANDROIDCONFIGURATION,
|
||||
&playerConfig);
|
||||
if (res != SL_RESULT_SUCCESS) {
|
||||
LOG("Failed to get android configuration interface. Error code: %lu", res);
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
|
||||
res = (*playerConfig)->SetConfiguration(playerConfig,
|
||||
SL_ANDROID_KEY_STREAM_TYPE,
|
||||
&stream_type,
|
||||
sizeof(SLint32));
|
||||
if (res != SL_RESULT_SUCCESS) {
|
||||
LOG("Failed to set android configuration interface. Error code: %lu", res);
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
res = (*stm->playerObj)->Realize(stm->playerObj, SL_BOOLEAN_FALSE);
|
||||
if (res != SL_RESULT_SUCCESS) {
|
||||
LOG("Failed to realize player object. Error code: %lu", res);
|
||||
|
@ -1654,7 +1617,7 @@ opensl_stream_get_position(cubeb_stream * stm, uint64_t * position)
|
|||
|
||||
samplerate = stm->inputrate;
|
||||
|
||||
r = stm->context->get_output_latency(&mixer_latency, stm->stream_type);
|
||||
r = stm->context->get_output_latency(&mixer_latency, AUDIO_STREAM_TYPE_MUSIC);
|
||||
if (r) {
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
|
@ -1690,7 +1653,7 @@ opensl_stream_get_latency(cubeb_stream * stm, uint32_t * latency)
|
|||
uint32_t mixer_latency; // The latency returned by AudioFlinger is in ms.
|
||||
|
||||
/* audio_stream_type_t is an int, so this is okay. */
|
||||
r = stm->context->get_output_latency(&mixer_latency, stm->stream_type);
|
||||
r = stm->context->get_output_latency(&mixer_latency, AUDIO_STREAM_TYPE_MUSIC);
|
||||
if (r) {
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
|
@ -1746,6 +1709,7 @@ static struct cubeb_ops const opensl_ops = {
|
|||
.stream_destroy = opensl_stream_destroy,
|
||||
.stream_start = opensl_stream_start,
|
||||
.stream_stop = opensl_stream_stop,
|
||||
.stream_reset_default_device = NULL,
|
||||
.stream_get_position = opensl_stream_get_position,
|
||||
.stream_get_latency = opensl_stream_get_latency,
|
||||
.stream_set_volume = opensl_stream_set_volume,
|
||||
|
|
|
@ -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;
|
||||
|
@ -630,7 +653,6 @@ pulse_init(cubeb ** context, char const * context_name)
|
|||
WRAP(pa_operation_unref)(o);
|
||||
}
|
||||
WRAP(pa_threaded_mainloop_unlock)(ctx->mainloop);
|
||||
assert(ctx->default_sink_info);
|
||||
|
||||
*context = ctx;
|
||||
|
||||
|
@ -650,6 +672,9 @@ pulse_get_max_channel_count(cubeb * ctx, uint32_t * max_channels)
|
|||
(void)ctx;
|
||||
assert(ctx && max_channels);
|
||||
|
||||
if (!ctx->default_sink_info)
|
||||
return CUBEB_ERROR;
|
||||
|
||||
*max_channels = ctx->default_sink_info->channel_map.channels;
|
||||
|
||||
return CUBEB_OK;
|
||||
|
@ -661,6 +686,9 @@ pulse_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate)
|
|||
assert(ctx && rate);
|
||||
(void)ctx;
|
||||
|
||||
if (!ctx->default_sink_info)
|
||||
return CUBEB_ERROR;
|
||||
|
||||
*rate = ctx->default_sink_info->sample_spec.rate;
|
||||
|
||||
return CUBEB_OK;
|
||||
|
@ -672,6 +700,9 @@ pulse_get_preferred_channel_layout(cubeb * ctx, cubeb_channel_layout * layout)
|
|||
assert(ctx && layout);
|
||||
(void)ctx;
|
||||
|
||||
if (!ctx->default_sink_info)
|
||||
return CUBEB_ERROR;
|
||||
|
||||
*layout = channel_map_to_layout(&ctx->default_sink_info->channel_map);
|
||||
|
||||
return CUBEB_OK;
|
||||
|
@ -717,6 +748,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);
|
||||
}
|
||||
|
@ -751,8 +786,9 @@ 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 &&
|
||||
CUBEB_CHANNEL_LAYOUT_MAPS[stream_params->layout].channels == stream_params->channels));
|
||||
(stream_params->layout == CUBEB_LAYOUT_UNDEFINED ||
|
||||
(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);
|
||||
|
@ -1049,6 +1085,7 @@ pulse_stream_set_volume(cubeb_stream * stm, float volume)
|
|||
pa_volume_t vol;
|
||||
pa_cvolume cvol;
|
||||
const pa_sample_spec * ss;
|
||||
cubeb * ctx;
|
||||
|
||||
if (!stm->output_stream) {
|
||||
return CUBEB_ERROR;
|
||||
|
@ -1058,7 +1095,9 @@ pulse_stream_set_volume(cubeb_stream * stm, float volume)
|
|||
|
||||
/* if the pulse daemon is configured to use flat volumes,
|
||||
* apply our own gain instead of changing the input volume on the sink. */
|
||||
if (stm->context->default_sink_info->flags & PA_SINK_FLAT_VOLUME) {
|
||||
ctx = stm->context;
|
||||
if (ctx->default_sink_info &&
|
||||
(ctx->default_sink_info->flags & PA_SINK_FLAT_VOLUME)) {
|
||||
stm->volume = volume;
|
||||
} else {
|
||||
ss = WRAP(pa_stream_get_sample_spec)(stm->output_stream);
|
||||
|
@ -1068,16 +1107,16 @@ pulse_stream_set_volume(cubeb_stream * stm, float volume)
|
|||
|
||||
index = WRAP(pa_stream_get_index)(stm->output_stream);
|
||||
|
||||
op = WRAP(pa_context_set_sink_input_volume)(stm->context->context,
|
||||
op = WRAP(pa_context_set_sink_input_volume)(ctx->context,
|
||||
index, &cvol, volume_success,
|
||||
stm);
|
||||
if (op) {
|
||||
operation_wait(stm->context, stm->output_stream, op);
|
||||
operation_wait(ctx, stm->output_stream, op);
|
||||
WRAP(pa_operation_unref)(op);
|
||||
}
|
||||
}
|
||||
|
||||
WRAP(pa_threaded_mainloop_unlock)(stm->context->mainloop);
|
||||
WRAP(pa_threaded_mainloop_unlock)(ctx->mainloop);
|
||||
|
||||
return CUBEB_OK;
|
||||
}
|
||||
|
@ -1202,18 +1241,30 @@ 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)
|
||||
if (eol) {
|
||||
WRAP(pa_threaded_mainloop_signal)(list_data->context->mainloop, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (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");
|
||||
|
@ -1239,8 +1290,6 @@ pulse_sink_info_cb(pa_context * context, const pa_sink_info * info,
|
|||
devinfo->latency_hi = 0;
|
||||
|
||||
list_data->count += 1;
|
||||
|
||||
WRAP(pa_threaded_mainloop_signal)(list_data->context->mainloop, 0);
|
||||
}
|
||||
|
||||
static cubeb_device_state
|
||||
|
@ -1264,18 +1313,27 @@ 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)
|
||||
if (eol) {
|
||||
WRAP(pa_threaded_mainloop_signal)(list_data->context->mainloop, 0);
|
||||
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");
|
||||
|
@ -1301,7 +1359,6 @@ pulse_source_info_cb(pa_context * context, const pa_source_info * info,
|
|||
devinfo->latency_hi = 0;
|
||||
|
||||
list_data->count += 1;
|
||||
WRAP(pa_threaded_mainloop_signal)(list_data->context->mainloop, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -1313,8 +1370,10 @@ pulse_server_info_cb(pa_context * c, const pa_server_info * i, void * userdata)
|
|||
|
||||
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);
|
||||
list_data->default_sink_name =
|
||||
i->default_sink_name ? strdup(i->default_sink_name) : NULL;
|
||||
list_data->default_source_name =
|
||||
i->default_source_name ? strdup(i->default_source_name) : NULL;
|
||||
|
||||
WRAP(pa_threaded_mainloop_signal)(list_data->context->mainloop, 0);
|
||||
}
|
||||
|
@ -1363,6 +1422,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)
|
||||
{
|
||||
|
@ -1492,12 +1566,13 @@ 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,
|
||||
.stream_start = pulse_stream_start,
|
||||
.stream_stop = pulse_stream_stop,
|
||||
.stream_reset_default_device = NULL,
|
||||
.stream_get_position = pulse_stream_get_position,
|
||||
.stream_get_latency = pulse_stream_get_latency,
|
||||
.stream_set_volume = pulse_stream_set_volume,
|
||||
|
|
|
@ -35,15 +35,22 @@ to_speex_quality(cubeb_resampler_quality q)
|
|||
}
|
||||
}
|
||||
|
||||
uint32_t min_buffered_audio_frame(uint32_t sample_rate)
|
||||
{
|
||||
return sample_rate / 20;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
passthrough_resampler<T>::passthrough_resampler(cubeb_stream * s,
|
||||
cubeb_data_callback cb,
|
||||
void * ptr,
|
||||
uint32_t input_channels)
|
||||
uint32_t input_channels,
|
||||
uint32_t sample_rate)
|
||||
: processor(input_channels)
|
||||
, stream(s)
|
||||
, data_callback(cb)
|
||||
, user_ptr(ptr)
|
||||
, sample_rate(sample_rate)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -73,6 +80,7 @@ long passthrough_resampler<T>::fill(void * input_buffer, long * input_frames_cou
|
|||
if (input_buffer) {
|
||||
internal_input_buffer.pop(nullptr, frames_to_samples(output_frames));
|
||||
*input_frames_count = output_frames;
|
||||
drop_audio_if_needed();
|
||||
}
|
||||
|
||||
return rv;
|
||||
|
@ -242,9 +250,15 @@ cubeb_resampler_speex<T, InputProcessor, OutputProcessor>
|
|||
|
||||
output_processor->written(got);
|
||||
|
||||
input_processor->drop_audio_if_needed();
|
||||
|
||||
/* Process the output. If not enough frames have been returned from the
|
||||
* callback, drain the processors. */
|
||||
return output_processor->output(out_buffer, output_frames_needed);
|
||||
got = output_processor->output(out_buffer, output_frames_needed);
|
||||
|
||||
output_processor->drop_audio_if_needed();
|
||||
|
||||
return got;
|
||||
}
|
||||
|
||||
/* Resampler C API */
|
||||
|
|
|
@ -39,6 +39,13 @@ MOZ_END_STD_NAMESPACE
|
|||
|
||||
/* This header file contains the internal C++ API of the resamplers, for testing. */
|
||||
|
||||
// When dropping audio input frames to prevent building
|
||||
// an input delay, this function returns the number of frames
|
||||
// to keep in the buffer.
|
||||
// @parameter sample_rate The sample rate of the stream.
|
||||
// @return A number of frames to keep.
|
||||
uint32_t min_buffered_audio_frame(uint32_t sample_rate);
|
||||
|
||||
int to_speex_quality(cubeb_resampler_quality q);
|
||||
|
||||
struct cubeb_resampler {
|
||||
|
@ -75,7 +82,8 @@ public:
|
|||
passthrough_resampler(cubeb_stream * s,
|
||||
cubeb_data_callback cb,
|
||||
void * ptr,
|
||||
uint32_t input_channels);
|
||||
uint32_t input_channels,
|
||||
uint32_t sample_rate);
|
||||
|
||||
virtual long fill(void * input_buffer, long * input_frames_count,
|
||||
void * output_buffer, long output_frames);
|
||||
|
@ -85,6 +93,15 @@ public:
|
|||
return 0;
|
||||
}
|
||||
|
||||
void drop_audio_if_needed()
|
||||
{
|
||||
uint32_t to_keep = min_buffered_audio_frame(sample_rate);
|
||||
uint32_t available = samples_to_frames(internal_input_buffer.length());
|
||||
if (available > to_keep) {
|
||||
internal_input_buffer.pop(nullptr, frames_to_samples(available - to_keep));
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
cubeb_stream * const stream;
|
||||
const cubeb_data_callback data_callback;
|
||||
|
@ -92,6 +109,7 @@ private:
|
|||
/* This allows to buffer some input to account for the fact that we buffer
|
||||
* some inputs. */
|
||||
auto_array<T> internal_input_buffer;
|
||||
uint32_t sample_rate;
|
||||
};
|
||||
|
||||
/** Bidirectional resampler, can resample an input and an output stream, or just
|
||||
|
@ -164,6 +182,7 @@ public:
|
|||
int quality)
|
||||
: processor(channels)
|
||||
, resampling_ratio(static_cast<float>(source_rate) / target_rate)
|
||||
, source_rate(source_rate)
|
||||
, additional_latency(0)
|
||||
, leftover_samples(0)
|
||||
{
|
||||
|
@ -296,6 +315,16 @@ public:
|
|||
resampling_in_buffer.set_length(leftover_samples +
|
||||
frames_to_samples(written_frames));
|
||||
}
|
||||
|
||||
void drop_audio_if_needed()
|
||||
{
|
||||
// Keep at most 100ms buffered.
|
||||
uint32_t available = samples_to_frames(resampling_in_buffer.length());
|
||||
uint32_t to_keep = min_buffered_audio_frame(source_rate);
|
||||
if (available > to_keep) {
|
||||
resampling_in_buffer.pop(nullptr, frames_to_samples(available - to_keep));
|
||||
}
|
||||
}
|
||||
private:
|
||||
/** Wrapper for the speex resampling functions to have a typed
|
||||
* interface. */
|
||||
|
@ -332,6 +361,7 @@ private:
|
|||
SpeexResamplerState * speex_resampler;
|
||||
/** Source rate / target rate. */
|
||||
const float resampling_ratio;
|
||||
const uint32_t source_rate;
|
||||
/** Storage for the input frames, to be resampled. Also contains
|
||||
* any unresampled frames after resampling. */
|
||||
auto_array<T> resampling_in_buffer;
|
||||
|
@ -350,11 +380,13 @@ class delay_line : public processor {
|
|||
public:
|
||||
/** Constructor
|
||||
* @parameter frames the number of frames of delay.
|
||||
* @parameter channels the number of channels of this delay line. */
|
||||
delay_line(uint32_t frames, uint32_t channels)
|
||||
* @parameter channels the number of channels of this delay line.
|
||||
* @parameter sample_rate sample-rate of the audio going through this delay line */
|
||||
delay_line(uint32_t frames, uint32_t channels, uint32_t sample_rate)
|
||||
: processor(channels)
|
||||
, length(frames)
|
||||
, leftover_samples(0)
|
||||
, sample_rate(sample_rate)
|
||||
{
|
||||
/* Fill the delay line with some silent frames to add latency. */
|
||||
delay_input_buffer.push_silence(frames * channels);
|
||||
|
@ -444,6 +476,15 @@ public:
|
|||
{
|
||||
return length;
|
||||
}
|
||||
|
||||
void drop_audio_if_needed()
|
||||
{
|
||||
size_t available = samples_to_frames(delay_input_buffer.length());
|
||||
uint32_t to_keep = min_buffered_audio_frame(sample_rate);
|
||||
if (available > to_keep) {
|
||||
delay_input_buffer.pop(nullptr, frames_to_samples(available - to_keep));
|
||||
}
|
||||
}
|
||||
private:
|
||||
/** The length, in frames, of this delay line */
|
||||
uint32_t length;
|
||||
|
@ -455,6 +496,7 @@ private:
|
|||
/** The output buffer. This is only ever used if using the ::output with a
|
||||
* single argument. */
|
||||
auto_array<T> delay_output_buffer;
|
||||
uint32_t sample_rate;
|
||||
};
|
||||
|
||||
/** This sits behind the C API and is more typed. */
|
||||
|
@ -485,7 +527,8 @@ cubeb_resampler_create_internal(cubeb_stream * stream,
|
|||
(output_params && !input_params && (output_params->rate == target_rate))) {
|
||||
return new passthrough_resampler<T>(stream, callback,
|
||||
user_ptr,
|
||||
input_params ? input_params->channels : 0);
|
||||
input_params ? input_params->channels : 0,
|
||||
target_rate);
|
||||
}
|
||||
|
||||
/* Determine if we need to resampler one or both directions, and create the
|
||||
|
@ -517,13 +560,15 @@ cubeb_resampler_create_internal(cubeb_stream * stream,
|
|||
* other direction so that the streams are synchronized. */
|
||||
if (input_resampler && !output_resampler && input_params && output_params) {
|
||||
output_delay.reset(new delay_line<T>(input_resampler->latency(),
|
||||
output_params->channels));
|
||||
output_params->channels,
|
||||
output_params->rate));
|
||||
if (!output_delay) {
|
||||
return NULL;
|
||||
}
|
||||
} else if (output_resampler && !input_resampler && input_params && output_params) {
|
||||
input_delay.reset(new delay_line<T>(output_resampler->latency(),
|
||||
input_params->channels));
|
||||
input_params->channels,
|
||||
output_params->rate));
|
||||
if (!input_delay) {
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
|
|
|
@ -376,6 +376,7 @@ static struct cubeb_ops const sndio_ops = {
|
|||
.stream_destroy = sndio_stream_destroy,
|
||||
.stream_start = sndio_stream_start,
|
||||
.stream_stop = sndio_stream_stop,
|
||||
.stream_reset_default_device = NULL,
|
||||
.stream_get_position = sndio_stream_get_position,
|
||||
.stream_get_latency = sndio_stream_get_latency,
|
||||
.stream_set_volume = sndio_stream_set_volume,
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -17,7 +17,7 @@
|
|||
#include <assert.h>
|
||||
#include <mutex>
|
||||
#include <type_traits>
|
||||
#if defined(WIN32)
|
||||
#if defined(_WIN32)
|
||||
#include "cubeb_utils_win.h"
|
||||
#else
|
||||
#include "cubeb_utils_unix.h"
|
||||
|
@ -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,32 +446,46 @@ 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];
|
||||
}
|
||||
|
||||
cubeb_channel_layout
|
||||
mask_to_channel_layout(DWORD mask)
|
||||
mask_to_channel_layout(WAVEFORMATEX const * fmt)
|
||||
{
|
||||
DWORD mask = 0;
|
||||
|
||||
if (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
|
||||
WAVEFORMATEXTENSIBLE const * ext = reinterpret_cast<WAVEFORMATEXTENSIBLE const *>(fmt);
|
||||
mask = ext->dwChannelMask;
|
||||
} else if (fmt->wFormatTag == WAVE_FORMAT_PCM ||
|
||||
fmt->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) {
|
||||
if (fmt->nChannels == 1) {
|
||||
mask = MASK_MONO;
|
||||
} else if (fmt->nChannels == 2) {
|
||||
mask = MASK_STEREO;
|
||||
}
|
||||
}
|
||||
|
||||
switch (mask) {
|
||||
// MASK_DUAL_MONO(_LFE) is same as STEREO(_LFE), so we skip it.
|
||||
case MASK_MONO: return CUBEB_LAYOUT_MONO;
|
||||
|
@ -483,27 +516,21 @@ get_rate(cubeb_stream * stm)
|
|||
}
|
||||
|
||||
uint32_t
|
||||
hns_to_ms(REFERENCE_TIME hns)
|
||||
hns_to_frames(uint32_t rate, REFERENCE_TIME hns)
|
||||
{
|
||||
return static_cast<uint32_t>(hns / 10000);
|
||||
return std::ceil(hns / 10000000.0 * rate);
|
||||
}
|
||||
|
||||
uint32_t
|
||||
hns_to_frames(cubeb_stream * stm, REFERENCE_TIME hns)
|
||||
{
|
||||
return hns_to_ms(hns * get_rate(stm)) / 1000;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
hns_to_frames(uint32_t rate, REFERENCE_TIME hns)
|
||||
{
|
||||
return hns_to_ms(hns * rate) / 1000;
|
||||
return hns_to_frames(get_rate(stm), hns);
|
||||
}
|
||||
|
||||
REFERENCE_TIME
|
||||
frames_to_hns(cubeb_stream * stm, uint32_t frames)
|
||||
{
|
||||
return frames * 1000 / get_rate(stm);
|
||||
return std::ceil(frames * 10000000.0 / get_rate(stm));
|
||||
}
|
||||
|
||||
/* This returns the size of a frame in the stream, before the eventual upmix
|
||||
|
@ -1145,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;
|
||||
|
||||
|
@ -1212,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;
|
||||
}
|
||||
|
||||
|
@ -1374,8 +1409,7 @@ wasapi_get_preferred_channel_layout(cubeb * context, cubeb_channel_layout * layo
|
|||
}
|
||||
com_heap_ptr<WAVEFORMATEX> mix_format(tmp);
|
||||
|
||||
WAVEFORMATEXTENSIBLE * format_pcm = reinterpret_cast<WAVEFORMATEXTENSIBLE *>(mix_format.get());
|
||||
*layout = mask_to_channel_layout(format_pcm->dwChannelMask);
|
||||
*layout = mask_to_channel_layout(mix_format.get());
|
||||
|
||||
LOG("Preferred channel layout: %s", CUBEB_CHANNEL_LAYOUT_MAPS[*layout].name);
|
||||
|
||||
|
@ -1400,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],
|
||||
|
@ -1434,6 +1466,7 @@ handle_channel_layout(cubeb_stream * stm, EDataFlow direction, com_heap_ptr<WAV
|
|||
and handle the eventual upmix/downmix ourselves. Ignore the subformat of
|
||||
the suggestion, since it seems to always be IEEE_FLOAT. */
|
||||
LOG("Using WASAPI suggested format: channels: %d", closest->nChannels);
|
||||
XASSERT(closest->wFormatTag == WAVE_FORMAT_EXTENSIBLE);
|
||||
WAVEFORMATEXTENSIBLE * closest_pcm = reinterpret_cast<WAVEFORMATEXTENSIBLE *>(closest);
|
||||
format_pcm->dwChannelMask = closest_pcm->dwChannelMask;
|
||||
mix_format->nChannels = closest->nChannels;
|
||||
|
@ -1442,6 +1475,7 @@ handle_channel_layout(cubeb_stream * stm, EDataFlow direction, com_heap_ptr<WAV
|
|||
/* 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. */
|
||||
XASSERT(mix_format->wFormatTag == WAVE_FORMAT_EXTENSIBLE);
|
||||
*reinterpret_cast<WAVEFORMATEXTENSIBLE *>(mix_format.get()) = hw_mix_format;
|
||||
} else if (hr == S_OK) {
|
||||
LOG("Requested format accepted by WASAPI.");
|
||||
|
@ -1520,21 +1554,23 @@ int setup_wasapi_stream_one_side(cubeb_stream * stm,
|
|||
}
|
||||
com_heap_ptr<WAVEFORMATEX> mix_format(tmp);
|
||||
|
||||
WAVEFORMATEXTENSIBLE * format_pcm = reinterpret_cast<WAVEFORMATEXTENSIBLE *>(mix_format.get());
|
||||
mix_format->wBitsPerSample = stm->bytes_per_sample * 8;
|
||||
format_pcm->SubFormat = stm->waveformatextensible_sub_format;
|
||||
if (mix_format->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
|
||||
WAVEFORMATEXTENSIBLE * format_pcm = reinterpret_cast<WAVEFORMATEXTENSIBLE *>(mix_format.get());
|
||||
format_pcm->SubFormat = stm->waveformatextensible_sub_format;
|
||||
}
|
||||
waveformatex_update_derived_properties(mix_format.get());
|
||||
/* Set channel layout only when there're more than two channels. Otherwise,
|
||||
* use the default setting retrieved from the stream format of the audio
|
||||
* engine's internal processing by GetMixFormat. */
|
||||
if (mix_format->nChannels > 2) {
|
||||
handle_channel_layout(stm, direction ,mix_format, stream_params);
|
||||
handle_channel_layout(stm, direction, mix_format, stream_params);
|
||||
}
|
||||
|
||||
mix_params->format = stream_params->format;
|
||||
mix_params->rate = mix_format->nSamplesPerSec;
|
||||
mix_params->channels = mix_format->nChannels;
|
||||
mix_params->layout = mask_to_channel_layout(format_pcm->dwChannelMask);
|
||||
mix_params->layout = mask_to_channel_layout(mix_format.get());
|
||||
if (mix_params->layout == CUBEB_LAYOUT_UNDEFINED) {
|
||||
LOG("Output using undefined layout!\n");
|
||||
} else if (mix_format->nChannels != CUBEB_CHANNEL_LAYOUT_MAPS[mix_params->layout].channels) {
|
||||
|
@ -1769,7 +1805,8 @@ wasapi_stream_init(cubeb * context, cubeb_stream ** stream,
|
|||
stm->output_stream_params = *output_stream_params;
|
||||
stm->output_device = utf8_to_wstr(reinterpret_cast<char const *>(output_device));
|
||||
// Make sure the layout matches the channel count.
|
||||
XASSERT(stm->output_stream_params.channels == CUBEB_CHANNEL_LAYOUT_MAPS[stm->output_stream_params.layout].channels);
|
||||
XASSERT(stm->output_stream_params.layout == CUBEB_LAYOUT_UNDEFINED ||
|
||||
stm->output_stream_params.channels == CUBEB_CHANNEL_LAYOUT_MAPS[stm->output_stream_params.layout].channels);
|
||||
}
|
||||
|
||||
switch (output_stream_params ? output_stream_params->format : input_stream_params->format) {
|
||||
|
@ -1960,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.");
|
||||
|
@ -2013,6 +2051,17 @@ int wasapi_stream_stop(cubeb_stream * stm)
|
|||
return CUBEB_OK;
|
||||
}
|
||||
|
||||
int wasapi_stream_reset_default_device(cubeb_stream * stm)
|
||||
{
|
||||
XASSERT(stm && stm->reconfigure_event);
|
||||
BOOL ok = SetEvent(stm->reconfigure_event);
|
||||
if (!ok) {
|
||||
LOG("SetEvent on reconfigure_event failed: %lx", GetLastError());
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
return CUBEB_OK;
|
||||
}
|
||||
|
||||
int wasapi_stream_get_position(cubeb_stream * stm, uint64_t * position)
|
||||
{
|
||||
XASSERT(stm && position);
|
||||
|
@ -2151,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;
|
||||
|
@ -2181,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) {
|
||||
|
@ -2204,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;
|
||||
|
@ -2300,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;
|
||||
|
@ -2313,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;
|
||||
}
|
||||
}
|
||||
|
@ -2323,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,
|
||||
|
@ -2331,12 +2399,13 @@ 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,
|
||||
/*.stream_start =*/ wasapi_stream_start,
|
||||
/*.stream_stop =*/ wasapi_stream_stop,
|
||||
/*.stream_reset_default_device =*/ wasapi_stream_reset_default_device,
|
||||
/*.stream_get_position =*/ wasapi_stream_get_position,
|
||||
/*.stream_get_latency =*/ wasapi_stream_get_latency,
|
||||
/*.stream_set_volume =*/ wasapi_stream_set_volume,
|
||||
|
|
|
@ -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,12 +1044,13 @@ 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,
|
||||
/*.stream_start =*/ winmm_stream_start,
|
||||
/*.stream_stop =*/ winmm_stream_stop,
|
||||
/*.stream_reset_default_device =*/ NULL,
|
||||
/*.stream_get_position =*/ winmm_stream_get_position,
|
||||
/*.stream_get_latency = */ winmm_stream_get_latency,
|
||||
/*.stream_set_volume =*/ winmm_stream_set_volume,
|
||||
|
|
|
@ -798,11 +798,10 @@ EXPORT SpeexResamplerState *speex_resampler_init(spx_uint32_t nb_channels, spx_u
|
|||
|
||||
EXPORT SpeexResamplerState *speex_resampler_init_frac(spx_uint32_t nb_channels, spx_uint32_t ratio_num, spx_uint32_t ratio_den, spx_uint32_t in_rate, spx_uint32_t out_rate, int quality, int *err)
|
||||
{
|
||||
spx_uint32_t i;
|
||||
SpeexResamplerState *st;
|
||||
int filter_err;
|
||||
|
||||
if (quality > 10 || quality < 0)
|
||||
if (nb_channels == 0 || ratio_num == 0 || ratio_den == 0 || quality > 10 || quality < 0)
|
||||
{
|
||||
if (err)
|
||||
*err = RESAMPLER_ERR_INVALID_ARG;
|
||||
|
@ -1111,6 +1110,10 @@ EXPORT int speex_resampler_set_rate_frac(SpeexResamplerState *st, spx_uint32_t r
|
|||
spx_uint32_t fact;
|
||||
spx_uint32_t old_den;
|
||||
spx_uint32_t i;
|
||||
|
||||
if (ratio_num == 0 || ratio_den == 0)
|
||||
return RESAMPLER_ERR_INVALID_ARG;
|
||||
|
||||
if (st->in_rate == in_rate && st->out_rate == out_rate && st->num_rate == ratio_num && st->den_rate == ratio_den)
|
||||
return RESAMPLER_ERR_SUCCESS;
|
||||
|
||||
|
|
|
@ -56,7 +56,7 @@ bool CubebStream::Start()
|
|||
}
|
||||
|
||||
u32 minimum_latency = 0;
|
||||
if (cubeb_get_min_latency(m_ctx.get(), params, &minimum_latency) != CUBEB_OK)
|
||||
if (cubeb_get_min_latency(m_ctx.get(), ¶ms, &minimum_latency) != CUBEB_OK)
|
||||
ERROR_LOG(AUDIO, "Error getting minimum latency");
|
||||
INFO_LOG(AUDIO, "Minimum latency: %i frames", minimum_latency);
|
||||
|
||||
|
|
|
@ -80,7 +80,7 @@ void CEXIMic::StreamStart()
|
|||
params.layout = CUBEB_LAYOUT_MONO;
|
||||
|
||||
u32 minimum_latency;
|
||||
if (cubeb_get_min_latency(m_cubeb_ctx.get(), params, &minimum_latency) != CUBEB_OK)
|
||||
if (cubeb_get_min_latency(m_cubeb_ctx.get(), ¶ms, &minimum_latency) != CUBEB_OK)
|
||||
{
|
||||
WARN_LOG(EXPANSIONINTERFACE, "Error getting minimum latency");
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue