зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1864141 - Update libcubeb to revision 30efcd1c. r=cubeb-reviewers,kinetik
Differential Revision: https://phabricator.services.mozilla.com/D193427
This commit is contained in:
Родитель
55a951769f
Коммит
80affd48e4
|
@ -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, ¶ms,
|
||||
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, ¶ms,
|
||||
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,
|
||||
¶ms, 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, ¶ms, &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, ¶ms, NULL, nullptr,
|
||||
4096, data_cb_record, state_cb_record, &stream_state);
|
||||
r = cubeb_stream_init(ctx, &stream, "Cubeb record (mono)", NULL, ¶ms,
|
||||
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, ¶ms, STREAM_LATENCY,
|
||||
test_data_callback, test_state_callback, &dummy);
|
||||
r = cubeb_stream_init(ctx, &stream, "test", NULL, NULL, NULL, ¶ms,
|
||||
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, ¶ms, STREAM_LATENCY,
|
||||
test_data_callback, test_state_callback, &dummy);
|
||||
r = cubeb_stream_init(ctx, &stream[i], "test", NULL, NULL, NULL, ¶ms,
|
||||
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, ¶ms, STREAM_LATENCY,
|
||||
test_data_callback, test_state_callback, &dummy);
|
||||
r = cubeb_stream_init(ctx, &stream, "test", NULL, NULL, NULL, ¶ms,
|
||||
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, ¶ms, STREAM_LATENCY,
|
||||
test_data_callback, test_state_callback, &dummy);
|
||||
r = cubeb_stream_init(ctx, &stream, "test", NULL, NULL, NULL, ¶ms,
|
||||
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, ¶ms, STREAM_LATENCY,
|
||||
test_data_callback, test_state_callback, &dummy);
|
||||
r = cubeb_stream_init(ctx, &stream[i], "test", NULL, NULL, NULL, ¶ms,
|
||||
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, ¶ms, STREAM_LATENCY,
|
||||
r = cubeb_stream_init(ctx[i], &stream[i * streams_per_ctx + j], "test",
|
||||
NULL, NULL, NULL, ¶ms, 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, ¶ms, STREAM_LATENCY,
|
||||
test_data_callback, test_state_callback, &dummy);
|
||||
r = cubeb_stream_init(ctx, &stream, "test", NULL, NULL, NULL, ¶ms,
|
||||
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, ¶ms, STREAM_LATENCY,
|
||||
test_data_callback, test_state_callback, &dummy);
|
||||
r = cubeb_stream_init(ctx, &stream, "test", NULL, NULL, NULL, ¶ms,
|
||||
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, ¶ms, STREAM_LATENCY,
|
||||
test_drain_data_callback, test_drain_state_callback, &dummy);
|
||||
r = cubeb_stream_init(ctx, &stream, "test", NULL, NULL, NULL, ¶ms,
|
||||
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, ¶ms,
|
||||
4096, data_cb_tone, state_cb_tone, user_data.get());
|
||||
r = cubeb_stream_init(ctx, &stream, "Cubeb tone (mono)", NULL, NULL, NULL,
|
||||
¶ms, 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);
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче