Bug 1864141 - Update libcubeb to revision 30efcd1c. r=cubeb-reviewers,kinetik

Differential Revision: https://phabricator.services.mozilla.com/D193427
This commit is contained in:
Paul Adenot 2023-11-21 10:47:59 +00:00
Родитель 55a951769f
Коммит 80affd48e4
24 изменённых файлов: 1208 добавлений и 1181 удалений

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

@ -163,6 +163,7 @@ typedef enum {
implications. */
} cubeb_log_level;
/// A single channel position, to be used in a bitmask.
typedef enum {
CHANNEL_UNKNOWN = 0,
CHANNEL_FRONT_LEFT = 1 << 0,
@ -185,6 +186,9 @@ typedef enum {
CHANNEL_TOP_BACK_RIGHT = 1 << 17
} cubeb_channel;
/// A bitmask representing the channel layout of a cubeb stream. This is
/// bit-compatible with WAVEFORMATEXENSIBLE and in the same order as the SMPTE
/// ordering.
typedef uint32_t cubeb_channel_layout;
// Some common layout definitions.
enum {
@ -433,7 +437,7 @@ typedef void (*cubeb_state_callback)(cubeb_stream * stream, void * user_ptr,
/**
* User supplied callback called when the underlying device changed.
* @param user The pointer passed to cubeb_stream_init. */
* @param user_ptr The pointer passed to cubeb_stream_init. */
typedef void (*cubeb_device_changed_callback)(void * user_ptr);
/**

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

@ -9,8 +9,8 @@ origin:
description: "Cross platform audio library"
url: https://github.com/mozilla/cubeb
license: ISC
release: d07ea5aa6641afc916784e9cc7d69d763b621f32 (2023-11-08T13:42:49Z).
revision: d07ea5aa6641afc916784e9cc7d69d763b621f32
release: 30efcd1cdf8a794e918fe2c6e9b74c8ea8bbcdd6 (2023-11-10T16:00:19Z).
revision: 30efcd1cdf8a794e918fe2c6e9b74c8ea8bbcdd6
vendoring:
url: https://github.com/mozilla/cubeb

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

@ -11,7 +11,6 @@
#include "cubeb_resampler.h"
#include "cubeb_triple_buffer.h"
#include <aaudio/AAudio.h>
#include <android/api-level.h>
#include <atomic>
#include <cassert>
#include <chrono>
@ -24,7 +23,6 @@
#include <memory>
#include <mutex>
#include <thread>
#include <time.h>
#include <vector>
using namespace std;
@ -67,11 +65,11 @@ using namespace std;
X(AAudioStream_getFramesWritten) \
X(AAudioStream_getFramesPerBurst) \
X(AAudioStreamBuilder_setInputPreset) \
X(AAudioStreamBuilder_setUsage)
X(AAudioStreamBuilder_setUsage) \
X(AAudioStreamBuilder_setFramesPerDataCallback)
// not needed or added later on
// X(AAudioStreamBuilder_setFramesPerDataCallback) \
// X(AAudioStreamBuilder_setDeviceId) \
// X(AAudioStreamBuilder_setDeviceId) \
// X(AAudioStreamBuilder_setSamplesPerFrame) \
// X(AAudioStream_getSamplesPerFrame) \
// X(AAudioStream_getDeviceId) \
@ -112,6 +110,9 @@ aaudio_stream_destroy_locked(cubeb_stream * stm, lock_guard<mutex> & lock);
static int
aaudio_stream_start_locked(cubeb_stream * stm, lock_guard<mutex> & lock);
static void
reinitialize_stream(cubeb_stream * stm);
enum class stream_state {
INIT = 0,
STOPPED,
@ -619,7 +620,9 @@ aaudio_get_latency(cubeb_stream * stm, aaudio_direction_t direction,
auto result = WRAP(AAudioStream_getTimestamp)(stream, CLOCK_MONOTONIC,
&hw_frame_index, &hw_tstamp);
if (result != AAUDIO_OK) {
LOG("AAudioStream_getTimestamp failure.");
LOG("AAudioStream_getTimestamp failure for %s: %s",
is_output ? "output" : "input",
WRAP(AAudio_convertResultToText)(result));
return 0;
}
@ -688,9 +691,6 @@ aaudio_duplex_data_cb(AAudioStream * astream, void * user_data,
stream_state state = atomic_load(&stm->state);
int istate = WRAP(AAudioStream_getState)(stm->istream);
int ostate = WRAP(AAudioStream_getState)(stm->ostream);
ALOGV("aaudio duplex data cb on stream %p: state %ld (in: %d, out: %d), "
"num_frames: %ld",
(void *)stm, state, istate, ostate, num_frames);
// all other states may happen since the callback might be called
// from within requestStart
@ -699,11 +699,13 @@ aaudio_duplex_data_cb(AAudioStream * astream, void * user_data,
// This might happen when we started draining but not yet actually
// stopped the stream from the state thread.
if (state == stream_state::DRAINING) {
LOG("Draining in duplex callback");
std::memset(audio_data, 0x0, num_frames * stm->out_frame_size);
return AAUDIO_CALLBACK_RESULT_CONTINUE;
}
if (num_frames * stm->in_frame_size > stm->in_buf.size()) {
LOG("Resizing input buffer in duplex callback");
stm->in_buf.resize(num_frames * stm->in_frame_size);
}
// The aaudio docs state that AAudioStream_read must not be called on
@ -714,12 +716,22 @@ aaudio_duplex_data_cb(AAudioStream * astream, void * user_data,
long in_num_frames =
WRAP(AAudioStream_read)(stm->istream, stm->in_buf.data(), num_frames, 0);
if (in_num_frames < 0) { // error
stm->state.store(stream_state::ERROR);
if (in_num_frames == AAUDIO_STREAM_STATE_DISCONNECTED) {
LOG("AAudioStream_read: %s (reinitializing)",
WRAP(AAudio_convertResultToText)(in_num_frames));
reinitialize_stream(stm);
} else {
stm->state.store(stream_state::ERROR);
}
LOG("AAudioStream_read: %s",
WRAP(AAudio_convertResultToText)(in_num_frames));
return AAUDIO_CALLBACK_RESULT_STOP;
}
ALOGV("aaudio duplex data cb on stream %p: state %ld (in: %d, out: %d), "
"num_frames: %ld, read: %ld",
(void *)stm, state, istate, ostate, num_frames, in_num_frames);
compute_and_report_latency_metrics(stm);
// This can happen shortly after starting the stream. AAudio might immediately
@ -1045,6 +1057,8 @@ aaudio_stream_init_impl(cubeb_stream * stm, lock_guard<mutex> & lock)
{
assert(stm->state.load() == stream_state::INIT);
cubeb_async_log_reset_threads();
aaudio_result_t res;
AAudioStreamBuilder * sb;
res = WRAP(AAudio_createStreamBuilder)(&sb);
@ -1108,12 +1122,29 @@ aaudio_stream_init_impl(cubeb_stream * stm, lock_guard<mutex> & lock)
WRAP(AAudioStreamBuilder_setUsage)(sb, output_preset);
WRAP(AAudioStreamBuilder_setDirection)(sb, AAUDIO_DIRECTION_OUTPUT);
WRAP(AAudioStreamBuilder_setDataCallback)(sb, out_data_callback, stm);
assert(stm->latency_frames < std::numeric_limits<int32_t>::max());
LOG("Frames per callback set to %d for output", stm->latency_frames);
WRAP(AAudioStreamBuilder_setFramesPerDataCallback)
(sb, static_cast<int32_t>(stm->latency_frames));
int res_err = realize_stream(sb, stm->output_stream_params.get(),
&stm->ostream, &frame_size);
if (res_err) {
return res_err;
}
int32_t output_burst_size =
WRAP(AAudioStream_getFramesPerBurst)(stm->ostream);
LOG("AAudio output burst size: %d", output_burst_size);
// 3 times the burst size seems to be robust.
res = WRAP(AAudioStream_setBufferSizeInFrames)(stm->ostream,
output_burst_size * 3);
if (res < 0) {
LOG("AAudioStream_setBufferSizeInFrames error (ostream): %s",
WRAP(AAudio_convertResultToText)(res));
// Not fatal
}
int rate = WRAP(AAudioStream_getSampleRate)(stm->ostream);
LOG("AAudio output stream sharing mode: %d",
WRAP(AAudioStream_getSharingMode)(stm->ostream));
@ -1147,12 +1178,28 @@ aaudio_stream_init_impl(cubeb_stream * stm, lock_guard<mutex> & lock)
WRAP(AAudioStreamBuilder_setInputPreset)(sb, input_preset);
WRAP(AAudioStreamBuilder_setDirection)(sb, AAUDIO_DIRECTION_INPUT);
WRAP(AAudioStreamBuilder_setDataCallback)(sb, in_data_callback, stm);
assert(stm->latency_frames < std::numeric_limits<int32_t>::max());
LOG("Frames per callback set to %d for input", stm->latency_frames);
WRAP(AAudioStreamBuilder_setFramesPerDataCallback)
(sb, static_cast<int32_t>(stm->latency_frames));
int res_err = realize_stream(sb, stm->input_stream_params.get(),
&stm->istream, &frame_size);
if (res_err) {
return res_err;
}
int32_t input_burst_size =
WRAP(AAudioStream_getFramesPerBurst)(stm->istream);
LOG("AAudio input burst size: %d", input_burst_size);
// 3 times the burst size seems to be robust.
res = WRAP(AAudioStream_setBufferSizeInFrames)(stm->istream,
input_burst_size * 3);
if (res < AAUDIO_OK) {
LOG("AAudioStream_setBufferSizeInFrames error (istream): %s",
WRAP(AAudio_convertResultToText)(res));
// Not fatal
}
int bcap = WRAP(AAudioStream_getBufferCapacityInFrames)(stm->istream);
int rate = WRAP(AAudioStream_getSampleRate)(stm->istream);
LOG("AAudio input stream sharing mode: %d",
@ -1709,9 +1756,6 @@ const static struct cubeb_ops aaudio_ops = {
extern "C" /*static*/ int
aaudio_init(cubeb ** context, char const * /* context_name */)
{
if (android_get_device_api_level() <= 30) {
return CUBEB_ERROR;
}
// load api
void * libaaudio = nullptr;
#ifndef DISABLE_LIBAAUDIO_DLOPEN

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

@ -183,7 +183,7 @@ MixerContext::auto_matrix()
{
double matrix[NUM_NAMED_CHANNELS][NUM_NAMED_CHANNELS] = {{0}};
double maxcoef = 0;
float maxval;
double maxval;
cubeb_channel_layout in_ch_layout = clean_layout(_in_ch_layout);
cubeb_channel_layout out_ch_layout = clean_layout(_out_ch_layout);

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

@ -592,6 +592,8 @@ player_fullduplex_callback(SLBufferQueueItf caller, void * user_ptr)
long input_frame_count = stm->input_buffer_length / stm->input_frame_size;
long sample_count = input_frame_count * stm->input_params->channels;
long frames_needed = stm->queuebuf_len / stm->framesize;
uint32_t output_sample_count =
stm->output_params->channels * stm->queuebuf_len / stm->framesize;
if (!input_buffer) {
LOG("Input hole set silent input buffer");
@ -601,7 +603,7 @@ player_fullduplex_callback(SLBufferQueueItf caller, void * user_ptr)
input_buffer =
convert_input_buffer_if_needed(stm, input_buffer, sample_count);
output_buffer = get_output_buffer(stm, output_buffer, sample_count);
output_buffer = get_output_buffer(stm, output_buffer, output_sample_count);
long written = 0;
// Trigger user callback through resampler
@ -1507,6 +1509,7 @@ opensl_stream_init(cubeb * ctx, cubeb_stream ** stream,
cubeb_state_callback state_callback, void * user_ptr)
{
cubeb_stream * stm = nullptr;
cubeb_async_log_reset_threads();
assert(ctx);
if (input_device || output_device) {

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

@ -9,6 +9,7 @@
#define CUBEB_RING_ARRAY_H
#include "cubeb_utils.h"
#include <CoreAudio/CoreAudioTypes.h>
/** Ring array of pointers is used to hold buffers. In case that
asynchronous producer/consumer callbacks do not arrive in a

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

@ -7,7 +7,7 @@
#if !defined(TEST_COMMON)
#define TEST_COMMON
#if defined( _WIN32)
#if defined(_WIN32)
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
@ -17,18 +17,22 @@
#include <unistd.h>
#endif
#include <cstdarg>
#include "cubeb/cubeb.h"
#include "cubeb_mixer.h"
#include "gtest/gtest.h"
#include <cstdarg>
#include <cstdio>
#include <cstring>
template<typename T, size_t N>
template <typename T, size_t N>
constexpr size_t
ARRAY_LENGTH(T(&)[N])
ARRAY_LENGTH(T (&)[N])
{
return N;
}
inline void delay(unsigned int ms)
inline void
delay(unsigned int ms)
{
#if defined(_WIN32)
Sleep(ms);
@ -49,33 +53,24 @@ typedef struct {
} layout_info;
struct backend_caps {
const char* id;
const char * id;
const int input_capabilities;
};
// This static table allows knowing if a backend has audio input capabilities.
// We don't rely on opening a stream and checking if it works, because this
// would make the test skip the tests that make use of audio input, if a
// particular backend has a bug that causes a failure during audio input stream
// creation
// creation
static backend_caps backend_capabilities[] = {
{"sun", 1},
{"wasapi", 1},
{"kai", 1},
{"audiounit", 1},
{"audiotrack", 0},
{"opensl", 1},
{"aaudio", 1},
{"jack", 1},
{"pulse", 1},
{"sndio", 1},
{"oss", 1},
{"winmm", 0},
{"alsa", 1},
{"sun", 1}, {"wasapi", 1}, {"kai", 1}, {"audiounit", 1},
{"audiotrack", 0}, {"opensl", 1}, {"aaudio", 1}, {"jack", 1},
{"pulse", 1}, {"sndio", 1}, {"oss", 1}, {"winmm", 0},
{"alsa", 1},
};
inline int can_run_audio_input_test(cubeb * ctx)
inline int
can_run_audio_input_test(cubeb * ctx)
{
cubeb_device_collection devices;
int input_device_available = 0;
@ -94,29 +89,32 @@ inline int can_run_audio_input_test(cubeb * ctx)
}
for (uint32_t i = 0; i < devices.count; i++) {
input_device_available |= (devices.device[i].state ==
CUBEB_DEVICE_STATE_ENABLED);
input_device_available |=
(devices.device[i].state == CUBEB_DEVICE_STATE_ENABLED);
}
if (!input_device_available) {
fprintf(stderr, "there are input devices, but they are not "
"available, skipping\n");
"available, skipping\n");
}
cubeb_device_collection_destroy(ctx, &devices);
int backend_has_input_capabilities;
const char * backend_id = cubeb_get_backend_id(ctx);
for (uint32_t i = 0; i < sizeof(backend_capabilities) / sizeof(backend_caps); i++) {
for (uint32_t i = 0; i < sizeof(backend_capabilities) / sizeof(backend_caps);
i++) {
if (strcmp(backend_capabilities[i].id, backend_id) == 0) {
backend_has_input_capabilities = backend_capabilities[i].input_capabilities;
backend_has_input_capabilities =
backend_capabilities[i].input_capabilities;
}
}
return !!input_device_available && !!backend_has_input_capabilities;
}
inline void print_log(const char * msg, ...)
inline void
print_log(const char * msg, ...)
{
va_list args;
va_start(args, msg);
@ -127,7 +125,8 @@ inline void print_log(const char * msg, ...)
/** Initialize cubeb with backend override.
* Create call cubeb_init passing value for CUBEB_BACKEND env var as
* override. */
inline int common_init(cubeb ** ctx, char const * ctx_name)
inline int
common_init(cubeb ** ctx, char const * ctx_name)
{
#ifdef ENABLE_NORMAL_LOG
if (cubeb_set_log_callback(CUBEB_LOG_NORMAL, print_log) != CUBEB_OK) {
@ -150,22 +149,21 @@ inline int common_init(cubeb ** ctx, char const * ctx_name)
if (r == CUBEB_OK && backend) {
ctx_backend = cubeb_get_backend_id(*ctx);
if (strcmp(backend, ctx_backend) != 0) {
fprintf(stderr, "Requested backend `%s', got `%s'\n",
backend, ctx_backend);
fprintf(stderr, "Requested backend `%s', got `%s'\n", backend,
ctx_backend);
}
}
return r;
}
#if defined( _WIN32)
#if defined(_WIN32)
class TestEnvironment : public ::testing::Environment {
public:
void SetUp() override {
hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
}
void SetUp() override { hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED); }
void TearDown() override {
void TearDown() override
{
if (SUCCEEDED(hr)) {
CoUninitialize();
}
@ -175,7 +173,8 @@ private:
HRESULT hr;
};
::testing::Environment* const foo_env = ::testing::AddGlobalTestEnvironment(new TestEnvironment);
::testing::Environment * const foo_env =
::testing::AddGlobalTestEnvironment(new TestEnvironment);
#endif
#endif /* TEST_COMMON */

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

@ -11,16 +11,16 @@
#if !defined(_XOPEN_SOURCE)
#define _XOPEN_SOURCE 600
#endif
#include <stdio.h>
#include <stdlib.h>
#include "cubeb/cubeb.h"
#include <math.h>
#include <memory>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "cubeb/cubeb.h"
#include <string>
//#define ENABLE_NORMAL_LOG
//#define ENABLE_VERBOSE_LOG
// #define ENABLE_NORMAL_LOG
// #define ENABLE_VERBOSE_LOG
#include "common.h"
using namespace std;
@ -28,37 +28,49 @@ using namespace std;
#define MAX_NUM_CHANNELS 32
#define VOLUME 0.2
float get_frequency(int channel_index)
float
get_frequency(int channel_index)
{
return 220.0f * (channel_index+1);
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); }
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 */
struct synth_state {
synth_state(int num_channels_, float sample_rate_)
: num_channels(num_channels_),
sample_rate(sample_rate_)
: num_channels(num_channels_), sample_rate(sample_rate_)
{
for(int i=0;i < MAX_NUM_CHANNELS;++i)
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;
}
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;
@ -66,45 +78,51 @@ private:
float sample_rate;
};
template<typename T>
long data_cb(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((T*)outputbuffer, nframes);
synth_state * synth = (synth_state *)user;
synth->run((T *)outputbuffer, nframes);
return nframes;
}
void state_cb_audio(cubeb_stream * /*stream*/, void * /*user*/, cubeb_state /*state*/)
void
state_cb_audio(cubeb_stream * /*stream*/, void * /*user*/,
cubeb_state /*state*/)
{
}
/* Our android backends don't support float, only int16. */
int supports_float32(string backend_id)
int
supports_float32(string backend_id)
{
return backend_id != "opensl"
&& backend_id != "audiotrack";
return backend_id != "opensl" && backend_id != "audiotrack";
}
/* Some backends don't have code to deal with more than mono or stereo. */
int supports_channel_count(string backend_id, int nchannels)
int
supports_channel_count(string backend_id, int nchannels)
{
return nchannels <= 2 ||
(backend_id != "opensl" && backend_id != "audiotrack");
(backend_id != "opensl" && backend_id != "audiotrack");
}
int run_test(int num_channels, int sampling_rate, int is_float)
int
run_test(int num_channels, int sampling_rate, int is_float)
{
int r = CUBEB_OK;
cubeb *ctx = NULL;
cubeb * ctx = NULL;
r = common_init(&ctx, "Cubeb audio test: channels");
if (r != CUBEB_OK) {
fprintf(stderr, "Error initializing cubeb library\n");
return r;
}
std::unique_ptr<cubeb, decltype(&cubeb_destroy)>
cleanup_cubeb_at_exit(ctx, cubeb_destroy);
std::unique_ptr<cubeb, decltype(&cubeb_destroy)> cleanup_cubeb_at_exit(
ctx, cubeb_destroy);
const char * backend_id = cubeb_get_backend_id(ctx);
@ -114,7 +132,9 @@ int run_test(int num_channels, int sampling_rate, int is_float)
return CUBEB_OK;
}
fprintf(stderr, "Testing %d channel(s), %d Hz, %s (%s)\n", num_channels, sampling_rate, is_float ? "float" : "short", cubeb_get_backend_id(ctx));
fprintf(stderr, "Testing %d channel(s), %d Hz, %s (%s)\n", num_channels,
sampling_rate, is_float ? "float" : "short",
cubeb_get_backend_id(ctx));
cubeb_stream_params params;
params.format = is_float ? CUBEB_SAMPLE_FLOAT32NE : CUBEB_SAMPLE_S16NE;
@ -125,16 +145,17 @@ int run_test(int num_channels, int sampling_rate, int is_float)
synth_state synth(params.channels, params.rate);
cubeb_stream *stream = NULL;
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);
return r;
}
std::unique_ptr<cubeb_stream, decltype(&cubeb_stream_destroy)>
cleanup_stream_at_exit(stream, cubeb_stream_destroy);
cleanup_stream_at_exit(stream, cubeb_stream_destroy);
cubeb_stream_start(stream);
delay(200);
@ -143,11 +164,12 @@ int run_test(int num_channels, int sampling_rate, int is_float)
return r;
}
int run_volume_test(int is_float)
int
run_volume_test(int is_float)
{
int r = CUBEB_OK;
cubeb *ctx = NULL;
cubeb * ctx = NULL;
r = common_init(&ctx, "Cubeb audio test");
if (r != CUBEB_OK) {
@ -155,8 +177,8 @@ int run_volume_test(int is_float)
return r;
}
std::unique_ptr<cubeb, decltype(&cubeb_destroy)>
cleanup_cubeb_at_exit(ctx, cubeb_destroy);
std::unique_ptr<cubeb, decltype(&cubeb_destroy)> cleanup_cubeb_at_exit(
ctx, cubeb_destroy);
const char * backend_id = cubeb_get_backend_id(ctx);
@ -174,24 +196,23 @@ int run_volume_test(int is_float)
synth_state synth(params.channels, params.rate);
cubeb_stream *stream = NULL;
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);
return r;
}
std::unique_ptr<cubeb_stream, decltype(&cubeb_stream_destroy)>
cleanup_stream_at_exit(stream, cubeb_stream_destroy);
cleanup_stream_at_exit(stream, cubeb_stream_destroy);
fprintf(stderr, "Testing: volume\n");
for(int i=0;i <= 4; ++i)
{
fprintf(stderr, "Volume: %d%%\n", i*25);
for (int i = 0; i <= 4; ++i) {
fprintf(stderr, "Volume: %d%%\n", i * 25);
cubeb_stream_set_volume(stream, i/4.0f);
cubeb_stream_set_volume(stream, i / 4.0f);
cubeb_stream_start(stream);
delay(400);
cubeb_stream_stop(stream);
@ -201,35 +222,25 @@ int run_volume_test(int is_float)
return r;
}
TEST(cubeb, run_volume_test_short)
{
ASSERT_EQ(run_volume_test(0), CUBEB_OK);
}
TEST(cubeb, run_volume_test_short) { ASSERT_EQ(run_volume_test(0), CUBEB_OK); }
TEST(cubeb, run_volume_test_float)
{
ASSERT_EQ(run_volume_test(1), CUBEB_OK);
}
TEST(cubeb, run_volume_test_float) { ASSERT_EQ(run_volume_test(1), CUBEB_OK); }
TEST(cubeb, run_channel_rate_test)
{
unsigned int channel_values[] = {
1,
2,
3,
4,
6,
1, 2, 3, 4, 6,
};
int freq_values[] = {
16000,
24000,
44100,
48000,
16000,
24000,
44100,
48000,
};
for(auto channels : channel_values) {
for(auto freq : freq_values) {
for (auto channels : channel_values) {
for (auto freq : freq_values) {
ASSERT_TRUE(channels < MAX_NUM_CHANNELS);
fprintf(stderr, "--------------------------\n");
ASSERT_EQ(run_test(channels, freq, 0), CUBEB_OK);
@ -238,6 +249,5 @@ TEST(cubeb, run_channel_rate_test)
}
}
#undef MAX_NUM_CHANNELS
#undef VOLUME

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

@ -1,9 +1,9 @@
/*
* Copyright <EFBFBD> 2017 Mozilla Foundation
*
* This program is made available under an ISC-style license. See the
* accompanying file LICENSE for details.
*/
* Copyright <EFBFBD> 2017 Mozilla Foundation
*
* This program is made available under an ISC-style license. See the
* accompanying file LICENSE for details.
*/
/* libcubeb api/function test. Test that different return values from user
specified callbacks are handled correctly. */
@ -11,36 +11,34 @@
#if !defined(_XOPEN_SOURCE)
#define _XOPEN_SOURCE 600
#endif
#include <memory>
#include <atomic>
#include <string>
#include "cubeb/cubeb.h"
#include <atomic>
#include <memory>
#include <string>
//#define ENABLE_NORMAL_LOG
//#define ENABLE_VERBOSE_LOG
// #define ENABLE_NORMAL_LOG
// #define ENABLE_VERBOSE_LOG
#include "common.h"
const uint32_t SAMPLE_FREQUENCY = 48000;
const cubeb_sample_format SAMPLE_FORMAT = CUBEB_SAMPLE_S16NE;
enum test_direction {
INPUT_ONLY,
OUTPUT_ONLY,
DUPLEX
};
enum test_direction { INPUT_ONLY, OUTPUT_ONLY, DUPLEX };
// Structure which is used by data callbacks to track the total callbacks
// executed vs the number of callbacks expected.
struct user_state_callback_ret {
std::atomic<int> cb_count{ 0 };
std::atomic<int> expected_cb_count{ 0 };
std::atomic<int> error_state{ 0 };
std::atomic<int> cb_count{0};
std::atomic<int> expected_cb_count{0};
std::atomic<int> error_state{0};
};
// Data callback that always returns 0
long data_cb_ret_zero(cubeb_stream * stream, void * user, const void * inputbuffer, void * outputbuffer, long nframes)
long
data_cb_ret_zero(cubeb_stream * stream, void * user, const void * inputbuffer,
void * outputbuffer, long nframes)
{
user_state_callback_ret * u = (user_state_callback_ret *) user;
user_state_callback_ret * u = (user_state_callback_ret *)user;
// If this is the first time the callback has been called set our expected
// callback count
if (u->cb_count == 0) {
@ -55,7 +53,10 @@ long data_cb_ret_zero(cubeb_stream * stream, void * user, const void * inputbuff
}
// Data callback that always returns nframes - 1
long data_cb_ret_nframes_minus_one(cubeb_stream * stream, void * user, const void * inputbuffer, void * outputbuffer, long nframes)
long
data_cb_ret_nframes_minus_one(cubeb_stream * stream, void * user,
const void * inputbuffer, void * outputbuffer,
long nframes)
{
user_state_callback_ret * u = (user_state_callback_ret *)user;
// If this is the first time the callback has been called set our expected
@ -70,7 +71,7 @@ long data_cb_ret_nframes_minus_one(cubeb_stream * stream, void * user, const voi
}
if (outputbuffer != NULL) {
// If we have an output buffer insert silence
short * ob = (short *) outputbuffer;
short * ob = (short *)outputbuffer;
for (long i = 0; i < nframes - 1; i++) {
ob[i] = 0;
}
@ -79,7 +80,9 @@ long data_cb_ret_nframes_minus_one(cubeb_stream * stream, void * user, const voi
}
// Data callback that always returns nframes
long data_cb_ret_nframes(cubeb_stream * stream, void * user, const void * inputbuffer, void * outputbuffer, long nframes)
long
data_cb_ret_nframes(cubeb_stream * stream, void * user,
const void * inputbuffer, void * outputbuffer, long nframes)
{
user_state_callback_ret * u = (user_state_callback_ret *)user;
u->cb_count++;
@ -91,7 +94,7 @@ long data_cb_ret_nframes(cubeb_stream * stream, void * user, const void * inputb
}
if (outputbuffer != NULL) {
// If we have an output buffer insert silence
short * ob = (short *) outputbuffer;
short * ob = (short *)outputbuffer;
for (long i = 0; i < nframes; i++) {
ob[i] = 0;
}
@ -102,7 +105,7 @@ long data_cb_ret_nframes(cubeb_stream * stream, void * user, const void * inputb
// Data callback that always returns CUBEB_ERROR
long
data_cb_ret_error(cubeb_stream * stream, void * user, const void * inputbuffer,
void * outputbuffer, long nframes)
void * outputbuffer, long nframes)
{
user_state_callback_ret * u = (user_state_callback_ret *)user;
// If this is the first time the callback has been called set our expected
@ -118,7 +121,8 @@ data_cb_ret_error(cubeb_stream * stream, void * user, const void * inputbuffer,
return CUBEB_ERROR;
}
void state_cb_ret(cubeb_stream * stream, void * user, cubeb_state state)
void
state_cb_ret(cubeb_stream * stream, void * user, cubeb_state state)
{
if (stream == NULL)
return;
@ -126,11 +130,14 @@ void state_cb_ret(cubeb_stream * stream, void * user, cubeb_state state)
switch (state) {
case CUBEB_STATE_STARTED:
fprintf(stderr, "stream started\n"); break;
fprintf(stderr, "stream started\n");
break;
case CUBEB_STATE_STOPPED:
fprintf(stderr, "stream stopped\n"); break;
fprintf(stderr, "stream stopped\n");
break;
case CUBEB_STATE_DRAINED:
fprintf(stderr, "stream drained\n"); break;
fprintf(stderr, "stream drained\n");
break;
case CUBEB_STATE_ERROR:
fprintf(stderr, "stream error\n");
u->error_state.fetch_add(1);
@ -140,9 +147,10 @@ void state_cb_ret(cubeb_stream * stream, void * user, cubeb_state state)
}
}
void run_test_callback(test_direction direction,
cubeb_data_callback data_cb,
const std::string & test_desc) {
void
run_test_callback(test_direction direction, cubeb_data_callback data_cb,
const std::string & test_desc)
{
cubeb * ctx;
cubeb_stream * stream;
cubeb_stream_params input_params;
@ -154,13 +162,13 @@ void run_test_callback(test_direction direction,
r = common_init(&ctx, "Cubeb callback return value example");
ASSERT_EQ(r, CUBEB_OK) << "Error initializing cubeb library";
std::unique_ptr<cubeb, decltype(&cubeb_destroy)>
cleanup_cubeb_at_exit(ctx, cubeb_destroy);
std::unique_ptr<cubeb, decltype(&cubeb_destroy)> cleanup_cubeb_at_exit(
ctx, cubeb_destroy);
if ((direction == INPUT_ONLY || direction == DUPLEX) &&
!can_run_audio_input_test(ctx)) {
/* This test needs an available input device, skip it if this host does not
* have one or if the backend doesn't implement input. */
* have one or if the backend doesn't implement input. */
return;
}
@ -175,22 +183,21 @@ void run_test_callback(test_direction direction,
r = cubeb_get_min_latency(ctx, &input_params, &latency_frames);
ASSERT_EQ(r, CUBEB_OK) << "Could not get minimal latency";
switch (direction)
{
switch (direction) {
case INPUT_ONLY:
r = cubeb_stream_init(ctx, &stream, "Cubeb callback ret input",
NULL, &input_params, NULL, NULL,
latency_frames, data_cb, state_cb_ret, &user_state);
r = cubeb_stream_init(ctx, &stream, "Cubeb callback ret input", NULL,
&input_params, NULL, NULL, latency_frames, data_cb,
state_cb_ret, &user_state);
break;
case OUTPUT_ONLY:
r = cubeb_stream_init(ctx, &stream, "Cubeb callback ret output",
NULL, NULL, NULL, &output_params,
latency_frames, data_cb, state_cb_ret, &user_state);
r = cubeb_stream_init(ctx, &stream, "Cubeb callback ret output", NULL, NULL,
NULL, &output_params, latency_frames, data_cb,
state_cb_ret, &user_state);
break;
case DUPLEX:
r = cubeb_stream_init(ctx, &stream, "Cubeb callback ret duplex",
NULL, &input_params, NULL, &output_params,
latency_frames, data_cb, state_cb_ret, &user_state);
r = cubeb_stream_init(ctx, &stream, "Cubeb callback ret duplex", NULL,
&input_params, NULL, &output_params, latency_frames,
data_cb, state_cb_ret, &user_state);
break;
default:
ASSERT_TRUE(false) << "Unrecognized test direction!";
@ -198,14 +205,14 @@ void run_test_callback(test_direction direction,
EXPECT_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);
cleanup_stream_at_exit(stream, cubeb_stream_destroy);
cubeb_stream_start(stream);
delay(100);
cubeb_stream_stop(stream);
ASSERT_EQ(user_state.expected_cb_count, user_state.cb_count) <<
"Callback called unexpected number of times for " << test_desc << "!";
ASSERT_EQ(user_state.expected_cb_count, user_state.cb_count)
<< "Callback called unexpected number of times for " << test_desc << "!";
// TODO: On some test configurations, the data_callback is never called.
if (data_cb == data_cb_ret_error && user_state.cb_count != 0) {
ASSERT_EQ(user_state.error_state, 1) << "Callback expected error state";
@ -215,8 +222,10 @@ void run_test_callback(test_direction direction,
TEST(cubeb, test_input_callback)
{
run_test_callback(INPUT_ONLY, data_cb_ret_zero, "input only, return 0");
run_test_callback(INPUT_ONLY, data_cb_ret_nframes_minus_one, "input only, return nframes - 1");
run_test_callback(INPUT_ONLY, data_cb_ret_nframes, "input only, return nframes");
run_test_callback(INPUT_ONLY, data_cb_ret_nframes_minus_one,
"input only, return nframes - 1");
run_test_callback(INPUT_ONLY, data_cb_ret_nframes,
"input only, return nframes");
run_test_callback(INPUT_ONLY, data_cb_ret_error,
"input only, return CUBEB_ERROR");
}
@ -224,8 +233,10 @@ TEST(cubeb, test_input_callback)
TEST(cubeb, test_output_callback)
{
run_test_callback(OUTPUT_ONLY, data_cb_ret_zero, "output only, return 0");
run_test_callback(OUTPUT_ONLY, data_cb_ret_nframes_minus_one, "output only, return nframes - 1");
run_test_callback(OUTPUT_ONLY, data_cb_ret_nframes, "output only, return nframes");
run_test_callback(OUTPUT_ONLY, data_cb_ret_nframes_minus_one,
"output only, return nframes - 1");
run_test_callback(OUTPUT_ONLY, data_cb_ret_nframes,
"output only, return nframes");
run_test_callback(OUTPUT_ONLY, data_cb_ret_error,
"output only, return CUBEB_ERROR");
}
@ -233,7 +244,8 @@ TEST(cubeb, test_output_callback)
TEST(cubeb, test_duplex_callback)
{
run_test_callback(DUPLEX, data_cb_ret_zero, "duplex, return 0");
run_test_callback(DUPLEX, data_cb_ret_nframes_minus_one, "duplex, return nframes - 1");
run_test_callback(DUPLEX, data_cb_ret_nframes_minus_one,
"duplex, return nframes - 1");
run_test_callback(DUPLEX, data_cb_ret_nframes, "duplex, return nframes");
run_test_callback(DUPLEX, data_cb_ret_error, "duplex, return CUBEB_ERROR");
}

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

@ -1,262 +0,0 @@
/*
* Copyright © 2017 Mozilla Foundation
*
* This program is made available under an ISC-style license. See the
* accompanying file LICENSE for details.
*
*
* Purpose
* =============================================================================
* In CoreAudio, the data callback will holds a mutex shared with AudioUnit
* (mutex_AU). Thus, if the callback request another mutex M held by the another
* function, without releasing mutex_AU, then it will cause a deadlock when the
* another function, which holds the mutex M, request to use AudioUnit.
*
* The following figure illustrates the deadlock in bug 1337805:
* https://bugzilla.mozilla.org/show_bug.cgi?id=1337805
* (The detail analysis can be found on bug 1350511:
* https://bugzilla.mozilla.org/show_bug.cgi?id=1350511)
*
* holds
* data_callback <---------- mutext_AudioUnit(mutex_AU)
* | ^
* | |
* | request | request
* | |
* v holds |
* mutex_cubeb ------------> get_channel_layout
*
* In this example, the "audiounit_get_channel_layout" in f4edfb8:
* https://github.com/kinetiknz/cubeb/blob/f4edfb8eea920887713325e44773f3a2d959860c/src/cubeb_audiounit.cpp#L2725
* requests the mutex_AU to create an AudioUnit, when it holds a mutex for cubeb
* context. Meanwhile, the data callback who holds the mutex_AU requests the
* mutex for cubeb context. As a result, it causes a deadlock.
*
* The problem is solve by pull 236: https://github.com/kinetiknz/cubeb/pull/236
* We store the latest channel layout and return it when there is an active
* AudioUnit, otherwise, we will create an AudioUnit to get it.
*
* Although the problem is solved, to prevent it happens again, we add the test
* here in case someone without such knowledge misuses the AudioUnit in
* get_channel_layout. Moreover, it's a good way to record the known issues
* to warn other developers.
*/
#include "gtest/gtest.h"
//#define ENABLE_NORMAL_LOG
//#define ENABLE_VERBOSE_LOG
#include "common.h" // for layout_infos
#include "cubeb/cubeb.h" // for cubeb utils
#include "cubeb_utils.h" // for owned_critical_section, auto_lock
#include <iostream> // for fprintf
#include <pthread.h> // for pthread
#include <signal.h> // for signal
#include <stdexcept> // for std::logic_error
#include <string> // for std::string
#include <unistd.h> // for sleep, usleep
#include <atomic> // for std::atomic
// The signal alias for calling our thread killer.
#define CALL_THREAD_KILLER SIGUSR1
// This indicator will become true when our pending task thread is killed by
// ourselves.
bool killed = false;
// This indicator will become true when the assigned task is done.
std::atomic<bool> task_done{ false };
// Indicating the data callback is fired or not.
bool called = false;
// Toggle to true when running data callback. Before data callback gets
// the mutex for cubeb context, it toggles back to false.
// The task to get channel layout should be executed when this is true.
std::atomic<bool> callbacking_before_getting_context{ false };
owned_critical_section context_mutex;
cubeb * context = nullptr;
cubeb * get_cubeb_context_unlocked()
{
if (context) {
return context;
}
int r = CUBEB_OK;
r = common_init(&context, "Cubeb deadlock test");
if (r != CUBEB_OK) {
context = nullptr;
}
return context;
}
cubeb * get_cubeb_context()
{
auto_lock lock(context_mutex);
return get_cubeb_context_unlocked();
}
void state_cb_audio(cubeb_stream * /*stream*/, void * /*user*/, cubeb_state /*state*/)
{
}
// Fired by coreaudio's rendering mechanism. It holds a mutex shared with the
// current used AudioUnit.
template<typename T>
long data_cb(cubeb_stream * /*stream*/, void * /*user*/,
const void * /*inputbuffer*/, void * outputbuffer, long nframes)
{
called = true;
uint64_t tid; // Current thread id.
pthread_threadid_np(NULL, &tid);
fprintf(stderr, "Audio output is on thread %llu\n", tid);
if (!task_done) {
callbacking_before_getting_context = true;
fprintf(stderr, "[%llu] time to switch thread\n", tid);
// Force to switch threads by sleeping 10 ms. Notice that anything over
// 10ms would create a glitch. It's intended here for test, so the delay
// is ok.
usleep(10000);
callbacking_before_getting_context = false;
}
fprintf(stderr, "[%llu] try getting backend id ...\n", tid);
// Try requesting mutex for context by get_cubeb_context()
// when holding a mutex for AudioUnit.
char const * backend_id = cubeb_get_backend_id(get_cubeb_context());
fprintf(stderr, "[%llu] callback on %s\n", tid, backend_id);
// Mute the output (or get deaf)
memset(outputbuffer, 0, nframes * 2 * sizeof(float));
return nframes;
}
// Called by wait_to_get_layout, which is run out of main thread.
void get_preferred_channel_layout()
{
auto_lock lock(context_mutex);
cubeb * context = get_cubeb_context_unlocked();
ASSERT_TRUE(!!context);
// We will cause a deadlock if cubeb_get_preferred_channel_layout requests
// mutex for AudioUnit when it holds mutex for context.
cubeb_channel_layout layout;
int r = cubeb_get_preferred_channel_layout(context, &layout);
ASSERT_EQ(r == CUBEB_OK, layout != CUBEB_LAYOUT_UNDEFINED);
fprintf(stderr, "layout is %s\n", layout_infos[layout].name);
}
void * wait_to_get_layout(void *)
{
uint64_t tid; // Current thread id.
pthread_threadid_np(NULL, &tid);
while(!callbacking_before_getting_context) {
fprintf(stderr, "[%llu] waiting for data callback ...\n", tid);
usleep(1000); // Force to switch threads by sleeping 1 ms.
}
fprintf(stderr, "[%llu] try getting channel layout ...\n", tid);
get_preferred_channel_layout(); // Deadlock checkpoint.
task_done = true;
return NULL;
}
void * watchdog(void * s)
{
uint64_t tid; // Current thread id.
pthread_threadid_np(NULL, &tid);
pthread_t subject = *((pthread_t *) s);
uint64_t stid; // task thread id.
pthread_threadid_np(subject, &stid);
unsigned int sec = 2;
fprintf(stderr, "[%llu] sleep %d seconds before checking task for thread %llu\n", tid, sec, stid);
sleep(sec); // Force to switch threads.
fprintf(stderr, "[%llu] check task for thread %llu now\n", tid, stid);
if (!task_done) {
fprintf(stderr, "[%llu] kill the task thread %llu\n", tid, stid);
pthread_kill(subject, CALL_THREAD_KILLER);
pthread_detach(subject);
// pthread_kill doesn't release the mutex held by the killed thread,
// so we need to unlock it manually.
context_mutex.unlock();
}
fprintf(stderr, "[%llu] the assigned task for thread %llu is %sdone\n", tid, stid, (task_done) ? "" : "not ");
return NULL;
}
void thread_killer(int signal)
{
ASSERT_EQ(signal, CALL_THREAD_KILLER);
fprintf(stderr, "task thread is killed!\n");
killed = true;
}
TEST(cubeb, run_deadlock_test)
{
#if !defined(__APPLE__)
FAIL() << "Deadlock test is only for OSX now";
#endif
cubeb * ctx = get_cubeb_context();
ASSERT_TRUE(!!ctx);
std::unique_ptr<cubeb, decltype(&cubeb_destroy)>
cleanup_cubeb_at_exit(ctx, cubeb_destroy);
cubeb_stream_params params;
params.format = CUBEB_SAMPLE_FLOAT32NE;
params.rate = 44100;
params.channels = 2;
params.layout = CUBEB_LAYOUT_STEREO;
params.prefs = CUBEB_STREAM_PREF_NONE;
cubeb_stream * stream = NULL;
int r = cubeb_stream_init(ctx, &stream, "test deadlock", NULL, NULL, NULL,
&params, 512, &data_cb<float>, state_cb_audio, NULL);
ASSERT_EQ(r, CUBEB_OK);
std::unique_ptr<cubeb_stream, decltype(&cubeb_stream_destroy)>
cleanup_stream_at_exit(stream, cubeb_stream_destroy);
// Install signal handler.
signal(CALL_THREAD_KILLER, thread_killer);
pthread_t subject, detector;
pthread_create(&subject, NULL, wait_to_get_layout, NULL);
pthread_create(&detector, NULL, watchdog, (void *) &subject);
uint64_t stid, dtid;
pthread_threadid_np(subject, &stid);
pthread_threadid_np(detector, &dtid);
fprintf(stderr, "task thread %llu, monitor thread %llu are created\n", stid, dtid);
cubeb_stream_start(stream);
pthread_join(subject, NULL);
pthread_join(detector, NULL);
ASSERT_TRUE(called);
fprintf(stderr, "\n%sDeadlock detected!\n", (called && !task_done.load()) ? "" : "No ");
// Check the task is killed by ourselves if deadlock happends.
// Otherwise, thread_killer should not be triggered.
ASSERT_NE(task_done.load(), killed);
ASSERT_TRUE(task_done.load());
cubeb_stream_stop(stream);
}
#undef CALL_THREAD_KILLER

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

@ -11,12 +11,12 @@
#if !defined(_XOPEN_SOURCE)
#define _XOPEN_SOURCE 600
#endif
#include <stdio.h>
#include <memory>
#include "cubeb/cubeb.h"
#include <memory>
#include <stdio.h>
//#define ENABLE_NORMAL_LOG
//#define ENABLE_VERBOSE_LOG
// #define ENABLE_NORMAL_LOG
// #define ENABLE_VERBOSE_LOG
#include "common.h"
#define SAMPLE_FREQUENCY 48000
@ -26,23 +26,28 @@
#define OUTPUT_CHANNELS 2
#define OUTPUT_LAYOUT CUBEB_LAYOUT_STEREO
long data_callback(cubeb_stream * stream, void * user, const void * inputbuffer, void * outputbuffer, long nframes)
long
data_callback(cubeb_stream * stream, void * user, const void * inputbuffer,
void * outputbuffer, long nframes)
{
return 0;
}
void state_callback(cubeb_stream * stream, void * user, cubeb_state state)
void
state_callback(cubeb_stream * stream, void * user, cubeb_state state)
{
}
void device_changed_callback(void * user)
void
device_changed_callback(void * user)
{
fprintf(stderr, "device changed callback\n");
ASSERT_TRUE(false) << "Error: device changed callback"
" called without changing devices";
}
void test_registering_null_callback_twice(cubeb_stream * stream)
void
test_registering_null_callback_twice(cubeb_stream * stream)
{
int r = cubeb_stream_register_device_changed_callback(stream, nullptr);
if (r == CUBEB_ERROR_NOT_SUPPORTED) {
@ -51,12 +56,15 @@ void test_registering_null_callback_twice(cubeb_stream * stream)
ASSERT_EQ(r, CUBEB_OK) << "Error registering null device changed callback";
r = cubeb_stream_register_device_changed_callback(stream, nullptr);
ASSERT_EQ(r, CUBEB_OK) << "Error registering null device changed callback again";
ASSERT_EQ(r, CUBEB_OK)
<< "Error registering null device changed callback again";
}
void test_registering_and_unregistering_callback(cubeb_stream * stream)
void
test_registering_and_unregistering_callback(cubeb_stream * stream)
{
int r = cubeb_stream_register_device_changed_callback(stream, device_changed_callback);
int r = cubeb_stream_register_device_changed_callback(
stream, device_changed_callback);
if (r == CUBEB_ERROR_NOT_SUPPORTED) {
return;
}
@ -75,7 +83,6 @@ TEST(cubeb, device_changed_callbacks)
int r = CUBEB_OK;
uint32_t latency_frames = 0;
r = common_init(&ctx, "Cubeb duplex example with device change");
ASSERT_EQ(r, CUBEB_OK) << "Error initializing cubeb library";
@ -83,8 +90,8 @@ TEST(cubeb, device_changed_callbacks)
return;
}
std::unique_ptr<cubeb, decltype(&cubeb_destroy)>
cleanup_cubeb_at_exit(ctx, cubeb_destroy);
std::unique_ptr<cubeb, decltype(&cubeb_destroy)> cleanup_cubeb_at_exit(
ctx, cubeb_destroy);
/* typical user-case: mono input, stereo output, low latency. */
input_params.format = STREAM_FORMAT;
@ -101,9 +108,9 @@ TEST(cubeb, device_changed_callbacks)
r = cubeb_get_min_latency(ctx, &output_params, &latency_frames);
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_callback, state_callback, nullptr);
r = cubeb_stream_init(ctx, &stream, "Cubeb duplex", NULL, &input_params, NULL,
&output_params, latency_frames, data_callback,
state_callback, nullptr);
ASSERT_EQ(r, CUBEB_OK) << "Error initializing cubeb stream";
test_registering_null_callback_twice(stream);

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

@ -7,24 +7,27 @@
/* libcubeb enumerate device test/example.
* Prints out a list of devices enumerated. */
#include "cubeb/cubeb.h"
#include "gtest/gtest.h"
#include <memory>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <memory>
#include "cubeb/cubeb.h"
//#define ENABLE_NORMAL_LOG
//#define ENABLE_VERBOSE_LOG
// #define ENABLE_NORMAL_LOG
// #define ENABLE_VERBOSE_LOG
#include "common.h"
long data_cb_duplex(cubeb_stream * stream, void * user, const void * inputbuffer, void * outputbuffer, long nframes)
static long
data_cb_duplex(cubeb_stream * stream, void * user, const void * inputbuffer,
void * outputbuffer, long nframes)
{
// noop, unused
return 0;
}
void state_cb_duplex(cubeb_stream * stream, void * /*user*/, cubeb_state state)
static void
state_cb_duplex(cubeb_stream * stream, void * /*user*/, cubeb_state state)
{
// noop, unused
}
@ -33,52 +36,52 @@ static void
print_device_info(cubeb_device_info * info, FILE * f)
{
char devfmts[64] = "";
const char * devtype, * devstate, * devdeffmt;
const char *devtype, *devstate, *devdeffmt;
switch (info->type) {
case CUBEB_DEVICE_TYPE_INPUT:
devtype = "input";
break;
case CUBEB_DEVICE_TYPE_OUTPUT:
devtype = "output";
break;
case CUBEB_DEVICE_TYPE_UNKNOWN:
default:
devtype = "unknown?";
break;
case CUBEB_DEVICE_TYPE_INPUT:
devtype = "input";
break;
case CUBEB_DEVICE_TYPE_OUTPUT:
devtype = "output";
break;
case CUBEB_DEVICE_TYPE_UNKNOWN:
default:
devtype = "unknown?";
break;
};
switch (info->state) {
case CUBEB_DEVICE_STATE_DISABLED:
devstate = "disabled";
break;
case CUBEB_DEVICE_STATE_UNPLUGGED:
devstate = "unplugged";
break;
case CUBEB_DEVICE_STATE_ENABLED:
devstate = "enabled";
break;
default:
devstate = "unknown?";
break;
case CUBEB_DEVICE_STATE_DISABLED:
devstate = "disabled";
break;
case CUBEB_DEVICE_STATE_UNPLUGGED:
devstate = "unplugged";
break;
case CUBEB_DEVICE_STATE_ENABLED:
devstate = "enabled";
break;
default:
devstate = "unknown?";
break;
};
switch (info->default_format) {
case CUBEB_DEVICE_FMT_S16LE:
devdeffmt = "S16LE";
break;
case CUBEB_DEVICE_FMT_S16BE:
devdeffmt = "S16BE";
break;
case CUBEB_DEVICE_FMT_F32LE:
devdeffmt = "F32LE";
break;
case CUBEB_DEVICE_FMT_F32BE:
devdeffmt = "F32BE";
break;
default:
devdeffmt = "unknown?";
break;
case CUBEB_DEVICE_FMT_S16LE:
devdeffmt = "S16LE";
break;
case CUBEB_DEVICE_FMT_S16BE:
devdeffmt = "S16BE";
break;
case CUBEB_DEVICE_FMT_F32LE:
devdeffmt = "F32LE";
break;
case CUBEB_DEVICE_FMT_F32BE:
devdeffmt = "F32BE";
break;
default:
devdeffmt = "unknown?";
break;
};
if (info->format & CUBEB_DEVICE_FMT_S16LE)
@ -91,23 +94,22 @@ print_device_info(cubeb_device_info * info, FILE * f)
strcat(devfmts, " F32BE");
fprintf(f,
"dev: \"%s\"%s\n"
"\tName: \"%s\"\n"
"\tGroup: \"%s\"\n"
"\tVendor: \"%s\"\n"
"\tType: %s\n"
"\tState: %s\n"
"\tCh: %u\n"
"\tFormat: %s (0x%x) (default: %s)\n"
"\tRate: %u - %u (default: %u)\n"
"\tLatency: lo %u frames, hi %u frames\n",
info->device_id, info->preferred ? " (PREFERRED)" : "",
info->friendly_name, info->group_id, info->vendor_name,
devtype, devstate, info->max_channels,
(devfmts[0] == '\0') ? devfmts : devfmts + 1,
(unsigned int)info->format, devdeffmt,
info->min_rate, info->max_rate, info->default_rate,
info->latency_lo, info->latency_hi);
"dev: \"%s\"%s\n"
"\tName: \"%s\"\n"
"\tGroup: \"%s\"\n"
"\tVendor: \"%s\"\n"
"\tType: %s\n"
"\tState: %s\n"
"\tCh: %u\n"
"\tFormat: %s (0x%x) (default: %s)\n"
"\tRate: %u - %u (default: %u)\n"
"\tLatency: lo %u frames, hi %u frames\n",
info->device_id, info->preferred ? " (PREFERRED)" : "",
info->friendly_name, info->group_id, info->vendor_name, devtype,
devstate, info->max_channels,
(devfmts[0] == '\0') ? devfmts : devfmts + 1,
(unsigned int)info->format, devdeffmt, info->min_rate, info->max_rate,
info->default_rate, info->latency_lo, info->latency_hi);
}
static void
@ -123,22 +125,22 @@ TEST(cubeb, destroy_default_collection)
{
int r;
cubeb * ctx = NULL;
cubeb_device_collection collection{ nullptr, 0 };
cubeb_device_collection collection{nullptr, 0};
r = common_init(&ctx, "Cubeb audio test");
ASSERT_EQ(r, CUBEB_OK) << "Error initializing cubeb library";
std::unique_ptr<cubeb, decltype(&cubeb_destroy)>
cleanup_cubeb_at_exit(ctx, cubeb_destroy);
std::unique_ptr<cubeb, decltype(&cubeb_destroy)> cleanup_cubeb_at_exit(
ctx, cubeb_destroy);
ASSERT_EQ(collection.device, nullptr);
ASSERT_EQ(collection.count, (size_t) 0);
ASSERT_EQ(collection.count, (size_t)0);
r = cubeb_device_collection_destroy(ctx, &collection);
if (r != CUBEB_ERROR_NOT_SUPPORTED) {
ASSERT_EQ(r, CUBEB_OK);
ASSERT_EQ(collection.device, nullptr);
ASSERT_EQ(collection.count, (size_t) 0);
ASSERT_EQ(collection.count, (size_t)0);
}
}
@ -151,11 +153,11 @@ TEST(cubeb, enumerate_devices)
r = common_init(&ctx, "Cubeb audio test");
ASSERT_EQ(r, CUBEB_OK) << "Error initializing cubeb library";
std::unique_ptr<cubeb, decltype(&cubeb_destroy)>
cleanup_cubeb_at_exit(ctx, cubeb_destroy);
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));
cubeb_get_backend_id(ctx));
r = cubeb_enumerate_devices(ctx, CUBEB_DEVICE_TYPE_INPUT, &collection);
if (r == CUBEB_ERROR_NOT_SUPPORTED) {
@ -198,9 +200,9 @@ TEST(cubeb, enumerate_devices)
input_params.layout = output_params.layout = CUBEB_LAYOUT_MONO;
input_params.prefs = output_params.prefs = CUBEB_STREAM_PREF_NONE;
r = cubeb_stream_init(ctx, &stream, "Cubeb duplex",
NULL, &input_params, NULL, &output_params,
1024, data_cb_duplex, state_cb_duplex, nullptr);
r = cubeb_stream_init(ctx, &stream, "Cubeb duplex", NULL, &input_params, NULL,
&output_params, 1024, data_cb_duplex, state_cb_duplex,
nullptr);
ASSERT_EQ(r, CUBEB_OK) << "Error initializing cubeb stream";
@ -218,11 +220,11 @@ TEST(cubeb, stream_get_current_device)
int r = common_init(&ctx, "Cubeb audio test");
ASSERT_EQ(r, CUBEB_OK) << "Error initializing cubeb library";
std::unique_ptr<cubeb, decltype(&cubeb_destroy)>
cleanup_cubeb_at_exit(ctx, cubeb_destroy);
std::unique_ptr<cubeb, decltype(&cubeb_destroy)> cleanup_cubeb_at_exit(
ctx, cubeb_destroy);
fprintf(stdout, "Getting current devices for backend %s\n",
cubeb_get_backend_id(ctx));
cubeb_get_backend_id(ctx));
if (!can_run_audio_input_test(ctx)) {
return;
@ -238,12 +240,12 @@ TEST(cubeb, stream_get_current_device)
input_params.layout = output_params.layout = CUBEB_LAYOUT_MONO;
input_params.prefs = output_params.prefs = CUBEB_STREAM_PREF_NONE;
r = cubeb_stream_init(ctx, &stream, "Cubeb duplex",
NULL, &input_params, NULL, &output_params,
1024, data_cb_duplex, state_cb_duplex, nullptr);
r = cubeb_stream_init(ctx, &stream, "Cubeb duplex", NULL, &input_params, NULL,
&output_params, 1024, data_cb_duplex, state_cb_duplex,
nullptr);
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);
cleanup_stream_at_exit(stream, cubeb_stream_destroy);
cubeb_device * device;
r = cubeb_stream_get_current_device(stream, &device);

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

@ -11,15 +11,15 @@
#if !defined(_XOPEN_SOURCE)
#define _XOPEN_SOURCE 600
#endif
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <memory>
#include "cubeb/cubeb.h"
#include <atomic>
#include <math.h>
#include <memory>
#include <stdio.h>
#include <stdlib.h>
//#define ENABLE_NORMAL_LOG
//#define ENABLE_VERBOSE_LOG
// #define ENABLE_NORMAL_LOG
// #define ENABLE_VERBOSE_LOG
#include "common.h"
#define SAMPLE_FREQUENCY 48000
@ -29,16 +29,17 @@
#define OUTPUT_CHANNELS 2
#define OUTPUT_LAYOUT CUBEB_LAYOUT_STEREO
struct user_state_duplex
{
std::atomic<int> invalid_audio_value{ 0 };
struct user_state_duplex {
std::atomic<int> invalid_audio_value{0};
};
long data_cb_duplex(cubeb_stream * stream, void * user, const void * inputbuffer, void * outputbuffer, long nframes)
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);
float *ib = (float *)inputbuffer;
float *ob = (float *)outputbuffer;
user_state_duplex * u = reinterpret_cast<user_state_duplex *>(user);
float * ib = (float *)inputbuffer;
float * ob = (float *)outputbuffer;
if (stream == NULL || inputbuffer == NULL || outputbuffer == NULL) {
return CUBEB_ERROR;
@ -58,18 +59,22 @@ long data_cb_duplex(cubeb_stream * stream, void * user, const void * inputbuffer
return nframes;
}
void state_cb_duplex(cubeb_stream * stream, void * /*user*/, cubeb_state state)
void
state_cb_duplex(cubeb_stream * stream, void * /*user*/, cubeb_state state)
{
if (stream == NULL)
return;
switch (state) {
case CUBEB_STATE_STARTED:
fprintf(stderr, "stream started\n"); break;
fprintf(stderr, "stream started\n");
break;
case CUBEB_STATE_STOPPED:
fprintf(stderr, "stream stopped\n"); break;
fprintf(stderr, "stream stopped\n");
break;
case CUBEB_STATE_DRAINED:
fprintf(stderr, "stream drained\n"); break;
fprintf(stderr, "stream drained\n");
break;
default:
fprintf(stderr, "unknown stream state %d\n", state);
}
@ -79,8 +84,8 @@ void state_cb_duplex(cubeb_stream * stream, void * /*user*/, cubeb_state state)
TEST(cubeb, duplex)
{
cubeb *ctx;
cubeb_stream *stream;
cubeb * ctx;
cubeb_stream * stream;
cubeb_stream_params input_params;
cubeb_stream_params output_params;
int r;
@ -90,8 +95,8 @@ TEST(cubeb, duplex)
r = common_init(&ctx, "Cubeb duplex example");
ASSERT_EQ(r, CUBEB_OK) << "Error initializing cubeb library";
std::unique_ptr<cubeb, decltype(&cubeb_destroy)>
cleanup_cubeb_at_exit(ctx, cubeb_destroy);
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. */
@ -114,13 +119,13 @@ TEST(cubeb, duplex)
r = cubeb_get_min_latency(ctx, &output_params, &latency_frames);
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);
r = cubeb_stream_init(ctx, &stream, "Cubeb duplex", NULL, &input_params, NULL,
&output_params, latency_frames, data_cb_duplex,
state_cb_duplex, &stream_state);
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);
cleanup_stream_at_exit(stream, cubeb_stream_destroy);
cubeb_stream_start(stream);
delay(500);
@ -129,7 +134,8 @@ TEST(cubeb, duplex)
ASSERT_FALSE(stream_state.invalid_audio_value.load());
}
void device_collection_changed_callback(cubeb * context, void * user)
void
device_collection_changed_callback(cubeb * context, void * user)
{
fprintf(stderr, "collection changed callback\n");
ASSERT_TRUE(false) << "Error: device collection changed callback"
@ -150,7 +156,6 @@ duplex_collection_change_impl(cubeb * ctx)
device_collection_changed_callback, nullptr);
ASSERT_EQ(r, CUBEB_OK) << "Error initializing cubeb stream";
/* typical user-case: mono input, stereo output, low latency. */
input_params.format = STREAM_FORMAT;
input_params.rate = SAMPLE_FREQUENCY;
@ -217,7 +222,9 @@ TEST(cubeb, duplex_collection_change_no_unregister)
duplex_collection_change_impl(ctx);
}
long data_cb_input(cubeb_stream * stream, void * user, const void * inputbuffer, void * outputbuffer, long nframes)
long
data_cb_input(cubeb_stream * stream, void * user, const void * inputbuffer,
void * outputbuffer, long nframes)
{
if (stream == NULL || inputbuffer == NULL || outputbuffer != NULL) {
return CUBEB_ERROR;
@ -226,20 +233,25 @@ long data_cb_input(cubeb_stream * stream, void * user, const void * inputbuffer,
return nframes;
}
void state_cb_input(cubeb_stream * stream, void * /*user*/, cubeb_state state)
void
state_cb_input(cubeb_stream * stream, void * /*user*/, cubeb_state state)
{
if (stream == NULL)
return;
switch (state) {
case CUBEB_STATE_STARTED:
fprintf(stderr, "stream started\n"); break;
fprintf(stderr, "stream started\n");
break;
case CUBEB_STATE_STOPPED:
fprintf(stderr, "stream stopped\n"); break;
fprintf(stderr, "stream stopped\n");
break;
case CUBEB_STATE_DRAINED:
fprintf(stderr, "stream drained\n"); break;
fprintf(stderr, "stream drained\n");
break;
case CUBEB_STATE_ERROR:
fprintf(stderr, "stream runs into error state\n"); break;
fprintf(stderr, "stream runs into error state\n");
break;
default:
fprintf(stderr, "unknown stream state %d\n", state);
}
@ -247,7 +259,9 @@ void state_cb_input(cubeb_stream * stream, void * /*user*/, cubeb_state state)
return;
}
std::vector<cubeb_devid> get_devices(cubeb * ctx, cubeb_device_type type) {
std::vector<cubeb_devid>
get_devices(cubeb * ctx, cubeb_device_type type)
{
std::vector<cubeb_devid> devices;
cubeb_device_collection collection;
@ -271,8 +285,8 @@ std::vector<cubeb_devid> get_devices(cubeb * ctx, cubeb_device_type type) {
TEST(cubeb, one_duplex_one_input)
{
cubeb *ctx;
cubeb_stream *duplex_stream;
cubeb * ctx;
cubeb_stream * duplex_stream;
cubeb_stream_params input_params;
cubeb_stream_params output_params;
int r;
@ -282,17 +296,19 @@ TEST(cubeb, one_duplex_one_input)
r = common_init(&ctx, "Cubeb duplex example");
ASSERT_EQ(r, CUBEB_OK) << "Error initializing cubeb library";
std::unique_ptr<cubeb, decltype(&cubeb_destroy)>
cleanup_cubeb_at_exit(ctx, cubeb_destroy);
std::unique_ptr<cubeb, decltype(&cubeb_destroy)> cleanup_cubeb_at_exit(
ctx, cubeb_destroy);
/* This test needs at least two available input devices. */
std::vector<cubeb_devid> input_devices = get_devices(ctx, CUBEB_DEVICE_TYPE_INPUT);
std::vector<cubeb_devid> input_devices =
get_devices(ctx, CUBEB_DEVICE_TYPE_INPUT);
if (input_devices.size() < 2) {
return;
}
/* This test needs at least one available output device. */
std::vector<cubeb_devid> output_devices = get_devices(ctx, CUBEB_DEVICE_TYPE_OUTPUT);
std::vector<cubeb_devid> output_devices =
get_devices(ctx, CUBEB_DEVICE_TYPE_OUTPUT);
if (output_devices.size() < 1) {
return;
}
@ -317,27 +333,28 @@ TEST(cubeb, one_duplex_one_input)
r = cubeb_get_min_latency(ctx, &output_params, &latency_frames);
ASSERT_EQ(r, CUBEB_OK) << "Could not get minimal latency";
r = cubeb_stream_init(ctx, &duplex_stream, "Cubeb duplex",
duplex_input, &input_params, duplex_output, &output_params,
latency_frames, data_cb_duplex, state_cb_duplex, &duplex_stream_state);
r = cubeb_stream_init(ctx, &duplex_stream, "Cubeb duplex", duplex_input,
&input_params, duplex_output, &output_params,
latency_frames, data_cb_duplex, state_cb_duplex,
&duplex_stream_state);
ASSERT_EQ(r, CUBEB_OK) << "Error initializing duplex cubeb stream";
std::unique_ptr<cubeb_stream, decltype(&cubeb_stream_destroy)>
cleanup_stream_at_exit(duplex_stream, cubeb_stream_destroy);
cleanup_stream_at_exit(duplex_stream, cubeb_stream_destroy);
r = cubeb_stream_start(duplex_stream);
ASSERT_EQ(r, CUBEB_OK) << "Could not start duplex stream";
delay(500);
cubeb_stream *input_stream;
r = cubeb_stream_init(ctx, &input_stream, "Cubeb input",
input_only, &input_params, NULL, NULL,
latency_frames, data_cb_input, state_cb_input, nullptr);
cubeb_stream * input_stream;
r = cubeb_stream_init(ctx, &input_stream, "Cubeb input", input_only,
&input_params, NULL, NULL, latency_frames,
data_cb_input, state_cb_input, nullptr);
ASSERT_EQ(r, CUBEB_OK) << "Error initializing input-only cubeb stream";
std::unique_ptr<cubeb_stream, decltype(&cubeb_stream_destroy)>
cleanup_input_stream_at_exit(input_stream, cubeb_stream_destroy);
cleanup_input_stream_at_exit(input_stream, cubeb_stream_destroy);
r = cubeb_stream_start(input_stream);
ASSERT_EQ(r, CUBEB_OK) << "Could not start input stream";
delay(500);

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

@ -1,9 +1,9 @@
#include "gtest/gtest.h"
#include <stdlib.h>
#include <memory>
#include "cubeb/cubeb.h"
//#define ENABLE_NORMAL_LOG
//#define ENABLE_VERBOSE_LOG
#include "gtest/gtest.h"
#include <memory>
#include <stdlib.h>
// #define ENABLE_NORMAL_LOG
// #define ENABLE_VERBOSE_LOG
#include "common.h"
TEST(cubeb, latency)
@ -17,8 +17,8 @@ TEST(cubeb, latency)
r = common_init(&ctx, "Cubeb audio test");
ASSERT_EQ(r, CUBEB_OK);
std::unique_ptr<cubeb, decltype(&cubeb_destroy)>
cleanup_cubeb_at_exit(ctx, cubeb_destroy);
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);
@ -32,13 +32,9 @@ TEST(cubeb, latency)
ASSERT_GT(preferred_rate, 0u);
}
cubeb_stream_params params = {
CUBEB_SAMPLE_FLOAT32NE,
preferred_rate,
max_channels,
CUBEB_LAYOUT_UNDEFINED,
CUBEB_STREAM_PREF_NONE
};
cubeb_stream_params params = {CUBEB_SAMPLE_FLOAT32NE, preferred_rate,
max_channels, CUBEB_LAYOUT_UNDEFINED,
CUBEB_STREAM_PREF_NONE};
r = cubeb_get_min_latency(ctx, &params, &latency_frames);
ASSERT_TRUE(r == CUBEB_OK || r == CUBEB_ERROR_NOT_SUPPORTED);
if (r == CUBEB_OK) {

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

@ -26,7 +26,7 @@
std::atomic<uint32_t> log_statements_received = {0};
std::atomic<uint32_t> data_callback_call_count = {0};
void
static void
test_logging_callback(char const * fmt, ...)
{
log_statements_received++;
@ -40,7 +40,7 @@ test_logging_callback(char const * fmt, ...)
#endif // PRINT_LOGS_TO_STDERR
}
long
static long
data_cb_load(cubeb_stream * stream, void * user, const void * inputbuffer,
void * outputbuffer, long nframes)
{
@ -48,7 +48,7 @@ data_cb_load(cubeb_stream * stream, void * user, const void * inputbuffer,
return nframes;
}
void
static void
state_cb(cubeb_stream * stream, void * /*user*/, cubeb_state state)
{
if (stream == NULL)

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

@ -5,41 +5,66 @@
* accompanying file LICENSE for details.
*/
/* libcubeb api/function test. Requests a loopback device and checks that
output is being looped back to input. NOTE: Usage of output devices while
performing this test will cause flakey results! */
/* libcubeb api/function test. Requests a loopback device and checks that
output is being looped back to input. NOTE: Usage of output devices while
performing this test will cause flakey results! */
#include "gtest/gtest.h"
#if !defined(_XOPEN_SOURCE)
#define _XOPEN_SOURCE 600
#endif
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "cubeb/cubeb.h"
#include <algorithm>
#include <math.h>
#include <memory>
#include <mutex>
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include "cubeb/cubeb.h"
//#define ENABLE_NORMAL_LOG
//#define ENABLE_VERBOSE_LOG
// #define ENABLE_NORMAL_LOG
// #define ENABLE_VERBOSE_LOG
#include "common.h"
const uint32_t SAMPLE_FREQUENCY = 48000;
const uint32_t TONE_FREQUENCY = 440;
const double OUTPUT_AMPLITUDE = 0.25;
const int32_t NUM_FRAMES_TO_OUTPUT = SAMPLE_FREQUENCY / 20; /* play ~50ms of samples */
const int32_t NUM_FRAMES_TO_OUTPUT =
SAMPLE_FREQUENCY / 20; /* play ~50ms of samples */
template<typename T> T ConvertSampleToOutput(double input);
template<> float ConvertSampleToOutput(double input) { return float(input); }
template<> short ConvertSampleToOutput(double input) { return short(input * 32767.0f); }
template <typename T>
T
ConvertSampleToOutput(double input);
template <>
float
ConvertSampleToOutput(double input)
{
return float(input);
}
template <>
short
ConvertSampleToOutput(double input)
{
return short(input * 32767.0f);
}
template<typename T> double ConvertSampleFromOutput(T sample);
template<> double ConvertSampleFromOutput(float sample) { return double(sample); }
template<> double ConvertSampleFromOutput(short sample) { return double(sample / 32767.0); }
template <typename T>
double
ConvertSampleFromOutput(T sample);
template <>
double
ConvertSampleFromOutput(float sample)
{
return double(sample);
}
template <>
double
ConvertSampleFromOutput(short sample)
{
return double(sample / 32767.0);
}
/* Simple cross correlation to help find phase shift. Not a performant impl */
std::vector<double> cross_correlate(std::vector<double> & f,
std::vector<double> & g,
size_t signal_length)
std::vector<double>
cross_correlate(std::vector<double> & f, std::vector<double> & g,
size_t signal_length)
{
/* the length we sweep our window through to find the cross correlation */
size_t sweep_length = f.size() - signal_length + 1;
@ -56,11 +81,12 @@ std::vector<double> cross_correlate(std::vector<double> & f,
}
/* best effort discovery of phase shift between output and (looped) input*/
size_t find_phase(std::vector<double> & output_frames,
std::vector<double> & input_frames,
size_t signal_length)
size_t
find_phase(std::vector<double> & output_frames,
std::vector<double> & input_frames, size_t signal_length)
{
std::vector<double> correlation = cross_correlate(output_frames, input_frames, signal_length);
std::vector<double> correlation =
cross_correlate(output_frames, input_frames, signal_length);
size_t phase = 0;
double max_correlation = correlation.at(0);
for (size_t i = 1; i < correlation.size(); i++) {
@ -72,9 +98,12 @@ size_t find_phase(std::vector<double> & output_frames,
return phase;
}
std::vector<double> normalize_frames(std::vector<double> & frames) {
double max = abs(*std::max_element(frames.begin(), frames.end(),
[](double a, double b) { return abs(a) < abs(b); }));
std::vector<double>
normalize_frames(std::vector<double> & frames)
{
double max = abs(
*std::max_element(frames.begin(), frames.end(),
[](double a, double b) { return abs(a) < abs(b); }));
std::vector<double> normalized_frames;
normalized_frames.reserve(frames.size());
for (const double frame : frames) {
@ -83,13 +112,17 @@ std::vector<double> normalize_frames(std::vector<double> & frames) {
return normalized_frames;
}
/* heuristic comparison of aligned output and input signals, gets flaky if TONE_FREQUENCY is too high */
void compare_signals(std::vector<double> & output_frames,
std::vector<double> & input_frames)
/* heuristic comparison of aligned output and input signals, gets flaky if
* TONE_FREQUENCY is too high */
void
compare_signals(std::vector<double> & output_frames,
std::vector<double> & input_frames)
{
ASSERT_EQ(output_frames.size(), input_frames.size()) << "#Output frames != #input frames";
ASSERT_EQ(output_frames.size(), input_frames.size())
<< "#Output frames != #input frames";
size_t num_frames = output_frames.size();
std::vector<double> normalized_output_frames = normalize_frames(output_frames);
std::vector<double> normalized_output_frames =
normalize_frames(output_frames);
std::vector<double> normalized_input_frames = normalize_frames(input_frames);
/* calculate mean absolute errors */
@ -100,7 +133,8 @@ void compare_signals(std::vector<double> & output_frames,
/* mean absolute errors between input and silence */
double input_silence_mas = 0.0;
for (size_t i = 0; i < num_frames; i++) {
io_mas += abs(normalized_output_frames.at(i) - normalized_input_frames.at(i));
io_mas +=
abs(normalized_output_frames.at(i) - normalized_input_frames.at(i));
output_silence_mas += abs(normalized_output_frames.at(i));
input_silence_mas += abs(normalized_input_frames.at(i));
}
@ -109,13 +143,16 @@ void compare_signals(std::vector<double> & output_frames,
input_silence_mas /= num_frames;
ASSERT_LT(io_mas, output_silence_mas)
<< "Error between output and input should be less than output and silence!";
<< "Error between output and input should be less than output and "
"silence!";
ASSERT_LT(io_mas, input_silence_mas)
<< "Error between output and input should be less than output and silence!";
<< "Error between output and input should be less than output and "
"silence!";
/* make sure extrema are in (roughly) correct location */
/* number of maxima + minama expected in the frames*/
const long NUM_EXTREMA = 2 * TONE_FREQUENCY * NUM_FRAMES_TO_OUTPUT / SAMPLE_FREQUENCY;
const long NUM_EXTREMA =
2 * TONE_FREQUENCY * NUM_FRAMES_TO_OUTPUT / SAMPLE_FREQUENCY;
/* expected index of first maxima */
const long FIRST_MAXIMUM_INDEX = SAMPLE_FREQUENCY / TONE_FREQUENCY / 4;
/* Threshold we expect all maxima and minima to be above or below. Ideally
@ -128,15 +165,19 @@ void compare_signals(std::vector<double> & output_frames,
/* expected offset to current extreme: i * stide between extrema */
size_t offset = i * SAMPLE_FREQUENCY / TONE_FREQUENCY / 2;
if (is_maximum) {
ASSERT_GT(normalized_output_frames.at(FIRST_MAXIMUM_INDEX + offset), THRESHOLD)
<< "Output frames have unexpected missing maximum!";
ASSERT_GT(normalized_input_frames.at(FIRST_MAXIMUM_INDEX + offset), THRESHOLD)
<< "Input frames have unexpected missing maximum!";
ASSERT_GT(normalized_output_frames.at(FIRST_MAXIMUM_INDEX + offset),
THRESHOLD)
<< "Output frames have unexpected missing maximum!";
ASSERT_GT(normalized_input_frames.at(FIRST_MAXIMUM_INDEX + offset),
THRESHOLD)
<< "Input frames have unexpected missing maximum!";
} else {
ASSERT_LT(normalized_output_frames.at(FIRST_MAXIMUM_INDEX + offset), -THRESHOLD)
<< "Output frames have unexpected missing minimum!";
ASSERT_LT(normalized_input_frames.at(FIRST_MAXIMUM_INDEX + offset), -THRESHOLD)
<< "Input frames have unexpected missing minimum!";
ASSERT_LT(normalized_output_frames.at(FIRST_MAXIMUM_INDEX + offset),
-THRESHOLD)
<< "Output frames have unexpected missing minimum!";
ASSERT_LT(normalized_input_frames.at(FIRST_MAXIMUM_INDEX + offset),
-THRESHOLD)
<< "Input frames have unexpected missing minimum!";
}
}
}
@ -150,12 +191,14 @@ struct user_state_loopback {
std::vector<double> input_frames;
};
template<typename T>
long data_cb_loop_duplex(cubeb_stream * stream, void * user, const void * inputbuffer, void * outputbuffer, long nframes)
template <typename T>
long
data_cb_loop_duplex(cubeb_stream * stream, void * user,
const void * inputbuffer, void * outputbuffer, long nframes)
{
struct user_state_loopback * u = (struct user_state_loopback *) user;
T * ib = (T *) inputbuffer;
T * ob = (T *) outputbuffer;
struct user_state_loopback * u = (struct user_state_loopback *)user;
T * ib = (T *)inputbuffer;
T * ob = (T *)outputbuffer;
if (stream == NULL || inputbuffer == NULL || outputbuffer == NULL) {
return CUBEB_ERROR;
@ -167,7 +210,8 @@ long data_cb_loop_duplex(cubeb_stream * stream, void * user, const void * inputb
double tone = 0.0;
if (u->position + i < NUM_FRAMES_TO_OUTPUT) {
/* generate sine wave */
tone = sin(2 * M_PI*(i + u->position) * TONE_FREQUENCY / SAMPLE_FREQUENCY);
tone =
sin(2 * M_PI * (i + u->position) * TONE_FREQUENCY / SAMPLE_FREQUENCY);
tone *= OUTPUT_AMPLITUDE;
}
ob[i] = ConvertSampleToOutput<T>(tone);
@ -181,15 +225,19 @@ long data_cb_loop_duplex(cubeb_stream * stream, void * user, const void * inputb
return nframes;
}
template<typename T>
long data_cb_loop_input_only(cubeb_stream * stream, void * user, const void * inputbuffer, void * outputbuffer, long nframes)
template <typename T>
long
data_cb_loop_input_only(cubeb_stream * stream, void * user,
const void * inputbuffer, void * outputbuffer,
long nframes)
{
struct user_state_loopback * u = (struct user_state_loopback *) user;
T * ib = (T *) inputbuffer;
struct user_state_loopback * u = (struct user_state_loopback *)user;
T * ib = (T *)inputbuffer;
if (outputbuffer != NULL) {
// Can't assert as it needs to return, so expect to fail instead
EXPECT_EQ(outputbuffer, (void *) NULL) << "outputbuffer should be null in input only callback";
EXPECT_EQ(outputbuffer, (void *)NULL)
<< "outputbuffer should be null in input only callback";
return CUBEB_ERROR;
}
@ -205,11 +253,13 @@ long data_cb_loop_input_only(cubeb_stream * stream, void * user, const void * in
return nframes;
}
template<typename T>
long data_cb_playback(cubeb_stream * stream, void * user, const void * inputbuffer, void * outputbuffer, long nframes)
template <typename T>
long
data_cb_playback(cubeb_stream * stream, void * user, const void * inputbuffer,
void * outputbuffer, long nframes)
{
struct user_state_loopback * u = (struct user_state_loopback *) user;
T * ob = (T *) outputbuffer;
struct user_state_loopback * u = (struct user_state_loopback *)user;
T * ob = (T *)outputbuffer;
if (stream == NULL || outputbuffer == NULL) {
return CUBEB_ERROR;
@ -221,7 +271,8 @@ long data_cb_playback(cubeb_stream * stream, void * user, const void * inputbuff
double tone = 0.0;
if (u->position + i < NUM_FRAMES_TO_OUTPUT) {
/* generate sine wave */
tone = sin(2 * M_PI*(i + u->position) * TONE_FREQUENCY / SAMPLE_FREQUENCY);
tone =
sin(2 * M_PI * (i + u->position) * TONE_FREQUENCY / SAMPLE_FREQUENCY);
tone *= OUTPUT_AMPLITUDE;
}
ob[i] = ConvertSampleToOutput<T>(tone);
@ -233,18 +284,22 @@ long data_cb_playback(cubeb_stream * stream, void * user, const void * inputbuff
return nframes;
}
void state_cb_loop(cubeb_stream * stream, void * /*user*/, cubeb_state state)
void
state_cb_loop(cubeb_stream * stream, void * /*user*/, cubeb_state state)
{
if (stream == NULL)
return;
switch (state) {
case CUBEB_STATE_STARTED:
fprintf(stderr, "stream started\n"); break;
fprintf(stderr, "stream started\n");
break;
case CUBEB_STATE_STOPPED:
fprintf(stderr, "stream stopped\n"); break;
fprintf(stderr, "stream stopped\n");
break;
case CUBEB_STATE_DRAINED:
fprintf(stderr, "stream drained\n"); break;
fprintf(stderr, "stream drained\n");
break;
default:
fprintf(stderr, "unknown stream state %d\n", state);
}
@ -252,7 +307,8 @@ void state_cb_loop(cubeb_stream * stream, void * /*user*/, cubeb_state state)
return;
}
void run_loopback_duplex_test(bool is_float)
void
run_loopback_duplex_test(bool is_float)
{
cubeb * ctx;
cubeb_stream * stream;
@ -264,8 +320,8 @@ void run_loopback_duplex_test(bool is_float)
r = common_init(&ctx, "Cubeb loopback example: duplex stream");
ASSERT_EQ(r, CUBEB_OK) << "Error initializing cubeb library";
std::unique_ptr<cubeb, decltype(&cubeb_destroy)>
cleanup_cubeb_at_exit(ctx, cubeb_destroy);
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. */
@ -291,35 +347,41 @@ void run_loopback_duplex_test(bool is_float)
ASSERT_EQ(r, CUBEB_OK) << "Could not get minimal latency";
/* setup a duplex stream with loopback */
r = cubeb_stream_init(ctx, &stream, "Cubeb loopback",
NULL, &input_params, NULL, &output_params, latency_frames,
is_float ? data_cb_loop_duplex<float> : data_cb_loop_duplex<short>,
r = cubeb_stream_init(ctx, &stream, "Cubeb loopback", NULL, &input_params,
NULL, &output_params, latency_frames,
is_float ? data_cb_loop_duplex<float>
: data_cb_loop_duplex<short>,
state_cb_loop, 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);
cleanup_stream_at_exit(stream, cubeb_stream_destroy);
cubeb_stream_start(stream);
delay(300);
cubeb_stream_stop(stream);
/* access after stop should not happen, but lock just in case and to appease sanitization tools */
/* access after stop should not happen, but lock just in case and to appease
* sanitization tools */
std::lock_guard<std::mutex> lock(user_data->user_state_mutex);
std::vector<double> & output_frames = user_data->output_frames;
std::vector<double> & input_frames = user_data->input_frames;
ASSERT_EQ(output_frames.size(), input_frames.size())
<< "#Output frames != #input frames";
<< "#Output frames != #input frames";
size_t phase = find_phase(user_data->output_frames, user_data->input_frames, NUM_FRAMES_TO_OUTPUT);
size_t phase = find_phase(user_data->output_frames, user_data->input_frames,
NUM_FRAMES_TO_OUTPUT);
/* extract vectors of just the relevant signal from output and input */
auto output_frames_signal_start = output_frames.begin();
auto output_frames_signal_end = output_frames.begin() + NUM_FRAMES_TO_OUTPUT;
std::vector<double> trimmed_output_frames(output_frames_signal_start, output_frames_signal_end);
std::vector<double> trimmed_output_frames(output_frames_signal_start,
output_frames_signal_end);
auto input_frames_signal_start = input_frames.begin() + phase;
auto input_frames_signal_end = input_frames.begin() + phase + NUM_FRAMES_TO_OUTPUT;
std::vector<double> trimmed_input_frames(input_frames_signal_start, input_frames_signal_end);
auto input_frames_signal_end =
input_frames.begin() + phase + NUM_FRAMES_TO_OUTPUT;
std::vector<double> trimmed_input_frames(input_frames_signal_start,
input_frames_signal_end);
compare_signals(trimmed_output_frames, trimmed_input_frames);
}
@ -330,7 +392,8 @@ TEST(cubeb, loopback_duplex)
run_loopback_duplex_test(false);
}
void run_loopback_separate_streams_test(bool is_float)
void
run_loopback_separate_streams_test(bool is_float)
{
cubeb * ctx;
cubeb_stream * input_stream;
@ -343,8 +406,8 @@ void run_loopback_separate_streams_test(bool is_float)
r = common_init(&ctx, "Cubeb loopback example: separate streams");
ASSERT_EQ(r, CUBEB_OK) << "Error initializing cubeb library";
std::unique_ptr<cubeb, decltype(&cubeb_destroy)>
cleanup_cubeb_at_exit(ctx, cubeb_destroy);
std::unique_ptr<cubeb, decltype(&cubeb_destroy)> cleanup_cubeb_at_exit(
ctx, cubeb_destroy);
if (!can_run_audio_input_test(ctx)) {
return;
@ -368,24 +431,26 @@ void run_loopback_separate_streams_test(bool is_float)
ASSERT_EQ(r, CUBEB_OK) << "Could not get minimal latency";
/* setup an input stream with loopback */
r = cubeb_stream_init(ctx, &input_stream, "Cubeb loopback input only",
NULL, &input_params, NULL, NULL, latency_frames,
is_float ? data_cb_loop_input_only<float> : data_cb_loop_input_only<short>,
r = cubeb_stream_init(ctx, &input_stream, "Cubeb loopback input only", NULL,
&input_params, NULL, NULL, latency_frames,
is_float ? data_cb_loop_input_only<float>
: data_cb_loop_input_only<short>,
state_cb_loop, user_data.get());
ASSERT_EQ(r, CUBEB_OK) << "Error initializing cubeb stream";
std::unique_ptr<cubeb_stream, decltype(&cubeb_stream_destroy)>
cleanup_input_stream_at_exit(input_stream, cubeb_stream_destroy);
cleanup_input_stream_at_exit(input_stream, cubeb_stream_destroy);
/* setup an output stream */
r = cubeb_stream_init(ctx, &output_stream, "Cubeb loopback output only",
NULL, NULL, NULL, &output_params, latency_frames,
is_float ? data_cb_playback<float> : data_cb_playback<short>,
r = cubeb_stream_init(ctx, &output_stream, "Cubeb loopback output only", NULL,
NULL, NULL, &output_params, latency_frames,
is_float ? data_cb_playback<float>
: data_cb_playback<short>,
state_cb_loop, user_data.get());
ASSERT_EQ(r, CUBEB_OK) << "Error initializing cubeb stream";
std::unique_ptr<cubeb_stream, decltype(&cubeb_stream_destroy)>
cleanup_output_stream_at_exit(output_stream, cubeb_stream_destroy);
cleanup_output_stream_at_exit(output_stream, cubeb_stream_destroy);
cubeb_stream_start(input_stream);
cubeb_stream_start(output_stream);
@ -393,22 +458,27 @@ void run_loopback_separate_streams_test(bool is_float)
cubeb_stream_stop(output_stream);
cubeb_stream_stop(input_stream);
/* access after stop should not happen, but lock just in case and to appease sanitization tools */
/* access after stop should not happen, but lock just in case and to appease
* sanitization tools */
std::lock_guard<std::mutex> lock(user_data->user_state_mutex);
std::vector<double> & output_frames = user_data->output_frames;
std::vector<double> & input_frames = user_data->input_frames;
ASSERT_LE(output_frames.size(), input_frames.size())
<< "#Output frames should be less or equal to #input frames";
<< "#Output frames should be less or equal to #input frames";
size_t phase = find_phase(user_data->output_frames, user_data->input_frames, NUM_FRAMES_TO_OUTPUT);
size_t phase = find_phase(user_data->output_frames, user_data->input_frames,
NUM_FRAMES_TO_OUTPUT);
/* extract vectors of just the relevant signal from output and input */
auto output_frames_signal_start = output_frames.begin();
auto output_frames_signal_end = output_frames.begin() + NUM_FRAMES_TO_OUTPUT;
std::vector<double> trimmed_output_frames(output_frames_signal_start, output_frames_signal_end);
std::vector<double> trimmed_output_frames(output_frames_signal_start,
output_frames_signal_end);
auto input_frames_signal_start = input_frames.begin() + phase;
auto input_frames_signal_end = input_frames.begin() + phase + NUM_FRAMES_TO_OUTPUT;
std::vector<double> trimmed_input_frames(input_frames_signal_start, input_frames_signal_end);
auto input_frames_signal_end =
input_frames.begin() + phase + NUM_FRAMES_TO_OUTPUT;
std::vector<double> trimmed_input_frames(input_frames_signal_start,
input_frames_signal_end);
compare_signals(trimmed_output_frames, trimmed_input_frames);
}
@ -419,7 +489,8 @@ TEST(cubeb, loopback_separate_streams)
run_loopback_separate_streams_test(false);
}
void run_loopback_silence_test(bool is_float)
void
run_loopback_silence_test(bool is_float)
{
cubeb * ctx;
cubeb_stream * input_stream;
@ -430,8 +501,8 @@ void run_loopback_silence_test(bool is_float)
r = common_init(&ctx, "Cubeb loopback example: record silence");
ASSERT_EQ(r, CUBEB_OK) << "Error initializing cubeb library";
std::unique_ptr<cubeb, decltype(&cubeb_destroy)>
cleanup_cubeb_at_exit(ctx, cubeb_destroy);
std::unique_ptr<cubeb, decltype(&cubeb_destroy)> cleanup_cubeb_at_exit(
ctx, cubeb_destroy);
if (!can_run_audio_input_test(ctx)) {
return;
@ -450,20 +521,22 @@ void run_loopback_silence_test(bool is_float)
ASSERT_EQ(r, CUBEB_OK) << "Could not get minimal latency";
/* setup an input stream with loopback */
r = cubeb_stream_init(ctx, &input_stream, "Cubeb loopback input only",
NULL, &input_params, NULL, NULL, latency_frames,
is_float ? data_cb_loop_input_only<float> : data_cb_loop_input_only<short>,
r = cubeb_stream_init(ctx, &input_stream, "Cubeb loopback input only", NULL,
&input_params, NULL, NULL, latency_frames,
is_float ? data_cb_loop_input_only<float>
: data_cb_loop_input_only<short>,
state_cb_loop, user_data.get());
ASSERT_EQ(r, CUBEB_OK) << "Error initializing cubeb stream";
std::unique_ptr<cubeb_stream, decltype(&cubeb_stream_destroy)>
cleanup_input_stream_at_exit(input_stream, cubeb_stream_destroy);
cleanup_input_stream_at_exit(input_stream, cubeb_stream_destroy);
cubeb_stream_start(input_stream);
delay(300);
cubeb_stream_stop(input_stream);
/* access after stop should not happen, but lock just in case and to appease sanitization tools */
/* access after stop should not happen, but lock just in case and to appease
* sanitization tools */
std::lock_guard<std::mutex> lock(user_data->user_state_mutex);
std::vector<double> & input_frames = user_data->input_frames;
@ -483,7 +556,8 @@ TEST(cubeb, loopback_silence)
run_loopback_silence_test(false);
}
void run_loopback_device_selection_test(bool is_float)
void
run_loopback_device_selection_test(bool is_float)
{
cubeb * ctx;
cubeb_device_collection collection;
@ -494,11 +568,12 @@ void run_loopback_device_selection_test(bool is_float)
int r;
uint32_t latency_frames = 0;
r = common_init(&ctx, "Cubeb loopback example: device selection, separate streams");
r = common_init(&ctx,
"Cubeb loopback example: device selection, separate streams");
ASSERT_EQ(r, CUBEB_OK) << "Error initializing cubeb library";
std::unique_ptr<cubeb, decltype(&cubeb_destroy)>
cleanup_cubeb_at_exit(ctx, cubeb_destroy);
std::unique_ptr<cubeb, decltype(&cubeb_destroy)> cleanup_cubeb_at_exit(
ctx, cubeb_destroy);
if (!can_run_audio_input_test(ctx)) {
return;
@ -507,7 +582,7 @@ void run_loopback_device_selection_test(bool is_float)
r = cubeb_enumerate_devices(ctx, CUBEB_DEVICE_TYPE_OUTPUT, &collection);
if (r == CUBEB_ERROR_NOT_SUPPORTED) {
fprintf(stderr, "Device enumeration not supported"
" for this backend, skipping this test.\n");
" for this backend, skipping this test.\n");
return;
}
@ -545,23 +620,26 @@ void run_loopback_device_selection_test(bool is_float)
/* setup an input stream with loopback */
r = cubeb_stream_init(ctx, &input_stream, "Cubeb loopback input only",
device_id.c_str(), &input_params, NULL, NULL, latency_frames,
is_float ? data_cb_loop_input_only<float> : data_cb_loop_input_only<short>,
device_id.c_str(), &input_params, NULL, NULL,
latency_frames,
is_float ? data_cb_loop_input_only<float>
: data_cb_loop_input_only<short>,
state_cb_loop, user_data.get());
ASSERT_EQ(r, CUBEB_OK) << "Error initializing cubeb stream";
std::unique_ptr<cubeb_stream, decltype(&cubeb_stream_destroy)>
cleanup_input_stream_at_exit(input_stream, cubeb_stream_destroy);
cleanup_input_stream_at_exit(input_stream, cubeb_stream_destroy);
/* setup an output stream */
r = cubeb_stream_init(ctx, &output_stream, "Cubeb loopback output only",
NULL, NULL, device_id.c_str(), &output_params, latency_frames,
is_float ? data_cb_playback<float> : data_cb_playback<short>,
r = cubeb_stream_init(ctx, &output_stream, "Cubeb loopback output only", NULL,
NULL, device_id.c_str(), &output_params, latency_frames,
is_float ? data_cb_playback<float>
: data_cb_playback<short>,
state_cb_loop, user_data.get());
ASSERT_EQ(r, CUBEB_OK) << "Error initializing cubeb stream";
std::unique_ptr<cubeb_stream, decltype(&cubeb_stream_destroy)>
cleanup_output_stream_at_exit(output_stream, cubeb_stream_destroy);
cleanup_output_stream_at_exit(output_stream, cubeb_stream_destroy);
cubeb_stream_start(input_stream);
cubeb_stream_start(output_stream);
@ -569,22 +647,27 @@ void run_loopback_device_selection_test(bool is_float)
cubeb_stream_stop(output_stream);
cubeb_stream_stop(input_stream);
/* access after stop should not happen, but lock just in case and to appease sanitization tools */
/* access after stop should not happen, but lock just in case and to appease
* sanitization tools */
std::lock_guard<std::mutex> lock(user_data->user_state_mutex);
std::vector<double> & output_frames = user_data->output_frames;
std::vector<double> & input_frames = user_data->input_frames;
ASSERT_LE(output_frames.size(), input_frames.size())
<< "#Output frames should be less or equal to #input frames";
<< "#Output frames should be less or equal to #input frames";
size_t phase = find_phase(user_data->output_frames, user_data->input_frames, NUM_FRAMES_TO_OUTPUT);
size_t phase = find_phase(user_data->output_frames, user_data->input_frames,
NUM_FRAMES_TO_OUTPUT);
/* extract vectors of just the relevant signal from output and input */
auto output_frames_signal_start = output_frames.begin();
auto output_frames_signal_end = output_frames.begin() + NUM_FRAMES_TO_OUTPUT;
std::vector<double> trimmed_output_frames(output_frames_signal_start, output_frames_signal_end);
std::vector<double> trimmed_output_frames(output_frames_signal_start,
output_frames_signal_end);
auto input_frames_signal_start = input_frames.begin() + phase;
auto input_frames_signal_end = input_frames.begin() + phase + NUM_FRAMES_TO_OUTPUT;
std::vector<double> trimmed_input_frames(input_frames_signal_start, input_frames_signal_end);
auto input_frames_signal_end =
input_frames.begin() + phase + NUM_FRAMES_TO_OUTPUT;
std::vector<double> trimmed_input_frames(input_frames_signal_start,
input_frames_signal_end);
compare_signals(trimmed_output_frames, trimmed_input_frames);
}

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

@ -9,22 +9,24 @@
#if !defined(_XOPEN_SOURCE)
#define _XOPEN_SOURCE 600
#endif
#include <stdio.h>
#include <stdlib.h>
#include "cubeb/cubeb.h"
#include <atomic>
#include <math.h>
#include <memory>
#include <atomic>
#include "cubeb/cubeb.h"
//#define ENABLE_NORMAL_LOG
//#define ENABLE_VERBOSE_LOG
#include <stdio.h>
#include <stdlib.h>
// #define ENABLE_NORMAL_LOG
// #define ENABLE_VERBOSE_LOG
#include "common.h"
#define SAMPLE_FREQUENCY 48000
#define STREAM_FORMAT CUBEB_SAMPLE_S16LE
std::atomic<bool> load_callback{ false };
std::atomic<bool> load_callback{false};
long data_cb(cubeb_stream * stream, void * user, const void * inputbuffer, void * outputbuffer, long nframes)
static long
data_cb(cubeb_stream * stream, void * user, const void * inputbuffer,
void * outputbuffer, long nframes)
{
if (load_callback) {
fprintf(stderr, "Sleeping...\n");
@ -34,21 +36,27 @@ long data_cb(cubeb_stream * stream, void * user, const void * inputbuffer, void
return nframes;
}
void state_cb(cubeb_stream * stream, void * /*user*/, cubeb_state state)
static void
state_cb(cubeb_stream * stream, void * /*user*/, cubeb_state state)
{
ASSERT_TRUE(!!stream);
switch (state) {
case CUBEB_STATE_STARTED:
fprintf(stderr, "stream started\n"); break;
fprintf(stderr, "stream started\n");
break;
case CUBEB_STATE_STOPPED:
fprintf(stderr, "stream stopped\n"); break;
fprintf(stderr, "stream stopped\n");
break;
case CUBEB_STATE_DRAINED:
FAIL() << "this test is not supposed to drain"; break;
FAIL() << "this test is not supposed to drain";
break;
case CUBEB_STATE_ERROR:
fprintf(stderr, "stream error\n"); break;
fprintf(stderr, "stream error\n");
break;
default:
FAIL() << "this test is not supposed to have a weird state"; break;
FAIL() << "this test is not supposed to have a weird state";
break;
}
}
@ -63,8 +71,8 @@ TEST(cubeb, overload_callback)
r = common_init(&ctx, "Cubeb callback overload");
ASSERT_EQ(r, CUBEB_OK);
std::unique_ptr<cubeb, decltype(&cubeb_destroy)>
cleanup_cubeb_at_exit(ctx, cubeb_destroy);
std::unique_ptr<cubeb, decltype(&cubeb_destroy)> cleanup_cubeb_at_exit(
ctx, cubeb_destroy);
// This test is specifically designed to test a behaviour of the WASAPI
// backend in a specific scenario.
@ -81,13 +89,12 @@ TEST(cubeb, overload_callback)
r = cubeb_get_min_latency(ctx, &output_params, &latency_frames);
ASSERT_EQ(r, CUBEB_OK);
r = cubeb_stream_init(ctx, &stream, "Cubeb",
NULL, NULL, NULL, &output_params,
r = cubeb_stream_init(ctx, &stream, "Cubeb", NULL, NULL, NULL, &output_params,
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);
cleanup_stream_at_exit(stream, cubeb_stream_destroy);
cubeb_stream_start(stream);
delay(500);

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

@ -10,31 +10,32 @@
#if !defined(_XOPEN_SOURCE)
#define _XOPEN_SOURCE 600
#endif
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <memory>
#include "cubeb/cubeb.h"
#include <atomic>
#include <math.h>
#include <memory>
#include <stdio.h>
#include <stdlib.h>
//#define ENABLE_NORMAL_LOG
//#define ENABLE_VERBOSE_LOG
// #define ENABLE_NORMAL_LOG
// #define ENABLE_VERBOSE_LOG
#include "common.h"
#define SAMPLE_FREQUENCY 48000
#define STREAM_FORMAT CUBEB_SAMPLE_FLOAT32LE
struct user_state_record
{
std::atomic<int> invalid_audio_value{ 0 };
struct user_state_record {
std::atomic<int> invalid_audio_value{0};
};
long data_cb_record(cubeb_stream * stream, void * user, const void * inputbuffer, void * outputbuffer, long nframes)
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);
float *b = (float *)inputbuffer;
user_state_record * u = reinterpret_cast<user_state_record *>(user);
float * b = (float *)inputbuffer;
if (stream == NULL || inputbuffer == NULL || outputbuffer != NULL) {
if (stream == NULL || inputbuffer == NULL || outputbuffer != NULL) {
return CUBEB_ERROR;
}
@ -48,18 +49,22 @@ long data_cb_record(cubeb_stream * stream, void * user, const void * inputbuffer
return nframes;
}
void state_cb_record(cubeb_stream * stream, void * /*user*/, cubeb_state state)
void
state_cb_record(cubeb_stream * stream, void * /*user*/, cubeb_state state)
{
if (stream == NULL)
return;
switch (state) {
case CUBEB_STATE_STARTED:
fprintf(stderr, "stream started\n"); break;
fprintf(stderr, "stream started\n");
break;
case CUBEB_STATE_STOPPED:
fprintf(stderr, "stream stopped\n"); break;
fprintf(stderr, "stream stopped\n");
break;
case CUBEB_STATE_DRAINED:
fprintf(stderr, "stream drained\n"); break;
fprintf(stderr, "stream drained\n");
break;
default:
fprintf(stderr, "unknown stream state %d\n", state);
}
@ -69,11 +74,12 @@ 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) {
if (cubeb_set_log_callback(CUBEB_LOG_DISABLED, nullptr /*print_log*/) !=
CUBEB_OK) {
fprintf(stderr, "Set log callback failed\n");
}
cubeb *ctx;
cubeb_stream *stream;
cubeb * ctx;
cubeb_stream * stream;
cubeb_stream_params params;
int r;
user_state_record stream_state;
@ -81,8 +87,8 @@ TEST(cubeb, record)
r = common_init(&ctx, "Cubeb record example");
ASSERT_EQ(r, CUBEB_OK) << "Error initializing cubeb library";
std::unique_ptr<cubeb, decltype(&cubeb_destroy)>
cleanup_cubeb_at_exit(ctx, cubeb_destroy);
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. */
@ -96,12 +102,13 @@ TEST(cubeb, record)
params.layout = CUBEB_LAYOUT_UNDEFINED;
params.prefs = CUBEB_STREAM_PREF_NONE;
r = cubeb_stream_init(ctx, &stream, "Cubeb record (mono)", NULL, &params, NULL, nullptr,
4096, data_cb_record, state_cb_record, &stream_state);
r = cubeb_stream_init(ctx, &stream, "Cubeb record (mono)", NULL, &params,
NULL, nullptr, 4096, data_cb_record, state_cb_record,
&stream_state);
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);
cleanup_stream_at_exit(stream, cubeb_stream_destroy);
cubeb_stream_start(stream);
delay(500);

Разница между файлами не показана из-за своего большого размера Загрузить разницу

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

@ -1,10 +1,10 @@
#include "gtest/gtest.h"
#ifdef __APPLE__
#include <string.h>
#include <iostream>
#include <CoreAudio/CoreAudioTypes.h>
#include "cubeb/cubeb.h"
#include "cubeb_ring_array.h"
#include <CoreAudio/CoreAudioTypes.h>
#include <iostream>
#include <string.h>
TEST(cubeb, ring_array)
{
@ -15,12 +15,12 @@ TEST(cubeb, ring_array)
unsigned int capacity = 8;
ring_array_init(&ra, capacity, sizeof(int), 1, 1);
int verify_data[capacity] ;// {1,2,3,4,5,6,7,8};
int verify_data[capacity]; // {1,2,3,4,5,6,7,8};
AudioBuffer * p_data = NULL;
for (unsigned int i = 0; i < capacity; ++i) {
verify_data[i] = i; // in case capacity change value
*(int*)ra.buffer_array[i].mData = i;
*(int *)ra.buffer_array[i].mData = i;
ASSERT_EQ(ra.buffer_array[i].mDataByteSize, sizeof(int));
ASSERT_EQ(ra.buffer_array[i].mNumberChannels, 1u);
}
@ -29,7 +29,7 @@ TEST(cubeb, ring_array)
for (unsigned int i = 0; i < capacity; ++i) {
p_data = ring_array_get_free_buffer(&ra);
ASSERT_NE(p_data, nullptr);
ASSERT_EQ(*(int*)p_data->mData, verify_data[i]);
ASSERT_EQ(*(int *)p_data->mData, verify_data[i]);
}
/*Now array is full extra store should give NULL*/
ASSERT_EQ(ring_array_get_free_buffer(&ra), nullptr);
@ -37,14 +37,14 @@ TEST(cubeb, ring_array)
for (unsigned int i = 0; i < capacity; ++i) {
p_data = ring_array_get_data_buffer(&ra);
ASSERT_NE(p_data, nullptr);
ASSERT_EQ(*(int*)p_data->mData, verify_data[i]);
ASSERT_EQ(*(int *)p_data->mData, verify_data[i]);
}
/*Now array is empty extra fetch should give NULL*/
ASSERT_EQ(ring_array_get_data_buffer(&ra), nullptr);
p_data = NULL;
/* Repeated store fetch should can go for ever*/
for (unsigned int i = 0; i < 2*capacity; ++i) {
for (unsigned int i = 0; i < 2 * capacity; ++i) {
p_data = ring_array_get_free_buffer(&ra);
ASSERT_NE(p_data, nullptr);
ASSERT_EQ(ring_array_get_data_buffer(&ra), p_data);
@ -55,19 +55,18 @@ TEST(cubeb, ring_array)
for (unsigned int i = 0; i < capacity; ++i) {
p_data = ring_array_get_free_buffer(&ra);
ASSERT_NE(p_data, nullptr);
ASSERT_EQ(*((int*)p_data->mData), verify_data[i]);
(*((int*)p_data->mData))++; // Modify data
ASSERT_EQ(*((int *)p_data->mData), verify_data[i]);
(*((int *)p_data->mData))++; // Modify data
}
for (unsigned int i = 0; i < capacity; ++i) {
p_data = ring_array_get_data_buffer(&ra);
ASSERT_NE(p_data, nullptr);
ASSERT_EQ(*((int*)p_data->mData), verify_data[i]+1); // Verify modified data
ASSERT_EQ(*((int *)p_data->mData),
verify_data[i] + 1); // Verify modified data
}
ring_array_destroy(&ra);
}
#else
TEST(cubeb, DISABLED_ring_array)
{
}
TEST(cubeb, DISABLED_ring_array) {}
#endif

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

@ -5,22 +5,20 @@
* accompanying file LICENSE for details.
*/
#ifndef NOMINMAX
#define NOMINMAX
#endif
#include "gtest/gtest.h"
#include "cubeb_ringbuffer.h"
#include "gtest/gtest.h"
#include <chrono>
#include <iostream>
#include <thread>
#include <chrono>
/* Generate a monotonically increasing sequence of numbers. */
template<typename T>
class sequence_generator
{
template <typename T> class sequence_generator {
public:
sequence_generator(size_t channels)
: channels(channels)
{ }
sequence_generator(size_t channels) : channels(channels) {}
void get(T * elements, size_t frames)
{
for (size_t i = 0; i < frames; i++) {
@ -30,44 +28,41 @@ public:
index_++;
}
}
void rewind(size_t frames)
{
index_ -= frames;
}
void rewind(size_t frames) { index_ -= frames; }
private:
size_t index_ = 0;
size_t channels = 0;
};
/* Checks that a sequence is monotonically increasing. */
template<typename T>
class sequence_verifier
{
public:
sequence_verifier(size_t channels)
: channels(channels)
{ }
void check(T * elements, size_t frames)
{
for (size_t i = 0; i < frames; i++) {
for (size_t c = 0; c < channels; c++) {
if (elements[i * channels + c] != static_cast<T>(index_)) {
std::cerr << "Element " << i << " is different. Expected "
<< static_cast<T>(index_) << ", got " << elements[i]
<< ". (channel count: " << channels << ")." << std::endl;
ASSERT_TRUE(false);
}
template <typename T> class sequence_verifier {
public:
sequence_verifier(size_t channels) : channels(channels) {}
void check(T * elements, size_t frames)
{
for (size_t i = 0; i < frames; i++) {
for (size_t c = 0; c < channels; c++) {
if (elements[i * channels + c] != static_cast<T>(index_)) {
std::cerr << "Element " << i << " is different. Expected "
<< static_cast<T>(index_) << ", got " << elements[i]
<< ". (channel count: " << channels << ")." << std::endl;
ASSERT_TRUE(false);
}
index_++;
}
index_++;
}
private:
size_t index_ = 0;
size_t channels = 0;
}
private:
size_t index_ = 0;
size_t channels = 0;
};
template<typename T>
void test_ring(lock_free_audio_ring_buffer<T>& buf, int channels, int capacity_frames)
template <typename T>
void
test_ring(lock_free_audio_ring_buffer<T> & buf, int channels,
int capacity_frames)
{
std::unique_ptr<T[]> seq(new T[capacity_frames * channels]);
sequence_generator<T> gen(channels);
@ -77,7 +72,7 @@ void test_ring(lock_free_audio_ring_buffer<T>& buf, int channels, int capacity_f
const int block_size = 128;
while(iterations--) {
while (iterations--) {
gen.get(seq.get(), block_size);
int rv = buf.enqueue(seq.get(), block_size);
ASSERT_EQ(rv, block_size);
@ -88,8 +83,10 @@ void test_ring(lock_free_audio_ring_buffer<T>& buf, int channels, int capacity_f
}
}
template<typename T>
void test_ring_multi(lock_free_audio_ring_buffer<T>& buf, int channels, int capacity_frames)
template <typename T>
void
test_ring_multi(lock_free_audio_ring_buffer<T> & buf, int channels,
int capacity_frames)
{
sequence_verifier<T> checker(channels);
std::unique_ptr<T[]> out_buffer(new T[capacity_frames * channels]);
@ -101,7 +98,7 @@ void test_ring_multi(lock_free_audio_ring_buffer<T>& buf, int channels, int capa
std::unique_ptr<T[]> in_buffer(new T[capacity_frames * channels]);
sequence_generator<T> gen(channels);
while(iterations--) {
while (iterations--) {
std::this_thread::yield();
gen.get(in_buffer.get(), block_size);
int rv = buf.enqueue(in_buffer.get(), block_size);
@ -114,7 +111,7 @@ void test_ring_multi(lock_free_audio_ring_buffer<T>& buf, int channels, int capa
int remaining = 1002;
while(remaining--) {
while (remaining--) {
std::this_thread::yield();
int rv = buf.dequeue(out_buffer.get(), block_size);
ASSERT_TRUE(rv <= block_size);
@ -124,8 +121,9 @@ void test_ring_multi(lock_free_audio_ring_buffer<T>& buf, int channels, int capa
t.join();
}
template<typename T>
void basic_api_test(T& ring)
template <typename T>
void
basic_api_test(T & ring)
{
ASSERT_EQ(ring.capacity(), 128);
@ -155,30 +153,32 @@ void basic_api_test(T& ring)
ASSERT_EQ(ring.available_write(), 128);
}
void test_reset_api() {
const size_t ring_buffer_size = 128;
const size_t enqueue_size = ring_buffer_size / 2;
void
test_reset_api()
{
const size_t ring_buffer_size = 128;
const size_t enqueue_size = ring_buffer_size / 2;
lock_free_queue<float> ring(ring_buffer_size);
std::thread t([=, &ring] {
std::unique_ptr<float[]> in_buffer(new float[enqueue_size]);
ring.enqueue(in_buffer.get(), enqueue_size);
});
lock_free_queue<float> ring(ring_buffer_size);
std::thread t([=, &ring] {
std::unique_ptr<float[]> in_buffer(new float[enqueue_size]);
ring.enqueue(in_buffer.get(), enqueue_size);
});
t.join();
t.join();
ring.reset_thread_ids();
ring.reset_thread_ids();
// Enqueue with a different thread. We have reset the thread ID
// in the ring buffer, this should work.
std::thread t2([=, &ring] {
std::unique_ptr<float[]> in_buffer(new float[enqueue_size]);
ring.enqueue(in_buffer.get(), enqueue_size);
});
// Enqueue with a different thread. We have reset the thread ID
// in the ring buffer, this should work.
std::thread t2([=, &ring] {
std::unique_ptr<float[]> in_buffer(new float[enqueue_size]);
ring.enqueue(in_buffer.get(), enqueue_size);
});
t2.join();
t2.join();
ASSERT_TRUE(true);
ASSERT_TRUE(true);
}
TEST(cubeb, ring_buffer)
@ -206,8 +206,8 @@ TEST(cubeb, ring_buffer)
/* Test mono to 9.1 */
for (size_t channels = min_channels; channels < max_channels; channels++) {
/* Use non power-of-two numbers to catch edge-cases. */
for (size_t capacity_frames = min_capacity;
capacity_frames < max_capacity; capacity_frames+=capacity_increment) {
for (size_t capacity_frames = min_capacity; capacity_frames < max_capacity;
capacity_frames += capacity_increment) {
lock_free_audio_ring_buffer<float> ring(channels, capacity_frames);
test_ring(ring, channels, capacity_frames);
}
@ -216,8 +216,8 @@ TEST(cubeb, ring_buffer)
/* Multi thread testing */
for (size_t channels = min_channels; channels < max_channels; channels++) {
/* Use non power-of-two numbers to catch edge-cases. */
for (size_t capacity_frames = min_capacity;
capacity_frames < max_capacity; capacity_frames+=capacity_increment) {
for (size_t capacity_frames = min_capacity; capacity_frames < max_capacity;
capacity_frames += capacity_increment) {
lock_free_audio_ring_buffer<short> ring(channels, capacity_frames);
test_ring_multi(ring, channels, capacity_frames);
}

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

@ -10,12 +10,12 @@
#endif
#include "cubeb/cubeb.h"
#include <atomic>
#include <math.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
//#define ENABLE_NORMAL_LOG
//#define ENABLE_VERBOSE_LOG
// #define ENABLE_NORMAL_LOG
// #define ENABLE_VERBOSE_LOG
#include "common.h"
#define STREAM_RATE 44100
@ -24,14 +24,17 @@
#define STREAM_LAYOUT CUBEB_LAYOUT_MONO
#define STREAM_FORMAT CUBEB_SAMPLE_S16LE
int is_windows_7()
int
is_windows_7()
{
#ifdef __MINGW32__
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");
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
#if (defined(_WIN32) || defined(__WIN32__)) && ( !defined(__MINGW32__))
#if (defined(_WIN32) || defined(__WIN32__)) && (!defined(__MINGW32__))
OSVERSIONINFOEX osvi;
DWORDLONG condition_mask = 0;
@ -45,7 +48,8 @@ int is_windows_7()
VER_SET_CONDITION(condition_mask, VER_MAJORVERSION, VER_EQUAL);
VER_SET_CONDITION(condition_mask, VER_MINORVERSION, VER_GREATER_EQUAL);
return VerifyVersionInfo(&osvi, VER_MAJORVERSION | VER_MINORVERSION, condition_mask);
return VerifyVersionInfo(&osvi, VER_MAJORVERSION | VER_MINORVERSION,
condition_mask);
#else
return 0;
#endif
@ -56,7 +60,9 @@ static std::atomic<uint64_t> total_frames_written;
static int delay_callback;
static long
test_data_callback(cubeb_stream * stm, void * user_ptr, const void * /*inputbuffer*/, void * outputbuffer, long nframes)
test_data_callback(cubeb_stream * stm, void * user_ptr,
const void * /*inputbuffer*/, void * outputbuffer,
long nframes)
{
EXPECT_TRUE(stm && user_ptr == &dummy && outputbuffer && nframes > 0);
assert(outputbuffer);
@ -70,7 +76,8 @@ test_data_callback(cubeb_stream * stm, void * user_ptr, const void * /*inputbuff
}
void
test_state_callback(cubeb_stream * /*stm*/, void * /*user_ptr*/, cubeb_state /*state*/)
test_state_callback(cubeb_stream * /*stm*/, void * /*user_ptr*/,
cubeb_state /*state*/)
{
}
@ -78,7 +85,7 @@ TEST(cubeb, init_destroy_context)
{
int r;
cubeb * ctx;
char const* backend_id;
char const * backend_id;
r = common_init(&ctx, "test_sanity");
ASSERT_EQ(r, CUBEB_OK);
@ -161,8 +168,9 @@ TEST(cubeb, init_destroy_stream)
params.layout = STREAM_LAYOUT;
params.prefs = CUBEB_STREAM_PREF_NONE;
r = cubeb_stream_init(ctx, &stream, "test", NULL, NULL, NULL, &params, STREAM_LATENCY,
test_data_callback, test_state_callback, &dummy);
r = cubeb_stream_init(ctx, &stream, "test", NULL, NULL, NULL, &params,
STREAM_LATENCY, test_data_callback, test_state_callback,
&dummy);
ASSERT_EQ(r, CUBEB_OK);
ASSERT_NE(stream, nullptr);
@ -189,8 +197,9 @@ TEST(cubeb, init_destroy_multiple_streams)
params.prefs = CUBEB_STREAM_PREF_NONE;
for (i = 0; i < ARRAY_LENGTH(stream); ++i) {
r = cubeb_stream_init(ctx, &stream[i], "test", NULL, NULL, NULL, &params, STREAM_LATENCY,
test_data_callback, test_state_callback, &dummy);
r = cubeb_stream_init(ctx, &stream[i], "test", NULL, NULL, NULL, &params,
STREAM_LATENCY, test_data_callback,
test_state_callback, &dummy);
ASSERT_EQ(r, CUBEB_OK);
ASSERT_NE(stream[i], nullptr);
}
@ -219,8 +228,9 @@ TEST(cubeb, configure_stream)
params.layout = CUBEB_LAYOUT_STEREO;
params.prefs = CUBEB_STREAM_PREF_NONE;
r = cubeb_stream_init(ctx, &stream, "test", NULL, NULL, NULL, &params, STREAM_LATENCY,
test_data_callback, test_state_callback, &dummy);
r = cubeb_stream_init(ctx, &stream, "test", NULL, NULL, NULL, &params,
STREAM_LATENCY, test_data_callback, test_state_callback,
&dummy);
ASSERT_EQ(r, CUBEB_OK);
ASSERT_NE(stream, nullptr);
@ -251,8 +261,9 @@ TEST(cubeb, configure_stream_undefined_layout)
params.layout = CUBEB_LAYOUT_UNDEFINED;
params.prefs = CUBEB_STREAM_PREF_NONE;
r = cubeb_stream_init(ctx, &stream, "test", NULL, NULL, NULL, &params, STREAM_LATENCY,
test_data_callback, test_state_callback, &dummy);
r = cubeb_stream_init(ctx, &stream, "test", NULL, NULL, NULL, &params,
STREAM_LATENCY, test_data_callback, test_state_callback,
&dummy);
ASSERT_EQ(r, CUBEB_OK);
ASSERT_NE(stream, nullptr);
@ -288,8 +299,9 @@ test_init_start_stop_destroy_multiple_streams(int early, int delay_ms)
params.prefs = CUBEB_STREAM_PREF_NONE;
for (i = 0; i < ARRAY_LENGTH(stream); ++i) {
r = cubeb_stream_init(ctx, &stream[i], "test", NULL, NULL, NULL, &params, STREAM_LATENCY,
test_data_callback, test_state_callback, &dummy);
r = cubeb_stream_init(ctx, &stream[i], "test", NULL, NULL, NULL, &params,
STREAM_LATENCY, test_data_callback,
test_state_callback, &dummy);
ASSERT_EQ(r, CUBEB_OK);
ASSERT_NE(stream[i], nullptr);
if (early) {
@ -378,7 +390,8 @@ TEST(cubeb, init_destroy_multiple_contexts_and_streams)
ASSERT_NE(ctx[i], nullptr);
for (j = 0; j < streams_per_ctx; ++j) {
r = cubeb_stream_init(ctx[i], &stream[i * streams_per_ctx + j], "test", NULL, NULL, NULL, &params, STREAM_LATENCY,
r = cubeb_stream_init(ctx[i], &stream[i * streams_per_ctx + j], "test",
NULL, NULL, NULL, &params, STREAM_LATENCY,
test_data_callback, test_state_callback, &dummy);
ASSERT_EQ(r, CUBEB_OK);
ASSERT_NE(stream[i * streams_per_ctx + j], nullptr);
@ -412,8 +425,9 @@ TEST(cubeb, basic_stream_operations)
params.layout = STREAM_LAYOUT;
params.prefs = CUBEB_STREAM_PREF_NONE;
r = cubeb_stream_init(ctx, &stream, "test", NULL, NULL, NULL, &params, STREAM_LATENCY,
test_data_callback, test_state_callback, &dummy);
r = cubeb_stream_init(ctx, &stream, "test", NULL, NULL, NULL, &params,
STREAM_LATENCY, test_data_callback, test_state_callback,
&dummy);
ASSERT_EQ(r, CUBEB_OK);
ASSERT_NE(stream, nullptr);
@ -470,8 +484,9 @@ TEST(cubeb, stream_position)
params.layout = STREAM_LAYOUT;
params.prefs = CUBEB_STREAM_PREF_NONE;
r = cubeb_stream_init(ctx, &stream, "test", NULL, NULL, NULL, &params, STREAM_LATENCY,
test_data_callback, test_state_callback, &dummy);
r = cubeb_stream_init(ctx, &stream, "test", NULL, NULL, NULL, &params,
STREAM_LATENCY, test_data_callback, test_state_callback,
&dummy);
ASSERT_EQ(r, CUBEB_OK);
ASSERT_NE(stream, nullptr);
@ -559,7 +574,9 @@ static std::atomic<int> do_drain;
static std::atomic<int> got_drain;
static long
test_drain_data_callback(cubeb_stream * stm, void * user_ptr, const void * /*inputbuffer*/, void * outputbuffer, long nframes)
test_drain_data_callback(cubeb_stream * stm, void * user_ptr,
const void * /*inputbuffer*/, void * outputbuffer,
long nframes)
{
EXPECT_TRUE(stm && user_ptr == &dummy && outputbuffer && nframes > 0);
assert(outputbuffer);
@ -575,7 +592,8 @@ test_drain_data_callback(cubeb_stream * stm, void * user_ptr, const void * /*inp
}
void
test_drain_state_callback(cubeb_stream * /*stm*/, void * /*user_ptr*/, cubeb_state state)
test_drain_state_callback(cubeb_stream * /*stm*/, void * /*user_ptr*/,
cubeb_state state)
{
if (state == CUBEB_STATE_DRAINED) {
ASSERT_TRUE(!got_drain);
@ -604,8 +622,9 @@ TEST(cubeb, drain)
params.layout = STREAM_LAYOUT;
params.prefs = CUBEB_STREAM_PREF_NONE;
r = cubeb_stream_init(ctx, &stream, "test", NULL, NULL, NULL, &params, STREAM_LATENCY,
test_drain_data_callback, test_drain_state_callback, &dummy);
r = cubeb_stream_init(ctx, &stream, "test", NULL, NULL, NULL, &params,
STREAM_LATENCY, test_drain_data_callback,
test_drain_state_callback, &dummy);
ASSERT_EQ(r, CUBEB_OK);
ASSERT_NE(stream, nullptr);
@ -631,9 +650,9 @@ TEST(cubeb, drain)
ASSERT_EQ(r, CUBEB_OK);
ASSERT_TRUE(got_drain);
// Really, we should be able to rely on position reaching our final written frame, but
// for now let's make sure it doesn't continue beyond that point.
//ASSERT_LE(position, total_frames_written.load());
// Really, we should be able to rely on position reaching our final written
// frame, but for now let's make sure it doesn't continue beyond that point.
// ASSERT_LE(position, total_frames_written.load());
cubeb_stream_destroy(stream);
cubeb_destroy(ctx);
@ -663,7 +682,7 @@ TEST(cubeb, stable_devid)
cubeb_device_collection first;
cubeb_device_collection second;
cubeb_device_type all_devices =
(cubeb_device_type) (CUBEB_DEVICE_TYPE_INPUT | CUBEB_DEVICE_TYPE_OUTPUT);
(cubeb_device_type)(CUBEB_DEVICE_TYPE_INPUT | CUBEB_DEVICE_TYPE_OUTPUT);
size_t n;
r = common_init(&ctx, "test_sanity");

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

@ -10,19 +10,18 @@
#if !defined(_XOPEN_SOURCE)
#define _XOPEN_SOURCE 600
#endif
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <memory>
#include <limits.h>
#include "cubeb/cubeb.h"
#include <atomic>
#include <limits.h>
#include <math.h>
#include <memory>
#include <stdio.h>
#include <stdlib.h>
//#define ENABLE_NORMAL_LOG
//#define ENABLE_VERBOSE_LOG
// #define ENABLE_NORMAL_LOG
// #define ENABLE_VERBOSE_LOG
#include "common.h"
#define SAMPLE_FREQUENCY 48000
#define STREAM_FORMAT CUBEB_SAMPLE_S16LE
@ -31,10 +30,12 @@ struct cb_user_data {
std::atomic<long> position;
};
long data_cb_tone(cubeb_stream *stream, void *user, const void* /*inputbuffer*/, void *outputbuffer, long nframes)
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;
short *b = (short *)outputbuffer;
struct cb_user_data * u = (struct cb_user_data *)user;
short * b = (short *)outputbuffer;
float t1, t2;
int i;
@ -44,9 +45,9 @@ long data_cb_tone(cubeb_stream *stream, void *user, const void* /*inputbuffer*/,
/* generate our test tone on the fly */
for (i = 0; i < nframes; i++) {
/* 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);
b[i] = (SHRT_MAX / 2) * t1;
t1 = sin(2 * M_PI * (i + u->position) * 350 / SAMPLE_FREQUENCY);
t2 = sin(2 * M_PI * (i + u->position) * 440 / SAMPLE_FREQUENCY);
b[i] = (SHRT_MAX / 2) * t1;
b[i] += (SHRT_MAX / 2) * t2;
/* European dial tone */
/*
@ -61,20 +62,24 @@ long data_cb_tone(cubeb_stream *stream, void *user, const void* /*inputbuffer*/,
return nframes;
}
void state_cb_tone(cubeb_stream *stream, void *user, cubeb_state state)
void
state_cb_tone(cubeb_stream * stream, void * user, cubeb_state state)
{
struct cb_user_data *u = (struct cb_user_data *)user;
struct cb_user_data * u = (struct cb_user_data *)user;
if (stream == NULL || u == NULL)
return;
switch (state) {
case CUBEB_STATE_STARTED:
fprintf(stderr, "stream started\n"); break;
fprintf(stderr, "stream started\n");
break;
case CUBEB_STATE_STOPPED:
fprintf(stderr, "stream stopped\n"); break;
fprintf(stderr, "stream stopped\n");
break;
case CUBEB_STATE_DRAINED:
fprintf(stderr, "stream drained\n"); break;
fprintf(stderr, "stream drained\n");
break;
default:
fprintf(stderr, "unknown stream state %d\n", state);
}
@ -84,16 +89,16 @@ void state_cb_tone(cubeb_stream *stream, void *user, cubeb_state state)
TEST(cubeb, tone)
{
cubeb *ctx;
cubeb_stream *stream;
cubeb * ctx;
cubeb_stream * stream;
cubeb_stream_params params;
int r;
r = common_init(&ctx, "Cubeb tone example");
ASSERT_EQ(r, CUBEB_OK) << "Error initializing cubeb library";
std::unique_ptr<cubeb, decltype(&cubeb_destroy)>
cleanup_cubeb_at_exit(ctx, cubeb_destroy);
std::unique_ptr<cubeb, decltype(&cubeb_destroy)> cleanup_cubeb_at_exit(
ctx, cubeb_destroy);
params.format = STREAM_FORMAT;
params.rate = SAMPLE_FREQUENCY;
@ -106,12 +111,13 @@ TEST(cubeb, tone)
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.get());
r = cubeb_stream_init(ctx, &stream, "Cubeb tone (mono)", NULL, NULL, NULL,
&params, 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);
cleanup_stream_at_exit(stream, cubeb_stream_destroy);
cubeb_stream_start(stream);
delay(5000);

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

@ -1,5 +1,5 @@
#include "gtest/gtest.h"
#include "cubeb_utils.h"
#include "gtest/gtest.h"
TEST(cubeb, auto_array)
{
@ -10,7 +10,6 @@ TEST(cubeb, auto_array)
ASSERT_EQ(array2.length(), 0u);
ASSERT_EQ(array2.capacity(), 10u);
for (uint32_t i = 0; i < 10; i++) {
a[i] = i;
}
@ -39,7 +38,7 @@ TEST(cubeb, auto_array)
ASSERT_EQ(b[i], i);
ASSERT_EQ(array.data()[i], 5 + i);
}
uint32_t* bb = b + 5;
uint32_t * bb = b + 5;
array.pop(bb, 5);
ASSERT_EQ(array.capacity(), 10u);
@ -69,4 +68,3 @@ TEST(cubeb, auto_array)
ASSERT_EQ(array.length(), 15u);
ASSERT_EQ(array.capacity(), 20u);
}