Externals: update cubeb to kinetiknz/cubeb@c2bd582

A bunch of changes, looks mainly like bug fixes and code cleanup.

Notable changes:
- `cubeb_get_min_latency`'s signature was changed to take params via
  pointer, requiring Dolphin code to be tweaked in two places.
- A fix for kinetiknz/cubeb#320, as reported by @shuffle2
- Fixed build on FreeBSD (kinetiknz/cubeb#344), as contributed by @endrift
This commit is contained in:
Michael M 2017-10-21 14:13:53 -07:00
parent 774fca4d01
commit aa40c4a7ce
31 changed files with 1058 additions and 601 deletions

View File

@ -43,9 +43,11 @@ add_library(cubeb
src/cubeb_resampler.cpp src/cubeb_resampler.cpp
src/cubeb_panner.cpp src/cubeb_panner.cpp
src/cubeb_log.cpp src/cubeb_log.cpp
src/cubeb_utils.c src/cubeb_strings.c
$<TARGET_OBJECTS:speex>) $<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_include_directories(cubeb PRIVATE src)
target_compile_definitions(cubeb PRIVATE OUTSIDE_SPEEX) target_compile_definitions(cubeb PRIVATE OUTSIDE_SPEEX)
target_compile_definitions(cubeb PRIVATE FLOATING_POINT) target_compile_definitions(cubeb PRIVATE FLOATING_POINT)
@ -56,7 +58,42 @@ add_sanitizers(cubeb)
include(GenerateExportHeader) include(GenerateExportHeader)
generate_export_header(cubeb EXPORT_FILE_NAME ${CMAKE_BINARY_DIR}/exports/cubeb_export.h) 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 add_library(speex OBJECT
src/speex/resample.c) src/speex/resample.c)
@ -82,6 +119,7 @@ if(USE_PULSE)
target_sources(cubeb PRIVATE target_sources(cubeb PRIVATE
src/cubeb_pulse.c) src/cubeb_pulse.c)
target_compile_definitions(cubeb PRIVATE USE_PULSE) target_compile_definitions(cubeb PRIVATE USE_PULSE)
target_link_libraries(cubeb PRIVATE pulse)
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
target_link_libraries(cubeb PRIVATE dl) target_link_libraries(cubeb PRIVATE dl)
endif() endif()
@ -93,6 +131,9 @@ if(USE_ALSA)
src/cubeb_alsa.c) src/cubeb_alsa.c)
target_compile_definitions(cubeb PRIVATE USE_ALSA) target_compile_definitions(cubeb PRIVATE USE_ALSA)
target_link_libraries(cubeb PRIVATE asound pthread) target_link_libraries(cubeb PRIVATE asound pthread)
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
target_link_libraries(cubeb PRIVATE dl)
endif()
endif() endif()
check_include_files(jack/jack.h USE_JACK) check_include_files(jack/jack.h USE_JACK)
@ -100,10 +141,10 @@ if(USE_JACK)
target_sources(cubeb PRIVATE target_sources(cubeb PRIVATE
src/cubeb_jack.cpp) src/cubeb_jack.cpp)
target_compile_definitions(cubeb PRIVATE USE_JACK) target_compile_definitions(cubeb PRIVATE USE_JACK)
target_link_libraries(cubeb PRIVATE jack pthread)
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
target_link_libraries(cubeb PRIVATE dl) target_link_libraries(cubeb PRIVATE dl)
endif() endif()
target_link_libraries(cubeb PRIVATE pthread)
endif() endif()
check_include_files(audioclient.h USE_WASAPI) check_include_files(audioclient.h USE_WASAPI)

4
Externals/cubeb/Config.cmake.in vendored Normal file
View File

@ -0,0 +1,4 @@
@PACKAGE_INIT@
include("${CMAKE_CURRENT_LIST_DIR}/cubebTargets.cmake")
check_required_components(cubeb)

View File

@ -33,11 +33,11 @@ extern "C" {
cubeb * app_ctx; cubeb * app_ctx;
cubeb_init(&app_ctx, "Example Application"); cubeb_init(&app_ctx, "Example Application");
int rv; int rv;
int rate; uint32_t rate;
int latency_frames; uint32_t latency_frames;
uint64_t ts; 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) { if (rv != CUBEB_OK) {
fprintf(stderr, "Could not get minimum latency"); fprintf(stderr, "Could not get minimum latency");
return rv; return rv;
@ -144,28 +144,6 @@ typedef enum {
#endif #endif
} cubeb_sample_format; } 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 /** An opaque handle used to refer a particular input or output device
* across calls. */ * across calls. */
typedef void const * cubeb_devid; typedef void const * cubeb_devid;
@ -239,12 +217,9 @@ typedef enum {
typedef struct { typedef struct {
cubeb_sample_format format; /**< Requested sample format. One of cubeb_sample_format format; /**< Requested sample format. One of
#cubeb_sample_format. */ #cubeb_sample_format. */
unsigned int rate; /**< Requested sample rate. Valid range is [1000, 192000]. */ uint32_t rate; /**< Requested sample rate. Valid range is [1000, 192000]. */
unsigned int channels; /**< Requested channel count. Valid range is [1, 8]. */ 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. */ 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; } cubeb_stream_params;
/** Audio device description */ /** Audio device description */
@ -348,13 +323,13 @@ typedef struct {
cubeb_device_fmt format; /**< Sample format supported. */ cubeb_device_fmt format; /**< Sample format supported. */
cubeb_device_fmt default_format; /**< The default sample format for this device. */ cubeb_device_fmt default_format; /**< The default sample format for this device. */
unsigned int max_channels; /**< Channels. */ uint32_t max_channels; /**< Channels. */
unsigned int default_rate; /**< Default/Preferred sample rate. */ uint32_t default_rate; /**< Default/Preferred sample rate. */
unsigned int max_rate; /**< Maximum sample rate supported. */ uint32_t max_rate; /**< Maximum sample rate supported. */
unsigned int min_rate; /**< Minimum sample rate supported. */ uint32_t min_rate; /**< Minimum sample rate supported. */
unsigned int latency_lo; /**< Lowest possible latency in frames. */ uint32_t latency_lo; /**< Lowest possible latency in frames. */
unsigned int latency_hi; /**< Higest possible latency in frames. */ uint32_t latency_hi; /**< Higest possible latency in frames. */
} cubeb_device_info; } cubeb_device_info;
/** Device collection. /** 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. * User supplied callback called when the underlying device collection changed.
* @param context A pointer to the cubeb context. * @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, typedef void (* cubeb_device_collection_changed_callback)(cubeb * context,
void * user_ptr); 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_INVALID_PARAMETER
@retval CUBEB_ERROR_NOT_SUPPORTED */ @retval CUBEB_ERROR_NOT_SUPPORTED */
CUBEB_EXPORT int cubeb_get_min_latency(cubeb * context, CUBEB_EXPORT int cubeb_get_min_latency(cubeb * context,
cubeb_stream_params params, cubeb_stream_params * params,
uint32_t * latency_frames); uint32_t * latency_frames);
/** Get the preferred sample rate for this backend: this is hardware and /** 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_stream_params * input_stream_params,
cubeb_devid output_device, cubeb_devid output_device,
cubeb_stream_params * output_stream_params, cubeb_stream_params * output_stream_params,
unsigned int latency_frames, uint32_t latency_frames,
cubeb_data_callback data_callback, cubeb_data_callback data_callback,
cubeb_state_callback state_callback, cubeb_state_callback state_callback,
void * user_ptr); void * user_ptr);
@ -534,6 +509,14 @@ CUBEB_EXPORT int cubeb_stream_start(cubeb_stream * stream);
@retval CUBEB_ERROR */ @retval CUBEB_ERROR */
CUBEB_EXPORT int cubeb_stream_stop(cubeb_stream * stream); 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. /** Get the current stream playback position.
@param stream @param stream
@param position Playback position in frames. @param position Playback position in frames.

View File

@ -57,6 +57,7 @@
<ClInclude Include="..\src\cubeb_resampler_internal.h" /> <ClInclude Include="..\src\cubeb_resampler_internal.h" />
<ClInclude Include="..\src\cubeb_ringbuffer.h" /> <ClInclude Include="..\src\cubeb_ringbuffer.h" />
<ClInclude Include="..\src\cubeb_ring_array.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.h" />
<ClInclude Include="..\src\cubeb_utils_unix.h" /> <ClInclude Include="..\src\cubeb_utils_unix.h" />
<ClInclude Include="..\src\cubeb_utils_win.h" /> <ClInclude Include="..\src\cubeb_utils_win.h" />
@ -74,7 +75,7 @@
<ClCompile Include="..\src\cubeb_mixer.cpp" /> <ClCompile Include="..\src\cubeb_mixer.cpp" />
<ClCompile Include="..\src\cubeb_panner.cpp" /> <ClCompile Include="..\src\cubeb_panner.cpp" />
<ClCompile Include="..\src\cubeb_resampler.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_wasapi.cpp" />
<ClCompile Include="..\src\cubeb_winmm.c" /> <ClCompile Include="..\src\cubeb_winmm.c" />
<ClCompile Include="..\src\speex\resample.c" /> <ClCompile Include="..\src\speex\resample.c" />

View File

@ -11,6 +11,7 @@
<ClInclude Include="..\src\cubeb_resampler_internal.h" /> <ClInclude Include="..\src\cubeb_resampler_internal.h" />
<ClInclude Include="..\src\cubeb_ring_array.h" /> <ClInclude Include="..\src\cubeb_ring_array.h" />
<ClInclude Include="..\src\cubeb_ringbuffer.h" /> <ClInclude Include="..\src\cubeb_ringbuffer.h" />
<ClInclude Include="..\src\cubeb_strings.h" />
<ClInclude Include="..\src\cubeb_utils.h" /> <ClInclude Include="..\src\cubeb_utils.h" />
<ClInclude Include="..\src\cubeb_utils_unix.h" /> <ClInclude Include="..\src\cubeb_utils_unix.h" />
<ClInclude Include="..\src\cubeb_utils_win.h" /> <ClInclude Include="..\src\cubeb_utils_win.h" />
@ -45,7 +46,7 @@
<ClCompile Include="..\src\cubeb_mixer.cpp" /> <ClCompile Include="..\src\cubeb_mixer.cpp" />
<ClCompile Include="..\src\cubeb_panner.cpp" /> <ClCompile Include="..\src\cubeb_panner.cpp" />
<ClCompile Include="..\src\cubeb_resampler.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_wasapi.cpp" />
<ClCompile Include="..\src\cubeb_winmm.c" /> <ClCompile Include="..\src\cubeb_winmm.c" />
<ClCompile Include="..\src\speex\resample.c"> <ClCompile Include="..\src\speex\resample.c">
@ -57,4 +58,4 @@
<UniqueIdentifier>{caf7c7d0-0918-4299-8423-b287285d6fd0}</UniqueIdentifier> <UniqueIdentifier>{caf7c7d0-0918-4299-8423-b287285d6fd0}</UniqueIdentifier>
</Filter> </Filter>
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -69,6 +69,7 @@ struct cubeb_ops {
void (* stream_destroy)(cubeb_stream * stream); void (* stream_destroy)(cubeb_stream * stream);
int (* stream_start)(cubeb_stream * stream); int (* stream_start)(cubeb_stream * stream);
int (* stream_stop)(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_position)(cubeb_stream * stream, uint64_t * position);
int (* stream_get_latency)(cubeb_stream * stream, uint32_t * latency); int (* stream_get_latency)(cubeb_stream * stream, uint32_t * latency);
int (* stream_set_volume)(cubeb_stream * stream, float volumes); int (* stream_set_volume)(cubeb_stream * stream, float volumes);

View File

@ -245,9 +245,9 @@ cubeb_get_max_channel_count(cubeb * context, uint32_t * max_channels)
} }
int 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; 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 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 int
@ -368,6 +368,20 @@ cubeb_stream_stop(cubeb_stream * stream)
return stream->context->ops->stream_stop(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 int
cubeb_stream_get_position(cubeb_stream * stream, uint64_t * position) 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, int cubeb_device_collection_destroy(cubeb * context,
cubeb_device_collection * collection) cubeb_device_collection * collection)
{ {
int r;
if (context == NULL || collection == NULL) if (context == NULL || collection == NULL)
return CUBEB_ERROR_INVALID_PARAMETER; return CUBEB_ERROR_INVALID_PARAMETER;
if (!context->ops->device_collection_destroy) if (!context->ops->device_collection_destroy)
return CUBEB_ERROR_NOT_SUPPORTED; 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, int cubeb_register_device_collection_changed(cubeb * context,

View File

@ -17,7 +17,6 @@
#include <alsa/asoundlib.h> #include <alsa/asoundlib.h>
#include "cubeb/cubeb.h" #include "cubeb/cubeb.h"
#include "cubeb-internal.h" #include "cubeb-internal.h"
#include "cubeb_utils.h"
#define CUBEB_STREAM_MAX 16 #define CUBEB_STREAM_MAX 16
#define CUBEB_WATCHDOG_MS 10000 #define CUBEB_WATCHDOG_MS 10000
@ -1361,6 +1360,7 @@ static struct cubeb_ops const alsa_ops = {
.stream_destroy = alsa_stream_destroy, .stream_destroy = alsa_stream_destroy,
.stream_start = alsa_stream_start, .stream_start = alsa_stream_start,
.stream_stop = alsa_stream_stop, .stream_stop = alsa_stream_stop,
.stream_reset_default_device = NULL,
.stream_get_position = alsa_stream_get_position, .stream_get_position = alsa_stream_get_position,
.stream_get_latency = alsa_stream_get_latency, .stream_get_latency = alsa_stream_get_latency,
.stream_set_volume = alsa_stream_set_volume, .stream_set_volume = alsa_stream_set_volume,

View File

@ -145,9 +145,9 @@ audiotrack_get_min_frame_count(cubeb * ctx, cubeb_stream_params * params, int *
status_t status; status_t status;
/* Recent Android have a getMinFrameCount method. */ /* Recent Android have a getMinFrameCount method. */
if (!audiotrack_version_is_gingerbread(ctx)) { 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 { } 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) { if (status != 0) {
ALOG("error getting the min frame count"); 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; 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, AUDIO_FORMAT_PCM_16_BIT, channels, min_frame_count, 0,
audiotrack_refill, stm, 0, 0); audiotrack_refill, stm, 0, 0);
@ -429,6 +429,7 @@ static struct cubeb_ops const audiotrack_ops = {
.stream_destroy = audiotrack_stream_destroy, .stream_destroy = audiotrack_stream_destroy,
.stream_start = audiotrack_stream_start, .stream_start = audiotrack_stream_start,
.stream_stop = audiotrack_stream_stop, .stream_stop = audiotrack_stream_stop,
.stream_reset_default_device = NULL,
.stream_get_position = audiotrack_stream_get_position, .stream_get_position = audiotrack_stream_get_position,
.stream_get_latency = audiotrack_stream_get_latency, .stream_get_latency = audiotrack_stream_get_latency,
.stream_set_volume = audiotrack_stream_set_volume, .stream_set_volume = audiotrack_stream_set_volume,

File diff suppressed because it is too large Load Diff

View File

@ -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_stream_get_current_device(cubeb_stream * stm, cubeb_device ** const device);
static int cbjack_enumerate_devices(cubeb * context, cubeb_device_type type, static int cbjack_enumerate_devices(cubeb * context, cubeb_device_type type,
cubeb_device_collection * collection); 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, static int cbjack_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_name,
cubeb_devid input_device, cubeb_devid input_device,
cubeb_stream_params * input_stream_params, 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_sample_rate = cbjack_get_preferred_sample_rate,
.get_preferred_channel_layout = NULL, .get_preferred_channel_layout = NULL,
.enumerate_devices = cbjack_enumerate_devices, .enumerate_devices = cbjack_enumerate_devices,
.device_collection_destroy = cubeb_utils_default_device_collection_destroy, .device_collection_destroy = cbjack_device_collection_destroy,
.destroy = cbjack_destroy, .destroy = cbjack_destroy,
.stream_init = cbjack_stream_init, .stream_init = cbjack_stream_init,
.stream_destroy = cbjack_stream_destroy, .stream_destroy = cbjack_stream_destroy,
.stream_start = cbjack_stream_start, .stream_start = cbjack_stream_start,
.stream_stop = cbjack_stream_stop, .stream_stop = cbjack_stream_stop,
.stream_reset_default_device = NULL,
.stream_get_position = cbjack_stream_get_position, .stream_get_position = cbjack_stream_get_position,
.stream_get_latency = cbjack_get_latency, .stream_get_latency = cbjack_get_latency,
.stream_set_volume = cbjack_stream_set_volume, .stream_set_volume = cbjack_stream_set_volume,
@ -973,6 +976,9 @@ cbjack_stream_device_destroy(cubeb_stream * /*stream*/,
return CUBEB_OK; return CUBEB_OK;
} }
#define JACK_DEFAULT_IN "JACK capture"
#define JACK_DEFAULT_OUT "JACK playback"
static int static int
cbjack_enumerate_devices(cubeb * context, cubeb_device_type type, cbjack_enumerate_devices(cubeb * context, cubeb_device_type type,
cubeb_device_collection * collection) cubeb_device_collection * collection)
@ -982,20 +988,20 @@ cbjack_enumerate_devices(cubeb * context, cubeb_device_type type,
uint32_t rate; uint32_t rate;
cbjack_get_preferred_sample_rate(context, &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]; 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; collection->count = 0;
if (type & CUBEB_DEVICE_TYPE_OUTPUT) { if (type & CUBEB_DEVICE_TYPE_OUTPUT) {
cubeb_device_info * cur = &devices[collection->count]; 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->devid = (cubeb_devid) cur->device_id;
cur->friendly_name = strdup(j_out); cur->friendly_name = JACK_DEFAULT_OUT;
cur->group_id = strdup(j_out); cur->group_id = JACK_DEFAULT_OUT;
cur->vendor_name = strdup(j_out); cur->vendor_name = JACK_DEFAULT_OUT;
cur->type = CUBEB_DEVICE_TYPE_OUTPUT; cur->type = CUBEB_DEVICE_TYPE_OUTPUT;
cur->state = CUBEB_DEVICE_STATE_ENABLED; cur->state = CUBEB_DEVICE_STATE_ENABLED;
cur->preferred = CUBEB_DEVICE_PREF_ALL; 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) { if (type & CUBEB_DEVICE_TYPE_INPUT) {
cubeb_device_info * cur = &devices[collection->count]; 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->devid = (cubeb_devid) cur->device_id;
cur->friendly_name = strdup(j_in); cur->friendly_name = JACK_DEFAULT_IN;
cur->group_id = strdup(j_in); cur->group_id = JACK_DEFAULT_IN;
cur->vendor_name = strdup(j_in); cur->vendor_name = JACK_DEFAULT_IN;
cur->type = CUBEB_DEVICE_TYPE_INPUT; cur->type = CUBEB_DEVICE_TYPE_INPUT;
cur->state = CUBEB_DEVICE_STATE_ENABLED; cur->state = CUBEB_DEVICE_STATE_ENABLED;
cur->preferred = CUBEB_DEVICE_PREF_ALL; cur->preferred = CUBEB_DEVICE_PREF_ALL;
@ -1035,3 +1041,12 @@ cbjack_enumerate_devices(cubeb * context, cubeb_device_type type,
return CUBEB_OK; return CUBEB_OK;
} }
static int
cbjack_device_collection_destroy(cubeb * /*ctx*/,
cubeb_device_collection * collection)
{
XASSERT(collection);
delete [] collection->device;
return CUBEB_OK;
}

View File

@ -351,6 +351,7 @@ static struct cubeb_ops const kai_ops = {
/*.stream_destroy =*/ kai_stream_destroy, /*.stream_destroy =*/ kai_stream_destroy,
/*.stream_start =*/ kai_stream_start, /*.stream_start =*/ kai_stream_start,
/*.stream_stop =*/ kai_stream_stop, /*.stream_stop =*/ kai_stream_stop,
/*.stream_reset_default_device =*/ NULL,
/*.stream_get_position =*/ kai_stream_get_position, /*.stream_get_position =*/ kai_stream_get_position,
/*.stream_get_latency = */ kai_stream_get_latency, /*.stream_get_latency = */ kai_stream_get_latency,
/*.stream_set_volume =*/ kai_stream_set_volume, /*.stream_set_volume =*/ kai_stream_set_volume,

View File

@ -95,6 +95,12 @@ public:
} }
}).detach(); }).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: private:
#ifndef _WIN32 #ifndef _WIN32
const struct timespec sleep_for = { const struct timespec sleep_for = {
@ -128,3 +134,11 @@ void cubeb_async_log(char const * fmt, ...)
cubeb_async_logger::get().push(msg); cubeb_async_logger::get().push(msg);
va_end(args); va_end(args);
} }
void cubeb_async_log_reset_threads()
{
if (!g_cubeb_log_callback) {
return;
}
cubeb_async_logger::get().reset_producer_thread();
}

View File

@ -23,6 +23,7 @@ extern "C" {
extern cubeb_log_level g_cubeb_log_level; extern cubeb_log_level g_cubeb_log_level;
extern cubeb_log_callback g_cubeb_log_callback PRINTF_FORMAT(1, 2); extern cubeb_log_callback g_cubeb_log_callback PRINTF_FORMAT(1, 2);
void cubeb_async_log(const char * fmt, ...); void cubeb_async_log(const char * fmt, ...);
void cubeb_async_log_reset_threads();
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@ -299,7 +299,7 @@ mix_remap(long inframes,
T * out, unsigned long out_len, T * out, unsigned long out_len,
cubeb_channel_layout in_layout, cubeb_channel_layout out_layout) 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 // 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]) // (e.g. upmixing from stereo to 2F1 or mapping [L, R] to [R, L])
@ -325,15 +325,16 @@ mix_remap(long inframes,
return false; 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) { for (unsigned int j = 0; j < out_channels; ++j) {
cubeb_channel channel = CHANNEL_INDEX_TO_ORDER[out_layout][j]; cubeb_channel channel = CHANNEL_INDEX_TO_ORDER[out_layout][j];
uint32_t channel_mask = 1 << channel; uint32_t channel_mask = 1 << channel;
int channel_index = CHANNEL_ORDER_TO_INDEX[in_layout][channel]; int channel_index = CHANNEL_ORDER_TO_INDEX[in_layout][channel];
assert(channel_index >= -1);
assert(out_index + j < out_len); assert(out_index + j < out_len);
if (in_layout_mask & channel_mask) { if (in_layout_mask & channel_mask) {
assert(i + channel_index < in_len);
assert(channel_index != -1); assert(channel_index != -1);
assert(i + channel_index < in_len);
out[out_index + j] = in[i + channel_index]; out[out_index + j] = in[i + channel_index];
} else { } else {
assert(channel_index == -1); assert(channel_index == -1);
@ -353,13 +354,13 @@ downmix_fallback(long inframes,
T * out, unsigned long out_len, T * out, unsigned long out_len,
unsigned int in_channels, unsigned int out_channels) 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) { if (in_channels == out_channels && in == out) {
return; 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) { for (unsigned int j = 0; j < out_channels; ++j) {
assert(i + j < in_len && out_index + j < out_len); assert(i + j < in_len && out_index + j < out_len);
out[out_index + j] = in[i + j]; 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) T * out, unsigned long out_len, unsigned int out_channels)
{ {
for (long i = 0, j = 0; i < insamples; ++i, j += 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]; out[j] = out[j + 1] = in[i];
} }
} }
@ -460,7 +461,7 @@ cubeb_upmix(long inframes,
/* Put silence in remaining channels. */ /* Put silence in remaining channels. */
for (long i = 0, o = 0; i < inframes; ++i, o += out_channels) { for (long i = 0, o = 0; i < inframes; ++i, o += out_channels) {
for (unsigned int j = 2; j < out_channels; ++j) { 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; out[o + j] = 0.0;
} }
} }
@ -490,7 +491,8 @@ cubeb_should_downmix(cubeb_stream_params const * stream, cubeb_stream_params con
bool bool
cubeb_should_mix(cubeb_stream_params const * stream, cubeb_stream_params const * mixer) 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 { struct cubeb_mixer {
@ -559,11 +561,11 @@ void cubeb_mixer_destroy(cubeb_mixer * mixer)
void cubeb_mixer_mix(cubeb_mixer * mixer, long frames, void cubeb_mixer_mix(cubeb_mixer * mixer, long frames,
void * input_buffer, unsigned long input_buffer_length, 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 * stream_params,
cubeb_stream_params const * mixer_params) cubeb_stream_params const * mixer_params)
{ {
assert(mixer); 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); stream_params, mixer_params);
} }

View File

@ -79,7 +79,7 @@ cubeb_mixer * cubeb_mixer_create(cubeb_sample_format format,
void cubeb_mixer_destroy(cubeb_mixer * mixer); void cubeb_mixer_destroy(cubeb_mixer * mixer);
void cubeb_mixer_mix(cubeb_mixer * mixer, long frames, void cubeb_mixer_mix(cubeb_mixer * mixer, long frames,
void * input_buffer, unsigned long input_buffer_length, 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 * stream_params,
cubeb_stream_params const * mixer_params); cubeb_stream_params const * mixer_params);

View File

@ -105,7 +105,6 @@ struct cubeb_stream {
/* Flag indicating draining. Synchronized /* Flag indicating draining. Synchronized
* by stream::mutex lock. */ * by stream::mutex lock. */
int draining; int draining;
cubeb_stream_type stream_type;
/* Flags to determine in/out.*/ /* Flags to determine in/out.*/
uint32_t input_enabled; uint32_t input_enabled;
uint32_t output_enabled; uint32_t output_enabled;
@ -187,6 +186,29 @@ opensl_set_draining(cubeb_stream * stm, int value)
stm->draining = 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 static uint32_t
opensl_get_shutdown(cubeb_stream * stm) opensl_get_shutdown(cubeb_stream * stm)
{ {
@ -217,24 +239,7 @@ play_callback(SLPlayItf caller, void * user_ptr, SLuint32 event)
assert(stm); assert(stm);
switch (event) { switch (event) {
case SL_PLAYEVENT_HEADATMARKER: case SL_PLAYEVENT_HEADATMARKER:
{ opensl_notify_drained(stm);
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);
}
}
}
break; break;
default: default:
break; break;
@ -330,9 +335,16 @@ bufferqueue_callback(SLBufferQueueItf caller, void * user_ptr)
opensl_set_draining(stm, 1); opensl_set_draining(stm, 1);
r = pthread_mutex_unlock(&stm->mutex); r = pthread_mutex_unlock(&stm->mutex);
assert(r == 0); assert(r == 0);
// Use SL_PLAYEVENT_HEADATMARKER event from slPlayCallback of SLPlayItf
// to make sure all the data has been processed. if (written_duration == 0) {
(*stm->play)->SetMarkerPosition(stm->play, (SLmillisecond)written_duration); // 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; return;
} }
} }
@ -586,31 +598,6 @@ player_fullduplex_callback(SLBufferQueueItf caller, void * user_ptr)
TIMESTAMP("EXIT"); 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); static void opensl_destroy(cubeb * ctx);
#if defined(__ANDROID__) #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) { if (get_primary_output_frame_count) {
primary_buffer_size = get_primary_output_frame_count(); primary_buffer_size = get_primary_output_frame_count();
} else { } 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; return CUBEB_ERROR;
} }
} }
@ -1127,7 +1114,6 @@ opensl_configure_playback(cubeb_stream * stm, cubeb_stream_params * params) {
assert(params); assert(params);
stm->inputrate = params->rate; stm->inputrate = params->rate;
stm->stream_type = params->stream_type;
stm->framesize = params->channels * sizeof(int16_t); stm->framesize = params->channels * sizeof(int16_t);
stm->lastPosition = -1; stm->lastPosition = -1;
stm->lastPositionTimeStamp = 0; stm->lastPositionTimeStamp = 0;
@ -1228,29 +1214,6 @@ opensl_configure_playback(cubeb_stream * stm, cubeb_stream_params * params) {
assert(stm->queuebuf[i]); 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); res = (*stm->playerObj)->Realize(stm->playerObj, SL_BOOLEAN_FALSE);
if (res != SL_RESULT_SUCCESS) { if (res != SL_RESULT_SUCCESS) {
LOG("Failed to realize player object. Error code: %lu", res); 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; 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) { if (r) {
return CUBEB_ERROR; 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. uint32_t mixer_latency; // The latency returned by AudioFlinger is in ms.
/* audio_stream_type_t is an int, so this is okay. */ /* 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) { if (r) {
return CUBEB_ERROR; return CUBEB_ERROR;
} }
@ -1746,6 +1709,7 @@ static struct cubeb_ops const opensl_ops = {
.stream_destroy = opensl_stream_destroy, .stream_destroy = opensl_stream_destroy,
.stream_start = opensl_stream_start, .stream_start = opensl_stream_start,
.stream_stop = opensl_stream_stop, .stream_stop = opensl_stream_stop,
.stream_reset_default_device = NULL,
.stream_get_position = opensl_stream_get_position, .stream_get_position = opensl_stream_get_position,
.stream_get_latency = opensl_stream_get_latency, .stream_get_latency = opensl_stream_get_latency,
.stream_set_volume = opensl_stream_set_volume, .stream_set_volume = opensl_stream_set_volume,

View File

@ -13,7 +13,7 @@
#include "cubeb/cubeb.h" #include "cubeb/cubeb.h"
#include "cubeb-internal.h" #include "cubeb-internal.h"
#include "cubeb_mixer.h" #include "cubeb_mixer.h"
#include "cubeb_utils.h" #include "cubeb_strings.h"
#include <stdio.h> #include <stdio.h>
#ifdef DISABLE_LIBPULSE_DLOPEN #ifdef DISABLE_LIBPULSE_DLOPEN
@ -102,6 +102,7 @@ struct cubeb {
int error; int error;
cubeb_device_collection_changed_callback collection_changed_callback; cubeb_device_collection_changed_callback collection_changed_callback;
void * collection_changed_user_ptr; void * collection_changed_user_ptr;
cubeb_strings * device_ids;
}; };
struct cubeb_stream { struct cubeb_stream {
@ -127,6 +128,24 @@ enum cork_state {
NOTIFY = 1 << 1 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 static void
sink_info_callback(pa_context * context, const pa_sink_info * info, int eol, void * u) 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->ops = &pulse_ops;
ctx->libpulse = libpulse; 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->mainloop = WRAP(pa_threaded_mainloop_new)();
ctx->default_sink_info = NULL; ctx->default_sink_info = NULL;
@ -630,7 +653,6 @@ pulse_init(cubeb ** context, char const * context_name)
WRAP(pa_operation_unref)(o); WRAP(pa_operation_unref)(o);
} }
WRAP(pa_threaded_mainloop_unlock)(ctx->mainloop); WRAP(pa_threaded_mainloop_unlock)(ctx->mainloop);
assert(ctx->default_sink_info);
*context = ctx; *context = ctx;
@ -650,6 +672,9 @@ pulse_get_max_channel_count(cubeb * ctx, uint32_t * max_channels)
(void)ctx; (void)ctx;
assert(ctx && max_channels); assert(ctx && max_channels);
if (!ctx->default_sink_info)
return CUBEB_ERROR;
*max_channels = ctx->default_sink_info->channel_map.channels; *max_channels = ctx->default_sink_info->channel_map.channels;
return CUBEB_OK; return CUBEB_OK;
@ -661,6 +686,9 @@ pulse_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate)
assert(ctx && rate); assert(ctx && rate);
(void)ctx; (void)ctx;
if (!ctx->default_sink_info)
return CUBEB_ERROR;
*rate = ctx->default_sink_info->sample_spec.rate; *rate = ctx->default_sink_info->sample_spec.rate;
return CUBEB_OK; return CUBEB_OK;
@ -672,6 +700,9 @@ pulse_get_preferred_channel_layout(cubeb * ctx, cubeb_channel_layout * layout)
assert(ctx && layout); assert(ctx && layout);
(void)ctx; (void)ctx;
if (!ctx->default_sink_info)
return CUBEB_ERROR;
*layout = channel_map_to_layout(&ctx->default_sink_info->channel_map); *layout = channel_map_to_layout(&ctx->default_sink_info->channel_map);
return CUBEB_OK; return CUBEB_OK;
@ -717,6 +748,10 @@ pulse_destroy(cubeb * ctx)
WRAP(pa_threaded_mainloop_free)(ctx->mainloop); WRAP(pa_threaded_mainloop_free)(ctx->mainloop);
} }
if (ctx->device_ids) {
cubeb_strings_destroy(ctx->device_ids);
}
if (ctx->libpulse) { if (ctx->libpulse) {
dlclose(ctx->libpulse); dlclose(ctx->libpulse);
} }
@ -751,8 +786,9 @@ create_pa_stream(cubeb_stream * stm,
{ {
assert(stm && stream_params); assert(stm && stream_params);
assert(&stm->input_stream == pa_stm || (&stm->output_stream == pa_stm && assert(&stm->input_stream == pa_stm || (&stm->output_stream == pa_stm &&
stream_params->layout != CUBEB_LAYOUT_UNDEFINED && (stream_params->layout == CUBEB_LAYOUT_UNDEFINED ||
CUBEB_CHANNEL_LAYOUT_MAPS[stream_params->layout].channels == stream_params->channels)); (stream_params->layout != CUBEB_LAYOUT_UNDEFINED &&
CUBEB_CHANNEL_LAYOUT_MAPS[stream_params->layout].channels == stream_params->channels))));
*pa_stm = NULL; *pa_stm = NULL;
pa_sample_spec ss; pa_sample_spec ss;
ss.format = to_pulse_format(stream_params->format); 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_volume_t vol;
pa_cvolume cvol; pa_cvolume cvol;
const pa_sample_spec * ss; const pa_sample_spec * ss;
cubeb * ctx;
if (!stm->output_stream) { if (!stm->output_stream) {
return CUBEB_ERROR; 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, /* if the pulse daemon is configured to use flat volumes,
* apply our own gain instead of changing the input volume on the sink. */ * 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; stm->volume = volume;
} else { } else {
ss = WRAP(pa_stream_get_sample_spec)(stm->output_stream); 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); 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, index, &cvol, volume_success,
stm); stm);
if (op) { if (op) {
operation_wait(stm->context, stm->output_stream, op); operation_wait(ctx, stm->output_stream, op);
WRAP(pa_operation_unref)(op); WRAP(pa_operation_unref)(op);
} }
} }
WRAP(pa_threaded_mainloop_unlock)(stm->context->mainloop); WRAP(pa_threaded_mainloop_unlock)(ctx->mainloop);
return CUBEB_OK; 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; pulse_dev_list_data * list_data = user_data;
cubeb_device_info * devinfo; cubeb_device_info * devinfo;
const char * prop; char const * prop = NULL;
char const * device_id = NULL;
(void)context; (void)context;
if (eol || info == NULL) if (eol) {
WRAP(pa_threaded_mainloop_signal)(list_data->context->mainloop, 0);
return; 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); pulse_ensure_dev_list_data_list_size(list_data);
devinfo = &list_data->devinfo[list_data->count]; devinfo = &list_data->devinfo[list_data->count];
memset(devinfo, 0, sizeof(cubeb_device_info)); 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->devid = (cubeb_devid) devinfo->device_id;
devinfo->friendly_name = strdup(info->description); devinfo->friendly_name = strdup(info->description);
prop = WRAP(pa_proplist_gets)(info->proplist, "sysfs.path"); 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; devinfo->latency_hi = 0;
list_data->count += 1; list_data->count += 1;
WRAP(pa_threaded_mainloop_signal)(list_data->context->mainloop, 0);
} }
static cubeb_device_state 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; pulse_dev_list_data * list_data = user_data;
cubeb_device_info * devinfo; cubeb_device_info * devinfo;
const char * prop; char const * prop = NULL;
char const * device_id = NULL;
(void)context; (void)context;
if (eol) if (eol) {
WRAP(pa_threaded_mainloop_signal)(list_data->context->mainloop, 0);
return; 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); pulse_ensure_dev_list_data_list_size(list_data);
devinfo = &list_data->devinfo[list_data->count]; devinfo = &list_data->devinfo[list_data->count];
memset(devinfo, 0, sizeof(cubeb_device_info)); 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->devid = (cubeb_devid) devinfo->device_id;
devinfo->friendly_name = strdup(info->description); devinfo->friendly_name = strdup(info->description);
prop = WRAP(pa_proplist_gets)(info->proplist, "sysfs.path"); 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; devinfo->latency_hi = 0;
list_data->count += 1; list_data->count += 1;
WRAP(pa_threaded_mainloop_signal)(list_data->context->mainloop, 0);
} }
static void 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_sink_name);
free(list_data->default_source_name); free(list_data->default_source_name);
list_data->default_sink_name = strdup(i->default_sink_name); list_data->default_sink_name =
list_data->default_source_name = strdup(i->default_source_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); 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; 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 static int
pulse_stream_get_current_device(cubeb_stream * stm, cubeb_device ** const device) 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_sample_rate = pulse_get_preferred_sample_rate,
.get_preferred_channel_layout = pulse_get_preferred_channel_layout, .get_preferred_channel_layout = pulse_get_preferred_channel_layout,
.enumerate_devices = pulse_enumerate_devices, .enumerate_devices = pulse_enumerate_devices,
.device_collection_destroy = cubeb_utils_default_device_collection_destroy, .device_collection_destroy = pulse_device_collection_destroy,
.destroy = pulse_destroy, .destroy = pulse_destroy,
.stream_init = pulse_stream_init, .stream_init = pulse_stream_init,
.stream_destroy = pulse_stream_destroy, .stream_destroy = pulse_stream_destroy,
.stream_start = pulse_stream_start, .stream_start = pulse_stream_start,
.stream_stop = pulse_stream_stop, .stream_stop = pulse_stream_stop,
.stream_reset_default_device = NULL,
.stream_get_position = pulse_stream_get_position, .stream_get_position = pulse_stream_get_position,
.stream_get_latency = pulse_stream_get_latency, .stream_get_latency = pulse_stream_get_latency,
.stream_set_volume = pulse_stream_set_volume, .stream_set_volume = pulse_stream_set_volume,

View File

@ -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> template<typename T>
passthrough_resampler<T>::passthrough_resampler(cubeb_stream * s, passthrough_resampler<T>::passthrough_resampler(cubeb_stream * s,
cubeb_data_callback cb, cubeb_data_callback cb,
void * ptr, void * ptr,
uint32_t input_channels) uint32_t input_channels,
uint32_t sample_rate)
: processor(input_channels) : processor(input_channels)
, stream(s) , stream(s)
, data_callback(cb) , data_callback(cb)
, user_ptr(ptr) , 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) { if (input_buffer) {
internal_input_buffer.pop(nullptr, frames_to_samples(output_frames)); internal_input_buffer.pop(nullptr, frames_to_samples(output_frames));
*input_frames_count = output_frames; *input_frames_count = output_frames;
drop_audio_if_needed();
} }
return rv; return rv;
@ -242,9 +250,15 @@ cubeb_resampler_speex<T, InputProcessor, OutputProcessor>
output_processor->written(got); output_processor->written(got);
input_processor->drop_audio_if_needed();
/* Process the output. If not enough frames have been returned from the /* Process the output. If not enough frames have been returned from the
* callback, drain the processors. */ * 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 */ /* Resampler C API */

View File

@ -39,6 +39,13 @@ MOZ_END_STD_NAMESPACE
/* This header file contains the internal C++ API of the resamplers, for testing. */ /* 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); int to_speex_quality(cubeb_resampler_quality q);
struct cubeb_resampler { struct cubeb_resampler {
@ -75,7 +82,8 @@ public:
passthrough_resampler(cubeb_stream * s, passthrough_resampler(cubeb_stream * s,
cubeb_data_callback cb, cubeb_data_callback cb,
void * ptr, void * ptr,
uint32_t input_channels); uint32_t input_channels,
uint32_t sample_rate);
virtual long fill(void * input_buffer, long * input_frames_count, virtual long fill(void * input_buffer, long * input_frames_count,
void * output_buffer, long output_frames); void * output_buffer, long output_frames);
@ -85,6 +93,15 @@ public:
return 0; 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: private:
cubeb_stream * const stream; cubeb_stream * const stream;
const cubeb_data_callback data_callback; 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 /* This allows to buffer some input to account for the fact that we buffer
* some inputs. */ * some inputs. */
auto_array<T> internal_input_buffer; auto_array<T> internal_input_buffer;
uint32_t sample_rate;
}; };
/** Bidirectional resampler, can resample an input and an output stream, or just /** Bidirectional resampler, can resample an input and an output stream, or just
@ -164,6 +182,7 @@ public:
int quality) int quality)
: processor(channels) : processor(channels)
, resampling_ratio(static_cast<float>(source_rate) / target_rate) , resampling_ratio(static_cast<float>(source_rate) / target_rate)
, source_rate(source_rate)
, additional_latency(0) , additional_latency(0)
, leftover_samples(0) , leftover_samples(0)
{ {
@ -296,6 +315,16 @@ public:
resampling_in_buffer.set_length(leftover_samples + resampling_in_buffer.set_length(leftover_samples +
frames_to_samples(written_frames)); 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: private:
/** Wrapper for the speex resampling functions to have a typed /** Wrapper for the speex resampling functions to have a typed
* interface. */ * interface. */
@ -332,6 +361,7 @@ private:
SpeexResamplerState * speex_resampler; SpeexResamplerState * speex_resampler;
/** Source rate / target rate. */ /** Source rate / target rate. */
const float resampling_ratio; const float resampling_ratio;
const uint32_t source_rate;
/** Storage for the input frames, to be resampled. Also contains /** Storage for the input frames, to be resampled. Also contains
* any unresampled frames after resampling. */ * any unresampled frames after resampling. */
auto_array<T> resampling_in_buffer; auto_array<T> resampling_in_buffer;
@ -350,11 +380,13 @@ class delay_line : public processor {
public: public:
/** Constructor /** Constructor
* @parameter frames the number of frames of delay. * @parameter frames the number of frames of delay.
* @parameter channels the number of channels of this delay line. */ * @parameter channels the number of channels of this delay line.
delay_line(uint32_t frames, uint32_t channels) * @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) : processor(channels)
, length(frames) , length(frames)
, leftover_samples(0) , leftover_samples(0)
, sample_rate(sample_rate)
{ {
/* Fill the delay line with some silent frames to add latency. */ /* Fill the delay line with some silent frames to add latency. */
delay_input_buffer.push_silence(frames * channels); delay_input_buffer.push_silence(frames * channels);
@ -444,6 +476,15 @@ public:
{ {
return length; 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: private:
/** The length, in frames, of this delay line */ /** The length, in frames, of this delay line */
uint32_t length; uint32_t length;
@ -455,6 +496,7 @@ private:
/** The output buffer. This is only ever used if using the ::output with a /** The output buffer. This is only ever used if using the ::output with a
* single argument. */ * single argument. */
auto_array<T> delay_output_buffer; auto_array<T> delay_output_buffer;
uint32_t sample_rate;
}; };
/** This sits behind the C API and is more typed. */ /** 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))) { (output_params && !input_params && (output_params->rate == target_rate))) {
return new passthrough_resampler<T>(stream, callback, return new passthrough_resampler<T>(stream, callback,
user_ptr, 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 /* 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. */ * other direction so that the streams are synchronized. */
if (input_resampler && !output_resampler && input_params && output_params) { if (input_resampler && !output_resampler && input_params && output_params) {
output_delay.reset(new delay_line<T>(input_resampler->latency(), output_delay.reset(new delay_line<T>(input_resampler->latency(),
output_params->channels)); output_params->channels,
output_params->rate));
if (!output_delay) { if (!output_delay) {
return NULL; return NULL;
} }
} else if (output_resampler && !input_resampler && input_params && output_params) { } else if (output_resampler && !input_resampler && input_params && output_params) {
input_delay.reset(new delay_line<T>(output_resampler->latency(), input_delay.reset(new delay_line<T>(output_resampler->latency(),
input_params->channels)); input_params->channels,
output_params->rate));
if (!input_delay) { if (!input_delay) {
return NULL; return NULL;
} }

View File

@ -226,6 +226,17 @@ public:
{ {
return storage_capacity() - 1; 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: private:
/** Return true if the ring buffer is empty. /** Return true if the ring buffer is empty.
* *

View File

@ -376,6 +376,7 @@ static struct cubeb_ops const sndio_ops = {
.stream_destroy = sndio_stream_destroy, .stream_destroy = sndio_stream_destroy,
.stream_start = sndio_stream_start, .stream_start = sndio_stream_start,
.stream_stop = sndio_stream_stop, .stream_stop = sndio_stream_stop,
.stream_reset_default_device = NULL,
.stream_get_position = sndio_stream_get_position, .stream_get_position = sndio_stream_get_position,
.stream_get_latency = sndio_stream_get_latency, .stream_get_latency = sndio_stream_get_latency,
.stream_set_volume = sndio_stream_set_volume, .stream_set_volume = sndio_stream_set_volume,

155
Externals/cubeb/src/cubeb_strings.c vendored Normal file
View File

@ -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);
}

44
Externals/cubeb/src/cubeb_strings.h vendored Normal file
View File

@ -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

View File

@ -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;
}

View File

@ -17,7 +17,7 @@
#include <assert.h> #include <assert.h>
#include <mutex> #include <mutex>
#include <type_traits> #include <type_traits>
#if defined(WIN32) #if defined(_WIN32)
#include "cubeb_utils_win.h" #include "cubeb_utils_win.h"
#else #else
#include "cubeb_utils_unix.h" #include "cubeb_utils_unix.h"
@ -336,17 +336,4 @@ private:
using auto_lock = std::lock_guard<owned_critical_section>; using auto_lock = std::lock_guard<owned_critical_section>;
#endif // __cplusplus #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 */ #endif /* CUBEB_UTILS */

View File

@ -30,6 +30,7 @@
#include "cubeb-internal.h" #include "cubeb-internal.h"
#include "cubeb_mixer.h" #include "cubeb_mixer.h"
#include "cubeb_resampler.h" #include "cubeb_resampler.h"
#include "cubeb_strings.h"
#include "cubeb_utils.h" #include "cubeb_utils.h"
#ifndef PKEY_Device_FriendlyName #ifndef PKEY_Device_FriendlyName
@ -173,6 +174,7 @@ static std::unique_ptr<wchar_t const []> utf8_to_wstr(char const * str);
struct cubeb { struct cubeb {
cubeb_ops const * ops = &wasapi_ops; cubeb_ops const * ops = &wasapi_ops;
cubeb_strings * device_ids;
}; };
class wasapi_endpoint_notification_client; class wasapi_endpoint_notification_client;
@ -382,6 +384,23 @@ private:
}; };
namespace { 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) bool has_input(cubeb_stream * stm)
{ {
return stm->input_stream_params.rate != 0; 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. // 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. // Use static to allocate this local variable in data space instead of stack.
static DWORD map[CUBEB_LAYOUT_MAX] = { static DWORD map[CUBEB_LAYOUT_MAX] = {
0, // CUBEB_LAYOUT_UNDEFINED KSAUDIO_SPEAKER_DIRECTOUT, // CUBEB_LAYOUT_UNDEFINED
MASK_DUAL_MONO, // CUBEB_LAYOUT_DUAL_MONO MASK_DUAL_MONO, // CUBEB_LAYOUT_DUAL_MONO
MASK_DUAL_MONO_LFE, // CUBEB_LAYOUT_DUAL_MONO_LFE MASK_DUAL_MONO_LFE, // CUBEB_LAYOUT_DUAL_MONO_LFE
MASK_MONO, // CUBEB_LAYOUT_MONO MASK_MONO, // CUBEB_LAYOUT_MONO
MASK_MONO_LFE, // CUBEB_LAYOUT_MONO_LFE MASK_MONO_LFE, // CUBEB_LAYOUT_MONO_LFE
MASK_STEREO, // CUBEB_LAYOUT_STEREO MASK_STEREO, // CUBEB_LAYOUT_STEREO
MASK_STEREO_LFE, // CUBEB_LAYOUT_STEREO_LFE MASK_STEREO_LFE, // CUBEB_LAYOUT_STEREO_LFE
MASK_3F, // CUBEB_LAYOUT_3F MASK_3F, // CUBEB_LAYOUT_3F
MASK_3F_LFE, // CUBEB_LAYOUT_3F_LFE MASK_3F_LFE, // CUBEB_LAYOUT_3F_LFE
MASK_2F1, // CUBEB_LAYOUT_2F1 MASK_2F1, // CUBEB_LAYOUT_2F1
MASK_2F1_LFE, // CUBEB_LAYOUT_2F1_LFE MASK_2F1_LFE, // CUBEB_LAYOUT_2F1_LFE
MASK_3F1, // CUBEB_LAYOUT_3F1 MASK_3F1, // CUBEB_LAYOUT_3F1
MASK_3F1_LFE, // CUBEB_LAYOUT_3F1_LFE MASK_3F1_LFE, // CUBEB_LAYOUT_3F1_LFE
MASK_2F2, // CUBEB_LAYOUT_2F2 MASK_2F2, // CUBEB_LAYOUT_2F2
MASK_2F2_LFE, // CUBEB_LAYOUT_2F2_LFE MASK_2F2_LFE, // CUBEB_LAYOUT_2F2_LFE
MASK_3F2, // CUBEB_LAYOUT_3F2 MASK_3F2, // CUBEB_LAYOUT_3F2
MASK_3F2_LFE, // CUBEB_LAYOUT_3F2_LFE MASK_3F2_LFE, // CUBEB_LAYOUT_3F2_LFE
MASK_3F3R_LFE, // CUBEB_LAYOUT_3F3R_LFE MASK_3F3R_LFE, // CUBEB_LAYOUT_3F3R_LFE
MASK_3F4_LFE, // CUBEB_LAYOUT_3F4_LFE MASK_3F4_LFE, // CUBEB_LAYOUT_3F4_LFE
}; };
return map[layout]; return map[layout];
} }
cubeb_channel_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) { switch (mask) {
// MASK_DUAL_MONO(_LFE) is same as STEREO(_LFE), so we skip it. // MASK_DUAL_MONO(_LFE) is same as STEREO(_LFE), so we skip it.
case MASK_MONO: return CUBEB_LAYOUT_MONO; case MASK_MONO: return CUBEB_LAYOUT_MONO;
@ -483,27 +516,21 @@ get_rate(cubeb_stream * stm)
} }
uint32_t 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 uint32_t
hns_to_frames(cubeb_stream * stm, REFERENCE_TIME hns) hns_to_frames(cubeb_stream * stm, REFERENCE_TIME hns)
{ {
return hns_to_ms(hns * get_rate(stm)) / 1000; return hns_to_frames(get_rate(stm), hns);
}
uint32_t
hns_to_frames(uint32_t rate, REFERENCE_TIME hns)
{
return hns_to_ms(hns * rate) / 1000;
} }
REFERENCE_TIME REFERENCE_TIME
frames_to_hns(cubeb_stream * stm, uint32_t frames) 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 /* 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(); cubeb * ctx = new cubeb();
ctx->ops = &wasapi_ops; ctx->ops = &wasapi_ops;
if (cubeb_strings_init(&ctx->device_ids) != CUBEB_OK) {
free(ctx);
return CUBEB_ERROR;
}
*context = ctx; *context = ctx;
@ -1212,6 +1243,10 @@ bool stop_and_join_render_thread(cubeb_stream * stm)
void wasapi_destroy(cubeb * context) void wasapi_destroy(cubeb * context)
{ {
if (context->device_ids) {
cubeb_strings_destroy(context->device_ids);
}
delete context; delete context;
} }
@ -1374,8 +1409,7 @@ wasapi_get_preferred_channel_layout(cubeb * context, cubeb_channel_layout * layo
} }
com_heap_ptr<WAVEFORMATEX> mix_format(tmp); com_heap_ptr<WAVEFORMATEX> mix_format(tmp);
WAVEFORMATEXTENSIBLE * format_pcm = reinterpret_cast<WAVEFORMATEXTENSIBLE *>(mix_format.get()); *layout = mask_to_channel_layout(mix_format.get());
*layout = mask_to_channel_layout(format_pcm->dwChannelMask);
LOG("Preferred channel layout: %s", CUBEB_CHANNEL_LAYOUT_MAPS[*layout].name); LOG("Preferred channel layout: %s", CUBEB_CHANNEL_LAYOUT_MAPS[*layout].name);
@ -1400,8 +1434,6 @@ waveformatex_update_derived_properties(WAVEFORMATEX * format)
static void static void
handle_channel_layout(cubeb_stream * stm, EDataFlow direction, com_heap_ptr<WAVEFORMATEX> & mix_format, const cubeb_stream_params * stream_params) 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; com_ptr<IAudioClient> & audio_client = (direction == eRender) ? stm->output_client : stm->input_client;
XASSERT(audio_client); XASSERT(audio_client);
/* The docs say that GetMixFormat is always of type WAVEFORMATEXTENSIBLE [1], /* 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 and handle the eventual upmix/downmix ourselves. Ignore the subformat of
the suggestion, since it seems to always be IEEE_FLOAT. */ the suggestion, since it seems to always be IEEE_FLOAT. */
LOG("Using WASAPI suggested format: channels: %d", closest->nChannels); LOG("Using WASAPI suggested format: channels: %d", closest->nChannels);
XASSERT(closest->wFormatTag == WAVE_FORMAT_EXTENSIBLE);
WAVEFORMATEXTENSIBLE * closest_pcm = reinterpret_cast<WAVEFORMATEXTENSIBLE *>(closest); WAVEFORMATEXTENSIBLE * closest_pcm = reinterpret_cast<WAVEFORMATEXTENSIBLE *>(closest);
format_pcm->dwChannelMask = closest_pcm->dwChannelMask; format_pcm->dwChannelMask = closest_pcm->dwChannelMask;
mix_format->nChannels = closest->nChannels; 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 /* 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 field with some sound cards. We restore the mix format, and let the rest
of the code figure out the right conversion path. */ 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; *reinterpret_cast<WAVEFORMATEXTENSIBLE *>(mix_format.get()) = hw_mix_format;
} else if (hr == S_OK) { } else if (hr == S_OK) {
LOG("Requested format accepted by WASAPI."); 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); com_heap_ptr<WAVEFORMATEX> mix_format(tmp);
WAVEFORMATEXTENSIBLE * format_pcm = reinterpret_cast<WAVEFORMATEXTENSIBLE *>(mix_format.get());
mix_format->wBitsPerSample = stm->bytes_per_sample * 8; 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()); waveformatex_update_derived_properties(mix_format.get());
/* Set channel layout only when there're more than two channels. Otherwise, /* Set channel layout only when there're more than two channels. Otherwise,
* use the default setting retrieved from the stream format of the audio * use the default setting retrieved from the stream format of the audio
* engine's internal processing by GetMixFormat. */ * engine's internal processing by GetMixFormat. */
if (mix_format->nChannels > 2) { 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->format = stream_params->format;
mix_params->rate = mix_format->nSamplesPerSec; mix_params->rate = mix_format->nSamplesPerSec;
mix_params->channels = mix_format->nChannels; 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) { if (mix_params->layout == CUBEB_LAYOUT_UNDEFINED) {
LOG("Output using undefined layout!\n"); LOG("Output using undefined layout!\n");
} else if (mix_format->nChannels != CUBEB_CHANNEL_LAYOUT_MAPS[mix_params->layout].channels) { } 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_stream_params = *output_stream_params;
stm->output_device = utf8_to_wstr(reinterpret_cast<char const *>(output_device)); stm->output_device = utf8_to_wstr(reinterpret_cast<char const *>(output_device));
// Make sure the layout matches the channel count. // 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) { 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; 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); stm->thread = (HANDLE) _beginthreadex(NULL, 512 * 1024, wasapi_stream_render_loop, stm, STACK_SIZE_PARAM_IS_A_RESERVATION, NULL);
if (stm->thread == NULL) { if (stm->thread == NULL) {
LOG("could not create WASAPI render thread."); LOG("could not create WASAPI render thread.");
@ -2013,6 +2051,17 @@ int wasapi_stream_stop(cubeb_stream * stm)
return CUBEB_OK; 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) int wasapi_stream_get_position(cubeb_stream * stm, uint64_t * position)
{ {
XASSERT(stm && position); XASSERT(stm && position);
@ -2151,8 +2200,8 @@ wasapi_is_default_device(EDataFlow flow, ERole role, LPCWSTR device_id,
return ret; return ret;
} }
static int int
wasapi_create_device(cubeb_device_info * ret, IMMDeviceEnumerator * enumerator, IMMDevice * dev) wasapi_create_device(cubeb * ctx, cubeb_device_info& ret, IMMDeviceEnumerator * enumerator, IMMDevice * dev)
{ {
com_ptr<IMMEndpoint> endpoint; com_ptr<IMMEndpoint> endpoint;
com_ptr<IMMDevice> devnode; com_ptr<IMMDevice> devnode;
@ -2181,19 +2230,23 @@ wasapi_create_device(cubeb_device_info * ret, IMMDeviceEnumerator * enumerator,
if (FAILED(hr)) return CUBEB_ERROR; if (FAILED(hr)) return CUBEB_ERROR;
com_heap_ptr<wchar_t> device_id(tmp); 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()); hr = dev->OpenPropertyStore(STGM_READ, propstore.receive());
if (FAILED(hr)) return CUBEB_ERROR; if (FAILED(hr)) return CUBEB_ERROR;
hr = dev->GetState(&state); hr = dev->GetState(&state);
if (FAILED(hr)) return CUBEB_ERROR; if (FAILED(hr)) return CUBEB_ERROR;
XASSERT(ret); ret.device_id = device_id_intern;
ret->device_id = wstr_to_utf8(device_id.get()); ret.devid = reinterpret_cast<cubeb_devid>(ret.device_id);
ret->devid = reinterpret_cast<cubeb_devid>(ret->device_id);
prop_variant namevar; prop_variant namevar;
hr = propstore->GetValue(PKEY_Device_FriendlyName, &namevar); hr = propstore->GetValue(PKEY_Device_FriendlyName, &namevar);
if (SUCCEEDED(hr)) 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); devnode = wasapi_get_device_node(enumerator, dev);
if (devnode) { if (devnode) {
@ -2204,60 +2257,60 @@ wasapi_create_device(cubeb_device_info * ret, IMMDeviceEnumerator * enumerator,
prop_variant instancevar; prop_variant instancevar;
hr = ps->GetValue(PKEY_Device_InstanceId, &instancevar); hr = ps->GetValue(PKEY_Device_InstanceId, &instancevar);
if (SUCCEEDED(hr)) { 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)) 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)) 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)) 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; if (flow == eRender) ret.type = CUBEB_DEVICE_TYPE_OUTPUT;
else if (flow == eCapture) ret->type = CUBEB_DEVICE_TYPE_INPUT; else if (flow == eCapture) ret.type = CUBEB_DEVICE_TYPE_INPUT;
switch (state) { switch (state) {
case DEVICE_STATE_ACTIVE: case DEVICE_STATE_ACTIVE:
ret->state = CUBEB_DEVICE_STATE_ENABLED; ret.state = CUBEB_DEVICE_STATE_ENABLED;
break; break;
case DEVICE_STATE_UNPLUGGED: case DEVICE_STATE_UNPLUGGED:
ret->state = CUBEB_DEVICE_STATE_UNPLUGGED; ret.state = CUBEB_DEVICE_STATE_UNPLUGGED;
break; break;
default: default:
ret->state = CUBEB_DEVICE_STATE_DISABLED; ret.state = CUBEB_DEVICE_STATE_DISABLED;
break; break;
}; };
ret->format = static_cast<cubeb_device_fmt>(CUBEB_DEVICE_FMT_F32NE | CUBEB_DEVICE_FMT_S16NE); ret.format = static_cast<cubeb_device_fmt>(CUBEB_DEVICE_FMT_F32NE | CUBEB_DEVICE_FMT_S16NE);
ret->default_format = CUBEB_DEVICE_FMT_F32NE; ret.default_format = CUBEB_DEVICE_FMT_F32NE;
prop_variant fmtvar; prop_variant fmtvar;
hr = propstore->GetValue(PKEY_AudioEngine_DeviceFormat, &fmtvar); hr = propstore->GetValue(PKEY_AudioEngine_DeviceFormat, &fmtvar);
if (SUCCEEDED(hr) && fmtvar.vt == VT_BLOB) { if (SUCCEEDED(hr) && fmtvar.vt == VT_BLOB) {
if (fmtvar.blob.cbSize == sizeof(PCMWAVEFORMAT)) { if (fmtvar.blob.cbSize == sizeof(PCMWAVEFORMAT)) {
const PCMWAVEFORMAT * pcm = reinterpret_cast<const PCMWAVEFORMAT *>(fmtvar.blob.pBlobData); const PCMWAVEFORMAT * pcm = reinterpret_cast<const PCMWAVEFORMAT *>(fmtvar.blob.pBlobData);
ret->max_rate = ret->min_rate = ret->default_rate = pcm->wf.nSamplesPerSec; ret.max_rate = ret.min_rate = ret.default_rate = pcm->wf.nSamplesPerSec;
ret->max_channels = pcm->wf.nChannels; ret.max_channels = pcm->wf.nChannels;
} else if (fmtvar.blob.cbSize >= sizeof(WAVEFORMATEX)) { } else if (fmtvar.blob.cbSize >= sizeof(WAVEFORMATEX)) {
WAVEFORMATEX* wfx = reinterpret_cast<WAVEFORMATEX*>(fmtvar.blob.pBlobData); WAVEFORMATEX* wfx = reinterpret_cast<WAVEFORMATEX*>(fmtvar.blob.pBlobData);
if (fmtvar.blob.cbSize >= sizeof(WAVEFORMATEX) + wfx->cbSize || if (fmtvar.blob.cbSize >= sizeof(WAVEFORMATEX) + wfx->cbSize ||
wfx->wFormatTag == WAVE_FORMAT_PCM) { wfx->wFormatTag == WAVE_FORMAT_PCM) {
ret->max_rate = ret->min_rate = ret->default_rate = wfx->nSamplesPerSec; ret.max_rate = ret.min_rate = ret.default_rate = wfx->nSamplesPerSec;
ret->max_channels = wfx->nChannels; ret.max_channels = wfx->nChannels;
} }
} }
} }
if (SUCCEEDED(dev->Activate(__uuidof(IAudioClient), CLSCTX_INPROC_SERVER, NULL, client.receive_vpp())) && if (SUCCEEDED(dev->Activate(__uuidof(IAudioClient), CLSCTX_INPROC_SERVER, NULL, client.receive_vpp())) &&
SUCCEEDED(client->GetDevicePeriod(&def_period, &min_period))) { SUCCEEDED(client->GetDevicePeriod(&def_period, &min_period))) {
ret->latency_lo = hns_to_frames(ret->default_rate, 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_hi = hns_to_frames(ret.default_rate, def_period);
} else { } else {
ret->latency_lo = 0; ret.latency_lo = 0;
ret->latency_hi = 0; ret.latency_hi = 0;
} }
return CUBEB_OK; return CUBEB_OK;
@ -2300,11 +2353,11 @@ wasapi_enumerate_devices(cubeb * context, cubeb_device_type type,
LOG("IMMDeviceCollection::GetCount() failed: %lx", hr); LOG("IMMDeviceCollection::GetCount() failed: %lx", hr);
return CUBEB_ERROR; return CUBEB_ERROR;
} }
cubeb_device_info * devices = cubeb_device_info * devices = new cubeb_device_info[cc];
(cubeb_device_info *) calloc(cc, sizeof(cubeb_device_info)); if (!devices)
if (!devices) {
return CUBEB_ERROR; return CUBEB_ERROR;
}
PodZero(devices, cc);
out->count = 0; out->count = 0;
for (i = 0; i < cc; i++) { for (i = 0; i < cc; i++) {
com_ptr<IMMDevice> dev; 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); LOG("IMMDeviceCollection::Item(%u) failed: %lx", i-1, hr);
continue; continue;
} }
auto cur = &devices[out->count]; if (wasapi_create_device(context, devices[out->count],
if (wasapi_create_device(cur, enumerator.get(), dev.get()) == CUBEB_OK) { enumerator.get(), dev.get()) == CUBEB_OK) {
out->count += 1; out->count += 1;
} }
} }
@ -2323,6 +2376,21 @@ wasapi_enumerate_devices(cubeb * context, cubeb_device_type type,
return CUBEB_OK; 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 = { cubeb_ops const wasapi_ops = {
/*.init =*/ wasapi_init, /*.init =*/ wasapi_init,
/*.get_backend_id =*/ wasapi_get_backend_id, /*.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_sample_rate =*/ wasapi_get_preferred_sample_rate,
/*.get_preferred_channel_layout =*/ wasapi_get_preferred_channel_layout, /*.get_preferred_channel_layout =*/ wasapi_get_preferred_channel_layout,
/*.enumerate_devices =*/ wasapi_enumerate_devices, /*.enumerate_devices =*/ wasapi_enumerate_devices,
/*.device_collection_destroy =*/ cubeb_utils_default_device_collection_destroy, /*.device_collection_destroy =*/ wasapi_device_collection_destroy,
/*.destroy =*/ wasapi_destroy, /*.destroy =*/ wasapi_destroy,
/*.stream_init =*/ wasapi_stream_init, /*.stream_init =*/ wasapi_stream_init,
/*.stream_destroy =*/ wasapi_stream_destroy, /*.stream_destroy =*/ wasapi_stream_destroy,
/*.stream_start =*/ wasapi_stream_start, /*.stream_start =*/ wasapi_stream_start,
/*.stream_stop =*/ wasapi_stream_stop, /*.stream_stop =*/ wasapi_stream_stop,
/*.stream_reset_default_device =*/ wasapi_stream_reset_default_device,
/*.stream_get_position =*/ wasapi_stream_get_position, /*.stream_get_position =*/ wasapi_stream_get_position,
/*.stream_get_latency =*/ wasapi_stream_get_latency, /*.stream_get_latency =*/ wasapi_stream_get_latency,
/*.stream_set_volume =*/ wasapi_stream_set_volume, /*.stream_set_volume =*/ wasapi_stream_set_volume,

View File

@ -19,7 +19,6 @@
#include <math.h> #include <math.h>
#include "cubeb/cubeb.h" #include "cubeb/cubeb.h"
#include "cubeb-internal.h" #include "cubeb-internal.h"
#include "cubeb_utils.h"
/* This is missing from the MinGW headers. Use a safe fallback. */ /* This is missing from the MinGW headers. Use a safe fallback. */
#if !defined(MEMORY_ALLOCATION_ALIGNMENT) #if !defined(MEMORY_ALLOCATION_ALIGNMENT)
@ -1017,6 +1016,26 @@ winmm_enumerate_devices(cubeb * context, cubeb_device_type type,
return CUBEB_OK; 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 = { static struct cubeb_ops const winmm_ops = {
/*.init =*/ winmm_init, /*.init =*/ winmm_init,
/*.get_backend_id =*/ winmm_get_backend_id, /*.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_sample_rate =*/ winmm_get_preferred_sample_rate,
/*.get_preferred_channel_layout =*/ NULL, /*.get_preferred_channel_layout =*/ NULL,
/*.enumerate_devices =*/ winmm_enumerate_devices, /*.enumerate_devices =*/ winmm_enumerate_devices,
/*.device_collection_destroy =*/ cubeb_utils_default_device_collection_destroy, /*.device_collection_destroy =*/ winmm_device_collection_destroy,
/*.destroy =*/ winmm_destroy, /*.destroy =*/ winmm_destroy,
/*.stream_init =*/ winmm_stream_init, /*.stream_init =*/ winmm_stream_init,
/*.stream_destroy =*/ winmm_stream_destroy, /*.stream_destroy =*/ winmm_stream_destroy,
/*.stream_start =*/ winmm_stream_start, /*.stream_start =*/ winmm_stream_start,
/*.stream_stop =*/ winmm_stream_stop, /*.stream_stop =*/ winmm_stream_stop,
/*.stream_reset_default_device =*/ NULL,
/*.stream_get_position =*/ winmm_stream_get_position, /*.stream_get_position =*/ winmm_stream_get_position,
/*.stream_get_latency = */ winmm_stream_get_latency, /*.stream_get_latency = */ winmm_stream_get_latency,
/*.stream_set_volume =*/ winmm_stream_set_volume, /*.stream_set_volume =*/ winmm_stream_set_volume,

View File

@ -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) 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; SpeexResamplerState *st;
int filter_err; int filter_err;
if (quality > 10 || quality < 0) if (nb_channels == 0 || ratio_num == 0 || ratio_den == 0 || quality > 10 || quality < 0)
{ {
if (err) if (err)
*err = RESAMPLER_ERR_INVALID_ARG; *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 fact;
spx_uint32_t old_den; spx_uint32_t old_den;
spx_uint32_t i; 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) 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; return RESAMPLER_ERR_SUCCESS;

View File

@ -56,7 +56,7 @@ bool CubebStream::Start()
} }
u32 minimum_latency = 0; 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(), &params, &minimum_latency) != CUBEB_OK)
ERROR_LOG(AUDIO, "Error getting minimum latency"); ERROR_LOG(AUDIO, "Error getting minimum latency");
INFO_LOG(AUDIO, "Minimum latency: %i frames", minimum_latency); INFO_LOG(AUDIO, "Minimum latency: %i frames", minimum_latency);

View File

@ -80,7 +80,7 @@ void CEXIMic::StreamStart()
params.layout = CUBEB_LAYOUT_MONO; params.layout = CUBEB_LAYOUT_MONO;
u32 minimum_latency; 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(), &params, &minimum_latency) != CUBEB_OK)
{ {
WARN_LOG(EXPANSIONINTERFACE, "Error getting minimum latency"); WARN_LOG(EXPANSIONINTERFACE, "Error getting minimum latency");
} }