зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1405258 - Update libcubeb to revision ba2a89611875cd9f2dabae99a362461b03c0dd3d
MozReview-Commit-ID: PngLiVneAh --HG-- extra : rebase_source : 9f604112fad6cafb0883e3248823f78106ec5e19
This commit is contained in:
Родитель
f23193be11
Коммит
37bfa43273
|
@ -5,4 +5,4 @@ Makefile.in build files for the Mozilla build system.
|
|||
|
||||
The cubeb git repository is: git://github.com/kinetiknz/cubeb.git
|
||||
|
||||
The git commit ID used was ac532ad4f0defaf7a397db64c2c42d2665cd06e9 (2017-09-17 08:23:45 +1200)
|
||||
The git commit ID used was ba2a89611875cd9f2dabae99a362461b03c0dd3d (2017-10-03 11:34:20 +0200)
|
||||
|
|
|
@ -125,7 +125,7 @@ void test_delay_lines(uint32_t delay_frames, uint32_t channels, uint32_t chunk_m
|
|||
const size_t length_s = 2;
|
||||
const size_t rate = 44100;
|
||||
const size_t length_frames = rate * length_s;
|
||||
delay_line<float> delay(delay_frames, channels);
|
||||
delay_line<float> delay(delay_frames, channels, rate);
|
||||
auto_array<float> input;
|
||||
auto_array<float> output;
|
||||
uint32_t chunk_length = channels * chunk_ms * rate / 1000;
|
||||
|
@ -629,10 +629,12 @@ TEST(cubeb, resampler_passthrough_input_only)
|
|||
template<typename T>
|
||||
long seq(T* array, int stride, long start, long count)
|
||||
{
|
||||
uint32_t output_idx = 0;
|
||||
for(int i = 0; i < count; i++) {
|
||||
for (int j = 0; j < stride; j++) {
|
||||
array[i + j] = static_cast<T>(start + i);
|
||||
array[output_idx + j] = static_cast<T>(start + i);
|
||||
}
|
||||
output_idx += stride;
|
||||
}
|
||||
return start + count;
|
||||
}
|
||||
|
@ -649,11 +651,28 @@ void is_seq(T * array, int stride, long count, long expected_start)
|
|||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void is_not_seq(T * array, int stride, long count, long expected_start)
|
||||
{
|
||||
uint32_t output_index = 0;
|
||||
for (long i = 0; i < count; i++) {
|
||||
for (int j = 0; j < stride; j++) {
|
||||
ASSERT_NE(array[output_index + j], expected_start + i);
|
||||
}
|
||||
output_index += stride;
|
||||
}
|
||||
}
|
||||
|
||||
struct closure {
|
||||
int input_channel_count;
|
||||
};
|
||||
|
||||
// gtest does not support using ASSERT_EQ and friend in a function that returns
|
||||
// a value.
|
||||
template<typename T>
|
||||
void check_duplex(const T * input_buffer,
|
||||
T * output_buffer, long frame_count)
|
||||
T * output_buffer, long frame_count,
|
||||
int input_channel_count)
|
||||
{
|
||||
ASSERT_EQ(frame_count, 256);
|
||||
// Silence scan-build warning.
|
||||
|
@ -661,18 +680,28 @@ void check_duplex(const T * input_buffer,
|
|||
ASSERT_TRUE(!!input_buffer); assert(input_buffer);
|
||||
|
||||
int output_index = 0;
|
||||
int input_index = 0;
|
||||
for (int i = 0; i < frame_count; i++) {
|
||||
// output is two channels, input is one channel, we upmix.
|
||||
output_buffer[output_index] = output_buffer[output_index+1] = input_buffer[i];
|
||||
// output is two channels, input one or two channels.
|
||||
if (input_channel_count == 1) {
|
||||
output_buffer[output_index] = output_buffer[output_index + 1] = input_buffer[i];
|
||||
} else if (input_channel_count == 2) {
|
||||
output_buffer[output_index] = input_buffer[input_index];
|
||||
output_buffer[output_index + 1] = input_buffer[input_index + 1];
|
||||
}
|
||||
output_index += 2;
|
||||
input_index += input_channel_count;
|
||||
}
|
||||
}
|
||||
|
||||
long cb_passthrough_resampler_duplex(cubeb_stream * /*stm*/, void * /*user_ptr*/,
|
||||
long cb_passthrough_resampler_duplex(cubeb_stream * /*stm*/, void * user_ptr,
|
||||
const void * input_buffer,
|
||||
void * output_buffer, long frame_count)
|
||||
{
|
||||
check_duplex<float>(static_cast<const float*>(input_buffer), static_cast<float*>(output_buffer), frame_count);
|
||||
closure * c = reinterpret_cast<closure*>(user_ptr);
|
||||
check_duplex<float>(static_cast<const float*>(input_buffer),
|
||||
static_cast<float*>(output_buffer),
|
||||
frame_count, c->input_channel_count);
|
||||
return frame_count;
|
||||
}
|
||||
|
||||
|
@ -698,9 +727,12 @@ TEST(cubeb, resampler_passthrough_duplex_callback_reordering)
|
|||
|
||||
int target_rate = input_params.rate;
|
||||
|
||||
closure c;
|
||||
c.input_channel_count = input_channels;
|
||||
|
||||
cubeb_resampler * resampler =
|
||||
cubeb_resampler_create((cubeb_stream*)nullptr, &input_params, &output_params,
|
||||
target_rate, cb_passthrough_resampler_duplex, nullptr,
|
||||
target_rate, cb_passthrough_resampler_duplex, &c,
|
||||
CUBEB_RESAMPLER_QUALITY_VOIP);
|
||||
|
||||
const long BUF_BASE_SIZE = 256;
|
||||
|
@ -755,3 +787,101 @@ TEST(cubeb, resampler_passthrough_duplex_callback_reordering)
|
|||
|
||||
cubeb_resampler_destroy(resampler);
|
||||
}
|
||||
|
||||
// Artificially simulate output thread underruns,
|
||||
// by building up artificial delay in the input.
|
||||
// Check that the frame drop logic kicks in.
|
||||
TEST(cubeb, resampler_drift_drop_data)
|
||||
{
|
||||
for (uint32_t input_channels = 1; input_channels < 3; input_channels++) {
|
||||
cubeb_stream_params input_params;
|
||||
cubeb_stream_params output_params;
|
||||
|
||||
const int output_channels = 2;
|
||||
const int sample_rate = 44100;
|
||||
|
||||
input_params.channels = input_channels;
|
||||
input_params.rate = sample_rate;
|
||||
input_params.format = CUBEB_SAMPLE_FLOAT32NE;
|
||||
|
||||
output_params.channels = output_channels;
|
||||
output_params.rate = sample_rate;
|
||||
output_params.format = CUBEB_SAMPLE_FLOAT32NE;
|
||||
|
||||
int target_rate = input_params.rate;
|
||||
|
||||
closure c;
|
||||
c.input_channel_count = input_channels;
|
||||
|
||||
cubeb_resampler * resampler =
|
||||
cubeb_resampler_create((cubeb_stream*)nullptr, &input_params, &output_params,
|
||||
target_rate, cb_passthrough_resampler_duplex, &c,
|
||||
CUBEB_RESAMPLER_QUALITY_VOIP);
|
||||
|
||||
const long BUF_BASE_SIZE = 256;
|
||||
|
||||
// The factor by which the deadline is missed. This is intentionally
|
||||
// kind of large to trigger the frame drop quickly. In real life, multiple
|
||||
// smaller under-runs would accumulate.
|
||||
const long UNDERRUN_FACTOR = 10;
|
||||
// Number buffer used for pre-buffering, that some backends do.
|
||||
const long PREBUFFER_FACTOR = 2;
|
||||
|
||||
std::vector<float> input_buffer_prebuffer(input_channels * BUF_BASE_SIZE * PREBUFFER_FACTOR);
|
||||
std::vector<float> input_buffer_glitch(input_channels * BUF_BASE_SIZE * UNDERRUN_FACTOR);
|
||||
std::vector<float> input_buffer_normal(input_channels * BUF_BASE_SIZE);
|
||||
std::vector<float> output_buffer(output_channels * BUF_BASE_SIZE);
|
||||
|
||||
long seq_idx = 0;
|
||||
long output_seq_idx = 0;
|
||||
|
||||
long prebuffer_frames = input_buffer_prebuffer.size() / input_params.channels;
|
||||
seq_idx = seq(input_buffer_prebuffer.data(), input_channels, seq_idx,
|
||||
prebuffer_frames);
|
||||
|
||||
long got = cubeb_resampler_fill(resampler, input_buffer_prebuffer.data(), &prebuffer_frames,
|
||||
output_buffer.data(), BUF_BASE_SIZE);
|
||||
|
||||
output_seq_idx += BUF_BASE_SIZE;
|
||||
|
||||
// prebuffer_frames will hold the frames used by the resampler.
|
||||
ASSERT_EQ(prebuffer_frames, BUF_BASE_SIZE);
|
||||
ASSERT_EQ(got, BUF_BASE_SIZE);
|
||||
|
||||
for (uint32_t i = 0; i < 300; i++) {
|
||||
long int frames = BUF_BASE_SIZE;
|
||||
if (i != 0 && (i % 100) == 1) {
|
||||
// Once in a while, the output thread misses its deadline.
|
||||
// The input thread still produces data, so it ends up accumulating. Simulate this by providing a
|
||||
// much bigger input buffer. Check that the sequence is now unaligned, meaning we've dropped data
|
||||
// to keep everything in sync.
|
||||
seq_idx = seq(input_buffer_glitch.data(), input_channels, seq_idx, BUF_BASE_SIZE * UNDERRUN_FACTOR);
|
||||
frames = BUF_BASE_SIZE * UNDERRUN_FACTOR;
|
||||
got = cubeb_resampler_fill(resampler, input_buffer_glitch.data(), &frames, output_buffer.data(), BUF_BASE_SIZE);
|
||||
is_seq(output_buffer.data(), 2, BUF_BASE_SIZE, output_seq_idx);
|
||||
output_seq_idx += BUF_BASE_SIZE;
|
||||
}
|
||||
else if (i != 0 && (i % 100) == 2) {
|
||||
// On the next iteration, the sequence should be broken
|
||||
seq_idx = seq(input_buffer_normal.data(), input_channels, seq_idx, BUF_BASE_SIZE);
|
||||
long normal_input_frame_count = 256;
|
||||
got = cubeb_resampler_fill(resampler, input_buffer_normal.data(), &normal_input_frame_count, output_buffer.data(), BUF_BASE_SIZE);
|
||||
is_not_seq(output_buffer.data(), output_channels, BUF_BASE_SIZE, output_seq_idx);
|
||||
// Reclock so that we can use is_seq again.
|
||||
output_seq_idx = output_buffer[BUF_BASE_SIZE * output_channels - 1] + 1;
|
||||
}
|
||||
else {
|
||||
// normal case
|
||||
seq_idx = seq(input_buffer_normal.data(), input_channels, seq_idx, BUF_BASE_SIZE);
|
||||
long normal_input_frame_count = 256;
|
||||
got = cubeb_resampler_fill(resampler, input_buffer_normal.data(), &normal_input_frame_count, output_buffer.data(), BUF_BASE_SIZE);
|
||||
is_seq(output_buffer.data(), output_channels, BUF_BASE_SIZE, output_seq_idx);
|
||||
output_seq_idx += BUF_BASE_SIZE;
|
||||
}
|
||||
ASSERT_EQ(got, BUF_BASE_SIZE);
|
||||
}
|
||||
|
||||
cubeb_resampler_destroy(resampler);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -576,7 +576,7 @@ static int audiounit_stream_set_volume(cubeb_stream * stm, float volume);
|
|||
static int audiounit_uninstall_device_changed_callback(cubeb_stream * stm);
|
||||
static AudioObjectID audiounit_get_default_device_id(cubeb_device_type type);
|
||||
|
||||
static void
|
||||
static int
|
||||
audiounit_set_device_info(cubeb_stream * stm, AudioDeviceID id, io_side side)
|
||||
{
|
||||
assert(stm);
|
||||
|
@ -601,6 +601,9 @@ audiounit_set_device_info(cubeb_stream * stm, AudioDeviceID id, io_side side)
|
|||
}
|
||||
|
||||
AudioDeviceID default_device_id = audiounit_get_default_device_id(type);
|
||||
if (default_device_id == kAudioObjectUnknown) {
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
if (id == kAudioObjectUnknown) {
|
||||
info->id = default_device_id;
|
||||
info->flags |= DEV_SELECTED_DEFAULT;
|
||||
|
@ -613,6 +616,8 @@ audiounit_set_device_info(cubeb_stream * stm, AudioDeviceID id, io_side side)
|
|||
assert(info->id);
|
||||
assert(info->flags & DEV_INPUT && !(info->flags & DEV_OUTPUT) ||
|
||||
!(info->flags & DEV_INPUT) && info->flags & DEV_OUTPUT);
|
||||
|
||||
return CUBEB_OK;
|
||||
}
|
||||
|
||||
|
||||
|
@ -645,12 +650,14 @@ audiounit_reinit_stream(cubeb_stream * stm, device_flags_value flags)
|
|||
* default system device change. In both cases cubeb switch on the new default
|
||||
* device. This is considered the most expected behavior for the user. */
|
||||
if (flags & DEV_INPUT) {
|
||||
audiounit_set_device_info(stm, 0, INPUT);
|
||||
r = audiounit_set_device_info(stm, 0, INPUT);
|
||||
assert(r == CUBEB_OK);
|
||||
}
|
||||
/* Always use the default output on reinit. This is not correct in every case
|
||||
* but it is sufficient for Firefox and prevent reinit from reporting failures.
|
||||
* It will change soon when reinit mechanism will be updated. */
|
||||
audiounit_set_device_info(stm, 0, OUTPUT);
|
||||
r = audiounit_set_device_info(stm, 0, OUTPUT);
|
||||
assert(r == CUBEB_OK);
|
||||
|
||||
if (audiounit_setup_stream(stm) != CUBEB_OK) {
|
||||
LOG("(%p) Stream reinit failed.", stm);
|
||||
|
@ -2512,11 +2519,19 @@ audiounit_stream_init(cubeb * context,
|
|||
stm->latency_frames = latency_frames;
|
||||
if (input_stream_params) {
|
||||
stm->input_stream_params = *input_stream_params;
|
||||
audiounit_set_device_info(stm.get(), reinterpret_cast<uintptr_t>(input_device), INPUT);
|
||||
r = audiounit_set_device_info(stm.get(), reinterpret_cast<uintptr_t>(input_device), INPUT);
|
||||
if (r != CUBEB_OK) {
|
||||
LOG("(%p) Fail to set device info for input.", stm.get());
|
||||
return r;
|
||||
}
|
||||
}
|
||||
if (output_stream_params) {
|
||||
stm->output_stream_params = *output_stream_params;
|
||||
audiounit_set_device_info(stm.get(), reinterpret_cast<uintptr_t>(output_device), OUTPUT);
|
||||
r = audiounit_set_device_info(stm.get(), reinterpret_cast<uintptr_t>(output_device), OUTPUT);
|
||||
if (r != CUBEB_OK) {
|
||||
LOG("(%p) Fail to set device info for output.", stm.get());
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
auto_lock context_lock(context->mutex);
|
||||
|
|
|
@ -653,7 +653,6 @@ pulse_init(cubeb ** context, char const * context_name)
|
|||
WRAP(pa_operation_unref)(o);
|
||||
}
|
||||
WRAP(pa_threaded_mainloop_unlock)(ctx->mainloop);
|
||||
assert(ctx->default_sink_info);
|
||||
|
||||
*context = ctx;
|
||||
|
||||
|
@ -673,6 +672,9 @@ pulse_get_max_channel_count(cubeb * ctx, uint32_t * max_channels)
|
|||
(void)ctx;
|
||||
assert(ctx && max_channels);
|
||||
|
||||
if (!ctx->default_sink_info)
|
||||
return CUBEB_ERROR;
|
||||
|
||||
*max_channels = ctx->default_sink_info->channel_map.channels;
|
||||
|
||||
return CUBEB_OK;
|
||||
|
@ -684,6 +686,9 @@ pulse_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate)
|
|||
assert(ctx && rate);
|
||||
(void)ctx;
|
||||
|
||||
if (!ctx->default_sink_info)
|
||||
return CUBEB_ERROR;
|
||||
|
||||
*rate = ctx->default_sink_info->sample_spec.rate;
|
||||
|
||||
return CUBEB_OK;
|
||||
|
@ -695,6 +700,9 @@ pulse_get_preferred_channel_layout(cubeb * ctx, cubeb_channel_layout * layout)
|
|||
assert(ctx && layout);
|
||||
(void)ctx;
|
||||
|
||||
if (!ctx->default_sink_info)
|
||||
return CUBEB_ERROR;
|
||||
|
||||
*layout = channel_map_to_layout(&ctx->default_sink_info->channel_map);
|
||||
|
||||
return CUBEB_OK;
|
||||
|
@ -1077,6 +1085,7 @@ pulse_stream_set_volume(cubeb_stream * stm, float volume)
|
|||
pa_volume_t vol;
|
||||
pa_cvolume cvol;
|
||||
const pa_sample_spec * ss;
|
||||
cubeb * ctx;
|
||||
|
||||
if (!stm->output_stream) {
|
||||
return CUBEB_ERROR;
|
||||
|
@ -1086,7 +1095,9 @@ pulse_stream_set_volume(cubeb_stream * stm, float volume)
|
|||
|
||||
/* if the pulse daemon is configured to use flat volumes,
|
||||
* apply our own gain instead of changing the input volume on the sink. */
|
||||
if (stm->context->default_sink_info->flags & PA_SINK_FLAT_VOLUME) {
|
||||
ctx = stm->context;
|
||||
if (ctx->default_sink_info &&
|
||||
(ctx->default_sink_info->flags & PA_SINK_FLAT_VOLUME)) {
|
||||
stm->volume = volume;
|
||||
} else {
|
||||
ss = WRAP(pa_stream_get_sample_spec)(stm->output_stream);
|
||||
|
@ -1096,16 +1107,16 @@ pulse_stream_set_volume(cubeb_stream * stm, float volume)
|
|||
|
||||
index = WRAP(pa_stream_get_index)(stm->output_stream);
|
||||
|
||||
op = WRAP(pa_context_set_sink_input_volume)(stm->context->context,
|
||||
op = WRAP(pa_context_set_sink_input_volume)(ctx->context,
|
||||
index, &cvol, volume_success,
|
||||
stm);
|
||||
if (op) {
|
||||
operation_wait(stm->context, stm->output_stream, op);
|
||||
operation_wait(ctx, stm->output_stream, op);
|
||||
WRAP(pa_operation_unref)(op);
|
||||
}
|
||||
}
|
||||
|
||||
WRAP(pa_threaded_mainloop_unlock)(stm->context->mainloop);
|
||||
WRAP(pa_threaded_mainloop_unlock)(ctx->mainloop);
|
||||
|
||||
return CUBEB_OK;
|
||||
}
|
||||
|
@ -1235,7 +1246,12 @@ pulse_sink_info_cb(pa_context * context, const pa_sink_info * info,
|
|||
|
||||
(void)context;
|
||||
|
||||
if (eol || info == NULL)
|
||||
if (eol) {
|
||||
WRAP(pa_threaded_mainloop_signal)(list_data->context->mainloop, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (info == NULL)
|
||||
return;
|
||||
|
||||
device_id = info->name;
|
||||
|
@ -1274,8 +1290,6 @@ pulse_sink_info_cb(pa_context * context, const pa_sink_info * info,
|
|||
devinfo->latency_hi = 0;
|
||||
|
||||
list_data->count += 1;
|
||||
|
||||
WRAP(pa_threaded_mainloop_signal)(list_data->context->mainloop, 0);
|
||||
}
|
||||
|
||||
static cubeb_device_state
|
||||
|
@ -1304,8 +1318,10 @@ pulse_source_info_cb(pa_context * context, const pa_source_info * info,
|
|||
|
||||
(void)context;
|
||||
|
||||
if (eol)
|
||||
if (eol) {
|
||||
WRAP(pa_threaded_mainloop_signal)(list_data->context->mainloop, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
device_id = info->name;
|
||||
if (intern_device_id(list_data->context, &device_id) != CUBEB_OK) {
|
||||
|
@ -1343,7 +1359,6 @@ pulse_source_info_cb(pa_context * context, const pa_source_info * info,
|
|||
devinfo->latency_hi = 0;
|
||||
|
||||
list_data->count += 1;
|
||||
WRAP(pa_threaded_mainloop_signal)(list_data->context->mainloop, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -1355,8 +1370,10 @@ pulse_server_info_cb(pa_context * c, const pa_server_info * i, void * userdata)
|
|||
|
||||
free(list_data->default_sink_name);
|
||||
free(list_data->default_source_name);
|
||||
list_data->default_sink_name = strdup(i->default_sink_name);
|
||||
list_data->default_source_name = strdup(i->default_source_name);
|
||||
list_data->default_sink_name =
|
||||
i->default_sink_name ? strdup(i->default_sink_name) : NULL;
|
||||
list_data->default_source_name =
|
||||
i->default_source_name ? strdup(i->default_source_name) : NULL;
|
||||
|
||||
WRAP(pa_threaded_mainloop_signal)(list_data->context->mainloop, 0);
|
||||
}
|
||||
|
|
|
@ -39,11 +39,13 @@ template<typename T>
|
|||
passthrough_resampler<T>::passthrough_resampler(cubeb_stream * s,
|
||||
cubeb_data_callback cb,
|
||||
void * ptr,
|
||||
uint32_t input_channels)
|
||||
uint32_t input_channels,
|
||||
uint32_t sample_rate)
|
||||
: processor(input_channels)
|
||||
, stream(s)
|
||||
, data_callback(cb)
|
||||
, user_ptr(ptr)
|
||||
, sample_rate(sample_rate)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -72,6 +74,7 @@ long passthrough_resampler<T>::fill(void * input_buffer, long * input_frames_cou
|
|||
if (input_buffer) {
|
||||
internal_input_buffer.pop(nullptr, frames_to_samples(output_frames));
|
||||
*input_frames_count = output_frames;
|
||||
drop_audio_if_needed();
|
||||
}
|
||||
|
||||
return rv;
|
||||
|
@ -241,9 +244,15 @@ cubeb_resampler_speex<T, InputProcessor, OutputProcessor>
|
|||
|
||||
output_processor->written(got);
|
||||
|
||||
input_processor->drop_audio_if_needed();
|
||||
|
||||
/* Process the output. If not enough frames have been returned from the
|
||||
* callback, drain the processors. */
|
||||
return output_processor->output(out_buffer, output_frames_needed);
|
||||
got = output_processor->output(out_buffer, output_frames_needed);
|
||||
|
||||
output_processor->drop_audio_if_needed();
|
||||
|
||||
return got;
|
||||
}
|
||||
|
||||
/* Resampler C API */
|
||||
|
|
|
@ -38,6 +38,17 @@ MOZ_END_STD_NAMESPACE
|
|||
#include <stdio.h>
|
||||
|
||||
/* This header file contains the internal C++ API of the resamplers, for testing. */
|
||||
namespace {
|
||||
// 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)
|
||||
{
|
||||
return sample_rate / 20;
|
||||
}
|
||||
}
|
||||
|
||||
int to_speex_quality(cubeb_resampler_quality q);
|
||||
|
||||
|
@ -75,7 +86,8 @@ public:
|
|||
passthrough_resampler(cubeb_stream * s,
|
||||
cubeb_data_callback cb,
|
||||
void * ptr,
|
||||
uint32_t input_channels);
|
||||
uint32_t input_channels,
|
||||
uint32_t sample_rate);
|
||||
|
||||
virtual long fill(void * input_buffer, long * input_frames_count,
|
||||
void * output_buffer, long output_frames);
|
||||
|
@ -85,6 +97,15 @@ public:
|
|||
return 0;
|
||||
}
|
||||
|
||||
void drop_audio_if_needed()
|
||||
{
|
||||
uint32_t to_keep = min_buffered_audio_frame(sample_rate);
|
||||
uint32_t available = samples_to_frames(internal_input_buffer.length());
|
||||
if (available > to_keep) {
|
||||
internal_input_buffer.pop(nullptr, frames_to_samples(available - to_keep));
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
cubeb_stream * const stream;
|
||||
const cubeb_data_callback data_callback;
|
||||
|
@ -92,6 +113,7 @@ private:
|
|||
/* This allows to buffer some input to account for the fact that we buffer
|
||||
* some inputs. */
|
||||
auto_array<T> internal_input_buffer;
|
||||
uint32_t sample_rate;
|
||||
};
|
||||
|
||||
/** Bidirectional resampler, can resample an input and an output stream, or just
|
||||
|
@ -164,6 +186,7 @@ public:
|
|||
int quality)
|
||||
: processor(channels)
|
||||
, resampling_ratio(static_cast<float>(source_rate) / target_rate)
|
||||
, source_rate(source_rate)
|
||||
, additional_latency(0)
|
||||
, leftover_samples(0)
|
||||
{
|
||||
|
@ -296,6 +319,16 @@ public:
|
|||
resampling_in_buffer.set_length(leftover_samples +
|
||||
frames_to_samples(written_frames));
|
||||
}
|
||||
|
||||
void drop_audio_if_needed()
|
||||
{
|
||||
// Keep at most 100ms buffered.
|
||||
uint32_t available = samples_to_frames(resampling_in_buffer.length());
|
||||
uint32_t to_keep = min_buffered_audio_frame(source_rate);
|
||||
if (available > to_keep) {
|
||||
resampling_in_buffer.pop(nullptr, frames_to_samples(available - to_keep));
|
||||
}
|
||||
}
|
||||
private:
|
||||
/** Wrapper for the speex resampling functions to have a typed
|
||||
* interface. */
|
||||
|
@ -332,6 +365,7 @@ private:
|
|||
SpeexResamplerState * speex_resampler;
|
||||
/** Source rate / target rate. */
|
||||
const float resampling_ratio;
|
||||
const uint32_t source_rate;
|
||||
/** Storage for the input frames, to be resampled. Also contains
|
||||
* any unresampled frames after resampling. */
|
||||
auto_array<T> resampling_in_buffer;
|
||||
|
@ -350,11 +384,13 @@ class delay_line : public processor {
|
|||
public:
|
||||
/** Constructor
|
||||
* @parameter frames the number of frames of delay.
|
||||
* @parameter channels the number of channels of this delay line. */
|
||||
delay_line(uint32_t frames, uint32_t channels)
|
||||
* @parameter channels the number of channels of this delay line.
|
||||
* @parameter sample_rate sample-rate of the audio going through this delay line */
|
||||
delay_line(uint32_t frames, uint32_t channels, uint32_t sample_rate)
|
||||
: processor(channels)
|
||||
, length(frames)
|
||||
, leftover_samples(0)
|
||||
, sample_rate(sample_rate)
|
||||
{
|
||||
/* Fill the delay line with some silent frames to add latency. */
|
||||
delay_input_buffer.push_silence(frames * channels);
|
||||
|
@ -444,6 +480,15 @@ public:
|
|||
{
|
||||
return length;
|
||||
}
|
||||
|
||||
void drop_audio_if_needed()
|
||||
{
|
||||
size_t available = samples_to_frames(delay_input_buffer.length());
|
||||
uint32_t to_keep = min_buffered_audio_frame(sample_rate);
|
||||
if (available > to_keep) {
|
||||
delay_input_buffer.pop(nullptr, frames_to_samples(available - to_keep));
|
||||
}
|
||||
}
|
||||
private:
|
||||
/** The length, in frames, of this delay line */
|
||||
uint32_t length;
|
||||
|
@ -455,6 +500,7 @@ private:
|
|||
/** The output buffer. This is only ever used if using the ::output with a
|
||||
* single argument. */
|
||||
auto_array<T> delay_output_buffer;
|
||||
uint32_t sample_rate;
|
||||
};
|
||||
|
||||
/** This sits behind the C API and is more typed. */
|
||||
|
@ -485,7 +531,8 @@ cubeb_resampler_create_internal(cubeb_stream * stream,
|
|||
(output_params && !input_params && (output_params->rate == target_rate))) {
|
||||
return new passthrough_resampler<T>(stream, callback,
|
||||
user_ptr,
|
||||
input_params ? input_params->channels : 0);
|
||||
input_params ? input_params->channels : 0,
|
||||
target_rate);
|
||||
}
|
||||
|
||||
/* Determine if we need to resampler one or both directions, and create the
|
||||
|
@ -517,13 +564,15 @@ cubeb_resampler_create_internal(cubeb_stream * stream,
|
|||
* other direction so that the streams are synchronized. */
|
||||
if (input_resampler && !output_resampler && input_params && output_params) {
|
||||
output_delay.reset(new delay_line<T>(input_resampler->latency(),
|
||||
output_params->channels));
|
||||
output_params->channels,
|
||||
output_params->rate));
|
||||
if (!output_delay) {
|
||||
return NULL;
|
||||
}
|
||||
} else if (output_resampler && !input_resampler && input_params && output_params) {
|
||||
input_delay.reset(new delay_line<T>(output_resampler->latency(),
|
||||
input_params->channels));
|
||||
input_params->channels,
|
||||
output_params->rate));
|
||||
if (!input_delay) {
|
||||
return NULL;
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче