Bug 1357683 - Update cubeb from upstream to 6e52314. r=padenot

MozReview-Commit-ID: FKxbO0W50Xk
This commit is contained in:
Alex Chronopoulos 2017-04-20 12:01:37 +03:00
Родитель a7fa6eb9b7
Коммит 939147b2d1
22 изменённых файлов: 416 добавлений и 379 удалений

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

@ -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 04826edb13c23a2b53732d63b09b24e05ca83d87 (2017-03-31 09:05:07 -0700)
The git commit ID used was 6e52314f24bba463d6ca97951f7d9fcc677d76a4 (2017-04-18 17:28:50 +0200)

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

@ -14,6 +14,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <memory>
#include <string.h>
#include "cubeb/cubeb.h"
#include "common.h"
@ -75,20 +76,6 @@ long data_cb(cubeb_stream * /*stream*/, void * user, const void * /*inputbuffer*
return nframes;
}
struct CubebCleaner
{
CubebCleaner(cubeb* ctx_) : ctx(ctx_) {}
~CubebCleaner() { cubeb_destroy(ctx); }
cubeb* ctx;
};
struct CubebStreamCleaner
{
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*/)
{
}
@ -100,12 +87,6 @@ int supports_float32(string backend_id)
&& backend_id != "audiotrack";
}
/* The WASAPI backend only supports float. */
int supports_int16(string backend_id)
{
return backend_id != "wasapi";
}
/* Some backends don't have code to deal with more than mono or stereo. */
int supports_channel_count(string backend_id, int nchannels)
{
@ -124,12 +105,12 @@ int run_test(int num_channels, layout_info layout, int sampling_rate, int is_flo
fprintf(stderr, "Error initializing cubeb library\n");
return r;
}
CubebCleaner cleanup_cubeb_at_exit(ctx);
std::unique_ptr<cubeb, decltype(&cubeb_destroy)>
cleanup_cubeb_at_exit(ctx, cubeb_destroy);
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. */
return CUBEB_OK;
@ -153,7 +134,8 @@ int run_test(int num_channels, layout_info layout, int sampling_rate, int is_flo
return r;
}
CubebStreamCleaner cleanup_stream_at_exit(stream);
std::unique_ptr<cubeb_stream, decltype(&cubeb_stream_destroy)>
cleanup_stream_at_exit(stream, cubeb_stream_destroy);
cubeb_stream_start(stream);
delay(200);
@ -174,12 +156,12 @@ int run_panning_volume_test(int is_float)
return r;
}
CubebCleaner cleanup_cubeb_at_exit(ctx);
std::unique_ptr<cubeb, decltype(&cubeb_destroy)>
cleanup_cubeb_at_exit(ctx, cubeb_destroy);
const char * backend_id = cubeb_get_backend_id(ctx);
if ((is_float && !supports_float32(backend_id)) ||
(!is_float && !supports_int16(backend_id))) {
if ((is_float && !supports_float32(backend_id))) {
/* don't treat this as a test failure. */
return CUBEB_OK;
}
@ -201,7 +183,8 @@ int run_panning_volume_test(int is_float)
return r;
}
CubebStreamCleaner cleanup_stream_at_exit(stream);
std::unique_ptr<cubeb_stream, decltype(&cubeb_stream_destroy)>
cleanup_stream_at_exit(stream, cubeb_stream_destroy);
fprintf(stderr, "Testing: volume\n");
for(int i=0;i <= 4; ++i)

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

@ -11,6 +11,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <memory>
#include "cubeb/cubeb.h"
static void
@ -110,10 +111,10 @@ TEST(cubeb, enumerate_devices)
cubeb_device_collection * collection = NULL;
r = cubeb_init(&ctx, "Cubeb audio test", NULL);
if (r != CUBEB_OK) {
fprintf(stderr, "Error initializing cubeb library\n");
ASSERT_EQ(r, CUBEB_OK);
}
ASSERT_EQ(r, CUBEB_OK) << "Error initializing cubeb library";
std::unique_ptr<cubeb, decltype(&cubeb_destroy)>
cleanup_cubeb_at_exit(ctx, cubeb_destroy);
fprintf(stdout, "Enumerating input devices for backend %s\n",
cubeb_get_backend_id(ctx));
@ -123,12 +124,8 @@ TEST(cubeb, enumerate_devices)
fprintf(stderr, "Device enumeration not supported"
" for this backend, skipping this test.\n");
r = CUBEB_OK;
goto cleanup;
}
if (r != CUBEB_OK) {
fprintf(stderr, "Error enumerating devices %d\n", r);
goto cleanup;
}
ASSERT_EQ(r, CUBEB_OK) << "Error enumerating devices " << r;
fprintf(stdout, "Found %u input devices\n", collection->count);
print_device_collection(collection, stdout);
@ -138,17 +135,9 @@ TEST(cubeb, enumerate_devices)
cubeb_get_backend_id(ctx));
r = cubeb_enumerate_devices(ctx, CUBEB_DEVICE_TYPE_OUTPUT, &collection);
if (r != CUBEB_OK) {
fprintf(stderr, "Error enumerating devices %d\n", r);
goto cleanup;
}
ASSERT_EQ(r, CUBEB_OK) << "Error enumerating devices " << r;
fprintf(stdout, "Found %u output devices\n", collection->count);
print_device_collection(collection, stdout);
cubeb_device_collection_destroy(collection);
cleanup:
cubeb_destroy(ctx);
ASSERT_EQ(r, CUBEB_OK);
}

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

@ -14,6 +14,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <memory>
#include "cubeb/cubeb.h"
#include "common.h"
@ -60,13 +61,13 @@ void state_cb_duplex(cubeb_stream * stream, void * /*user*/, cubeb_state state)
switch (state) {
case CUBEB_STATE_STARTED:
printf("stream started\n"); break;
fprintf(stderr, "stream started\n"); break;
case CUBEB_STATE_STOPPED:
printf("stream stopped\n"); break;
fprintf(stderr, "stream stopped\n"); break;
case CUBEB_STATE_DRAINED:
printf("stream drained\n"); break;
fprintf(stderr, "stream drained\n"); break;
default:
printf("unknown stream state %d\n", state);
fprintf(stderr, "unknown stream state %d\n", state);
}
return;
@ -83,10 +84,10 @@ TEST(cubeb, duplex)
uint32_t latency_frames = 0;
r = cubeb_init(&ctx, "Cubeb duplex example", NULL);
if (r != CUBEB_OK) {
fprintf(stderr, "Error initializing cubeb library\n");
ASSERT_EQ(r, CUBEB_OK);
}
ASSERT_EQ(r, CUBEB_OK) << "Error initializing cubeb library";
std::unique_ptr<cubeb, decltype(&cubeb_destroy)>
cleanup_cubeb_at_exit(ctx, cubeb_destroy);
/* This test needs an available input device, skip it if this host does not
* have one. */
@ -105,26 +106,19 @@ TEST(cubeb, duplex)
output_params.layout = CUBEB_LAYOUT_STEREO;
r = cubeb_get_min_latency(ctx, output_params, &latency_frames);
if (r != CUBEB_OK) {
fprintf(stderr, "Could not get minimal latency\n");
ASSERT_EQ(r, CUBEB_OK);
}
ASSERT_EQ(r, CUBEB_OK) << "Could not get minimal latency";
r = cubeb_stream_init(ctx, &stream, "Cubeb duplex",
NULL, &input_params, NULL, &output_params,
latency_frames, data_cb_duplex, state_cb_duplex, &stream_state);
if (r != CUBEB_OK) {
fprintf(stderr, "Error initializing cubeb stream\n");
ASSERT_EQ(r, CUBEB_OK);
}
ASSERT_EQ(r, CUBEB_OK) << "Error initializing cubeb stream";
std::unique_ptr<cubeb_stream, decltype(&cubeb_stream_destroy)>
cleanup_stream_at_exit(stream, cubeb_stream_destroy);
cubeb_stream_start(stream);
delay(500);
cubeb_stream_stop(stream);
cubeb_stream_destroy(stream);
cubeb_destroy(ctx);
ASSERT_TRUE(stream_state.seen_audio);
}

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

@ -1,5 +1,6 @@
#include "gtest/gtest.h"
#include <stdlib.h>
#include <memory>
#include "cubeb/cubeb.h"
TEST(cubeb, latency)
@ -14,6 +15,9 @@ TEST(cubeb, latency)
r = cubeb_init(&ctx, "Cubeb audio test", NULL);
ASSERT_EQ(r, CUBEB_OK);
std::unique_ptr<cubeb, decltype(&cubeb_destroy)>
cleanup_cubeb_at_exit(ctx, cubeb_destroy);
r = cubeb_get_max_channel_count(ctx, &max_channels);
ASSERT_TRUE(r == CUBEB_OK || r == CUBEB_ERROR_NOT_SUPPORTED);
if (r == CUBEB_OK) {
@ -44,6 +48,4 @@ TEST(cubeb, latency)
if (r == CUBEB_OK) {
ASSERT_GT(latency_frames, 0u);
}
cubeb_destroy(ctx);
}

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

@ -153,7 +153,7 @@ downmix_test(float const * data, cubeb_channel_layout in_layout, cubeb_channel_l
out_layout >= CUBEB_LAYOUT_MONO && out_layout <= CUBEB_LAYOUT_2F2_LFE) {
auto & downmix_results = DOWNMIX_3F2_RESULTS[in_layout - CUBEB_LAYOUT_3F2][out_layout - CUBEB_LAYOUT_MONO];
fprintf(stderr, "[3f2] Expect: %lf, Get: %lf\n", downmix_results[index], out[index]);
ASSERT_EQ(out[index], downmix_results[index]);
ASSERT_EQ(downmix_results[index], out[index]);
continue;
}
@ -161,21 +161,21 @@ downmix_test(float const * data, cubeb_channel_layout in_layout, cubeb_channel_l
if (out_layout_mask & in_layout_mask) {
uint32_t mask = 1 << CHANNEL_INDEX_TO_ORDER[out_layout][index];
fprintf(stderr, "[map channels] Expect: %lf, Get: %lf\n", (mask & in_layout_mask) ? audio_inputs[out_layout].data[index] : 0, out[index]);
ASSERT_EQ(out[index], (mask & in_layout_mask) ? audio_inputs[out_layout].data[index] : 0);
ASSERT_EQ((mask & in_layout_mask) ? audio_inputs[out_layout].data[index] : 0, out[index]);
continue;
}
// downmix_fallback
fprintf(stderr, "[fallback] Expect: %lf, Get: %lf\n", audio_inputs[in_layout].data[index], out[index]);
ASSERT_EQ(out[index], audio_inputs[in_layout].data[index]);
ASSERT_EQ(audio_inputs[in_layout].data[index], out[index]);
}
}
TEST(cubeb, run_mixing_test)
TEST(cubeb, mixer)
{
for (unsigned int i = 0 ; i < ARRAY_LENGTH(audio_inputs) ; ++i) {
for (unsigned int j = 0 ; j < ARRAY_LENGTH(layout_infos) ; ++j) {
downmix_test(audio_inputs[i].data, audio_inputs[i].layout, layout_infos[j].layout);
for (auto audio_input : audio_inputs) {
for (auto audio_output : layout_infos) {
downmix_test(audio_input.data, audio_input.layout, audio_output.layout);
}
}
}

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

@ -12,16 +12,13 @@
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <memory>
#include <atomic>
#include "cubeb/cubeb.h"
#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
std::atomic<bool> load_callback{ false };
@ -64,6 +61,9 @@ TEST(cubeb, overload_callback)
r = cubeb_init(&ctx, "Cubeb callback overload", NULL);
ASSERT_EQ(r, CUBEB_OK);
std::unique_ptr<cubeb, decltype(&cubeb_destroy)>
cleanup_cubeb_at_exit(ctx, cubeb_destroy);
output_params.format = STREAM_FORMAT;
output_params.rate = 48000;
output_params.channels = 2;
@ -77,13 +77,13 @@ TEST(cubeb, overload_callback)
latency_frames, data_cb, state_cb, NULL);
ASSERT_EQ(r, CUBEB_OK);
std::unique_ptr<cubeb_stream, decltype(&cubeb_stream_destroy)>
cleanup_stream_at_exit(stream, cubeb_stream_destroy);
cubeb_stream_start(stream);
delay(500);
// This causes the callback to sleep for a large number of seconds.
load_callback = true;
delay(500);
cubeb_stream_stop(stream);
cubeb_stream_destroy(stream);
cubeb_destroy(ctx);
}

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

@ -13,6 +13,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <memory>
#include "cubeb/cubeb.h"
#include "common.h"
@ -53,13 +54,13 @@ void state_cb_record(cubeb_stream * stream, void * /*user*/, cubeb_state state)
switch (state) {
case CUBEB_STATE_STARTED:
printf("stream started\n"); break;
fprintf(stderr, "stream started\n"); break;
case CUBEB_STATE_STOPPED:
printf("stream stopped\n"); break;
fprintf(stderr, "stream stopped\n"); break;
case CUBEB_STATE_DRAINED:
printf("stream drained\n"); break;
fprintf(stderr, "stream drained\n"); break;
default:
printf("unknown stream state %d\n", state);
fprintf(stderr, "unknown stream state %d\n", state);
}
return;
@ -68,7 +69,7 @@ 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");
fprintf(stderr, "Set log callback failed\n");
}
cubeb *ctx;
cubeb_stream *stream;
@ -77,10 +78,10 @@ TEST(cubeb, record)
user_state_record stream_state = { false };
r = cubeb_init(&ctx, "Cubeb record example", NULL);
if (r != CUBEB_OK) {
fprintf(stderr, "Error initializing cubeb library\n");
ASSERT_EQ(r, CUBEB_OK);
}
ASSERT_EQ(r, CUBEB_OK) << "Error initializing cubeb library";
std::unique_ptr<cubeb, decltype(&cubeb_destroy)>
cleanup_cubeb_at_exit(ctx, cubeb_destroy);
/* This test needs an available input device, skip it if this host does not
* have one. */
@ -95,21 +96,18 @@ TEST(cubeb, record)
r = cubeb_stream_init(ctx, &stream, "Cubeb record (mono)", NULL, &params, NULL, nullptr,
4096, data_cb_record, state_cb_record, &stream_state);
if (r != CUBEB_OK) {
fprintf(stderr, "Error initializing cubeb stream\n");
ASSERT_EQ(r, CUBEB_OK);
}
ASSERT_EQ(r, CUBEB_OK) << "Error initializing cubeb stream";
std::unique_ptr<cubeb_stream, decltype(&cubeb_stream_destroy)>
cleanup_stream_at_exit(stream, cubeb_stream_destroy);
cubeb_stream_start(stream);
delay(500);
cubeb_stream_stop(stream);
cubeb_stream_destroy(stream);
cubeb_destroy(ctx);
#ifdef __linux__
// user callback does not arrive in Linux, silence the error
printf("Check is disabled in Linux\n");
fprintf(stderr, "Check is disabled in Linux\n");
#else
ASSERT_TRUE(stream_state.seen_audio);
#endif

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

@ -410,7 +410,7 @@ TEST(cubeb, resampler_one_way)
for (uint32_t source_rate = 0; source_rate < array_size(sample_rates); source_rate++) {
for (uint32_t dest_rate = 0; dest_rate < array_size(sample_rates); dest_rate++) {
for (uint32_t chunk_duration = min_chunks; chunk_duration < max_chunks; chunk_duration+=chunk_increment) {
printf("one_way: channels: %d, source_rate: %d, dest_rate: %d, chunk_duration: %d\n",
fprintf(stderr, "one_way: channels: %d, source_rate: %d, dest_rate: %d, chunk_duration: %d\n",
channels, sample_rates[source_rate], sample_rates[dest_rate], chunk_duration);
test_resampler_one_way<float>(channels, sample_rates[source_rate],
sample_rates[dest_rate], chunk_duration);
@ -428,7 +428,7 @@ TEST(cubeb, DISABLED_resampler_duplex)
for (uint32_t source_rate_output = 0; source_rate_output < array_size(sample_rates); source_rate_output++) {
for (uint32_t dest_rate = 0; dest_rate < array_size(sample_rates); dest_rate++) {
for (uint32_t chunk_duration = min_chunks; chunk_duration < max_chunks; chunk_duration+=chunk_increment) {
printf("input channels:%d output_channels:%d input_rate:%d "
fprintf(stderr, "input channels:%d output_channels:%d input_rate:%d "
"output_rate:%d target_rate:%d chunk_ms:%d\n",
input_channels, output_channels,
sample_rates[source_rate_input],
@ -453,7 +453,7 @@ TEST(cubeb, resampler_delay_line)
for (uint32_t channel = 1; channel <= 2; channel++) {
for (uint32_t delay_frames = 4; delay_frames <= 40; delay_frames+=chunk_increment) {
for (uint32_t chunk_size = 10; chunk_size <= 30; chunk_size++) {
printf("channel: %d, delay_frames: %d, chunk_size: %d\n",
fprintf(stderr, "channel: %d, delay_frames: %d, chunk_size: %d\n",
channel, delay_frames, chunk_size);
test_delay_lines(delay_frames, channel, chunk_size);
}

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

@ -19,16 +19,12 @@
#define STREAM_LATENCY 100 * STREAM_RATE / 1000
#define STREAM_CHANNELS 1
#define STREAM_LAYOUT CUBEB_LAYOUT_MONO
#if (defined(_WIN32) || defined(__WIN32__))
#define STREAM_FORMAT CUBEB_SAMPLE_FLOAT32LE
#else
#define STREAM_FORMAT CUBEB_SAMPLE_S16LE
#endif
int is_windows_7()
{
#ifdef __MINGW32__
printf("Warning: this test was built with MinGW.\n"
fprintf(stderr, "Warning: this test was built with MinGW.\n"
"MinGW does not contain necessary version checking infrastructure. Claiming to be Windows 7, even if we're not.\n");
return 1;
#endif
@ -61,11 +57,7 @@ test_data_callback(cubeb_stream * stm, void * user_ptr, const void * /*inputbuff
{
EXPECT_TRUE(stm && user_ptr == &dummy && outputbuffer && nframes > 0);
assert(outputbuffer);
#if (defined(_WIN32) || defined(__WIN32__))
memset(outputbuffer, 0, nframes * sizeof(float));
#else
memset(outputbuffer, 0, nframes * sizeof(short));
#endif
total_frames_written += nframes;
if (delay_callback) {
@ -545,11 +537,7 @@ test_drain_data_callback(cubeb_stream * stm, void * user_ptr, const void * /*inp
}
/* once drain has started, callback must never be called again */
EXPECT_TRUE(do_drain != 2);
#if (defined(_WIN32) || defined(__WIN32__))
memset(outputbuffer, 0, nframes * sizeof(float));
#else
memset(outputbuffer, 0, nframes * sizeof(short));
#endif
total_frames_written += nframes;
return nframes;
}

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

@ -13,16 +13,13 @@
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <memory>
#include <limits.h>
#include "cubeb/cubeb.h"
#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
/* store the phase of the generated waveform */
struct cb_user_data {
@ -32,11 +29,7 @@ struct cb_user_data {
long data_cb_tone(cubeb_stream *stream, void *user, const void* /*inputbuffer*/, void *outputbuffer, long nframes)
{
struct cb_user_data *u = (struct cb_user_data *)user;
#if (defined(_WIN32) || defined(__WIN32__))
float *b = (float *)outputbuffer;
#else
short *b = (short *)outputbuffer;
#endif
float t1, t2;
int i;
@ -48,21 +41,12 @@ long data_cb_tone(cubeb_stream *stream, void *user, const void* /*inputbuffer*/,
/* North American dial tone */
t1 = sin(2*M_PI*(i + u->position)*350/SAMPLE_FREQUENCY);
t2 = sin(2*M_PI*(i + u->position)*440/SAMPLE_FREQUENCY);
#if (defined(_WIN32) || defined(__WIN32__))
b[i] = 0.5 * t1;
b[i] += 0.5 * t2;
#else
b[i] = (SHRT_MAX / 2) * t1;
b[i] += (SHRT_MAX / 2) * t2;
#endif
/* European dial tone */
/*
t1 = sin(2*M_PI*(i + u->position)*425/SAMPLE_FREQUENCY);
#if (defined(_WIN32) || defined(__WIN32__))
b[i] = t1;
#else
b[i] = SHRT_MAX * t1;
#endif
*/
}
/* remember our phase to avoid clicking on buffer transitions */
@ -81,13 +65,13 @@ void state_cb_tone(cubeb_stream *stream, void *user, cubeb_state state)
switch (state) {
case CUBEB_STATE_STARTED:
printf("stream started\n"); break;
fprintf(stderr, "stream started\n"); break;
case CUBEB_STATE_STOPPED:
printf("stream stopped\n"); break;
fprintf(stderr, "stream stopped\n"); break;
case CUBEB_STATE_DRAINED:
printf("stream drained\n"); break;
fprintf(stderr, "stream drained\n"); break;
default:
printf("unknown stream state %d\n", state);
fprintf(stderr, "unknown stream state %d\n", state);
}
return;
@ -98,42 +82,34 @@ TEST(cubeb, tone)
cubeb *ctx;
cubeb_stream *stream;
cubeb_stream_params params;
struct cb_user_data *user_data;
int r;
r = cubeb_init(&ctx, "Cubeb tone example", NULL);
if (r != CUBEB_OK) {
fprintf(stderr, "Error initializing cubeb library\n");
ASSERT_EQ(r, CUBEB_OK);
}
ASSERT_EQ(r, CUBEB_OK) << "Error initializing cubeb library";
std::unique_ptr<cubeb, decltype(&cubeb_destroy)>
cleanup_cubeb_at_exit(ctx, cubeb_destroy);
params.format = STREAM_FORMAT;
params.rate = SAMPLE_FREQUENCY;
params.channels = 1;
params.layout = CUBEB_LAYOUT_MONO;
user_data = (struct cb_user_data *) malloc(sizeof(*user_data));
if (user_data == NULL) {
fprintf(stderr, "Error allocating user data\n");
FAIL();
}
std::unique_ptr<cb_user_data> user_data(new cb_user_data());
ASSERT_TRUE(!!user_data) << "Error allocating user data";
user_data->position = 0;
r = cubeb_stream_init(ctx, &stream, "Cubeb tone (mono)", NULL, NULL, NULL, &params,
4096, data_cb_tone, state_cb_tone, user_data);
if (r != CUBEB_OK) {
fprintf(stderr, "Error initializing cubeb stream\n");
ASSERT_EQ(r, CUBEB_OK);
}
4096, data_cb_tone, state_cb_tone, user_data.get());
ASSERT_EQ(r, CUBEB_OK) << "Error initializing cubeb stream";
std::unique_ptr<cubeb_stream, decltype(&cubeb_stream_destroy)>
cleanup_stream_at_exit(stream, cubeb_stream_destroy);
cubeb_stream_start(stream);
delay(500);
cubeb_stream_stop(stream);
cubeb_stream_destroy(stream);
cubeb_destroy(ctx);
ASSERT_TRUE(user_data->position);
free(user_data);
}

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

@ -318,15 +318,24 @@ cubeb_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_n
return r;
}
return context->ops->stream_init(context, stream, stream_name,
input_device,
input_stream_params,
output_device,
output_stream_params,
latency,
data_callback,
state_callback,
user_ptr);
r = context->ops->stream_init(context, stream, stream_name,
input_device,
input_stream_params,
output_device,
output_stream_params,
latency,
data_callback,
state_callback,
user_ptr);
if (r == CUBEB_ERROR_INVALID_FORMAT) {
LOG("Invalid format, %p %p %d %d",
output_stream_params, input_stream_params,
output_stream_params && output_stream_params->format,
input_stream_params && input_stream_params->format);
}
return r;
}
void
@ -553,7 +562,7 @@ int cubeb_enumerate_devices(cubeb * context,
rv = context->ops->enumerate_devices(context, devtype, collection);
if (g_log_callback) {
if (g_cubeb_log_callback) {
for (uint32_t i = 0; i < (*collection)->count; i++) {
log_device((*collection)->device[i]);
}
@ -617,12 +626,12 @@ int cubeb_set_log_callback(cubeb_log_level log_level,
return CUBEB_ERROR_INVALID_PARAMETER;
}
if (g_log_callback && log_callback) {
if (g_cubeb_log_callback && log_callback) {
return CUBEB_ERROR_NOT_SUPPORTED;
}
g_log_callback = log_callback;
g_log_level = log_level;
g_cubeb_log_callback = log_callback;
g_cubeb_log_level = log_level;
// Logging a message here allows to initialize the asynchronous logger from a
// thread that is not the audio rendering thread, and especially to not

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

@ -33,6 +33,7 @@
#include <algorithm>
#include <atomic>
#include <vector>
#include <sys/time.h>
#if MAC_OS_X_VERSION_MIN_REQUIRED < 101000
typedef UInt32 AudioFormatFlags;
@ -82,63 +83,6 @@ struct cubeb {
cubeb_channel_layout layout;
};
struct auto_array_wrapper {
virtual void push(void * elements, size_t length) = 0;
virtual size_t length() = 0;
virtual void push_silence(size_t length) = 0;
virtual bool pop(size_t length) = 0;
virtual void * data() = 0;
virtual void clear() = 0;
virtual ~auto_array_wrapper() {}
};
template <typename T>
struct auto_array_wrapper_impl : public auto_array_wrapper {
explicit auto_array_wrapper_impl(uint32_t size)
: ar(size)
{}
void push(void * elements, size_t length) override {
auto_lock l(lock);
ar.push(static_cast<T *>(elements), length);
}
size_t length() override {
auto_lock l(lock);
return ar.length();
}
void push_silence(size_t length) override {
auto_lock l(lock);
ar.push_silence(length);
}
bool pop(size_t length) override {
auto_lock l(lock);
return ar.pop(nullptr, length);
}
// XXX: Taking the lock here is pointless.
void * data() override {
auto_lock l(lock);
return ar.data();
}
void clear() override {
auto_lock l(lock);
ar.clear();
}
~auto_array_wrapper_impl() {
auto_lock l(lock);
ar.clear();
}
private:
owned_critical_section lock;
auto_array<T> ar;
};
static std::unique_ptr<AudioChannelLayout, decltype(&free)>
make_sized_audio_channel_layout(size_t sz)
{
@ -148,36 +92,36 @@ make_sized_audio_channel_layout(size_t sz)
return std::unique_ptr<AudioChannelLayout, decltype(&free)>(acl, free);
}
struct mixing_wrapper {
virtual void downmix(void * output_buffer, long output_frames,
cubeb_channel_layout output_layout,
cubeb_channel_layout mixing_layout) = 0;
virtual ~mixing_wrapper() {};
struct mixer_proxy {
virtual void downmix(void * buffer, long frames,
cubeb_channel_layout input_layout,
cubeb_channel_layout output_layout) = 0;
virtual ~mixer_proxy() {};
};
template <typename T>
struct mixing_impl : public mixing_wrapper {
struct mixer_impl : public mixer_proxy {
typedef void (*downmix_func)(T * const, long, T *,
unsigned int, unsigned int,
cubeb_channel_layout, cubeb_channel_layout);
mixing_impl(downmix_func dmfunc) {
mixer_impl(downmix_func dmfunc) {
downmix_wrapper = dmfunc;
}
~mixing_impl() {}
~mixer_impl() {}
void downmix(void * output_buffer, long output_frames,
cubeb_channel_layout output_layout,
cubeb_channel_layout mixing_layout) override {
void downmix(void * buffer, long frames,
cubeb_channel_layout input_layout,
cubeb_channel_layout output_layout) override {
uint32_t input_channels = CUBEB_CHANNEL_LAYOUT_MAPS[input_layout].channels;
uint32_t output_channels = CUBEB_CHANNEL_LAYOUT_MAPS[output_layout].channels;
uint32_t using_channels = CUBEB_CHANNEL_LAYOUT_MAPS[mixing_layout].channels;
T* out = static_cast<T *>(output_buffer);
T * out = static_cast<T*>(buffer);
// By using same buffer for downmixing input and output, we allow downmixing
// from 5.0/1 to 1.0/1, 2.0/1/2, 3.0/1, 4.0/1. Do nothing on other cases.
downmix_wrapper(out, output_frames, out, output_channels, using_channels,
output_layout, mixing_layout);
downmix_wrapper(out, frames, out, input_channels, output_channels,
input_layout, output_layout);
}
downmix_func downmix_wrapper;
@ -230,6 +174,7 @@ struct cubeb_stream {
/* Hold the input samples in every
* input callback iteration */
std::unique_ptr<auto_array_wrapper> input_linear_buffer;
owned_critical_section input_linear_buffer_lock;
// After the resampling some input data remains stored inside
// the resampler. This number is used in order to calculate
// the number of extra silence frames in input.
@ -253,8 +198,8 @@ struct cubeb_stream {
std::atomic<bool> buffer_size_change_state{ false };
AudioDeviceID aggregate_device_id = 0; // the aggregate device id
AudioObjectID plugin_id = 0; // used to create aggregate device
/* Mixing interface */
std::unique_ptr<mixing_wrapper> mixing;
/* Mixer interface */
std::unique_ptr<mixer_proxy> mixer;
};
bool has_input(cubeb_stream * stm)
@ -386,8 +331,11 @@ audiounit_render_input(cubeb_stream * stm,
}
/* Copy input data in linear buffer. */
stm->input_linear_buffer->push(input_buffer_list.mBuffers[0].mData,
input_frames * stm->input_desc.mChannelsPerFrame);
{
auto_lock l(stm->input_linear_buffer_lock);
stm->input_linear_buffer->push(input_buffer_list.mBuffers[0].mData,
input_frames * stm->input_desc.mChannelsPerFrame);
}
/* Advance input frame counter. */
assert(input_frames > 0);
@ -435,19 +383,19 @@ audiounit_input_callback(void * user_ptr,
/* Input only. Call the user callback through resampler.
Resampler will deliver input buffer in the correct rate. */
assert(input_frames <= stm->input_linear_buffer->length() / stm->input_desc.mChannelsPerFrame);
long total_input_frames = stm->input_linear_buffer->length() / stm->input_desc.mChannelsPerFrame;
long outframes = cubeb_resampler_fill(stm->resampler.get(),
stm->input_linear_buffer->data(),
&total_input_frames,
NULL,
0);
// Reset input buffer
stm->input_linear_buffer->clear();
{
auto_lock l(stm->input_linear_buffer_lock);
assert(input_frames <= stm->input_linear_buffer->length() / stm->input_desc.mChannelsPerFrame);
long total_input_frames = stm->input_linear_buffer->length() / stm->input_desc.mChannelsPerFrame;
long outframes = cubeb_resampler_fill(stm->resampler.get(),
stm->input_linear_buffer->data(),
&total_input_frames,
NULL,
0);
assert(outframes >= 0);
if (outframes < 0 || (UInt32) outframes != input_frames) {
stm->shutdown = true;
return noErr;
// Reset input buffer
stm->input_linear_buffer->clear();
}
return noErr;
@ -480,7 +428,7 @@ audiounit_mix_output_buffer(cubeb_stream * stm,
// the channels that audio device can provide, so we need to downmix the
// audio data by ourselves to keep all the information.
cubeb_stream_params mixed_params = {
cubeb_stream_params dest_params = {
stm->output_stream_params.format,
stm->output_stream_params.rate,
CUBEB_CHANNEL_LAYOUT_MAPS[stm->context->layout].channels,
@ -488,9 +436,9 @@ audiounit_mix_output_buffer(cubeb_stream * stm,
};
// We only handle downmixing for now.
if (cubeb_should_downmix(&stm->output_stream_params, &mixed_params)) {
stm->mixing->downmix(output_buffer, output_frames,
stm->output_stream_params.layout, mixed_params.layout);
if (cubeb_should_downmix(&stm->output_stream_params, &dest_params)) {
stm->mixer->downmix(output_buffer, output_frames,
stm->output_stream_params.layout, dest_params.layout);
}
}
@ -542,7 +490,10 @@ audiounit_output_callback(void * user_ptr,
if (stm->input_unit != NULL) {
if (is_extra_input_needed(stm)) {
uint32_t min_input_frames = minimum_resampling_input_frames(stm);
stm->input_linear_buffer->push_silence(min_input_frames * stm->input_desc.mChannelsPerFrame);
{
auto_lock l(stm->input_linear_buffer_lock);
stm->input_linear_buffer->push_silence(min_input_frames * stm->input_desc.mChannelsPerFrame);
}
stm->available_input_frames += min_input_frames;
ALOG("(%p) %s pushed %u frames of input silence.", stm, stm->frames_read == 0 ? "Input hasn't started," :
@ -568,6 +519,7 @@ audiounit_output_callback(void * user_ptr,
stm->available_input_frames -= input_frames;
assert(stm->available_input_frames.load() >= 0);
// Pop from the buffer the frames pushed to the resampler.
auto_lock l(stm->input_linear_buffer_lock);
stm->input_linear_buffer->pop(input_frames_before_fill * stm->input_desc.mChannelsPerFrame);
}
@ -1308,12 +1260,12 @@ audio_stream_desc_init(AudioStreamBasicDescription * ss,
}
void
audiounit_init_mixed_buffer(cubeb_stream * stm)
audiounit_init_mixer(cubeb_stream * stm)
{
if (stm->output_desc.mFormatFlags & kAudioFormatFlagIsFloat) {
stm->mixing.reset(new mixing_impl<float>(&cubeb_downmix_float));
stm->mixer.reset(new mixer_impl<float>(&cubeb_downmix_float));
} else { // stm->output_desc.mFormatFlags & kAudioFormatFlagIsSignedInteger
stm->mixing.reset(new mixing_impl<short>(&cubeb_downmix_short));
stm->mixer.reset(new mixer_impl<short>(&cubeb_downmix_short));
}
}
@ -1336,45 +1288,45 @@ audiounit_set_channel_layout(AudioUnit unit,
switch (stream_params->layout) {
case CUBEB_LAYOUT_DUAL_MONO:
case CUBEB_LAYOUT_STEREO:
layout.get()->mChannelLayoutTag = kAudioChannelLayoutTag_Stereo;
layout->mChannelLayoutTag = kAudioChannelLayoutTag_Stereo;
break;
case CUBEB_LAYOUT_MONO:
layout.get()->mChannelLayoutTag = kAudioChannelLayoutTag_Mono;
layout->mChannelLayoutTag = kAudioChannelLayoutTag_Mono;
break;
case CUBEB_LAYOUT_3F:
layout.get()->mChannelLayoutTag = kAudioChannelLayoutTag_ITU_3_0;
layout->mChannelLayoutTag = kAudioChannelLayoutTag_ITU_3_0;
break;
case CUBEB_LAYOUT_2F1:
layout.get()->mChannelLayoutTag = kAudioChannelLayoutTag_ITU_2_1;
layout->mChannelLayoutTag = kAudioChannelLayoutTag_ITU_2_1;
break;
case CUBEB_LAYOUT_3F1:
layout.get()->mChannelLayoutTag = kAudioChannelLayoutTag_ITU_3_1;
layout->mChannelLayoutTag = kAudioChannelLayoutTag_ITU_3_1;
break;
case CUBEB_LAYOUT_2F2:
layout.get()->mChannelLayoutTag = kAudioChannelLayoutTag_ITU_2_2;
layout->mChannelLayoutTag = kAudioChannelLayoutTag_ITU_2_2;
break;
case CUBEB_LAYOUT_3F2:
layout.get()->mChannelLayoutTag = kAudioChannelLayoutTag_ITU_3_2;
layout->mChannelLayoutTag = kAudioChannelLayoutTag_ITU_3_2;
break;
case CUBEB_LAYOUT_3F2_LFE:
layout.get()->mChannelLayoutTag = kAudioChannelLayoutTag_AudioUnit_5_1;
layout->mChannelLayoutTag = kAudioChannelLayoutTag_AudioUnit_5_1;
break;
default:
layout.get()->mChannelLayoutTag = kAudioChannelLayoutTag_Unknown;
layout->mChannelLayoutTag = kAudioChannelLayoutTag_Unknown;
break;
}
// For those layouts that can't be matched to coreaudio's predefined layout,
// we use customized layout.
if (layout.get()->mChannelLayoutTag == kAudioChannelLayoutTag_Unknown) {
if (layout->mChannelLayoutTag == kAudioChannelLayoutTag_Unknown) {
size = offsetof(AudioChannelLayout, mChannelDescriptions[stream_params->channels]);
layout = make_sized_audio_channel_layout(size);
layout.get()->mChannelLayoutTag = kAudioChannelLayoutTag_UseChannelDescriptions;
layout.get()->mNumberChannelDescriptions = stream_params->channels;
layout->mChannelLayoutTag = kAudioChannelLayoutTag_UseChannelDescriptions;
layout->mNumberChannelDescriptions = stream_params->channels;
for (UInt32 i = 0 ; i < stream_params->channels ; ++i) {
layout.get()->mChannelDescriptions[i].mChannelLabel =
layout->mChannelDescriptions[i].mChannelLabel =
cubeb_channel_to_channel_label(CHANNEL_INDEX_TO_ORDER[stream_params->layout][i]);
layout.get()->mChannelDescriptions[i].mChannelFlags = kAudioChannelFlags_AllOff;
layout->mChannelDescriptions[i].mChannelFlags = kAudioChannelFlags_AllOff;
}
}
@ -1496,12 +1448,16 @@ audiounit_create_blank_aggregate_device(AudioObjectID * plugin_id, AudioDeviceID
CFMutableDictionaryRef aggregate_device_dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
CFStringRef aggregate_device_name = CFSTR("CubebAggregateDevice");
struct timeval timestamp;
gettimeofday(&timestamp, NULL);
long long int time_id = timestamp.tv_sec * 1000000LL + timestamp.tv_usec;
CFStringRef aggregate_device_name = CFStringCreateWithFormat(NULL, NULL, CFSTR("CubebAggregateDevice_%llx"), time_id);
CFDictionaryAddValue(aggregate_device_dict, CFSTR(kAudioAggregateDeviceNameKey), aggregate_device_name);
CFRelease(aggregate_device_name);
CFStringRef aggregate_device_UID = CFSTR("org.mozilla.CubebAggregateDevice");
CFStringRef aggregate_device_UID = CFStringCreateWithFormat(NULL, NULL, CFSTR("org.mozilla.CubebAggregateDevice_%llx"), time_id);
CFDictionaryAddValue(aggregate_device_dict, CFSTR(kAudioAggregateDeviceUIDKey), aggregate_device_UID);
CFRelease(aggregate_device_UID);
int private_key = 1;
CFNumberRef aggregate_device_private_key = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &private_key);
@ -1541,6 +1497,8 @@ audiounit_set_aggregate_sub_device_list(AudioDeviceID aggregate_device_id,
AudioDeviceID input_device_id,
AudioDeviceID output_device_id)
{
LOG("Add devices input %u and output %u into aggregate device %u",
input_device_id, output_device_id, aggregate_device_id);
const std::vector<AudioDeviceID> input_sub_devices = audiounit_get_sub_devices(input_device_id);
const std::vector<AudioDeviceID> output_sub_devices = audiounit_get_sub_devices(output_device_id);
@ -2292,7 +2250,7 @@ audiounit_configure_output(cubeb_stream * stm)
}
audiounit_layout_init(stm, OUTPUT);
audiounit_init_mixed_buffer(stm);
audiounit_init_mixer(stm);
LOG("(%p) Output audiounit init successfully.", stm);
return CUBEB_OK;

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

@ -15,8 +15,8 @@
#include <time.h>
#endif
cubeb_log_level g_log_level;
cubeb_log_callback g_log_callback;
cubeb_log_level g_cubeb_log_level;
cubeb_log_callback g_cubeb_log_callback;
/** The maximum size of a log message, after having been formatted. */
const size_t CUBEB_LOG_MESSAGE_MAX_SIZE = 256;
@ -115,7 +115,7 @@ private:
void cubeb_async_log(char const * fmt, ...)
{
if (!g_log_callback) {
if (!g_cubeb_log_callback) {
return;
}
// This is going to copy a 256 bytes array around, which is fine.

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

@ -20,8 +20,8 @@ extern "C" {
#define PRINTF_FORMAT(fmt, args)
#endif
extern cubeb_log_level g_log_level;
extern cubeb_log_callback g_log_callback PRINTF_FORMAT(1, 2);
extern cubeb_log_level g_cubeb_log_level;
extern cubeb_log_callback g_cubeb_log_callback PRINTF_FORMAT(1, 2);
void cubeb_async_log(const char * fmt, ...);
#ifdef __cplusplus
@ -32,8 +32,8 @@ void cubeb_async_log(const char * fmt, ...);
#define LOG(msg, ...) LOG_INTERNAL(CUBEB_LOG_NORMAL, msg, ##__VA_ARGS__)
#define LOG_INTERNAL(level, fmt, ...) do { \
if (g_log_callback && level <= g_log_level) { \
g_log_callback("%s:%d: " fmt "\n", __FILE__, __LINE__, ##__VA_ARGS__); \
if (g_cubeb_log_callback && level <= g_cubeb_log_level) { \
g_cubeb_log_callback("%s:%d: " fmt "\n", __FILE__, __LINE__, ##__VA_ARGS__); \
} \
} while(0)

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

@ -446,6 +446,12 @@ cubeb_should_downmix(cubeb_stream_params const * stream, cubeb_stream_params con
mixer->layout == CUBEB_LAYOUT_3F1_LFE));
}
bool
cubeb_should_mix(cubeb_stream_params const * stream, cubeb_stream_params const * mixer)
{
return cubeb_should_upmix(stream, mixer) || cubeb_should_downmix(stream, mixer);
}
void
cubeb_downmix_float(float * const in, long inframes, float * out,
unsigned int in_channels, unsigned int out_channels,
@ -462,6 +468,14 @@ cubeb_downmix_short(short * const in, long inframes, short * out,
cubeb_downmix(in, inframes, out, in_channels, out_channels, in_layout, out_layout);
}
void
cubeb_upmix_short(short * const in, long inframes, short * out,
unsigned int in_channels, unsigned int out_channels)
{
cubeb_upmix(in, inframes, out, in_channels, out_channels);
}
void
cubeb_upmix_float(float * const in, long inframes, float * out,
unsigned int in_channels, unsigned int out_channels)

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

@ -63,6 +63,8 @@ bool cubeb_should_upmix(cubeb_stream_params const * stream, cubeb_stream_params
bool cubeb_should_downmix(cubeb_stream_params const * stream, cubeb_stream_params const * mixer);
bool cubeb_should_mix(cubeb_stream_params const * stream, cubeb_stream_params const * mixer);
void cubeb_downmix_float(float * const in, long inframes, float * out,
unsigned int in_channels, unsigned int out_channels,
cubeb_channel_layout in_layout, cubeb_channel_layout out_layout);
@ -74,6 +76,11 @@ void cubeb_downmix_short(short * const in, long inframes, short * out,
void cubeb_upmix_float(float * const in, long inframes, float * out,
unsigned int in_channels, unsigned int out_channels);
void cubeb_upmix_short(short * const in, long inframes, short * out,
unsigned int in_channels, unsigned int out_channels);
#if defined(__cplusplus)
}
#endif

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

@ -614,16 +614,11 @@ convert_stream_type_to_sl_stream(cubeb_stream_type stream_type)
static void opensl_destroy(cubeb * ctx);
#if defined(__ANDROID__)
// The bionic header file on B2G contains the required
// declarations on all releases.
#ifndef MOZ_WIDGET_GONK
#if (__ANDROID_API__ >= ANDROID_VERSION_LOLLIPOP)
typedef int (system_property_get)(const char*, char*);
static int
__system_property_get(const char* name, char* value)
wrap_system_property_get(const char* name, char* value)
{
void* libc = dlopen("libc.so", RTLD_LAZY);
if (!libc) {
@ -640,7 +635,6 @@ __system_property_get(const char* name, char* value)
return ret;
}
#endif
#endif
static int
get_android_version(void)
@ -649,7 +643,11 @@ get_android_version(void)
memset(version_string, 0, PROP_VALUE_MAX);
#if (__ANDROID_API__ >= ANDROID_VERSION_LOLLIPOP)
int len = wrap_system_property_get("ro.build.version.sdk", version_string);
#else
int len = __system_property_get("ro.build.version.sdk", version_string);
#endif
if (len <= 0) {
LOG("Failed to get Android version!\n");
return len;

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

@ -877,7 +877,7 @@ pulse_stream_init(cubeb * context,
return CUBEB_ERROR;
}
if (g_log_level) {
if (g_cubeb_log_level) {
if (output_stream_params){
const pa_buffer_attr * output_att;
output_att = WRAP(pa_stream_get_buffer_attr)(stm->output_stream);
@ -1408,7 +1408,7 @@ pulse_subscribe_callback(pa_context * ctx,
case PA_SUBSCRIPTION_EVENT_SOURCE:
case PA_SUBSCRIPTION_EVENT_SINK:
if (g_log_level) {
if (g_cubeb_log_level) {
if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE &&
(t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
LOG("Removing sink index %d", index);

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

@ -217,6 +217,8 @@ sndio_stream_init(cubeb * context,
wpar.le = SIO_LE_NATIVE;
break;
default:
sio_close(s->hdl);
free(s);
DPR("sndio_stream_init() unsupported format\n");
return CUBEB_ERROR_INVALID_FORMAT;
}

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

@ -124,6 +124,11 @@ public:
return data_;
}
T * end() const
{
return data_ + length_;
}
const T& at(size_t index) const
{
assert(index < length_ && "out of range");
@ -259,6 +264,71 @@ private:
size_t length_;
};
struct auto_array_wrapper {
virtual void push(void * elements, size_t length) = 0;
virtual size_t length() = 0;
virtual void push_silence(size_t length) = 0;
virtual bool pop(size_t length) = 0;
virtual void * data() = 0;
virtual void * end() = 0;
virtual void clear() = 0;
virtual bool reserve(size_t capacity) = 0;
virtual void set_length(size_t length) = 0;
virtual ~auto_array_wrapper() {}
};
template <typename T>
struct auto_array_wrapper_impl : public auto_array_wrapper {
auto_array_wrapper_impl() {}
explicit auto_array_wrapper_impl(uint32_t size)
: ar(size)
{}
void push(void * elements, size_t length) override {
ar.push(static_cast<T *>(elements), length);
}
size_t length() override {
return ar.length();
}
void push_silence(size_t length) override {
ar.push_silence(length);
}
bool pop(size_t length) override {
return ar.pop(nullptr, length);
}
void * data() override {
return ar.data();
}
void * end() override {
return ar.end();
}
void clear() override {
ar.clear();
}
bool reserve(size_t capacity) override {
return ar.reserve(capacity);
}
void set_length(size_t length) override {
ar.set_length(length);
}
~auto_array_wrapper_impl() {
ar.clear();
}
private:
auto_array<T> ar;
};
using auto_lock = std::lock_guard<owned_critical_section>;
#endif /* CUBEB_UTILS */

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

@ -160,6 +160,45 @@ private:
HRESULT result;
};
struct mixing_wrapper {
virtual void mix(void * const input_buffer, long input_frames, void * output_buffer,
cubeb_stream_params const * stream_params,
cubeb_stream_params const * mixer_params) = 0;
virtual ~mixing_wrapper() {};
};
template <typename T>
struct mixing_impl : public mixing_wrapper {
typedef void (*downmix_func)(T * const, long, T *,
unsigned int, unsigned int,
cubeb_channel_layout, cubeb_channel_layout);
typedef void (*upmix_func)(T * const, long, T *,
unsigned int, unsigned int);
mixing_impl(downmix_func dmfunc, upmix_func umfunc) {
downmix_wrapper = dmfunc;
upmix_wrapper = umfunc;
}
~mixing_impl() {}
void mix(void * const input_buffer, long input_frames, void * output_buffer,
cubeb_stream_params const * stream_params,
cubeb_stream_params const * mixer_params) override {
T * const in = static_cast<T *>(input_buffer);
T * out = static_cast<T *>(output_buffer);
if (cubeb_should_upmix(stream_params, mixer_params)) {
upmix_wrapper(in, input_frames, out, stream_params->channels, mixer_params->channels);
} else if (cubeb_should_downmix(stream_params, mixer_params)) {
downmix_wrapper(in, input_frames, out, stream_params->channels, mixer_params->channels, stream_params->layout, mixer_params->layout);
}
}
downmix_func downmix_wrapper;
upmix_func upmix_wrapper;
};
extern cubeb_ops const wasapi_ops;
int wasapi_stream_stop(cubeb_stream * stm);
@ -264,11 +303,18 @@ struct cubeb_stream {
uint32_t output_buffer_frame_count = 0;
/* Resampler instance. Resampling will only happen if necessary. */
std::unique_ptr<cubeb_resampler, decltype(&cubeb_resampler_destroy)> resampler = { nullptr, cubeb_resampler_destroy };
/* Mixing interface */
std::unique_ptr<mixing_wrapper> mixing;
/* A buffer for up/down mixing multi-channel audio. */
std::vector<float> mix_buffer;
std::vector<BYTE> mix_buffer;
/* WASAPI input works in "packets". We re-linearize the audio packets
* into this buffer before handing it to the resampler. */
auto_array<float> linear_input_buffer;
std::unique_ptr<auto_array_wrapper> linear_input_buffer;
/* Bytes per sample. This multiplied by the number of channels is the number
* of bytes per frame. */
size_t bytes_per_sample = 0;
/* WAVEFORMATEXTENSIBLE sub-format: either PCM or float. */
GUID waveformatextensible_sub_format = GUID_NULL;
/* Stream volume. Set via stream_set_volume and used to reset volume on
device changes. */
float volume = 1.0;
@ -507,23 +553,21 @@ frames_to_bytes_before_mix(cubeb_stream * stm, size_t frames)
{
// This is called only when we has a output client.
XASSERT(has_output(stm));
size_t stream_frame_size = stm->output_stream_params.channels * sizeof(float);
return stream_frame_size * frames;
return stm->output_stream_params.channels * stm->bytes_per_sample * frames;
}
/* This function handles the processing of the input and output audio,
* converting it to rate and channel layout specified at initialization.
* It then calls the data callback, via the resampler. */
long
refill(cubeb_stream * stm, float * input_buffer, long input_frames_count,
float * output_buffer, long output_frames_needed)
refill(cubeb_stream * stm, void * input_buffer, long input_frames_count,
void * output_buffer, long output_frames_needed)
{
/* If we need to upmix after resampling, resample into the mix buffer to
avoid a copy. */
float * dest = nullptr;
void * dest = nullptr;
if (has_output(stm)) {
if (cubeb_should_upmix(&stm->output_stream_params, &stm->output_mix_params) ||
cubeb_should_downmix(&stm->output_stream_params, &stm->output_mix_params)) {
if (cubeb_should_mix(&stm->output_stream_params, &stm->output_mix_params)) {
dest = stm->mix_buffer.data();
} else {
dest = output_buffer;
@ -553,15 +597,8 @@ refill(cubeb_stream * stm, float * input_buffer, long input_frames_count,
It is alright to have produced less frames if we are draining, though. */
XASSERT(out_frames == output_frames_needed || stm->draining || !has_output(stm));
if (has_output(stm)) {
if (cubeb_should_upmix(&stm->output_stream_params, &stm->output_mix_params)) {
cubeb_upmix_float(dest, out_frames, output_buffer,
stm->output_stream_params.channels, stm->output_mix_params.channels);
} else if (cubeb_should_downmix(&stm->output_stream_params, &stm->output_mix_params)) {
cubeb_downmix_float(dest, out_frames, output_buffer,
stm->output_stream_params.channels, stm->output_mix_params.channels,
stm->output_stream_params.layout, stm->output_mix_params.layout);
}
if (has_output(stm) && cubeb_should_mix(&stm->output_stream_params, &stm->output_mix_params)) {
stm->mixing->mix(dest, out_frames, output_buffer, &stm->output_stream_params, &stm->output_mix_params);
}
return out_frames;
@ -617,30 +654,19 @@ bool get_input_buffer(cubeb_stream * stm)
XASSERT(packet_size == next);
if (flags & AUDCLNT_BUFFERFLAGS_SILENT) {
LOG("insert silence: ps=%u", packet_size);
stm->linear_input_buffer.push_silence(packet_size * stm->input_stream_params.channels);
stm->linear_input_buffer->push_silence(packet_size * stm->input_stream_params.channels);
} else {
if (cubeb_should_upmix(&stm->input_mix_params, &stm->input_stream_params)) {
bool ok = stm->linear_input_buffer.reserve(stm->linear_input_buffer.length() +
if (cubeb_should_mix(&stm->input_mix_params, &stm->input_stream_params)) {
bool ok = stm->linear_input_buffer->reserve(stm->linear_input_buffer->length() +
packet_size * stm->input_stream_params.channels);
XASSERT(ok);
cubeb_upmix_float(reinterpret_cast<float*>(input_packet), packet_size,
stm->linear_input_buffer.data() + stm->linear_input_buffer.length(),
stm->input_mix_params.channels,
stm->input_stream_params.channels);
stm->linear_input_buffer.set_length(stm->linear_input_buffer.length() + packet_size * stm->input_stream_params.channels);
} else if (cubeb_should_downmix(&stm->input_mix_params, &stm->input_stream_params)) {
bool ok = stm->linear_input_buffer.reserve(stm->linear_input_buffer.length() +
packet_size * stm->input_stream_params.channels);
XASSERT(ok);
cubeb_downmix_float(reinterpret_cast<float*>(input_packet), packet_size,
stm->linear_input_buffer.data() + stm->linear_input_buffer.length(),
stm->input_mix_params.channels,
stm->input_stream_params.channels,
stm->input_mix_params.layout,
stm->input_stream_params.layout);
stm->linear_input_buffer.set_length(stm->linear_input_buffer.length() + packet_size * stm->input_stream_params.channels);
stm->mixing->mix(input_packet, packet_size,
stm->linear_input_buffer->end(),
&stm->input_mix_params,
&stm->input_stream_params);
stm->linear_input_buffer->set_length(stm->linear_input_buffer->length() + packet_size * stm->input_stream_params.channels);
} else {
stm->linear_input_buffer.push(reinterpret_cast<float*>(input_packet),
stm->linear_input_buffer->push(input_packet,
packet_size * stm->input_stream_params.channels);
}
}
@ -652,7 +678,7 @@ bool get_input_buffer(cubeb_stream * stm)
offset += packet_size;
}
XASSERT(stm->linear_input_buffer.length() >= total_available_input &&
XASSERT(stm->linear_input_buffer->length() >= total_available_input &&
offset == total_available_input);
return true;
@ -660,7 +686,7 @@ bool get_input_buffer(cubeb_stream * stm)
/* Get an output buffer from the render_client. It has to be released before
* exiting the callback. */
bool get_output_buffer(cubeb_stream * stm, float *& buffer, size_t & frame_count)
bool get_output_buffer(cubeb_stream * stm, void *& buffer, size_t & frame_count)
{
UINT32 padding_out;
HRESULT hr;
@ -693,7 +719,7 @@ bool get_output_buffer(cubeb_stream * stm, float *& buffer, size_t & frame_count
return false;
}
buffer = reinterpret_cast<float*>(output_buffer);
buffer = output_buffer;
return true;
}
@ -705,7 +731,7 @@ bool
refill_callback_duplex(cubeb_stream * stm)
{
HRESULT hr;
float * output_buffer = nullptr;
void * output_buffer = nullptr;
size_t output_frames = 0;
size_t input_frames;
bool rv;
@ -717,7 +743,7 @@ refill_callback_duplex(cubeb_stream * stm)
return rv;
}
input_frames = stm->linear_input_buffer.length() / stm->input_stream_params.channels;
input_frames = stm->linear_input_buffer->length() / stm->input_stream_params.channels;
if (!input_frames) {
return true;
}
@ -736,15 +762,15 @@ refill_callback_duplex(cubeb_stream * stm)
ALOGV("Duplex callback: input frames: %Iu, output frames: %Iu",
stm->linear_input_buffer.length(), output_frames);
stm->linear_input_buffer->length(), output_frames);
refill(stm,
stm->linear_input_buffer.data(),
stm->linear_input_buffer.length(),
stm->linear_input_buffer->data(),
stm->linear_input_buffer->length(),
output_buffer,
output_frames);
stm->linear_input_buffer.clear();
stm->linear_input_buffer->clear();
hr = stm->render_client->ReleaseBuffer(output_frames, 0);
if (FAILED(hr)) {
@ -767,21 +793,21 @@ refill_callback_input(cubeb_stream * stm)
}
// This can happen at the very beginning of the stream.
if (!stm->linear_input_buffer.length()) {
if (!stm->linear_input_buffer->length()) {
return true;
}
ALOGV("Input callback: input frames: %Iu", stm->linear_input_buffer.length());
ALOGV("Input callback: input frames: %Iu", stm->linear_input_buffer->length());
long read = refill(stm,
stm->linear_input_buffer.data(),
stm->linear_input_buffer.length(),
stm->linear_input_buffer->data(),
stm->linear_input_buffer->length(),
nullptr,
0);
XASSERT(read >= 0);
stm->linear_input_buffer.clear();
stm->linear_input_buffer->clear();
return !stm->draining;
}
@ -791,7 +817,7 @@ refill_callback_output(cubeb_stream * stm)
{
bool rv;
HRESULT hr;
float * output_buffer = nullptr;
void * output_buffer = nullptr;
size_t output_frames = 0;
XASSERT(!has_input(stm) && has_output(stm));
@ -1271,7 +1297,7 @@ wasapi_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * laten
return CUBEB_ERROR;
}
if (params.format != CUBEB_SAMPLE_FLOAT32NE) {
if (params.format != CUBEB_SAMPLE_FLOAT32NE && params.format != CUBEB_SAMPLE_S16NE) {
return CUBEB_ERROR_INVALID_FORMAT;
}
@ -1388,6 +1414,17 @@ wasapi_get_preferred_channel_layout(cubeb * context, cubeb_channel_layout * layo
void wasapi_stream_destroy(cubeb_stream * stm);
static void
waveformatex_update_derived_properties(WAVEFORMATEX * format)
{
format->nBlockAlign = format->wBitsPerSample * format->nChannels / 8;
format->nAvgBytesPerSec = format->nSamplesPerSec * format->nBlockAlign;
if (format->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
WAVEFORMATEXTENSIBLE * format_pcm = reinterpret_cast<WAVEFORMATEXTENSIBLE *>(format);
format_pcm->Samples.wValidBitsPerSample = format->wBitsPerSample;
}
}
/* Based on the mix format and the stream format, try to find a way to play
what the user requested. */
static void
@ -1411,13 +1448,8 @@ handle_channel_layout(cubeb_stream * stm, com_heap_ptr<WAVEFORMATEX> & mix_form
/* Get the channel mask by the channel layout.
If the layout is not supported, we will get a closest settings below. */
format_pcm->dwChannelMask = channel_layout_to_mask(stream_params->layout);
mix_format->nChannels = stream_params->channels;
mix_format->nBlockAlign = mix_format->wBitsPerSample * mix_format->nChannels / 8;
mix_format->nAvgBytesPerSec = mix_format->nSamplesPerSec * mix_format->nBlockAlign;
format_pcm->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
mix_format->wBitsPerSample = 32;
format_pcm->Samples.wValidBitsPerSample = mix_format->wBitsPerSample;
waveformatex_update_derived_properties(mix_format.get());
/* Check if wasapi will accept our channel layout request. */
WAVEFORMATEX * closest;
@ -1425,12 +1457,14 @@ handle_channel_layout(cubeb_stream * stm, com_heap_ptr<WAVEFORMATEX> & mix_form
mix_format.get(),
&closest);
if (hr == S_FALSE) {
/* Not supported, but WASAPI gives us a suggestion. Use it, and handle the
eventual upmix/downmix ourselves */
/* Channel layout not supported, but WASAPI gives us a suggestion. Use it,
and handle the eventual upmix/downmix ourselves. Ignore the subformat of
the suggestion, since it seems to always be IEEE_FLOAT. */
LOG("Using WASAPI suggested format: channels: %d", closest->nChannels);
WAVEFORMATEXTENSIBLE * closest_pcm = reinterpret_cast<WAVEFORMATEXTENSIBLE *>(closest);
XASSERT(closest_pcm->SubFormat == format_pcm->SubFormat);
mix_format.reset(closest);
format_pcm->dwChannelMask = closest_pcm->dwChannelMask;
mix_format->nChannels = closest->nChannels;
waveformatex_update_derived_properties(mix_format.get());
} else if (hr == AUDCLNT_E_UNSUPPORTED_FORMAT) {
/* Not supported, no suggestion. This should not happen, but it does in the
field with some sound cards. We restore the mix format, and let the rest
@ -1513,6 +1547,11 @@ int setup_wasapi_stream_one_side(cubeb_stream * stm,
}
com_heap_ptr<WAVEFORMATEX> mix_format(tmp);
WAVEFORMATEXTENSIBLE * format_pcm = reinterpret_cast<WAVEFORMATEXTENSIBLE *>(mix_format.get());
mix_format->wBitsPerSample = stm->bytes_per_sample * 8;
format_pcm->SubFormat = stm->waveformatextensible_sub_format;
waveformatex_update_derived_properties(mix_format.get());
/* Set channel layout only when there're more than two channels. Otherwise,
* use the default setting retrieved from the stream format of the audio
* engine's internal processing by GetMixFormat. */
@ -1525,12 +1564,9 @@ int setup_wasapi_stream_one_side(cubeb_stream * stm,
handle_channel_layout(stm, mix_format, stream_params);
}
/* Shared mode WASAPI always supports float32 sample format, so this
* is safe. */
mix_params->format = CUBEB_SAMPLE_FLOAT32NE;
mix_params->format = stream_params->format;
mix_params->rate = mix_format->nSamplesPerSec;
mix_params->channels = mix_format->nChannels;
WAVEFORMATEXTENSIBLE * format_pcm = reinterpret_cast<WAVEFORMATEXTENSIBLE *>(mix_format.get());
mix_params->layout = mask_to_channel_layout(format_pcm->dwChannelMask);
if (mix_params->layout == CUBEB_LAYOUT_UNDEFINED) {
LOG("Output using undefined layout!\n");
@ -1566,9 +1602,7 @@ int setup_wasapi_stream_one_side(cubeb_stream * stm,
return CUBEB_ERROR;
}
// Input is up/down mixed when depacketized in get_input_buffer.
if (has_output(stm) &&
(cubeb_should_upmix(stream_params, mix_params) ||
cubeb_should_downmix(stream_params, mix_params))) {
if (has_output(stm) && cubeb_should_mix(stream_params, mix_params)) {
stm->mix_buffer.resize(frames_to_bytes_before_mix(stm, *buffer_frame_count));
}
@ -1628,7 +1662,7 @@ int setup_wasapi_stream(cubeb_stream * stm)
#else
const int silent_buffer_count = 4;
#endif
stm->linear_input_buffer.push_silence(stm->input_buffer_frame_count *
stm->linear_input_buffer->push_silence(stm->input_buffer_frame_count *
stm->input_stream_params.channels *
silent_buffer_count);
@ -1746,12 +1780,8 @@ wasapi_stream_init(cubeb * context, cubeb_stream ** stream,
XASSERT(context && stream && (input_stream_params || output_stream_params));
if ((output_stream_params && output_stream_params->format != CUBEB_SAMPLE_FLOAT32NE) ||
(input_stream_params && input_stream_params->format != CUBEB_SAMPLE_FLOAT32NE)) {
LOG("Invalid format, %p %p %d %d",
output_stream_params, input_stream_params,
output_stream_params && output_stream_params->format,
input_stream_params && input_stream_params->format);
if (output_stream_params && input_stream_params &&
output_stream_params->format != input_stream_params->format) {
return CUBEB_ERROR_INVALID_FORMAT;
}
@ -1774,6 +1804,23 @@ wasapi_stream_init(cubeb * context, cubeb_stream ** stream,
XASSERT(stm->output_stream_params.channels == CUBEB_CHANNEL_LAYOUT_MAPS[stm->output_stream_params.layout].channels);
}
switch (output_stream_params ? output_stream_params->format : input_stream_params->format) {
case CUBEB_SAMPLE_S16NE:
stm->bytes_per_sample = sizeof(short);
stm->waveformatextensible_sub_format = KSDATAFORMAT_SUBTYPE_PCM;
stm->mixing.reset(new mixing_impl<short>(cubeb_downmix_short, cubeb_upmix_short));
stm->linear_input_buffer.reset(new auto_array_wrapper_impl<short>);
break;
case CUBEB_SAMPLE_FLOAT32NE:
stm->bytes_per_sample = sizeof(float);
stm->waveformatextensible_sub_format = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
stm->mixing.reset(new mixing_impl<float>(cubeb_downmix_float, cubeb_upmix_float));
stm->linear_input_buffer.reset(new auto_array_wrapper_impl<float>);
break;
default:
return CUBEB_ERROR_INVALID_FORMAT;
}
stm->latency = latency_frames;
stm->reconfigure_event = CreateEvent(NULL, 0, 0, NULL);
@ -1839,6 +1886,8 @@ void close_wasapi_stream(cubeb_stream * stm)
stm->resampler.reset();
stm->mix_buffer.clear();
stm->mixing.reset();
stm->linear_input_buffer.reset();
}
void wasapi_stream_destroy(cubeb_stream * stm)
@ -2207,7 +2256,7 @@ wasapi_create_device(IMMDeviceEnumerator * enumerator, IMMDevice * dev)
break;
};
ret->format = CUBEB_DEVICE_FMT_F32NE; /* cubeb only supports 32bit float at the moment */
ret->format = static_cast<cubeb_device_fmt>(CUBEB_DEVICE_FMT_F32NE | CUBEB_DEVICE_FMT_S16NE);
ret->default_format = CUBEB_DEVICE_FMT_F32NE;
prop_variant fmtvar;
hr = propstore->GetValue(PKEY_AudioEngine_DeviceFormat, &fmtvar);
@ -2266,7 +2315,7 @@ wasapi_enumerate_devices(cubeb * context, cubeb_device_type type,
if (type == CUBEB_DEVICE_TYPE_OUTPUT) flow = eRender;
else if (type == CUBEB_DEVICE_TYPE_INPUT) flow = eCapture;
else if (type & (CUBEB_DEVICE_TYPE_INPUT | CUBEB_DEVICE_TYPE_INPUT)) flow = eAll;
else if (type & (CUBEB_DEVICE_TYPE_INPUT | CUBEB_DEVICE_TYPE_OUTPUT)) flow = eAll;
else return CUBEB_ERROR;
hr = enumerator->EnumAudioEndpoints(flow, DEVICE_STATEMASK_ALL, collection.receive());