dep/cubeb: Bump to a37dadd

This commit is contained in:
Stenzek
2026-05-02 14:05:06 +10:00
parent dea0480166
commit c1d24b4e9c
21 changed files with 775 additions and 406 deletions
+14 -16
View File
@@ -1,7 +1,7 @@
# TODO
# - backend selection via command line, rather than simply detecting headers.
cmake_minimum_required(VERSION 3.14 FATAL_ERROR)
cmake_minimum_required(VERSION 3.19 FATAL_ERROR)
project(cubeb C CXX)
option(LAZY_LOAD_LIBS "Lazily load shared libraries" ON)
@@ -33,21 +33,19 @@ add_library(cubeb
src/cubeb_strings.c
src/cubeb_utils.cpp
)
target_include_directories(cubeb
PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include> $<INSTALL_INTERFACE:include>
)
target_include_directories(cubeb PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
add_library(speex OBJECT subprojects/speex/resample.c)
set_target_properties(speex PROPERTIES POSITION_INDEPENDENT_CODE TRUE)
target_include_directories(speex INTERFACE subprojects)
target_compile_definitions(speex PUBLIC
OUTSIDE_SPEEX
FLOATING_POINT
EXPORT=
RANDOM_PREFIX=speex
)
add_library(speex_resampler_headers INTERFACE)
target_include_directories(speex_resampler_headers INTERFACE subprojects)
add_library(speex OBJECT subprojects/speex/resample.c)
set_target_properties(speex PROPERTIES POSITION_INDEPENDENT_CODE TRUE)
target_include_directories(speex INTERFACE subprojects)
target_compile_definitions(speex PUBLIC
OUTSIDE_SPEEX
FLOATING_POINT
EXPORT=
RANDOM_PREFIX=speex
)
add_library(speex_resampler_headers INTERFACE)
target_include_directories(speex_resampler_headers INTERFACE subprojects)
# $<BUILD_INTERFACE:> required because of https://gitlab.kitware.com/cmake/cmake/-/issues/15415
target_link_libraries(cubeb PRIVATE $<BUILD_INTERFACE:speex>)
@@ -144,7 +142,7 @@ if(USE_WASAPI)
target_sources(cubeb PRIVATE
src/cubeb_wasapi.cpp)
target_compile_definitions(cubeb PRIVATE USE_WASAPI)
target_link_libraries(cubeb PRIVATE ole32 ksuser)
target_link_libraries(cubeb PRIVATE avrt ole32 ksuser)
endif()
check_include_files("windows.h;mmsystem.h" USE_WINMM)
+113 -3
View File
@@ -1,7 +1,117 @@
# libcubeb - Cross-platform Audio I/O Library
[![Build Status](https://github.com/mozilla/cubeb/actions/workflows/build.yml/badge.svg)](https://github.com/mozilla/cubeb/actions/workflows/build.yml)
See INSTALL.md for build instructions.
`libcubeb` is a cross-platform C library for high and low-latency audio input/output. It provides a simple, consistent API for audio playback and recording across multiple platforms and audio backends. It is written in C, C++ and Rust, with a C ABI and [Rust](https://github.com/mozilla/cubeb-rs) bindings. While originally written for use in the Firefox Web browser, a number of other software projects have adopted it.
See [Backend Support](https://github.com/mozilla/cubeb/wiki/Backend-Support) in the wiki for the support level of each backend.
## Features
Licensed under an ISC-style license. See LICENSE for details.
- **Cross-platform support**: Windows, macOS, Linux, Android, and other platforms
- **Versatile**: Optimized for low-latency real-time audio applications, or power efficient higher latency playback
- **A/V sync**: Latency compensated audio clock reporting for easy audio/video synchronization
- **Full-duplex support**: Simultaneous audio input and output, reclocked
- **Device enumeration**: Query available audio devices
- **Audio processing for speech**: Can use VoiceProcessing IO on recent macOS
## Supported Backends & status
| *Backend* | *Support Level* | *Platform version* | *Notes* |
|-------------------|-----------------|--------------------|--------------------------------------------------|
| PulseAudio (Rust) | Tier-1 | | Main Linux desktop backend |
| AudioUnit (Rust) | Tier-1 | | Main macOS backend |
| WASAPI | Tier-1 | Windows >= 7 | Main Windows backend |
| AAudio | Tier-1 | Android >= 8 | Main Android backend for most devices |
| OpenSL | Tier-1 | Android >= 2.3 | Android backend for older devices |
| OSS | Tier-2 | | |
| sndio | Tier-2 | | |
| Sun | Tier-2 | | |
| WinMM | Tier-3 | Windows XP | Was Tier-1, Firefox minimum Windows version 7. |
| AudioTrack | Tier-3 | Android < 2.3 | Was Tier-1, Firefox minimum Android version 4.1. |
| ALSA | Tier-3 | | |
| JACK | Tier-3 | | |
| KAI | Tier-3 | | |
| PulseAudio (C) | Tier-4 | | Was Tier-1, superseded by Rust |
| AudioUnit (C++) | Tier-4 | | Was Tier-1, superseded by Rust |
Tier-1: Actively maintained. Should have CI coverage. Critical for Firefox.
Tier-2: Actively maintained by contributors. CI coverage appreciated.
Tier-3: Maintainers/patches accepted. Status unclear.
Tier-4: Deprecated, obsolete. Scheduled to be removed.
Note that the support level is not a judgement of the relative merits
of a backend, only the current state of support, which is informed
by Firefox's needs, the responsiveness of a backend's
maintainer, and the level of contributions to that backend.
## Building
### Prerequisites
- CMake 3.15 or later
- Non-ancient MSVC, clang or gcc, for compiling both C and C++
- Platform-specific audio libraries (automatically detected)
- Optional but recommended: Rust compiler to compile and link more recent backends for macOS and PulseAudio
### Quick build
```bash
git clone https://github.com/mozilla/cubeb.git
cd cubeb
cmake -B build
cmake --build build
```
### Better build with Rust backends
```bash
git clone --recursive https://github.com/mozilla/cubeb.git
cd cubeb
cmake -B build -DBUILD_RUST_LIBS=ON
cmake --build build
```
### Platform-Specific Notes
**Windows**: Supports Visual Studio 2015+ and MinGW-w64. Use `-G "Visual Studio 16 2019"` or `-G "MinGW Makefiles"`.
**macOS**: Requires Xcode command line tools. Audio frameworks are automatically linked.
**Linux**: Development packages for desired backends:
```bash
# Ubuntu/Debian
sudo apt-get install libpulse-dev libasound2-dev libjack-dev
# Fedora/RHEL
sudo dnf install pulseaudio-libs-devel alsa-lib-devel jack-audio-connection-kit-devel
```
**Android**: Use with Android NDK. AAudio requires API level 26+.
## Testing
Run the test suite:
```bash
cd build
ctest
```
Use the interactive test tool:
```bash
./cubeb-test
```
## License
Licensed under an ISC-style license. See [LICENSE](LICENSE) for details.
## Contributing
Contributions are welcome! Please see the [contribution guidelines](CONTRIBUTING.md) and check the [issue tracker](https://github.com/mozilla/cubeb/issues).
## Links
- [GitHub Repository](https://github.com/mozilla/cubeb)
- [API Documentation](https://mozilla.github.io/cubeb/)
-1
View File
@@ -22,7 +22,6 @@
<ClInclude Include="subprojects\speex\resample_sse.h" />
<ClInclude Include="subprojects\speex\speex_config_types.h" />
<ClInclude Include="subprojects\speex\speex_resampler.h" />
<ClInclude Include="subprojects\speex\stack_alloc.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="src\cubeb.c" />
-3
View File
@@ -27,9 +27,6 @@
<ClInclude Include="subprojects\speex\speex_resampler.h">
<Filter>speex</Filter>
</ClInclude>
<ClInclude Include="subprojects\speex\stack_alloc.h">
<Filter>speex</Filter>
</ClInclude>
<ClInclude Include="subprojects\speex\arch.h">
<Filter>speex</Filter>
</ClInclude>
+29 -11
View File
@@ -49,6 +49,7 @@ extern "C" {
output_params.channels = 2;
output_params.layout = CUBEB_LAYOUT_UNDEFINED;
output_params.prefs = CUBEB_STREAM_PREF_NONE;
output_params.input_params = CUBEB_INPUT_PROCESSING_PARAM_NONE;
rv = cubeb_get_min_latency(app_ctx, &output_params, &latency_frames);
if (rv != CUBEB_OK) {
@@ -62,6 +63,7 @@ extern "C" {
input_params.channels = 1;
input_params.layout = CUBEB_LAYOUT_UNDEFINED;
input_params.prefs = CUBEB_STREAM_PREF_NONE;
input_params.input_params = CUBEB_INPUT_PROCESSING_PARAM_NONE;
cubeb_stream * stm;
rv = cubeb_stream_init(app_ctx, &stm, "Example Stream 1",
@@ -279,7 +281,10 @@ typedef struct {
cubeb_channel_layout
layout; /**< Requested channel layout. This must be consistent with the
provided channels. CUBEB_LAYOUT_UNDEFINED if unknown */
cubeb_stream_prefs prefs; /**< Requested preferences. */
cubeb_stream_prefs prefs; /**< Requested preferences. */
cubeb_input_processing_params input_params; /**< Requested input processing
params. Ignored for output streams. At present, only supported on the
WASAPI backend; others should use cubeb_set_input_processing_params. */
} cubeb_stream_params;
/** Audio device description */
@@ -414,6 +419,13 @@ typedef struct {
size_t count; /**< Device count in collection. */
} cubeb_device_collection;
/** Array of compiled backends returned by `cubeb_get_backend_names`. */
typedef struct {
const char * const *
names; /**< Array of strings representing backend names. */
size_t count; /**< Length of the array. */
} cubeb_backend_names;
/** User supplied data callback.
- Calling other cubeb functions from this callback is unsafe.
- The code in the callback should be non-blocking.
@@ -454,6 +466,8 @@ typedef void (*cubeb_device_changed_callback)(void * user_ptr);
/**
* User supplied callback called when the underlying device collection changed.
* This callback will be called when devices are added or removed from the
* system, or when the default device changes for the specified device type.
* @param context A pointer to the cubeb context.
* @param user_ptr The pointer passed to
* cubeb_register_device_collection_changed. */
@@ -485,17 +499,18 @@ CUBEB_EXPORT int
cubeb_init(cubeb ** context, char const * context_name,
char const * backend_name);
/** Returns a list of backend names which can be supplid to cubeb_init().
Array is null-terminated. */
CUBEB_EXPORT const char**
cubeb_get_backend_names();
/** Get a read-only string identifying this context's current backend.
@param context A pointer to the cubeb context.
@retval Read-only string identifying current backend. */
CUBEB_EXPORT char const *
cubeb_get_backend_id(cubeb * context);
/** Get a read-only array of strings identifying available backends.
These can be passed as `backend_name` parameter to `cubeb_init`.
@retval Struct containing the array with backend names. */
CUBEB_EXPORT cubeb_backend_names
cubeb_get_backend_names();
/** Get the maximum possible number of channels.
@param context A pointer to the cubeb context.
@param max_channels The maximum number of channels.
@@ -570,8 +585,9 @@ cubeb_destroy(cubeb * context);
NULL if this stream is input only. When input
and output stream parameters are supplied, their
rate has to be the same.
@param latency_frames Stream latency in frames. Valid range
is [1, 96000].
@param latency_frames Requested stream latency in frames. Valid range is
[1, 96000]. The actual latency may differ depending
on the backend, platform, and hardware.
@param data_callback Will be called to preroll data before playback is
started by cubeb_stream_start.
@param state_callback A pointer to a state callback.
@@ -674,7 +690,7 @@ cubeb_stream_get_current_device(cubeb_stream * stm,
application is accessing audio input. When all inputs are muted they can
prove to the user that the application is not actively capturing any input.
@param stream the stream for which to set input mute state
@param muted whether the input should mute or not
@param mute whether the input should mute or not
@retval CUBEB_OK
@retval CUBEB_ERROR_INVALID_PARAMETER if this stream does not have an input
device
@@ -745,14 +761,16 @@ cubeb_device_collection_destroy(cubeb * context,
cubeb_device_collection * collection);
/** Registers a callback which is called when the system detects
a new device or a device is removed.
a new device or a device is removed, or when the default device
changes for the specified device type.
@param context
@param devtype device type to include. Different callbacks and user pointers
can be registered for each devtype. The hybrid devtype
`CUBEB_DEVICE_TYPE_INPUT | CUBEB_DEVICE_TYPE_OUTPUT` is also valid
and will register the provided callback and user pointer in both
sides.
@param callback a function called whenever the system device list changes.
@param callback a function called whenever the system device list changes,
including when default devices change.
Passing NULL allow to unregister a function. You have to unregister
first before you register a new callback.
@param user_ptr pointer to user specified data which will be present in
+37 -14
View File
@@ -214,13 +214,26 @@ cubeb_init(cubeb ** context, char const * context_name,
return CUBEB_ERROR;
}
const char**
char const *
cubeb_get_backend_id(cubeb * context)
{
if (!context) {
return NULL;
}
return context->ops->get_backend_id(context);
}
cubeb_backend_names
cubeb_get_backend_names()
{
static const char* backend_names[] = {
static const char * const backend_names[] = {
#if defined(USE_PULSE)
"pulse",
#endif
#if defined(USE_PULSE_RUST)
"pulse-rust",
#endif
#if defined(USE_JACK)
"jack",
#endif
@@ -230,6 +243,9 @@ cubeb_get_backend_names()
#if defined(USE_AUDIOUNIT)
"audiounit",
#endif
#if defined(USE_AUDIOUNIT_RUST)
"audiounit-rust",
#endif
#if defined(USE_WASAPI)
"wasapi",
#endif
@@ -239,23 +255,30 @@ cubeb_get_backend_names()
#if defined(USE_SNDIO)
"sndio",
#endif
#if defined(USE_SUN)
"sun",
#endif
#if defined(USE_OPENSL)
"opensl",
#endif
#if defined(USE_OSS)
"oss",
#endif
NULL,
#if defined(USE_AAUDIO)
"aaudio",
#endif
#if defined(USE_AUDIOTRACK)
"audiotrack",
#endif
#if defined(USE_KAI)
"kai",
#endif
};
return backend_names;
}
char const *
cubeb_get_backend_id(cubeb * context)
{
if (!context) {
return NULL;
}
return context->ops->get_backend_id(context);
return (cubeb_backend_names){
.names = backend_names,
.count = NELEMS(backend_names),
};
}
int
+3
View File
@@ -294,6 +294,9 @@ set_timeout(struct timeval * timeout, unsigned int ms)
static void
stream_buffer_decrement(cubeb_stream * stm, long count)
{
if (count < 0 || (snd_pcm_uframes_t)count > stm->bufframes) {
count = stm->bufframes;
}
char * bufremains =
stm->buffer + WRAP(snd_pcm_frames_to_bytes)(stm->pcm, count);
memmove(stm->buffer, bufremains,
+12 -5
View File
@@ -213,12 +213,19 @@ struct cubeb_stream {
cubeb_device_changed_callback device_changed_callback = nullptr;
owned_critical_section device_changed_callback_lock;
/* Stream creation parameters */
cubeb_stream_params input_stream_params = {CUBEB_SAMPLE_FLOAT32NE, 0, 0,
cubeb_stream_params input_stream_params = {CUBEB_SAMPLE_FLOAT32NE,
0,
0,
CUBEB_LAYOUT_UNDEFINED,
CUBEB_STREAM_PREF_NONE};
cubeb_stream_params output_stream_params = {CUBEB_SAMPLE_FLOAT32NE, 0, 0,
CUBEB_LAYOUT_UNDEFINED,
CUBEB_STREAM_PREF_NONE};
CUBEB_STREAM_PREF_NONE,
CUBEB_INPUT_PROCESSING_PARAM_NONE};
cubeb_stream_params output_stream_params = {
CUBEB_SAMPLE_FLOAT32NE,
0,
0,
CUBEB_LAYOUT_UNDEFINED,
CUBEB_STREAM_PREF_NONE,
CUBEB_INPUT_PROCESSING_PARAM_NONE};
device_info input_device;
device_info output_device;
/* Format descriptions */
+13 -2
View File
@@ -405,12 +405,15 @@ cbjack_process(jack_nframes_t nframes, void * arg)
for (int j = 0; j < MAX_STREAMS; j++) {
cubeb_stream * stm = &ctx->streams[j];
float * bufs_out[stm->out_params.channels];
float * bufs_in[stm->in_params.channels];
if (!stm->in_use)
continue;
float * bufs_out[MAX_CHANNELS] = {};
float * bufs_in[MAX_CHANNELS] = {};
XASSERT(stm->out_params.channels <= MAX_CHANNELS);
XASSERT(stm->in_params.channels <= MAX_CHANNELS);
// handle xruns by skipping audio that should have been played
stm->position += t_jack_xruns * ctx->fragment_size * stm->ratio;
@@ -851,6 +854,14 @@ cbjack_stream_init(cubeb * context, cubeb_stream ** stream,
return CUBEB_ERROR_INVALID_FORMAT;
}
if ((output_stream_params &&
(output_stream_params->channels < 1 ||
output_stream_params->channels > MAX_CHANNELS)) ||
(input_stream_params && (input_stream_params->channels < 1 ||
input_stream_params->channels > MAX_CHANNELS))) {
return CUBEB_ERROR_INVALID_FORMAT;
}
if ((input_device && input_device != JACK_DEFAULT_IN) ||
(output_device && output_device != JACK_DEFAULT_OUT)) {
return CUBEB_ERROR_NOT_SUPPORTED;
+2 -1
View File
@@ -39,7 +39,8 @@ cubeb_log_get_callback(void);
void
cubeb_log_internal_no_format(const char * msg);
void
cubeb_log_internal(const char * filename, uint32_t line, const char * fmt, ...);
cubeb_log_internal(const char * filename, uint32_t line, const char * fmt, ...)
PRINTF_FORMAT(3, 4);
#ifdef __cplusplus
}
+6
View File
@@ -371,3 +371,9 @@ cubeb_resampler_latency(cubeb_resampler * resampler)
{
return resampler->latency();
}
cubeb_resampler_stats
cubeb_resampler_stats_get(cubeb_resampler * resampler)
{
return resampler->stats();
}
+14
View File
@@ -84,6 +84,20 @@ cubeb_resampler_destroy(cubeb_resampler * resampler);
long
cubeb_resampler_latency(cubeb_resampler * resampler);
/**
* Test-only introspection API to ensure that there is no buffering
* buildup when resampling.
*/
typedef struct {
size_t input_input_buffer_size;
size_t input_output_buffer_size;
size_t output_input_buffer_size;
size_t output_output_buffer_size;
} cubeb_resampler_stats;
cubeb_resampler_stats
cubeb_resampler_stats_get(cubeb_resampler * resampler);
#if defined(__cplusplus)
}
#endif
+62 -30
View File
@@ -56,6 +56,7 @@ struct cubeb_resampler {
virtual long fill(void * input_buffer, long * input_frames_count,
void * output_buffer, long frames_needed) = 0;
virtual long latency() = 0;
virtual cubeb_resampler_stats stats() = 0;
virtual ~cubeb_resampler() {}
};
@@ -86,6 +87,16 @@ public:
virtual long latency() { return 0; }
virtual cubeb_resampler_stats stats()
{
cubeb_resampler_stats stats;
stats.input_input_buffer_size = internal_input_buffer.length();
stats.input_output_buffer_size = 0;
stats.output_input_buffer_size = 0;
stats.output_output_buffer_size = 0;
return stats;
}
void drop_audio_if_needed()
{
uint32_t to_keep = min_buffered_audio_frame(sample_rate);
@@ -122,6 +133,20 @@ public:
virtual long fill(void * input_buffer, long * input_frames_count,
void * output_buffer, long output_frames_needed);
virtual cubeb_resampler_stats stats()
{
cubeb_resampler_stats stats = {};
if (input_processor) {
stats.input_input_buffer_size = input_processor->input_buffer_size();
stats.input_output_buffer_size = input_processor->output_buffer_size();
}
if (output_processor) {
stats.output_input_buffer_size = output_processor->input_buffer_size();
stats.output_output_buffer_size = output_processor->output_buffer_size();
}
return stats;
}
virtual long latency()
{
if (input_processor && output_processor) {
@@ -280,29 +305,28 @@ public:
}
/** Returns the number of frames to pass in the input of the resampler to have
* exactly `output_frame_count` resampled frames. This can return a number
* slightly bigger than what is strictly necessary, but it guaranteed that the
* number of output frames will be exactly equal. */
* at least `output_frame_count` resampled frames. */
uint32_t input_needed_for_output(int32_t output_frame_count) const
{
assert(output_frame_count >= 0); // Check overflow
int32_t unresampled_frames_left =
samples_to_frames(resampling_in_buffer.length());
int32_t resampled_frames_left =
samples_to_frames(resampling_out_buffer.length());
float input_frames_needed =
(output_frame_count - unresampled_frames_left) * resampling_ratio -
resampled_frames_left;
if (input_frames_needed < 0) {
return 0;
}
return (uint32_t)ceilf(input_frames_needed);
float input_frames_needed_frac =
static_cast<float>(output_frame_count) * resampling_ratio;
// speex_resample()` can be irregular in its consumption of input samples.
// Provide one more frame than the number that would be required with
// regular consumption, to make the speex resampler behave more regularly,
// and so predictably.
auto input_frame_needed =
1 + static_cast<int32_t>(ceilf(input_frames_needed_frac));
input_frame_needed -= std::min(unresampled_frames_left, input_frame_needed);
return input_frame_needed;
}
/** Returns a pointer to the input buffer, that contains empty space for at
* least `frame_count` elements. This is useful so that consumer can directly
* write into the input buffer of the resampler. The pointer returned is
* adjusted so that leftover data are not overwritten.
* least `frame_count` elements. This is useful so that consumer can
* directly write into the input buffer of the resampler. The pointer
* returned is adjusted so that leftover data are not overwritten.
*/
T * input_buffer(size_t frame_count)
{
@@ -312,8 +336,8 @@ public:
return resampling_in_buffer.data() + leftover_samples;
}
/** This method works with `input_buffer`, and allows to inform the processor
how much frames have been written in the provided buffer. */
/** This method works with `input_buffer`, and allows to inform the
processor how much frames have been written in the provided buffer. */
void written(size_t written_frames)
{
resampling_in_buffer.set_length(leftover_samples +
@@ -331,6 +355,9 @@ public:
}
}
size_t input_buffer_size() const { return resampling_in_buffer.length(); }
size_t output_buffer_size() const { return resampling_out_buffer.length(); }
private:
/** Wrapper for the speex resampling functions to have a typed
* interface. */
@@ -359,6 +386,7 @@ private:
output_frame_count);
assert(rv == RESAMPLER_ERR_SUCCESS);
}
/** The state for the speex resampler used internaly. */
SpeexResamplerState * speex_resampler;
/** Source rate / target rate. */
@@ -371,8 +399,8 @@ private:
auto_array<T> resampling_out_buffer;
/** Additional latency inserted into the pipeline for synchronisation. */
uint32_t additional_latency;
/** When `input_buffer` is called, this allows tracking the number of samples
that were in the buffer. */
/** When `input_buffer` is called, this allows tracking the number of
samples that were in the buffer. */
uint32_t leftover_samples;
};
@@ -417,8 +445,8 @@ public:
return delay_output_buffer.data();
}
/** Get a pointer to the first writable location in the input buffer>
* @parameter frames_needed the number of frames the user needs to write into
* the buffer.
* @parameter frames_needed the number of frames the user needs to write
* into the buffer.
* @returns a pointer to a location in the input buffer where #frames_needed
* can be writen. */
T * input_buffer(uint32_t frames_needed)
@@ -428,8 +456,8 @@ public:
frames_to_samples(frames_needed));
return delay_input_buffer.data() + leftover_samples;
}
/** This method works with `input_buffer`, and allows to inform the processor
how much frames have been written in the provided buffer. */
/** This method works with `input_buffer`, and allows to inform the
processor how much frames have been written in the provided buffer. */
void written(size_t frames_written)
{
delay_input_buffer.set_length(leftover_samples +
@@ -450,8 +478,8 @@ public:
return to_pop;
}
/** Returns the number of frames one needs to input into the delay line to get
* #frames_needed frames back.
/** Returns the number of frames one needs to input into the delay line to
* get #frames_needed frames back.
* @parameter frames_needed the number of frames one want to write into the
* delay_line
* @returns the number of frames one will get. */
@@ -469,19 +497,23 @@ public:
void drop_audio_if_needed()
{
size_t available = samples_to_frames(delay_input_buffer.length());
uint32_t available = samples_to_frames(delay_input_buffer.length());
uint32_t to_keep = min_buffered_audio_frame(sample_rate);
if (available > to_keep) {
ALOGV("Dropping %u frames", available - to_keep);
delay_input_buffer.pop(nullptr, frames_to_samples(available - to_keep));
}
}
size_t input_buffer_size() const { return delay_input_buffer.length(); }
size_t output_buffer_size() const { return delay_output_buffer.length(); }
private:
/** The length, in frames, of this delay line */
uint32_t length;
/** When `input_buffer` is called, this allows tracking the number of samples
that where in the buffer. */
/** When `input_buffer` is called, this allows tracking the number of
samples that where in the buffer. */
uint32_t leftover_samples;
/** The input buffer, where the delay is applied. */
auto_array<T> delay_input_buffer;
@@ -511,8 +543,8 @@ cubeb_resampler_create_internal(cubeb_stream * stream,
"need at least one valid parameter pointer.");
/* All the streams we have have a sample rate that matches the target
sample rate, use a no-op resampler, that simply forwards the buffers to the
callback. */
sample rate, use a no-op resampler, that simply forwards the buffers to
the callback. */
if (((input_params && input_params->rate == target_rate) &&
(output_params && output_params->rate == target_rate)) ||
(input_params && !output_params && (input_params->rate == target_rate)) ||
+252 -122
View File
@@ -4,12 +4,17 @@
* This program is made available under an ISC-style license. See the
* accompanying file LICENSE for details.
*/
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0603
#endif // !_WIN32_WINNT
#ifndef NOMINMAX
#define NOMINMAX
#endif // !NOMINMAX
#include <algorithm>
#include <atomic>
#include <audioclient.h>
#include <audiopolicy.h>
#include <avrt.h>
#include <cmath>
#include <devicetopology.h>
@@ -229,11 +234,6 @@ struct auto_stream_ref {
cubeb_stream * stm;
};
using set_mm_thread_characteristics_function =
decltype(&AvSetMmThreadCharacteristicsW);
using revert_mm_thread_characteristics_function =
decltype(&AvRevertMmThreadCharacteristics);
extern cubeb_ops const wasapi_ops;
static com_heap_ptr<wchar_t>
@@ -304,8 +304,8 @@ wasapi_enumerate_devices_internal(cubeb * context, cubeb_device_type type,
static int
wasapi_device_collection_destroy(cubeb * ctx,
cubeb_device_collection * collection);
static char const *
wstr_to_utf8(wchar_t const * str);
static std::unique_ptr<char const[]>
wstr_to_utf8(LPCWSTR str);
static std::unique_ptr<wchar_t const[]>
utf8_to_wstr(char const * str);
@@ -314,6 +314,15 @@ utf8_to_wstr(char const * str);
class wasapi_collection_notification_client;
class monitor_device_notifications;
typedef enum {
/* Clear options */
CUBEB_AUDIO_CLIENT2_NONE,
/* Use AUDCLNT_STREAMOPTIONS_RAW */
CUBEB_AUDIO_CLIENT2_RAW,
/* Use CUBEB_STREAM_PREF_COMMUNICATIONS */
CUBEB_AUDIO_CLIENT2_VOICE
} AudioClient2Option;
struct cubeb {
cubeb_ops const * ops = &wasapi_ops;
owned_critical_section lock;
@@ -331,16 +340,10 @@ struct cubeb {
nullptr;
void * output_collection_changed_user_ptr = nullptr;
UINT64 performance_counter_frequency;
/* Library dynamically opened to increase the render thread priority, and
the two function pointers we need. */
HMODULE mmcss_module = nullptr;
set_mm_thread_characteristics_function set_mm_thread_characteristics =
nullptr;
revert_mm_thread_characteristics_function revert_mm_thread_characteristics =
nullptr;
};
class wasapi_endpoint_notification_client;
class wasapi_session_notification_client;
/* We have three possible callbacks we can use with a stream:
* - input only
@@ -360,20 +363,33 @@ struct cubeb_stream {
/* Mixer pameters. We need to convert the input stream to this
samplerate/channel layout, as WASAPI does not resample nor upmix
itself. */
cubeb_stream_params input_mix_params = {CUBEB_SAMPLE_FLOAT32NE, 0, 0,
cubeb_stream_params input_mix_params = {CUBEB_SAMPLE_FLOAT32NE,
0,
0,
CUBEB_LAYOUT_UNDEFINED,
CUBEB_STREAM_PREF_NONE};
cubeb_stream_params output_mix_params = {CUBEB_SAMPLE_FLOAT32NE, 0, 0,
CUBEB_STREAM_PREF_NONE,
CUBEB_INPUT_PROCESSING_PARAM_NONE};
cubeb_stream_params output_mix_params = {CUBEB_SAMPLE_FLOAT32NE,
0,
0,
CUBEB_LAYOUT_UNDEFINED,
CUBEB_STREAM_PREF_NONE};
CUBEB_STREAM_PREF_NONE,
CUBEB_INPUT_PROCESSING_PARAM_NONE};
/* Stream parameters. This is what the client requested,
* and what will be presented in the callback. */
cubeb_stream_params input_stream_params = {CUBEB_SAMPLE_FLOAT32NE, 0, 0,
cubeb_stream_params input_stream_params = {CUBEB_SAMPLE_FLOAT32NE,
0,
0,
CUBEB_LAYOUT_UNDEFINED,
CUBEB_STREAM_PREF_NONE};
cubeb_stream_params output_stream_params = {CUBEB_SAMPLE_FLOAT32NE, 0, 0,
CUBEB_LAYOUT_UNDEFINED,
CUBEB_STREAM_PREF_NONE};
CUBEB_STREAM_PREF_NONE,
CUBEB_INPUT_PROCESSING_PARAM_NONE};
cubeb_stream_params output_stream_params = {
CUBEB_SAMPLE_FLOAT32NE,
0,
0,
CUBEB_LAYOUT_UNDEFINED,
CUBEB_STREAM_PREF_NONE,
CUBEB_INPUT_PROCESSING_PARAM_NONE};
/* A MMDevice role for this stream: either communication or console here. */
ERole role;
/* True if this stream will transport voice-data. */
@@ -426,6 +442,10 @@ struct cubeb_stream {
audio device changes and route the audio to the new default audio output
device */
com_ptr<wasapi_endpoint_notification_client> notification_client;
/* Session notification client, to be notified when the audio session is
disconnected (e.g. when an audio device is removed from the system). */
com_ptr<IAudioSessionControl> session_control;
com_ptr<wasapi_session_notification_client> session_notification_client;
/* Main andle to the WASAPI capture stream. */
com_ptr<IAudioClient> input_client;
/* Interface to use the event driven capture interface */
@@ -662,6 +682,10 @@ public:
LPCWSTR device_id)
{
LOG("collection: Audio device default changed, id = %S.", device_id);
/* Default device changes count as device collection changes */
monitor_notifications.notify(flow);
return S_OK;
}
@@ -772,7 +796,7 @@ public:
LPCWSTR device_id)
{
LOG("endpoint: Audio device default changed flow=%d role=%d "
"new_device_id=%ws.",
"new_device_id=%S.",
flow, role, device_id);
/* we only support a single stream type for now. */
@@ -783,11 +807,13 @@ public:
DWORD last_change_ms = timeGetTime() - last_device_change;
bool same_device = default_device_id && device_id &&
wcscmp(default_device_id.get(), device_id) == 0;
LOG("endpoint: Audio device default changed last_change=%u same_device=%d",
LOG("endpoint: Audio device default changed last_change=%lu same_device=%d",
last_change_ms, same_device);
if (last_change_ms > DEVICE_CHANGE_DEBOUNCE_MS || !same_device) {
if (device_id) {
default_device_id.reset(_wcsdup(device_id));
wchar_t * new_device_id = new wchar_t[wcslen(device_id) + 1];
wcscpy(new_device_id, device_id);
default_device_id.reset(new_device_id);
} else {
default_device_id.reset();
}
@@ -839,6 +865,89 @@ private:
DWORD last_device_change;
};
class wasapi_session_notification_client : public IAudioSessionEvents {
public:
ULONG STDMETHODCALLTYPE AddRef() { return InterlockedIncrement(&ref_count); }
ULONG STDMETHODCALLTYPE Release()
{
ULONG ulRef = InterlockedDecrement(&ref_count);
if (0 == ulRef) {
delete this;
}
return ulRef;
}
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, VOID ** ppvInterface)
{
if (__uuidof(IUnknown) == riid) {
AddRef();
*ppvInterface = (IUnknown *)this;
} else if (__uuidof(IAudioSessionEvents) == riid) {
AddRef();
*ppvInterface = (IAudioSessionEvents *)this;
} else {
*ppvInterface = NULL;
return E_NOINTERFACE;
}
return S_OK;
}
wasapi_session_notification_client(HANDLE event)
: ref_count(1), reconfigure_event(event)
{
}
virtual ~wasapi_session_notification_client() {}
HRESULT STDMETHODCALLTYPE
OnSessionDisconnected(AudioSessionDisconnectReason reason)
{
LOG("session: Audio session disconnected, reason: %d", reason);
BOOL ok = SetEvent(reconfigure_event);
if (!ok) {
LOG("session: SetEvent on reconfigure_event failed: %lx", GetLastError());
}
return S_OK;
}
HRESULT STDMETHODCALLTYPE OnDisplayNameChanged(LPCWSTR value,
LPCGUID event_context)
{
return S_OK;
}
HRESULT STDMETHODCALLTYPE OnIconPathChanged(LPCWSTR value,
LPCGUID event_context)
{
return S_OK;
}
HRESULT STDMETHODCALLTYPE OnSimpleVolumeChanged(float volume, BOOL mute,
LPCGUID event_context)
{
return S_OK;
}
HRESULT STDMETHODCALLTYPE OnChannelVolumeChanged(DWORD channel_count,
float volumes[],
DWORD changed_channel,
LPCGUID event_context)
{
return S_OK;
}
HRESULT STDMETHODCALLTYPE OnGroupingParamChanged(LPCGUID grouping_param,
LPCGUID event_context)
{
return S_OK;
}
HRESULT STDMETHODCALLTYPE OnStateChanged(AudioSessionState state)
{
return S_OK;
}
private:
LONG ref_count;
HANDLE reconfigure_event;
};
namespace {
long
@@ -863,16 +972,12 @@ intern_device_id(cubeb * ctx, wchar_t const * id)
auto_lock lock(ctx->lock);
char const * tmp = wstr_to_utf8(id);
std::unique_ptr<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;
return cubeb_strings_intern(ctx->device_ids, tmp.get());
}
bool
@@ -977,7 +1082,7 @@ refill(cubeb_stream * stm, void * input_buffer, long input_frames_count,
cubeb_resampler_fill(stm->resampler.get(), input_buffer,
&input_frames_count, dest, output_frames_needed);
if (out_frames < 0) {
ALOGV("Callback refill error: %d", out_frames);
ALOGV("Callback refill error: %ld", out_frames);
wasapi_state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR);
return out_frames;
}
@@ -1263,8 +1368,8 @@ refill_callback_duplex(cubeb_stream * stm)
XASSERT(has_input(stm) && has_output(stm));
if (stm->input_stream_params.prefs & CUBEB_STREAM_PREF_LOOPBACK) {
HRESULT rv = get_input_buffer(stm);
if (FAILED(rv)) {
rv = get_input_buffer(stm);
if (!rv) {
return rv;
}
}
@@ -1274,7 +1379,6 @@ refill_callback_duplex(cubeb_stream * stm)
rv = get_output_buffer(stm, output_buffer, output_frames);
if (!rv) {
hr = stm->render_client->ReleaseBuffer(output_frames, 0);
return rv;
}
@@ -1291,9 +1395,11 @@ refill_callback_duplex(cubeb_stream * stm)
stm->total_output_frames += output_frames;
ALOGV("in: %zu, out: %zu, missing: %ld, ratio: %f", stm->total_input_frames,
stm->total_output_frames,
static_cast<long>(stm->total_output_frames) - stm->total_input_frames,
ALOGV("in: %llu, out: %llu, missing: %ld, ratio: %f",
(unsigned long long)stm->total_input_frames,
(unsigned long long)stm->total_output_frames,
static_cast<long long>(stm->total_output_frames) -
static_cast<long long>(stm->total_input_frames),
static_cast<float>(stm->total_output_frames) / stm->total_input_frames);
long got;
@@ -1438,8 +1544,7 @@ static unsigned int __stdcall wasapi_stream_render_loop(LPVOID stream)
/* We could consider using "Pro Audio" here for WebAudio and
maybe WebRTC. */
mmcss_handle =
stm->context->set_mm_thread_characteristics(L"Audio", &mmcss_task_index);
mmcss_handle = AvSetMmThreadCharacteristicsA("Audio", &mmcss_task_index);
if (!mmcss_handle) {
/* This is not fatal, but we might glitch under heavy load. */
LOG("Unable to use mmcss to bump the render thread priority: %lx",
@@ -1469,13 +1574,12 @@ static unsigned int __stdcall wasapi_stream_render_loop(LPVOID stream)
XASSERT(stm->output_client || stm->input_client);
LOG("Reconfiguring the stream");
/* Close the stream */
bool was_running = false;
if (stm->output_client) {
was_running = stm->output_client->Stop() == S_OK;
stm->output_client->Stop();
LOG("Output stopped.");
}
if (stm->input_client) {
was_running = stm->input_client->Stop() == S_OK;
stm->input_client->Stop();
LOG("Input stopped.");
}
close_wasapi_stream(stm);
@@ -1493,7 +1597,7 @@ static unsigned int __stdcall wasapi_stream_render_loop(LPVOID stream)
}
LOG("Stream setup successfuly.");
XASSERT(stm->output_client || stm->input_client);
if (was_running && stm->output_client) {
if (stm->output_client) {
hr = stm->output_client->Start();
if (FAILED(hr)) {
LOG("Error starting output after reconfigure, error: %lx", hr);
@@ -1502,7 +1606,7 @@ static unsigned int __stdcall wasapi_stream_render_loop(LPVOID stream)
}
LOG("Output started after reconfigure.");
}
if (was_running && stm->input_client) {
if (stm->input_client) {
hr = stm->input_client->Start();
if (FAILED(hr)) {
LOG("Error starting input after reconfiguring, error: %lx", hr);
@@ -1519,8 +1623,8 @@ static unsigned int __stdcall wasapi_stream_render_loop(LPVOID stream)
is_playing = stm->refill_callback(stm);
break;
case WAIT_OBJECT_0 + 3: { /* input available */
HRESULT rv = get_input_buffer(stm);
if (FAILED(rv)) {
bool rv = get_input_buffer(stm);
if (!rv) {
is_playing = false;
continue;
}
@@ -1532,8 +1636,11 @@ static unsigned int __stdcall wasapi_stream_render_loop(LPVOID stream)
break;
}
default:
LOG("case %lu not handled in render loop.", waitResult);
XASSERT(false);
LOG("render_loop: waitResult=%lu (lastError=%lu) unhandled, exiting",
waitResult, GetLastError());
is_playing = false;
hr = E_FAIL;
continue;
}
}
@@ -1547,7 +1654,7 @@ static unsigned int __stdcall wasapi_stream_render_loop(LPVOID stream)
}
if (mmcss_handle) {
stm->context->revert_mm_thread_characteristics(mmcss_handle);
AvRevertMmThreadCharacteristics(mmcss_handle);
}
if (FAILED(hr)) {
@@ -1560,18 +1667,6 @@ static unsigned int __stdcall wasapi_stream_render_loop(LPVOID stream)
void
wasapi_destroy(cubeb * context);
HANDLE WINAPI
set_mm_thread_characteristics_noop(LPCWSTR, LPDWORD mmcss_task_index)
{
return (HANDLE)1;
}
BOOL WINAPI
revert_mm_thread_characteristics_noop(HANDLE mmcss_handle)
{
return true;
}
HRESULT
register_notification_client(cubeb_stream * stm)
{
@@ -1807,31 +1902,6 @@ wasapi_init(cubeb ** context, char const * context_name)
ctx->performance_counter_frequency = 0;
}
ctx->mmcss_module = LoadLibraryW(L"Avrt.dll");
bool success = false;
if (ctx->mmcss_module) {
ctx->set_mm_thread_characteristics =
reinterpret_cast<set_mm_thread_characteristics_function>(
GetProcAddress(ctx->mmcss_module, "AvSetMmThreadCharacteristicsW"));
ctx->revert_mm_thread_characteristics =
reinterpret_cast<revert_mm_thread_characteristics_function>(
GetProcAddress(ctx->mmcss_module,
"AvRevertMmThreadCharacteristics"));
success = ctx->set_mm_thread_characteristics &&
ctx->revert_mm_thread_characteristics;
}
if (!success) {
// This is not a fatal error, but we might end up glitching when
// the system is under high load.
LOG("Could not load avrt.dll or fetch AvSetMmThreadCharacteristicsW "
"AvRevertMmThreadCharacteristics: %lx",
GetLastError());
ctx->set_mm_thread_characteristics = &set_mm_thread_characteristics_noop;
ctx->revert_mm_thread_characteristics =
&revert_mm_thread_characteristics_noop;
}
*context = ctx;
return CUBEB_OK;
@@ -1839,7 +1909,6 @@ wasapi_init(cubeb ** context, char const * context_name)
}
namespace {
enum ShutdownPhase { OnStop, OnDestroy };
bool
stop_and_join_render_thread(cubeb_stream * stm)
@@ -1855,16 +1924,7 @@ stop_and_join_render_thread(cubeb_stream * stm)
return false;
}
/* Wait five seconds for the rendering thread to return. It's supposed to
* check its event loop very often, five seconds is rather conservative.
* Note: 5*1s loop to work around timer sleep issues on pre-Windows 8. */
DWORD r;
for (int i = 0; i < 5; ++i) {
r = WaitForSingleObject(stm->thread, 1000);
if (r == WAIT_OBJECT_0) {
break;
}
}
DWORD r = WaitForSingleObject(stm->thread, INFINITE);
if (r != WAIT_OBJECT_0) {
LOG("stop_and_join_render_thread: WaitForSingleObject on thread failed: "
"%lx, %lx",
@@ -1888,10 +1948,6 @@ wasapi_destroy(cubeb * context)
}
}
if (context->mmcss_module) {
FreeLibrary(context->mmcss_module);
}
delete context;
}
@@ -2044,6 +2100,21 @@ wasapi_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate)
return CUBEB_OK;
}
int
wasapi_get_supported_input_processing_params(
cubeb * ctx, cubeb_input_processing_params * params)
{
// This is not entirely accurate -- windows doesn't document precisely what
// AudioCategory_Communications does -- but assume that we can set all or none
// of them.
*params = static_cast<cubeb_input_processing_params>(
CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION |
CUBEB_INPUT_PROCESSING_PARAM_NOISE_SUPPRESSION |
CUBEB_INPUT_PROCESSING_PARAM_AUTOMATIC_GAIN_CONTROL |
CUBEB_INPUT_PROCESSING_PARAM_VOICE_ISOLATION);
return CUBEB_OK;
}
static void
waveformatex_update_derived_properties(WAVEFORMATEX * format)
{
@@ -2122,7 +2193,8 @@ handle_channel_layout(cubeb_stream * stm, EDataFlow direction,
}
static int
initialize_iaudioclient2(com_ptr<IAudioClient> & audio_client)
initialize_iaudioclient2(com_ptr<IAudioClient> & audio_client,
AudioClient2Option option)
{
com_ptr<IAudioClient2> audio_client2;
audio_client->QueryInterface<IAudioClient2>(audio_client2.receive());
@@ -2131,10 +2203,14 @@ initialize_iaudioclient2(com_ptr<IAudioClient> & audio_client)
"AUDCLNT_STREAMOPTIONS_RAW.");
return CUBEB_OK;
}
AudioClientProperties properties = {0};
AudioClientProperties properties = {};
properties.cbSize = sizeof(AudioClientProperties);
#ifndef __MINGW32__
properties.Options |= AUDCLNT_STREAMOPTIONS_RAW;
if (option == CUBEB_AUDIO_CLIENT2_RAW) {
properties.Options |= AUDCLNT_STREAMOPTIONS_RAW;
} else if (option == CUBEB_AUDIO_CLIENT2_VOICE) {
properties.eCategory = AudioCategory_Communications;
}
#endif
HRESULT hr = audio_client2->SetClientProperties(&properties);
if (FAILED(hr)) {
@@ -2494,10 +2570,29 @@ setup_wasapi_stream_one_side(cubeb_stream * stm,
}
if (stream_params->prefs & CUBEB_STREAM_PREF_RAW) {
if (initialize_iaudioclient2(audio_client) != CUBEB_OK) {
if (initialize_iaudioclient2(audio_client, CUBEB_AUDIO_CLIENT2_RAW) !=
CUBEB_OK) {
LOG("Can't initialize an IAudioClient2, error: %lx", GetLastError());
// This is not fatal.
}
} else if (direction == eCapture &&
(stream_params->prefs & CUBEB_STREAM_PREF_VOICE) &&
stream_params->input_params != CUBEB_INPUT_PROCESSING_PARAM_NONE) {
if (stream_params->input_params ==
(CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION |
CUBEB_INPUT_PROCESSING_PARAM_NOISE_SUPPRESSION |
CUBEB_INPUT_PROCESSING_PARAM_AUTOMATIC_GAIN_CONTROL |
CUBEB_INPUT_PROCESSING_PARAM_VOICE_ISOLATION)) {
if (initialize_iaudioclient2(audio_client, CUBEB_AUDIO_CLIENT2_VOICE) !=
CUBEB_OK) {
LOG("Can't initialize an IAudioClient2, error: %lx", GetLastError());
// This is not fatal.
}
} else {
LOG("Invalid combination of input processing params %#x",
stream_params->input_params);
return CUBEB_ERROR;
}
}
if (allow_audio_client_3 &&
@@ -2737,6 +2832,22 @@ setup_wasapi_stream(cubeb_stream * stm)
return CUBEB_ERROR;
}
hr = stm->output_client->GetService(__uuidof(IAudioSessionControl),
stm->session_control.receive_vpp());
if (SUCCEEDED(hr)) {
stm->session_notification_client.reset(
new wasapi_session_notification_client(stm->reconfigure_event));
hr = stm->session_control->RegisterAudioSessionNotification(
stm->session_notification_client.get());
if (FAILED(hr)) {
LOG("Could not register session notification client: %lx", hr);
stm->session_notification_client = nullptr;
stm->session_control = nullptr;
}
} else {
LOG("Could not get the IAudioSessionControl: %lx", hr);
}
#ifdef CUBEB_WASAPI_USE_IAUDIOSTREAMVOLUME
/* Restore the stream volume over a device change. */
if (stream_set_volume(stm, stm->volume) != CUBEB_OK) {
@@ -2999,6 +3110,13 @@ close_wasapi_stream(cubeb_stream * stm)
stm->stream_reset_lock.assert_current_thread_owns();
if (stm->session_control && stm->session_notification_client) {
stm->session_control->UnregisterAudioSessionNotification(
stm->session_notification_client.get());
stm->session_notification_client = nullptr;
stm->session_control = nullptr;
}
#ifdef CUBEB_WASAPI_USE_IAUDIOSTREAMVOLUME
stm->audio_stream_volume = nullptr;
#endif
@@ -3031,7 +3149,7 @@ wasapi_stream_add_ref(cubeb_stream * stm)
{
XASSERT(stm);
LONG result = InterlockedIncrement(&stm->ref_count);
LOGV("Stream ref count incremented = %i (%p)", result, stm);
LOGV("Stream ref count incremented = %ld (%p)", result, stm);
return result;
}
@@ -3041,7 +3159,7 @@ wasapi_stream_release(cubeb_stream * stm)
XASSERT(stm);
LONG result = InterlockedDecrement(&stm->ref_count);
LOGV("Stream ref count decremented = %i (%p)", result, stm);
LOGV("Stream ref count decremented = %ld (%p)", result, stm);
if (result == 0) {
LOG("Stream ref count hit zero, destroying (%p)", stm);
@@ -3303,7 +3421,7 @@ wasapi_stream_set_volume(cubeb_stream * stm, float volume)
return CUBEB_OK;
}
static char const *
static std::unique_ptr<char const[]>
wstr_to_utf8(LPCWSTR str)
{
int size = ::WideCharToMultiByte(CP_UTF8, 0, str, -1, nullptr, 0, NULL, NULL);
@@ -3311,8 +3429,8 @@ wstr_to_utf8(LPCWSTR str)
return nullptr;
}
char * ret = static_cast<char *>(malloc(size));
::WideCharToMultiByte(CP_UTF8, 0, str, -1, ret, size, NULL, NULL);
std::unique_ptr<char[]> ret(new char[size]);
::WideCharToMultiByte(CP_UTF8, 0, str, -1, ret.get(), size, NULL, NULL);
return ret;
}
@@ -3440,7 +3558,7 @@ wasapi_create_device(cubeb * ctx, cubeb_device_info & ret,
prop_variant namevar;
hr = propstore->GetValue(PKEY_Device_FriendlyName, &namevar);
if (SUCCEEDED(hr) && namevar.vt == VT_LPWSTR) {
ret.friendly_name = wstr_to_utf8(namevar.pwszVal);
ret.friendly_name = wstr_to_utf8(namevar.pwszVal).release();
}
if (!ret.friendly_name) {
// This is not fatal, but a valid string is expected in all cases.
@@ -3461,7 +3579,7 @@ wasapi_create_device(cubeb * ctx, cubeb_device_info & ret,
prop_variant instancevar;
hr = ps->GetValue(PKEY_Device_InstanceId, &instancevar);
if (SUCCEEDED(hr) && instancevar.vt == VT_LPWSTR) {
ret.group_id = wstr_to_utf8(instancevar.pwszVal);
ret.group_id = wstr_to_utf8(instancevar.pwszVal).release();
}
}
@@ -3477,7 +3595,8 @@ wasapi_create_device(cubeb * ctx, cubeb_device_info & ret,
ret.preferred =
(cubeb_device_pref)(ret.preferred | CUBEB_DEVICE_PREF_MULTIMEDIA |
CUBEB_DEVICE_PREF_NOTIFICATION);
} else if (defaults->is_default(flow, eCommunications, device_id.get())) {
}
if (defaults->is_default(flow, eCommunications, device_id.get())) {
ret.preferred =
(cubeb_device_pref)(ret.preferred | CUBEB_DEVICE_PREF_VOICE);
}
@@ -3656,6 +3775,14 @@ wasapi_device_collection_destroy(cubeb * /*ctx*/,
return CUBEB_OK;
}
int
wasapi_set_input_processing_params(cubeb_stream * stream,
cubeb_input_processing_params params)
{
LOG("Cannot set voice processing params after init. Use cubeb_stream_init.");
return CUBEB_ERROR_NOT_SUPPORTED;
}
static int
wasapi_register_device_collection_changed(
cubeb * context, cubeb_device_type devtype,
@@ -3668,11 +3795,13 @@ wasapi_register_device_collection_changed(
}
if (collection_changed_callback) {
// Make sure it has been unregistered first.
XASSERT(((devtype & CUBEB_DEVICE_TYPE_INPUT) &&
!context->input_collection_changed_callback) ||
((devtype & CUBEB_DEVICE_TYPE_OUTPUT) &&
!context->output_collection_changed_callback));
if (((devtype & CUBEB_DEVICE_TYPE_INPUT) &&
context->input_collection_changed_callback) ||
((devtype & CUBEB_DEVICE_TYPE_OUTPUT) &&
context->output_collection_changed_callback)) {
LOG("register_device_collection_changed: callback already registered");
return CUBEB_ERROR_INVALID_PARAMETER;
}
// Stop the notification client. Notifications arrive on
// a separate thread. We stop them here to avoid
@@ -3736,7 +3865,8 @@ cubeb_ops const wasapi_ops = {
/*.get_max_channel_count =*/wasapi_get_max_channel_count,
/*.get_min_latency =*/wasapi_get_min_latency,
/*.get_preferred_sample_rate =*/wasapi_get_preferred_sample_rate,
/*.get_supported_input_processing_params =*/NULL,
/*.get_supported_input_processing_params =*/
wasapi_get_supported_input_processing_params,
/*.enumerate_devices =*/wasapi_enumerate_devices,
/*.device_collection_destroy =*/wasapi_device_collection_destroy,
/*.destroy =*/wasapi_destroy,
@@ -3751,7 +3881,7 @@ cubeb_ops const wasapi_ops = {
/*.stream_set_name =*/NULL,
/*.stream_get_current_device =*/NULL,
/*.stream_set_input_mute =*/NULL,
/*.stream_set_input_processing_params =*/NULL,
/*.stream_set_input_processing_params =*/wasapi_set_input_processing_params,
/*.stream_device_destroy =*/NULL,
/*.stream_register_device_changed_callback =*/NULL,
/*.register_device_collection_changed =*/
+8 -11
View File
@@ -41,10 +41,10 @@
#ifdef FLOATING_POINT
#error You cannot compile as floating point and fixed point at the same time
#endif
#ifdef _USE_SSE
#ifdef USE_SSE
#error SSE is only for floating-point
#endif
#if ((defined (ARM4_ASM)||defined (ARM4_ASM)) && defined(BFIN_ASM)) || (defined (ARM4_ASM)&&defined(ARM5E_ASM))
#if defined(ARM4_ASM) + defined(ARM5E_ASM) + defined(BFIN_ASM) > 1
#error Make up your mind. What CPU do you have?
#endif
#ifdef VORBIS_PSYCHO
@@ -56,10 +56,10 @@
#ifndef FLOATING_POINT
#error You now need to define either FIXED_POINT or FLOATING_POINT
#endif
#if defined (ARM4_ASM) || defined(ARM5E_ASM) || defined(BFIN_ASM)
#if defined(ARM4_ASM) || defined(ARM5E_ASM) || defined(BFIN_ASM)
#error I suppose you can have a [ARM4/ARM5E/Blackfin] that has float instructions?
#endif
#ifdef FIXED_POINT_DEBUG
#ifdef FIXED_DEBUG
#error "Don't you think enabling fixed-point is a good thing to do if you want to debug that?"
#endif
@@ -117,9 +117,9 @@ typedef spx_word32_t spx_sig_t;
#ifdef ARM5E_ASM
#include "fixed_arm5e.h"
#elif defined (ARM4_ASM)
#elif defined(ARM4_ASM)
#include "fixed_arm4.h"
#elif defined (BFIN_ASM)
#elif defined(BFIN_ASM)
#include "fixed_bfin.h"
#endif
@@ -177,16 +177,13 @@ typedef float spx_word32_t;
#define ADD32(a,b) ((a)+(b))
#define SUB32(a,b) ((a)-(b))
#define MULT16_16_16(a,b) ((a)*(b))
#define MULT16_32_32(a,b) ((a)*(b))
#define MULT16_16(a,b) ((spx_word32_t)(a)*(spx_word32_t)(b))
#define MAC16_16(c,a,b) ((c)+(spx_word32_t)(a)*(spx_word32_t)(b))
#define MULT16_32_Q11(a,b) ((a)*(b))
#define MULT16_32_Q13(a,b) ((a)*(b))
#define MULT16_32_Q14(a,b) ((a)*(b))
#define MULT16_32_Q15(a,b) ((a)*(b))
#define MULT16_32_P15(a,b) ((a)*(b))
#define MAC16_32_Q11(c,a,b) ((c)+(a)*(b))
#define MAC16_32_Q15(c,a,b) ((c)+(a)*(b))
#define MAC16_16_Q11(c,a,b) ((c)+(a)*(b))
@@ -210,7 +207,7 @@ typedef float spx_word32_t;
#endif
#if defined (CONFIG_TI_C54X) || defined (CONFIG_TI_C55X)
#if defined(CONFIG_TI_C54X) || defined(CONFIG_TI_C55X)
/* 2 on TI C5x DSP */
#define BYTES_PER_CHAR 2
+6 -10
View File
@@ -69,22 +69,18 @@
/* result fits in 16 bits */
#define MULT16_16_16(a,b) ((((spx_word16_t)(a))*((spx_word16_t)(b))))
#define MULT16_16_16(a,b) (((spx_word16_t)(a))*((spx_word16_t)(b)))
/* result fits in 32 bits */
#define MULT16_32_32(a,b) (((spx_word16_t)(a))*((spx_word32_t)(b)))
/* (spx_word32_t)(spx_word16_t) gives TI compiler a hint that it's 16x16->32 multiply */
#define MULT16_16(a,b) (((spx_word32_t)(spx_word16_t)(a))*((spx_word32_t)(spx_word16_t)(b)))
#define MAC16_16(c,a,b) (ADD32((c),MULT16_16((a),(b))))
#define MULT16_32_Q12(a,b) ADD32(MULT16_16((a),SHR((b),12)), SHR(MULT16_16((a),((b)&0x00000fff)),12))
#define MULT16_32_Q13(a,b) ADD32(MULT16_16((a),SHR((b),13)), SHR(MULT16_16((a),((b)&0x00001fff)),13))
#define MULT16_32_Q14(a,b) ADD32(MULT16_16((a),SHR((b),14)), SHR(MULT16_16((a),((b)&0x00003fff)),14))
#define MULT16_32_Q11(a,b) ADD32(MULT16_16((a),SHR((b),11)), SHR(MULT16_16((a),((b)&0x000007ff)),11))
#define MAC16_32_Q11(c,a,b) ADD32(c,ADD32(MULT16_16((a),SHR((b),11)), SHR(MULT16_16((a),((b)&0x000007ff)),11)))
#define MULT16_32_P15(a,b) ADD32(MULT16_16((a),SHR((b),15)), PSHR(MULT16_16((a),((b)&0x00007fff)),15))
#define MULT16_32_Q15(a,b) ADD32(MULT16_16((a),SHR((b),15)), SHR(MULT16_16((a),((b)&0x00007fff)),15))
#define MAC16_32_Q15(c,a,b) ADD32(c,ADD32(MULT16_16((a),SHR((b),15)), SHR(MULT16_16((a),((b)&0x00007fff)),15)))
#define MULT16_32_P15(a,b) ADD32(MULT16_32_32(a,SHR((b),15)), PSHR(MULT16_16((a),((b)&0x00007fff)),15))
#define MULT16_32_Q15(a,b) ADD32(MULT16_32_32(a,SHR((b),15)), SHR(MULT16_16((a),((b)&0x00007fff)),15))
#define MAC16_32_Q15(c,a,b) ADD32(c,MULT16_32_Q15(a,b))
#define MAC16_16_Q11(c,a,b) (ADD32((c),SHR(MULT16_16((a),(b)),11)))
+46 -44
View File
@@ -46,7 +46,7 @@
Smith, Julius O. Digital Audio Resampling Home Page
Center for Computer Research in Music and Acoustics (CCRMA),
Stanford University, 2007.
Web published at http://ccrma.stanford.edu/~jos/resample/.
Web published at https://ccrma.stanford.edu/~jos/resample/.
There is one main difference, though. This resampler uses cubic
interpolation instead of linear interpolation in the above paper. This
@@ -63,9 +63,12 @@
#ifdef OUTSIDE_SPEEX
#include <stdlib.h>
static void *speex_alloc (int size) {return calloc(size,1);}
static void *speex_realloc (void *ptr, int size) {return realloc(ptr, size);}
static void speex_free (void *ptr) {free(ptr);}
static void *speex_alloc(int size) {return calloc(size,1);}
static void *speex_realloc(void *ptr, int size) {return realloc(ptr, size);}
static void speex_free(void *ptr) {free(ptr);}
#ifndef EXPORT
#define EXPORT
#endif
#include "speex_resampler.h"
#include "arch.h"
#else /* OUTSIDE_SPEEX */
@@ -75,7 +78,6 @@ static void speex_free (void *ptr) {free(ptr);}
#include "os_support.h"
#endif /* OUTSIDE_SPEEX */
#include "stack_alloc.h"
#include <math.h>
#include <limits.h>
@@ -91,18 +93,18 @@ static void speex_free (void *ptr) {free(ptr);}
#endif
#ifndef UINT32_MAX
#define UINT32_MAX 4294967296U
#define UINT32_MAX 4294967295U
#endif
#ifdef _USE_SSE
#ifdef USE_SSE
#include "resample_sse.h"
#endif
#ifdef _USE_NEON
#ifdef USE_NEON
#include "resample_neon.h"
#endif
/* Numer of elements to allocate on the stack */
/* Number of elements to allocate on the stack */
#ifdef VAR_ARRAYS
#define FIXED_STACK_ALLOC 8192
#else
@@ -194,16 +196,14 @@ struct FuncDef {
int oversample;
};
static const struct FuncDef _KAISER12 = {kaiser12_table, 64};
#define KAISER12 (&_KAISER12)
/*static struct FuncDef _KAISER12 = {kaiser12_table, 32};
#define KAISER12 (&_KAISER12)*/
static const struct FuncDef _KAISER10 = {kaiser10_table, 32};
#define KAISER10 (&_KAISER10)
static const struct FuncDef _KAISER8 = {kaiser8_table, 32};
#define KAISER8 (&_KAISER8)
static const struct FuncDef _KAISER6 = {kaiser6_table, 32};
#define KAISER6 (&_KAISER6)
static const struct FuncDef kaiser12_funcdef = {kaiser12_table, 64};
#define KAISER12 (&kaiser12_funcdef)
static const struct FuncDef kaiser10_funcdef = {kaiser10_table, 32};
#define KAISER10 (&kaiser10_funcdef)
static const struct FuncDef kaiser8_funcdef = {kaiser8_table, 32};
#define KAISER8 (&kaiser8_funcdef)
static const struct FuncDef kaiser6_funcdef = {kaiser6_table, 32};
#define KAISER6 (&kaiser6_funcdef)
struct QualityMapping {
int base_length;
@@ -473,7 +473,7 @@ static int resampler_basic_interpolate_single(SpeexResamplerState *st, spx_uint3
}
cubic_coef(frac, interp);
sum = MULT16_32_Q15(interp[0],SHR32(accum[0], 1)) + MULT16_32_Q15(interp[1],SHR32(accum[1], 1)) + MULT16_32_Q15(interp[2],SHR32(accum[2], 1)) + MULT16_32_Q15(interp[3],SHR32(accum[3], 1));
sum = MULT16_32_Q15(interp[0],accum[0]) + MULT16_32_Q15(interp[1],accum[1]) + MULT16_32_Q15(interp[2],accum[2]) + MULT16_32_Q15(interp[3],accum[3]);
sum = SATURATE32PSHR(sum, 15, 32767);
#else
cubic_coef(frac, interp);
@@ -572,6 +572,7 @@ static int resampler_basic_zero(SpeexResamplerState *st, spx_uint32_t channel_in
const int frac_advance = st->frac_advance;
const spx_uint32_t den_rate = st->den_rate;
(void)in;
while (!(last_sample >= (spx_int32_t)*in_len || out_sample >= (spx_int32_t)*out_len))
{
out[out_stride * out_sample++] = 0;
@@ -589,16 +590,15 @@ static int resampler_basic_zero(SpeexResamplerState *st, spx_uint32_t channel_in
return out_sample;
}
static int _muldiv(spx_uint32_t *result, spx_uint32_t value, spx_uint32_t mul, spx_uint32_t div)
static int multiply_frac(spx_uint32_t *result, spx_uint32_t value, spx_uint32_t num, spx_uint32_t den)
{
speex_assert(result);
spx_uint32_t major = value / div;
spx_uint32_t remainder = value % div;
spx_uint32_t major = value / den;
spx_uint32_t remain = value % den;
/* TODO: Could use 64 bits operation to check for overflow. But only guaranteed in C99+ */
if (remainder > UINT32_MAX / mul || major > UINT32_MAX / mul
|| major * mul > UINT32_MAX - remainder * mul / div)
if (remain > UINT32_MAX / num || major > UINT32_MAX / num
|| major * num > UINT32_MAX - remain * num / den)
return RESAMPLER_ERR_OVERFLOW;
*result = remainder * mul / div + major * mul;
*result = remain * num / den + major * num;
return RESAMPLER_ERR_SUCCESS;
}
@@ -619,7 +619,7 @@ static int update_filter(SpeexResamplerState *st)
{
/* down-sampling */
st->cutoff = quality_map[st->quality].downsample_bandwidth * st->den_rate / st->num_rate;
if (_muldiv(&st->filt_len,st->filt_len,st->num_rate,st->den_rate) != RESAMPLER_ERR_SUCCESS)
if (multiply_frac(&st->filt_len,st->filt_len,st->num_rate,st->den_rate) != RESAMPLER_ERR_SUCCESS)
goto fail;
/* Round up to make sure we have a multiple of 8 for SSE */
st->filt_len = ((st->filt_len-1)&(~0x7))+8;
@@ -638,12 +638,12 @@ static int update_filter(SpeexResamplerState *st)
st->cutoff = quality_map[st->quality].upsample_bandwidth;
}
/* Choose the resampling type that requires the least amount of memory */
#ifdef RESAMPLE_FULL_SINC_TABLE
use_direct = 1;
if (INT_MAX/sizeof(spx_word16_t)/st->den_rate < st->filt_len)
goto fail;
#else
/* Choose the resampling type that requires the least amount of memory */
use_direct = st->filt_len*st->den_rate <= st->filt_len*st->oversample+8
&& INT_MAX/sizeof(spx_word16_t)/st->den_rate >= st->filt_len;
#endif
@@ -733,16 +733,18 @@ static int update_filter(SpeexResamplerState *st)
{
spx_uint32_t j;
spx_uint32_t olen = old_length;
spx_uint32_t start = i*st->mem_alloc_size;
spx_uint32_t magic_samples = st->magic_samples[i];
/*if (st->magic_samples[i])*/
{
/* Try and remove the magic samples as if nothing had happened */
/* FIXME: This is wrong but for now we need it to avoid going over the array bounds */
olen = old_length + 2*st->magic_samples[i];
for (j=old_length-1+st->magic_samples[i];j--;)
st->mem[i*st->mem_alloc_size+j+st->magic_samples[i]] = st->mem[i*old_alloc_size+j];
for (j=0;j<st->magic_samples[i];j++)
st->mem[i*st->mem_alloc_size+j] = 0;
olen = old_length + 2*magic_samples;
for (j=old_length-1+magic_samples;j--;)
st->mem[start+j+magic_samples] = st->mem[i*old_alloc_size+j];
for (j=0;j<magic_samples;j++)
st->mem[start+j] = 0;
st->magic_samples[i] = 0;
}
if (st->filt_len > olen)
@@ -750,17 +752,18 @@ static int update_filter(SpeexResamplerState *st)
/* If the new filter length is still bigger than the "augmented" length */
/* Copy data going backward */
for (j=0;j<olen-1;j++)
st->mem[i*st->mem_alloc_size+(st->filt_len-2-j)] = st->mem[i*st->mem_alloc_size+(olen-2-j)];
st->mem[start+(st->filt_len-2-j)] = st->mem[start+(olen-2-j)];
/* Then put zeros for lack of anything better */
for (;j<st->filt_len-1;j++)
st->mem[i*st->mem_alloc_size+(st->filt_len-2-j)] = 0;
st->mem[start+(st->filt_len-2-j)] = 0;
/* Adjust last_sample */
st->last_sample[i] += (st->filt_len - olen)/2;
} else {
/* Put back some of the magic! */
st->magic_samples[i] = (olen - st->filt_len)/2;
for (j=0;j<st->filt_len-1+st->magic_samples[i];j++)
st->mem[i*st->mem_alloc_size+j] = st->mem[i*st->mem_alloc_size+j+st->magic_samples[i]];
magic_samples = (olen - st->filt_len)/2;
for (j=0;j<st->filt_len-1+magic_samples;j++)
st->mem[start+j] = st->mem[start+j+magic_samples];
st->magic_samples[i] = magic_samples;
}
}
} else if (st->filt_len < old_length)
@@ -977,8 +980,7 @@ EXPORT int speex_resampler_process_int(SpeexResamplerState *st, spx_uint32_t cha
const spx_uint32_t xlen = st->mem_alloc_size - (st->filt_len - 1);
#ifdef VAR_ARRAYS
const unsigned int ylen = (olen < FIXED_STACK_ALLOC) ? olen : FIXED_STACK_ALLOC;
VARDECL(spx_word16_t *ystack);
ALLOC(ystack, ylen, spx_word16_t);
spx_word16_t ystack[ylen];
#else
const unsigned int ylen = FIXED_STACK_ALLOC;
spx_word16_t ystack[FIXED_STACK_ALLOC];
@@ -1093,7 +1095,7 @@ EXPORT void speex_resampler_get_rate(SpeexResamplerState *st, spx_uint32_t *in_r
*out_rate = st->out_rate;
}
static inline spx_uint32_t _gcd(spx_uint32_t a, spx_uint32_t b)
static inline spx_uint32_t compute_gcd(spx_uint32_t a, spx_uint32_t b)
{
while (b != 0)
{
@@ -1123,7 +1125,7 @@ EXPORT int speex_resampler_set_rate_frac(SpeexResamplerState *st, spx_uint32_t r
st->num_rate = ratio_num;
st->den_rate = ratio_den;
fact = _gcd (st->num_rate, st->den_rate);
fact = compute_gcd(st->num_rate, st->den_rate);
st->num_rate /= fact;
st->den_rate /= fact;
@@ -1132,7 +1134,7 @@ EXPORT int speex_resampler_set_rate_frac(SpeexResamplerState *st, spx_uint32_t r
{
for (i=0;i<st->nb_channels;i++)
{
if (_muldiv(&st->samp_frac_num[i],st->samp_frac_num[i],st->den_rate,old_den) != RESAMPLER_ERR_SUCCESS)
if (multiply_frac(&st->samp_frac_num[i],st->samp_frac_num[i],st->den_rate,old_den) != RESAMPLER_ERR_SUCCESS)
return RESAMPLER_ERR_OVERFLOW;
/* Safety net */
if (st->samp_frac_num[i] >= st->den_rate)
+154 -14
View File
@@ -36,14 +36,26 @@
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <arm_neon.h>
#include <stdint.h>
#ifdef FIXED_POINT
#ifdef __thumb2__
#if defined(__aarch64__)
static inline int32_t saturate_32bit_to_16bit(int32_t a) {
int32_t ret;
asm ("fmov s0, %w[a]\n"
"sqxtn h0, s0\n"
"sxtl v0.4s, v0.4h\n"
"fmov %w[ret], s0\n"
: [ret] "=r" (ret)
: [a] "r" (a)
: "v0" );
return ret;
}
#elif defined(__thumb2__)
static inline int32_t saturate_32bit_to_16bit(int32_t a) {
int32_t ret;
asm ("ssat %[ret], #16, %[a]"
: [ret] "=&r" (ret)
: [ret] "=r" (ret)
: [a] "r" (a)
: );
return ret;
@@ -54,7 +66,7 @@ static inline int32_t saturate_32bit_to_16bit(int32_t a) {
asm ("vmov.s32 d0[0], %[a]\n"
"vqmovn.s32 d0, q0\n"
"vmov.s16 %[ret], d0[0]\n"
: [ret] "=&r" (ret)
: [ret] "=r" (ret)
: [a] "r" (a)
: "q0");
return ret;
@@ -64,7 +76,63 @@ static inline int32_t saturate_32bit_to_16bit(int32_t a) {
#define WORD2INT(x) (saturate_32bit_to_16bit(x))
#define OVERRIDE_INNER_PRODUCT_SINGLE
/* Only works when len % 4 == 0 */
/* Only works when len % 4 == 0 and len >= 4 */
#if defined(__aarch64__)
static inline int32_t inner_product_single(const int16_t *a, const int16_t *b, unsigned int len)
{
int32_t ret;
uint32_t remainder = len % 16;
len = len - remainder;
asm volatile (" cmp %w[len], #0\n"
" b.ne 1f\n"
" ld1 {v16.4h}, [%[b]], #8\n"
" ld1 {v20.4h}, [%[a]], #8\n"
" subs %w[remainder], %w[remainder], #4\n"
" smull v0.4s, v16.4h, v20.4h\n"
" b.ne 4f\n"
" b 5f\n"
"1:"
" ld1 {v16.4h, v17.4h, v18.4h, v19.4h}, [%[b]], #32\n"
" ld1 {v20.4h, v21.4h, v22.4h, v23.4h}, [%[a]], #32\n"
" subs %w[len], %w[len], #16\n"
" smull v0.4s, v16.4h, v20.4h\n"
" smlal v0.4s, v17.4h, v21.4h\n"
" smlal v0.4s, v18.4h, v22.4h\n"
" smlal v0.4s, v19.4h, v23.4h\n"
" b.eq 3f\n"
"2:"
" ld1 {v16.4h, v17.4h, v18.4h, v19.4h}, [%[b]], #32\n"
" ld1 {v20.4h, v21.4h, v22.4h, v23.4h}, [%[a]], #32\n"
" subs %w[len], %w[len], #16\n"
" smlal v0.4s, v16.4h, v20.4h\n"
" smlal v0.4s, v17.4h, v21.4h\n"
" smlal v0.4s, v18.4h, v22.4h\n"
" smlal v0.4s, v19.4h, v23.4h\n"
" b.ne 2b\n"
"3:"
" cmp %w[remainder], #0\n"
" b.eq 5f\n"
"4:"
" ld1 {v18.4h}, [%[b]], #8\n"
" ld1 {v22.4h}, [%[a]], #8\n"
" subs %w[remainder], %w[remainder], #4\n"
" smlal v0.4s, v18.4h, v22.4h\n"
" b.ne 4b\n"
"5:"
" saddlv d0, v0.4s\n"
" sqxtn s0, d0\n"
" sqrshrn h0, s0, #15\n"
" sxtl v0.4s, v0.4h\n"
" fmov %w[ret], s0\n"
: [ret] "=r" (ret), [a] "+r" (a), [b] "+r" (b),
[len] "+r" (len), [remainder] "+r" (remainder)
:
: "cc", "v0",
"v16", "v17", "v18", "v19", "v20", "v21", "v22", "v23");
return ret;
}
#else
static inline int32_t inner_product_single(const int16_t *a, const int16_t *b, unsigned int len)
{
int32_t ret;
@@ -112,33 +180,104 @@ static inline int32_t inner_product_single(const int16_t *a, const int16_t *b, u
" vqmovn.s64 d0, q0\n"
" vqrshrn.s32 d0, q0, #15\n"
" vmov.s16 %[ret], d0[0]\n"
: [ret] "=&r" (ret), [a] "+r" (a), [b] "+r" (b),
: [ret] "=r" (ret), [a] "+r" (a), [b] "+r" (b),
[len] "+r" (len), [remainder] "+r" (remainder)
:
: "cc", "q0",
"d16", "d17", "d18", "d19",
"d20", "d21", "d22", "d23");
"d16", "d17", "d18", "d19", "d20", "d21", "d22", "d23");
return ret;
}
#elif defined(FLOATING_POINT)
#endif // !defined(__aarch64__)
#elif defined(FLOATING_POINT)
#if defined(__aarch64__)
static inline int32_t saturate_float_to_16bit(float a) {
int32_t ret;
asm ("fcvtas s1, %s[a]\n"
"sqxtn h1, s1\n"
"sxtl v1.4s, v1.4h\n"
"fmov %w[ret], s1\n"
: [ret] "=r" (ret)
: [a] "w" (a)
: "v1");
return ret;
}
#else
static inline int32_t saturate_float_to_16bit(float a) {
int32_t ret;
asm ("vmov.f32 d0[0], %[a]\n"
"vcvt.s32.f32 d0, d0, #15\n"
"vqrshrn.s32 d0, q0, #15\n"
"vmov.s16 %[ret], d0[0]\n"
: [ret] "=&r" (ret)
: [ret] "=r" (ret)
: [a] "r" (a)
: "q0");
return ret;
}
#endif
#undef WORD2INT
#define WORD2INT(x) (saturate_float_to_16bit(x))
#define OVERRIDE_INNER_PRODUCT_SINGLE
/* Only works when len % 4 == 0 */
/* Only works when len % 4 == 0 and len >= 4 */
#if defined(__aarch64__)
static inline float inner_product_single(const float *a, const float *b, unsigned int len)
{
float ret;
uint32_t remainder = len % 16;
len = len - remainder;
asm volatile (" cmp %w[len], #0\n"
" b.ne 1f\n"
" ld1 {v16.4s}, [%[b]], #16\n"
" ld1 {v20.4s}, [%[a]], #16\n"
" subs %w[remainder], %w[remainder], #4\n"
" fmul v1.4s, v16.4s, v20.4s\n"
" b.ne 4f\n"
" b 5f\n"
"1:"
" ld1 {v16.4s, v17.4s, v18.4s, v19.4s}, [%[b]], #64\n"
" ld1 {v20.4s, v21.4s, v22.4s, v23.4s}, [%[a]], #64\n"
" subs %w[len], %w[len], #16\n"
" fmul v1.4s, v16.4s, v20.4s\n"
" fmul v2.4s, v17.4s, v21.4s\n"
" fmul v3.4s, v18.4s, v22.4s\n"
" fmul v4.4s, v19.4s, v23.4s\n"
" b.eq 3f\n"
"2:"
" ld1 {v16.4s, v17.4s, v18.4s, v19.4s}, [%[b]], #64\n"
" ld1 {v20.4s, v21.4s, v22.4s, v23.4s}, [%[a]], #64\n"
" subs %w[len], %w[len], #16\n"
" fmla v1.4s, v16.4s, v20.4s\n"
" fmla v2.4s, v17.4s, v21.4s\n"
" fmla v3.4s, v18.4s, v22.4s\n"
" fmla v4.4s, v19.4s, v23.4s\n"
" b.ne 2b\n"
"3:"
" fadd v16.4s, v1.4s, v2.4s\n"
" fadd v17.4s, v3.4s, v4.4s\n"
" cmp %w[remainder], #0\n"
" fadd v1.4s, v16.4s, v17.4s\n"
" b.eq 5f\n"
"4:"
" ld1 {v18.4s}, [%[b]], #16\n"
" ld1 {v22.4s}, [%[a]], #16\n"
" subs %w[remainder], %w[remainder], #4\n"
" fmla v1.4s, v18.4s, v22.4s\n"
" b.ne 4b\n"
"5:"
" faddp v1.4s, v1.4s, v1.4s\n"
" faddp %[ret].4s, v1.4s, v1.4s\n"
: [ret] "=w" (ret), [a] "+r" (a), [b] "+r" (b),
[len] "+r" (len), [remainder] "+r" (remainder)
:
: "cc", "v1", "v2", "v3", "v4",
"v16", "v17", "v18", "v19", "v20", "v21", "v22", "v23");
return ret;
}
#else
static inline float inner_product_single(const float *a, const float *b, unsigned int len)
{
float ret;
@@ -191,11 +330,12 @@ static inline float inner_product_single(const float *a, const float *b, unsigne
" vadd.f32 d0, d0, d1\n"
" vpadd.f32 d0, d0, d0\n"
" vmov.f32 %[ret], d0[0]\n"
: [ret] "=&r" (ret), [a] "+r" (a), [b] "+r" (b),
: [ret] "=r" (ret), [a] "+r" (a), [b] "+r" (b),
[len] "+l" (len), [remainder] "+l" (remainder)
:
: "cc", "q0", "q1", "q2", "q3", "q4", "q5", "q6", "q7", "q8",
"q9", "q10", "q11");
: "cc", "q0", "q1", "q2", "q3",
"q4", "q5", "q6", "q7", "q8", "q9", "q10", "q11");
return ret;
}
#endif // defined(__aarch64__)
#endif
+1 -1
View File
@@ -71,7 +71,7 @@ static inline float interpolate_product_single(const float *a, const float *b, u
return ret;
}
#ifdef _USE_SSE2
#ifdef USE_SSE2
#include <emmintrin.h>
#define OVERRIDE_INNER_PRODUCT_DOUBLE
-115
View File
@@ -1,115 +0,0 @@
/* Copyright (C) 2002 Jean-Marc Valin */
/**
@file stack_alloc.h
@brief Temporary memory allocation on stack
*/
/*
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of the Xiph.org Foundation nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef STACK_ALLOC_H
#define STACK_ALLOC_H
#ifdef USE_ALLOCA
# ifdef WIN32
# include <malloc.h>
# else
# ifdef HAVE_ALLOCA_H
# include <alloca.h>
# else
# include <stdlib.h>
# endif
# endif
#endif
/**
* @def ALIGN(stack, size)
*
* Aligns the stack to a 'size' boundary
*
* @param stack Stack
* @param size New size boundary
*/
/**
* @def PUSH(stack, size, type)
*
* Allocates 'size' elements of type 'type' on the stack
*
* @param stack Stack
* @param size Number of elements
* @param type Type of element
*/
/**
* @def VARDECL(var)
*
* Declare variable on stack
*
* @param var Variable to declare
*/
/**
* @def ALLOC(var, size, type)
*
* Allocate 'size' elements of 'type' on stack
*
* @param var Name of variable to allocate
* @param size Number of elements
* @param type Type of element
*/
#ifdef ENABLE_VALGRIND
#include <valgrind/memcheck.h>
#define ALIGN(stack, size) ((stack) += ((size) - (long)(stack)) & ((size) - 1))
#define PUSH(stack, size, type) (VALGRIND_MAKE_NOACCESS(stack, 1000),ALIGN((stack),sizeof(type)),VALGRIND_MAKE_WRITABLE(stack, ((size)*sizeof(type))),(stack)+=((size)*sizeof(type)),(type*)((stack)-((size)*sizeof(type))))
#else
#define ALIGN(stack, size) ((stack) += ((size) - (long)(stack)) & ((size) - 1))
#define PUSH(stack, size, type) (ALIGN((stack),sizeof(type)),(stack)+=((size)*sizeof(type)),(type*)((stack)-((size)*sizeof(type))))
#endif
#if defined(VAR_ARRAYS)
#define VARDECL(var)
#define ALLOC(var, size, type) type var[size]
#elif defined(USE_ALLOCA)
#define VARDECL(var) var
#define ALLOC(var, size, type) var = alloca(sizeof(type)*(size))
#else
#define VARDECL(var) var
#define ALLOC(var, size, type) var = PUSH(stack, size, type)
#endif
#endif
+3 -3
View File
@@ -337,9 +337,9 @@ std::vector<std::pair<std::string, std::string>> AudioStream::GetCubebDriverName
std::vector<std::pair<std::string, std::string>> names;
names.emplace_back(std::string(), TRANSLATE_STR("AudioStream", "Default"));
const char** cubeb_names = cubeb_get_backend_names();
for (u32 i = 0; cubeb_names[i] != nullptr; i++)
names.emplace_back(cubeb_names[i], cubeb_names[i]);
const cubeb_backend_names backend_names = cubeb_get_backend_names();
for (size_t i = 0; i < backend_names.count; i++)
names.emplace_back(backend_names.names[i], backend_names.names[i]);
return names;
}