Bug 1337328 - Uplift cubeb to revision 927877. r=achronop

MozReview-Commit-ID: 1VxCdWZQtR4

--HG--
extra : rebase_source : 5430bd5c97d4cbd3854e0089bac324b5a4b134f7
This commit is contained in:
Paul Adenot 2017-02-07 12:13:30 +01:00
Родитель cf1f2ae0a3
Коммит 42b97bbf05
14 изменённых файлов: 427 добавлений и 208 удалений

Просмотреть файл

@ -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 d96e35f02dbb9a093e5bfdff4f2948b7a6e9d3f9.
The git commit ID used was 927877c3204d6b8467b6dc782ca2aa740d240d41.

Просмотреть файл

@ -16,6 +16,9 @@
#include <unistd.h>
#endif
#include <cstdarg>
#include "cubeb/cubeb.h"
template<typename T, size_t N>
constexpr size_t
ARRAY_LENGTH(T(&)[N])
@ -96,4 +99,12 @@ int has_available_input_device(cubeb * ctx)
return 1;
}
void print_log(const char * msg, ...)
{
va_list args;
va_start(args, msg);
vprintf(msg, args);
va_end(args);
}
#endif /* TEST_COMMON */

Просмотреть файл

@ -17,6 +17,9 @@
#include <string.h>
#include "cubeb/cubeb.h"
#include "common.h"
#include <string>
using namespace std;
#define MAX_NUM_CHANNELS 32
@ -31,90 +34,83 @@ float get_frequency(int channel_index)
return 220.0f * (channel_index+1);
}
template<typename T> T ConvertSample(double input);
template<> float ConvertSample(double input) { return input; }
template<> short ConvertSample(double input) { return short(input * 32767.0f); }
/* store the phase of the generated waveform */
typedef struct {
struct synth_state {
synth_state(int num_channels_, float sample_rate_)
: num_channels(num_channels_),
sample_rate(sample_rate_)
{
for(int i=0;i < MAX_NUM_CHANNELS;++i)
phase[i] = 0.0f;
}
template<typename T>
void run(T* audiobuffer, long nframes)
{
for(int c=0;c < num_channels;++c) {
float freq = get_frequency(c);
float phase_inc = 2.0 * M_PI * freq / sample_rate;
for(long n=0;n < nframes;++n) {
audiobuffer[n*num_channels+c] = ConvertSample<T>(sin(phase[c]) * VOLUME);
phase[c] += phase_inc;
}
}
}
private:
int num_channels;
float phase[MAX_NUM_CHANNELS];
float sample_rate;
} synth_state;
};
synth_state* synth_create(int num_channels, float sample_rate)
{
synth_state* synth = (synth_state *) malloc(sizeof(synth_state));
if (!synth)
return NULL;
for(int i=0;i < MAX_NUM_CHANNELS;++i)
synth->phase[i] = 0.0f;
synth->num_channels = num_channels;
synth->sample_rate = sample_rate;
return synth;
}
void synth_destroy(synth_state* synth)
{
free(synth);
}
void synth_run_float(synth_state* synth, float* audiobuffer, long nframes)
{
for(int c=0;c < synth->num_channels;++c) {
float freq = get_frequency(c);
float phase_inc = 2.0 * M_PI * freq / synth->sample_rate;
for(long n=0;n < nframes;++n) {
audiobuffer[n*synth->num_channels+c] = sin(synth->phase[c]) * VOLUME;
synth->phase[c] += phase_inc;
}
}
}
long data_cb_float(cubeb_stream * /*stream*/, void * user, const void * /*inputbuffer*/, void * outputbuffer, long nframes)
template<typename T>
long data_cb(cubeb_stream * /*stream*/, void * user, const void * /*inputbuffer*/, void * outputbuffer, long nframes)
{
synth_state *synth = (synth_state *)user;
synth_run_float(synth, (float*)outputbuffer, nframes);
synth->run((T*)outputbuffer, nframes);
return nframes;
}
void synth_run_16bit(synth_state* synth, short* audiobuffer, long nframes)
struct CubebCleaner
{
for(int c=0;c < synth->num_channels;++c) {
float freq = get_frequency(c);
float phase_inc = 2.0 * M_PI * freq / synth->sample_rate;
for(long n=0;n < nframes;++n) {
audiobuffer[n*synth->num_channels+c] = sin(synth->phase[c]) * VOLUME * 32767.0f;
synth->phase[c] += phase_inc;
}
}
}
CubebCleaner(cubeb* ctx_) : ctx(ctx_) {}
~CubebCleaner() { cubeb_destroy(ctx); }
cubeb* ctx;
};
long data_cb_short(cubeb_stream * /*stream*/, void * user, const void * /*inputbuffer*/, void * outputbuffer, long nframes)
struct CubebStreamCleaner
{
synth_state *synth = (synth_state *)user;
synth_run_16bit(synth, (short*)outputbuffer, nframes);
return nframes;
}
CubebStreamCleaner(cubeb_stream* ctx_) : ctx(ctx_) {}
~CubebStreamCleaner() { cubeb_stream_destroy(ctx); }
cubeb_stream* ctx;
};
void state_cb_audio(cubeb_stream * /*stream*/, void * /*user*/, cubeb_state /*state*/)
{
}
/* Our android backends don't support float, only int16. */
int supports_float32(const char* backend_id)
int supports_float32(string backend_id)
{
return (strcmp(backend_id, "opensl") != 0 &&
strcmp(backend_id, "audiotrack") != 0);
return backend_id != "opensl"
&& backend_id != "audiotrack";
}
/* The WASAPI backend only supports float. */
int supports_int16(const char* backend_id)
int supports_int16(string backend_id)
{
return strcmp(backend_id, "wasapi") != 0;
return backend_id != "wasapi";
}
/* Some backends don't have code to deal with more than mono or stereo. */
int supports_channel_count(const char* backend_id, int nchannels)
int supports_channel_count(string backend_id, int nchannels)
{
return nchannels <= 2 ||
(strcmp(backend_id, "opensl") != 0 && strcmp(backend_id, "audiotrack") != 0);
(backend_id != "opensl" && backend_id != "audiotrack");
}
int run_test(int num_channels, layout_info layout, int sampling_rate, int is_float)
@ -122,23 +118,21 @@ int run_test(int num_channels, layout_info layout, int sampling_rate, int is_flo
int r = CUBEB_OK;
cubeb *ctx = NULL;
synth_state* synth = NULL;
cubeb_stream *stream = NULL;
const char * backend_id = NULL;
r = cubeb_init(&ctx, "Cubeb audio test: channels");
if (r != CUBEB_OK) {
fprintf(stderr, "Error initializing cubeb library\n");
goto cleanup;
return r;
}
CubebCleaner cleanup_cubeb_at_exit(ctx);
backend_id = cubeb_get_backend_id(ctx);
const char * backend_id = cubeb_get_backend_id(ctx);
if ((is_float && !supports_float32(backend_id)) ||
(!is_float && !supports_int16(backend_id)) ||
!supports_channel_count(backend_id, num_channels)) {
/* don't treat this as a test failure. */
goto cleanup;
return CUBEB_OK;
}
fprintf(stderr, "Testing %d channel(s), layout: %s, %d Hz, %s (%s)\n", num_channels, layout.name, sampling_rate, is_float ? "float" : "short", cubeb_get_backend_id(ctx));
@ -149,28 +143,22 @@ int run_test(int num_channels, layout_info layout, int sampling_rate, int is_flo
params.channels = num_channels;
params.layout = layout.layout;
synth = synth_create(params.channels, params.rate);
if (synth == NULL) {
fprintf(stderr, "Out of memory\n");
goto cleanup;
}
synth_state synth(params.channels, params.rate);
cubeb_stream *stream = NULL;
r = cubeb_stream_init(ctx, &stream, "test tone", NULL, NULL, NULL, &params,
4096, is_float ? data_cb_float : data_cb_short, state_cb_audio, synth);
4096, is_float ? &data_cb<float> : &data_cb<short>, state_cb_audio, &synth);
if (r != CUBEB_OK) {
fprintf(stderr, "Error initializing cubeb stream: %d\n", r);
goto cleanup;
return r;
}
CubebStreamCleaner cleanup_stream_at_exit(stream);
cubeb_stream_start(stream);
delay(200);
cubeb_stream_stop(stream);
cleanup:
cubeb_stream_destroy(stream);
cubeb_destroy(ctx);
synth_destroy(synth);
return r;
}
@ -179,21 +167,21 @@ int run_panning_volume_test(int is_float)
int r = CUBEB_OK;
cubeb *ctx = NULL;
synth_state* synth = NULL;
cubeb_stream *stream = NULL;
const char * backend_id = NULL;
r = cubeb_init(&ctx, "Cubeb audio test");
if (r != CUBEB_OK) {
fprintf(stderr, "Error initializing cubeb library\n");
goto cleanup;
return r;
}
backend_id = cubeb_get_backend_id(ctx);
CubebCleaner cleanup_cubeb_at_exit(ctx);
const char * backend_id = cubeb_get_backend_id(ctx);
if ((is_float && !supports_float32(backend_id)) ||
(!is_float && !supports_int16(backend_id))) {
/* don't treat this as a test failure. */
goto cleanup;
return CUBEB_OK;
}
cubeb_stream_params params;
@ -202,20 +190,19 @@ int run_panning_volume_test(int is_float)
params.channels = 2;
params.layout = CUBEB_LAYOUT_STEREO;
synth = synth_create(params.channels, params.rate);
if (synth == NULL) {
fprintf(stderr, "Out of memory\n");
goto cleanup;
}
synth_state synth(params.channels, params.rate);
cubeb_stream *stream = NULL;
r = cubeb_stream_init(ctx, &stream, "test tone", NULL, NULL, NULL, &params,
4096, is_float ? data_cb_float : data_cb_short,
state_cb_audio, synth);
4096, is_float ? &data_cb<float> : &data_cb<short>,
state_cb_audio, &synth);
if (r != CUBEB_OK) {
fprintf(stderr, "Error initializing cubeb stream: %d\n", r);
goto cleanup;
return r;
}
CubebStreamCleaner cleanup_stream_at_exit(stream);
fprintf(stderr, "Testing: volume\n");
for(int i=0;i <= 4; ++i)
{
@ -240,11 +227,6 @@ int run_panning_volume_test(int is_float)
delay(100);
}
cleanup:
cubeb_stream_destroy(stream);
cubeb_destroy(ctx);
synth_destroy(synth);
return r;
}
@ -275,14 +257,14 @@ TEST(cubeb, run_channel_rate_test)
48000,
};
for(unsigned int j = 0; j < ARRAY_LENGTH(channel_values); ++j) {
for(unsigned int i = 0; i < ARRAY_LENGTH(freq_values); ++i) {
ASSERT_TRUE(channel_values[j] < MAX_NUM_CHANNELS);
for(auto channels : channel_values) {
for(auto freq : freq_values) {
ASSERT_TRUE(channels < MAX_NUM_CHANNELS);
fprintf(stderr, "--------------------------\n");
for (unsigned int k = 0 ; k < ARRAY_LENGTH(layout_infos); ++k ) {
if (layout_infos[k].channels == channel_values[j]) {
ASSERT_EQ(run_test(channel_values[j], layout_infos[k], freq_values[i], 0), CUBEB_OK);
ASSERT_EQ(run_test(channel_values[j], layout_infos[k], freq_values[i], 1), CUBEB_OK);
for (auto layout : layout_infos) {
if (layout.channels == channels) {
ASSERT_EQ(run_test(channels, layout, freq, 0), CUBEB_OK);
ASSERT_EQ(run_test(channels, layout, freq, 1), CUBEB_OK);
}
}
}

Просмотреть файл

@ -18,30 +18,19 @@
#include "common.h"
#define SAMPLE_FREQUENCY 48000
#if (defined(_WIN32) || defined(__WIN32__))
#define STREAM_FORMAT CUBEB_SAMPLE_FLOAT32LE
#define SILENT_SAMPLE 0.0f
#else
#define STREAM_FORMAT CUBEB_SAMPLE_S16LE
#define SILENT_SAMPLE 0
#endif
struct user_state_duplex
{
bool seen_noise;
bool seen_audio;
};
long data_cb_duplex(cubeb_stream * stream, void * user, const void * inputbuffer, void * outputbuffer, long nframes)
{
user_state_duplex * u = reinterpret_cast<user_state_duplex*>(user);
#if (defined(_WIN32) || defined(__WIN32__))
float *ib = (float *)inputbuffer;
float *ob = (float *)outputbuffer;
#else
short *ib = (short *)inputbuffer;
short *ob = (short *)outputbuffer;
#endif
bool seen_noise = false;
bool seen_audio = true;
if (stream == NULL || inputbuffer == NULL || outputbuffer == NULL) {
return CUBEB_ERROR;
@ -51,14 +40,15 @@ long data_cb_duplex(cubeb_stream * stream, void * user, const void * inputbuffer
// checking if there is noise in the process.
long output_index = 0;
for (long i = 0; i < nframes; i++) {
if (ib[i] != SILENT_SAMPLE) {
seen_noise = true;
if (ib[i] <= -1.0 && ib[i] >= 1.0) {
seen_audio = false;
break;
}
ob[output_index] = ob[output_index + 1] = ib[i];
output_index += 2;
}
u->seen_noise |= seen_noise;
u->seen_audio |= seen_audio;
return nframes;
}
@ -136,5 +126,5 @@ TEST(cubeb, duplex)
cubeb_stream_destroy(stream);
cubeb_destroy(ctx);
ASSERT_TRUE(stream_state.seen_noise);
ASSERT_TRUE(stream_state.seen_audio);
}

Просмотреть файл

@ -17,38 +17,31 @@
#include "common.h"
#define SAMPLE_FREQUENCY 48000
#if (defined(_WIN32) || defined(__WIN32__))
#define STREAM_FORMAT CUBEB_SAMPLE_FLOAT32LE
#else
#define STREAM_FORMAT CUBEB_SAMPLE_S16LE
#endif
struct user_state_record
{
bool seen_noise;
bool seen_audio;
};
long data_cb_record(cubeb_stream * stream, void * user, const void * inputbuffer, void * outputbuffer, long nframes)
{
user_state_record * u = reinterpret_cast<user_state_record*>(user);
#if STREAM_FORMAT != CUBEB_SAMPLE_FLOAT32LE
short *b = (short *)inputbuffer;
#else
float *b = (float *)inputbuffer;
#endif
if (stream == NULL || inputbuffer == NULL || outputbuffer != NULL) {
return CUBEB_ERROR;
}
bool seen_noise = false;
bool seen_audio = true;
for (long i = 0; i < nframes; i++) {
if (b[i] != 0.0) {
seen_noise = true;
if (b[i] <= -1.0 && b[i] >= 1.0) {
seen_audio = false;
break;
}
}
u->seen_noise |= seen_noise;
u->seen_audio |= seen_audio;
return nframes;
}
@ -74,6 +67,9 @@ void state_cb_record(cubeb_stream * stream, void * /*user*/, cubeb_state state)
TEST(cubeb, record)
{
if (cubeb_set_log_callback(CUBEB_LOG_DISABLED, nullptr /*print_log*/) != CUBEB_OK) {
printf("Set log callback failed\n");
}
cubeb *ctx;
cubeb_stream *stream;
cubeb_stream_params params;
@ -111,5 +107,10 @@ TEST(cubeb, record)
cubeb_stream_destroy(stream);
cubeb_destroy(ctx);
ASSERT_TRUE(stream_state.seen_noise);
#ifdef __linux__
// user callback does not arrive in Linux, silence the error
printf("Check is disabled in Linux\n");
#else
ASSERT_TRUE(stream_state.seen_audio);
#endif
}

Просмотреть файл

@ -8,6 +8,7 @@
#define NOMINMAX
#endif // NOMINMAX
#include "gtest/gtest.h"
#include "common.h"
#include "cubeb_resampler_internal.h"
#include <stdio.h>
#include <algorithm>
@ -391,8 +392,11 @@ void test_resampler_duplex(uint32_t input_channels, uint32_t output_channels,
dump("input.raw", state.input.data(), state.input.length());
dump("output.raw", state.output.data(), state.output.length());
ASSERT_TRUE(array_fuzzy_equal(state.input, expected_resampled_input, epsilon<T>(input_rate/target_rate)));
ASSERT_TRUE(array_fuzzy_equal(state.output, expected_resampled_output, epsilon<T>(output_rate/target_rate)));
// This is disabled because the latency estimation in the resampler code is
// slightly off so we can generate expected vectors.
// See https://github.com/kinetiknz/cubeb/issues/93
// ASSERT_TRUE(array_fuzzy_equal(state.input, expected_resampled_input, epsilon<T>(input_rate/target_rate)));
// ASSERT_TRUE(array_fuzzy_equal(state.output, expected_resampled_output, epsilon<T>(output_rate/target_rate)));
cubeb_resampler_destroy(resampler);
}
@ -416,9 +420,6 @@ TEST(cubeb, resampler_one_way)
}
}
// This is disabled because the latency estimation in the resampler code is
// slightly off so we can generate expected vectors.
// See https://github.com/kinetiknz/cubeb/issues/93
TEST(cubeb, DISABLED_resampler_duplex)
{
for (uint32_t input_channels = 1; input_channels <= max_channels; input_channels++) {
@ -536,3 +537,214 @@ TEST(cubeb, resampler_drain)
cubeb_resampler_destroy(resampler);
}
// gtest does not support using ASSERT_EQ and friend in a function that returns
// a value.
void check_output(const void * input_buffer, void * output_buffer, long frame_count)
{
ASSERT_EQ(input_buffer, nullptr);
ASSERT_EQ(frame_count, 256);
ASSERT_TRUE(!!output_buffer);
}
long cb_passthrough_resampler_output(cubeb_stream * /*stm*/, void * /*user_ptr*/,
const void * input_buffer,
void * output_buffer, long frame_count)
{
check_output(input_buffer, output_buffer, frame_count);
return frame_count;
}
TEST(cubeb, resampler_passthrough_output_only)
{
// Test that the passthrough resampler works when there is only an output stream.
cubeb_stream_params output_params;
const size_t output_channels = 2;
output_params.channels = output_channels;
output_params.rate = 44100;
output_params.format = CUBEB_SAMPLE_FLOAT32NE;
int target_rate = output_params.rate;
cubeb_resampler * resampler =
cubeb_resampler_create((cubeb_stream*)nullptr, nullptr, &output_params,
target_rate, cb_passthrough_resampler_output, nullptr,
CUBEB_RESAMPLER_QUALITY_VOIP);
float output_buffer[output_channels * 256];
long got;
for (uint32_t i = 0; i < 30; i++) {
got = cubeb_resampler_fill(resampler, nullptr, nullptr, output_buffer, 256);
ASSERT_EQ(got, 256);
}
}
// gtest does not support using ASSERT_EQ and friend in a function that returns
// a value.
void check_input(const void * input_buffer, void * output_buffer, long frame_count)
{
ASSERT_EQ(output_buffer, nullptr);
ASSERT_EQ(frame_count, 256);
ASSERT_TRUE(!!input_buffer);
}
long cb_passthrough_resampler_input(cubeb_stream * /*stm*/, void * /*user_ptr*/,
const void * input_buffer,
void * output_buffer, long frame_count)
{
check_input(input_buffer, output_buffer, frame_count);
return frame_count;
}
TEST(cubeb, resampler_passthrough_input_only)
{
// Test that the passthrough resampler works when there is only an output stream.
cubeb_stream_params input_params;
const size_t input_channels = 2;
input_params.channels = input_channels;
input_params.rate = 44100;
input_params.format = CUBEB_SAMPLE_FLOAT32NE;
int target_rate = input_params.rate;
cubeb_resampler * resampler =
cubeb_resampler_create((cubeb_stream*)nullptr, &input_params, nullptr,
target_rate, cb_passthrough_resampler_input, nullptr,
CUBEB_RESAMPLER_QUALITY_VOIP);
float input_buffer[input_channels * 256];
long got;
for (uint32_t i = 0; i < 30; i++) {
long int frames = 256;
got = cubeb_resampler_fill(resampler, input_buffer, &frames, nullptr, 0);
ASSERT_EQ(got, 256);
}
}
template<typename T>
long seq(T* array, int stride, long start, long count)
{
for(int i = 0; i < count; i++) {
for (int j = 0; j < stride; j++) {
array[i + j] = static_cast<T>(start + i);
}
}
return start + count;
}
template<typename T>
void is_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_EQ(array[output_index + j], expected_start + i);
}
output_index += stride;
}
}
// 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)
{
ASSERT_EQ(frame_count, 256);
// Silence scan-build warning.
ASSERT_TRUE(!!output_buffer); assert(output_buffer);
ASSERT_TRUE(!!input_buffer); assert(input_buffer);
int output_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_index += 2;
}
}
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);
return frame_count;
}
TEST(cubeb, resampler_passthrough_duplex_callback_reordering)
{
// Test that when pre-buffering on resampler creation, we can survive an input
// callback being delayed.
cubeb_stream_params input_params;
cubeb_stream_params output_params;
const int input_channels = 1;
const int output_channels = 2;
input_params.channels = input_channels;
input_params.rate = 44100;
input_params.format = CUBEB_SAMPLE_FLOAT32NE;
output_params.channels = output_channels;
output_params.rate = input_params.rate;
output_params.format = CUBEB_SAMPLE_FLOAT32NE;
int target_rate = input_params.rate;
cubeb_resampler * resampler =
cubeb_resampler_create((cubeb_stream*)nullptr, &input_params, &output_params,
target_rate, cb_passthrough_resampler_duplex, nullptr,
CUBEB_RESAMPLER_QUALITY_VOIP);
const long BUF_BASE_SIZE = 256;
float input_buffer_prebuffer[input_channels * BUF_BASE_SIZE * 2];
float input_buffer_glitch[input_channels * BUF_BASE_SIZE * 2];
float input_buffer_normal[input_channels * BUF_BASE_SIZE];
float output_buffer[output_channels * BUF_BASE_SIZE];
long seq_idx = 0;
long output_seq_idx = 0;
long prebuffer_frames = ARRAY_LENGTH(input_buffer_prebuffer) / input_params.channels;
seq_idx = seq(input_buffer_prebuffer, input_channels, seq_idx,
prebuffer_frames);
long got = cubeb_resampler_fill(resampler, input_buffer_prebuffer, &prebuffer_frames,
output_buffer, BUF_BASE_SIZE);
output_seq_idx += BUF_BASE_SIZE;
ASSERT_EQ(prebuffer_frames, static_cast<long>(ARRAY_LENGTH(input_buffer_prebuffer) / input_params.channels));
ASSERT_EQ(got, BUF_BASE_SIZE);
for (uint32_t i = 0; i < 300; i++) {
long int frames = BUF_BASE_SIZE;
// Simulate that sometimes, we don't have the input callback on time
if (i != 0 && (i % 100) == 0) {
long zero = 0;
got = cubeb_resampler_fill(resampler, input_buffer_normal /* unused here */,
&zero, output_buffer, BUF_BASE_SIZE);
is_seq(output_buffer, 2, BUF_BASE_SIZE, output_seq_idx);
output_seq_idx += BUF_BASE_SIZE;
} else if (i != 0 && (i % 100) == 1) {
// if this is the case, the on the next iteration, we'll have twice the
// amount of input frames
seq_idx = seq(input_buffer_glitch, input_channels, seq_idx, BUF_BASE_SIZE * 2);
frames = 2 * BUF_BASE_SIZE;
got = cubeb_resampler_fill(resampler, input_buffer_glitch, &frames, output_buffer, BUF_BASE_SIZE);
is_seq(output_buffer, 2, BUF_BASE_SIZE, output_seq_idx);
output_seq_idx += BUF_BASE_SIZE;
} else {
// normal case
seq_idx = seq(input_buffer_normal, input_channels, seq_idx, BUF_BASE_SIZE);
long normal_input_frame_count = 256;
got = cubeb_resampler_fill(resampler, input_buffer_normal, &normal_input_frame_count, output_buffer, BUF_BASE_SIZE);
is_seq(output_buffer, 2, BUF_BASE_SIZE, output_seq_idx);
output_seq_idx += BUF_BASE_SIZE;
}
ASSERT_EQ(got, BUF_BASE_SIZE);
}
}

Просмотреть файл

@ -9,6 +9,7 @@
#include "cubeb/cubeb.h"
#include "cubeb_log.h"
#include "cubeb_assert.h"
#include <stdio.h>
#include <string.h>
@ -28,9 +29,6 @@
extern "C" {
#endif
/* Crash the caller. */
void cubeb_crash() CLANG_ANALYZER_NORETURN;
#if defined(__cplusplus)
}
#endif
@ -85,11 +83,4 @@ struct cubeb_ops {
void * user_ptr);
};
#define XASSERT(expr) do { \
if (!(expr)) { \
fprintf(stderr, "%s:%d - fatal error: %s\n", __FILE__, __LINE__, #expr); \
cubeb_crash(); \
} \
} while (0)
#endif /* CUBEB_INTERNAL_0eb56756_4e20_4404_a76d_42bf88cd15a5 */

Просмотреть файл

@ -108,12 +108,12 @@ int
cubeb_init(cubeb ** context, char const * context_name)
{
int (* init[])(cubeb **, char const *) = {
#if defined(USE_JACK)
jack_init,
#endif
#if defined(USE_PULSE)
pulse_init,
#endif
#if defined(USE_JACK)
jack_init,
#endif
#if defined(USE_ALSA)
alsa_init,
#endif
@ -570,9 +570,3 @@ int cubeb_set_log_callback(cubeb_log_level log_level,
return CUBEB_OK;
}
void
cubeb_crash()
{
abort();
*((volatile int *) NULL) = 0;
}

Просмотреть файл

@ -0,0 +1,17 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef CUBEB_ASSERT
#define CUBEB_ASSERT
#include <stdio.h>
#include <stdlib.h>
#include <mozilla/Assertions.h>
/* Forward fatal asserts to MOZ_ASSERT when built inside Gecko. */
#define XASSERT(expr) MOZ_ASSERT(expr)
#endif

Просмотреть файл

@ -1240,7 +1240,7 @@ pulse_get_state_from_sink_port(pa_sink_port_info * info)
return CUBEB_DEVICE_STATE_ENABLED;
}
return CUBEB_DEVICE_STATE_DISABLED;
return CUBEB_DEVICE_STATE_ENABLED;
}
static void
@ -1270,7 +1270,8 @@ pulse_sink_info_cb(pa_context * context, const pa_sink_info * info,
devinfo->type = CUBEB_DEVICE_TYPE_OUTPUT;
devinfo->state = pulse_get_state_from_sink_port(info->active_port);
devinfo->preferred = strcmp(info->name, list_data->default_sink_name) == 0;
devinfo->preferred = (strcmp(info->name, list_data->default_sink_name) == 0) ?
CUBEB_DEVICE_PREF_ALL : CUBEB_DEVICE_PREF_NONE;
devinfo->format = CUBEB_DEVICE_FMT_ALL;
devinfo->default_format = pulse_format_to_cubeb_format(info->sample_spec.format);
@ -1300,7 +1301,7 @@ pulse_get_state_from_source_port(pa_source_port_info * info)
return CUBEB_DEVICE_STATE_ENABLED;
}
return CUBEB_DEVICE_STATE_DISABLED;
return CUBEB_DEVICE_STATE_ENABLED;
}
static void
@ -1330,7 +1331,8 @@ pulse_source_info_cb(pa_context * context, const pa_source_info * info,
devinfo->type = CUBEB_DEVICE_TYPE_INPUT;
devinfo->state = pulse_get_state_from_source_port(info->active_port);
devinfo->preferred = strcmp(info->name, list_data->default_source_name) == 0;
devinfo->preferred = (strcmp(info->name, list_data->default_source_name) == 0) ?
CUBEB_DEVICE_PREF_ALL : CUBEB_DEVICE_PREF_NONE;
devinfo->format = CUBEB_DEVICE_FMT_ALL;
devinfo->default_format = pulse_format_to_cubeb_format(info->sample_spec.format);

Просмотреть файл

@ -35,29 +35,46 @@ to_speex_quality(cubeb_resampler_quality q)
}
}
long noop_resampler::fill(void * input_buffer, long * input_frames_count,
void * output_buffer, long output_frames)
template<typename T>
passthrough_resampler<T>::passthrough_resampler(cubeb_stream * s,
cubeb_data_callback cb,
void * ptr,
uint32_t input_channels)
: processor(input_channels)
, stream(s)
, data_callback(cb)
, user_ptr(ptr)
{
}
template<typename T>
long passthrough_resampler<T>::fill(void * input_buffer, long * input_frames_count,
void * output_buffer, long output_frames)
{
if (input_buffer) {
assert(input_frames_count);
}
assert((input_buffer && output_buffer &&
*input_frames_count >= output_frames) ||
(!input_buffer && (!input_frames_count || *input_frames_count == 0)) ||
(!output_buffer && output_frames == 0));
*input_frames_count + static_cast<int>(samples_to_frames(internal_input_buffer.length())) >= output_frames) ||
(output_buffer && !input_buffer && (!input_frames_count || *input_frames_count == 0)) ||
(input_buffer && !output_buffer && output_frames == 0));
if (output_buffer == nullptr) {
assert(input_buffer);
output_frames = *input_frames_count;
if (input_buffer) {
if (!output_buffer) {
output_frames = *input_frames_count;
}
internal_input_buffer.push(static_cast<T*>(input_buffer),
frames_to_samples(*input_frames_count));
}
if (input_buffer && *input_frames_count != output_frames) {
assert(*input_frames_count > output_frames);
*input_frames_count = output_frames;
long rv = data_callback(stream, user_ptr, internal_input_buffer.data(),
output_buffer, output_frames);
if (input_buffer) {
internal_input_buffer.pop(nullptr, frames_to_samples(output_frames));
}
return data_callback(stream, user_ptr,
input_buffer, output_buffer, output_frames);
return rv;
}
template<typename T, typename InputProcessor, typename OutputProcessor>

Просмотреть файл

@ -48,31 +48,6 @@ struct cubeb_resampler {
virtual ~cubeb_resampler() {}
};
class noop_resampler : public cubeb_resampler {
public:
noop_resampler(cubeb_stream * s,
cubeb_data_callback cb,
void * ptr)
: stream(s)
, data_callback(cb)
, user_ptr(ptr)
{
}
virtual long fill(void * input_buffer, long * input_frames_count,
void * output_buffer, long output_frames);
virtual long latency()
{
return 0;
}
private:
cubeb_stream * const stream;
const cubeb_data_callback data_callback;
void * const user_ptr;
};
/** Base class for processors. This is just used to share methods for now. */
class processor {
public:
@ -93,6 +68,32 @@ protected:
const uint32_t channels;
};
template<typename T>
class passthrough_resampler : public cubeb_resampler
, public processor {
public:
passthrough_resampler(cubeb_stream * s,
cubeb_data_callback cb,
void * ptr,
uint32_t input_channels);
virtual long fill(void * input_buffer, long * input_frames_count,
void * output_buffer, long output_frames);
virtual long latency()
{
return 0;
}
private:
cubeb_stream * const stream;
const cubeb_data_callback data_callback;
void * const user_ptr;
/* This allows to buffer some input to account for the fact that we buffer
* some inputs. */
auto_array<T> internal_input_buffer;
};
/** Bidirectional resampler, can resample an input and an output stream, or just
* an input stream or output stream. In this case a delay is inserted in the
* opposite direction to keep the streams synchronized. */
@ -480,7 +481,9 @@ cubeb_resampler_create_internal(cubeb_stream * stream,
(output_params && output_params->rate == target_rate)) ||
(input_params && !output_params && (input_params->rate == target_rate)) ||
(output_params && !input_params && (output_params->rate == target_rate))) {
return new noop_resampler(stream, callback, user_ptr);
return new passthrough_resampler<T>(stream, callback,
user_ptr,
input_params ? input_params->channels : 0);
}
/* Determine if we need to resampler one or both directions, and create the

Просмотреть файл

@ -245,7 +245,7 @@ sndio_stream_init(cubeb * context,
s->data_cb = data_callback;
s->state_cb = state_callback;
s->arg = user_ptr;
s->mtx = PTHREAD_MUTEX_INITIALIZER;
s->mtx = (pthread_mutex_t)PTHREAD_MUTEX_INITIALIZER;
s->rdpos = s->wrpos = 0;
if (output_stream_params->format == CUBEB_SAMPLE_FLOAT32LE) {
s->conv = 1;

Просмотреть файл

@ -747,15 +747,6 @@ refill_callback_duplex(cubeb_stream * stm)
return true;
}
// When WASAPI has not filled the input buffer yet, send silence.
double output_duration = double(output_frames) / stm->output_mix_params.rate;
double input_duration = double(stm->linear_input_buffer.length() / stm->input_stream_params.channels) / stm->input_mix_params.rate;
if (input_duration < output_duration) {
size_t padding = size_t(round((output_duration - input_duration) * stm->input_mix_params.rate));
LOG("padding silence: out=%f in=%f pad=%u", output_duration, input_duration, padding);
stm->linear_input_buffer.push_front_silence(padding * stm->input_stream_params.channels);
}
LOGV("Duplex callback: input frames: %Iu, output frames: %Iu",
stm->linear_input_buffer.length(), output_frames);
@ -1651,6 +1642,14 @@ int setup_wasapi_stream(cubeb_stream * stm)
stm->input_available_event,
stm->capture_client,
&stm->input_mix_params);
// We initializing an input stream, buffer ahead two buffers worth of silence.
// This delays the input side slightly, but allow to not glitch when no input
// is available when calling into the resampler to call the callback: the input
// refill event will be set shortly after to compensate for this lack of data.
stm->linear_input_buffer.push_silence(stm->input_buffer_frame_count *
stm->input_stream_params.channels * 2);
if (rv != CUBEB_OK) {
LOG("Failure to open the input side.");
return rv;