cubeb/test/test_sanity.cpp

662 строки
15 KiB
C++
Исходник Обычный вид История

/*
* Copyright © 2011 Mozilla Foundation
*
* This program is made available under an ISC-style license. See the
* accompanying file LICENSE for details.
*/
#ifdef NDEBUG
#undef NDEBUG
#endif
#define _XOPEN_SOURCE 600
#include "cubeb/cubeb.h"
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
2013-09-02 22:50:58 +04:00
#include "common.h"
#ifdef CUBEB_GECKO_BUILD
#include "TestHarness.h"
#endif
#define BEGIN_TEST fprintf(stderr, "START %s\n", __func__)
#define END_TEST fprintf(stderr, "END %s\n", __func__)
2014-04-04 05:57:30 +04:00
#define STREAM_RATE 44100
#define STREAM_LATENCY 100 * STREAM_RATE / 1000
#define STREAM_CHANNELS 1
#if (defined(_WIN32) || defined(__WIN32__))
#define STREAM_FORMAT CUBEB_SAMPLE_FLOAT32LE
#else
#define STREAM_FORMAT CUBEB_SAMPLE_S16LE
#endif
2016-10-13 02:04:35 +03:00
template<typename T, size_t N>
constexpr size_t
ARRAY_LENGTH(T(&)[N])
2016-10-13 02:04:35 +03:00
{
return N;
}
static int dummy;
static uint64_t total_frames_written;
2012-05-02 08:53:57 +04:00
static int delay_callback;
static long
2016-08-23 04:20:54 +03:00
test_data_callback(cubeb_stream * stm, void * user_ptr, const void * /*inputbuffer*/, void * outputbuffer, long nframes)
{
assert(stm && user_ptr == &dummy && outputbuffer && nframes > 0);
#if (defined(_WIN32) || defined(__WIN32__))
memset(outputbuffer, 0, nframes * sizeof(float));
#else
memset(outputbuffer, 0, nframes * sizeof(short));
#endif
total_frames_written += nframes;
2012-05-02 08:53:57 +04:00
if (delay_callback) {
delay(10);
2012-05-02 08:53:57 +04:00
}
return nframes;
}
void
2016-08-23 04:20:54 +03:00
test_state_callback(cubeb_stream * /*stm*/, void * /*user_ptr*/, cubeb_state /*state*/)
{
}
static void
test_init_destroy_context(void)
{
int r;
cubeb * ctx;
2014-10-13 09:52:37 +04:00
char const* backend_id;
BEGIN_TEST;
2014-04-04 05:57:30 +04:00
r = cubeb_init(&ctx, "test_sanity");
assert(r == 0 && ctx);
2014-10-13 09:52:37 +04:00
backend_id = cubeb_get_backend_id(ctx);
assert(backend_id);
fprintf(stderr, "Backend: %s\n", backend_id);
cubeb_destroy(ctx);
2014-04-04 05:57:30 +04:00
END_TEST;
}
static void
test_init_destroy_multiple_contexts(void)
{
2014-09-29 05:04:25 +04:00
size_t i;
int r;
cubeb * ctx[4];
int order[4] = {2, 0, 3, 1};
assert(ARRAY_LENGTH(ctx) == ARRAY_LENGTH(order));
BEGIN_TEST;
2014-04-04 05:57:30 +04:00
for (i = 0; i < ARRAY_LENGTH(ctx); ++i) {
r = cubeb_init(&ctx[i], NULL);
assert(r == 0 && ctx[i]);
}
/* destroy in a different order */
for (i = 0; i < ARRAY_LENGTH(ctx); ++i) {
cubeb_destroy(ctx[order[i]]);
}
2014-04-04 05:57:30 +04:00
END_TEST;
}
static void
test_context_variables(void)
{
int r;
cubeb * ctx;
uint32_t value;
cubeb_stream_params params;
BEGIN_TEST;
r = cubeb_init(&ctx, "test_context_variables");
assert(r == 0 && ctx);
params.channels = STREAM_CHANNELS;
params.format = STREAM_FORMAT;
params.rate = STREAM_RATE;
2016-06-09 12:45:25 +03:00
#if defined(__ANDROID__)
2016-06-09 13:31:11 +03:00
params.stream_type = CUBEB_STREAM_TYPE_MUSIC;
2016-06-09 12:45:25 +03:00
#endif
r = cubeb_get_min_latency(ctx, params, &value);
assert(r == CUBEB_OK || r == CUBEB_ERROR_NOT_SUPPORTED);
if (r == CUBEB_OK) {
assert(value > 0);
}
r = cubeb_get_preferred_sample_rate(ctx, &value);
assert(r == CUBEB_OK || r == CUBEB_ERROR_NOT_SUPPORTED);
if (r == CUBEB_OK) {
assert(value > 0);
}
cubeb_destroy(ctx);
END_TEST;
}
static void
test_init_destroy_stream(void)
{
int r;
cubeb * ctx;
cubeb_stream * stream;
cubeb_stream_params params;
BEGIN_TEST;
2014-04-04 05:57:30 +04:00
r = cubeb_init(&ctx, "test_sanity");
assert(r == 0 && ctx);
params.format = STREAM_FORMAT;
params.rate = STREAM_RATE;
params.channels = STREAM_CHANNELS;
2016-06-09 12:45:25 +03:00
#if defined(__ANDROID__)
2016-06-09 13:31:11 +03:00
params.stream_type = CUBEB_STREAM_TYPE_MUSIC;
2016-06-09 12:45:25 +03:00
#endif
r = cubeb_stream_init(ctx, &stream, "test", NULL, NULL, NULL, &params, STREAM_LATENCY,
test_data_callback, test_state_callback, &dummy);
assert(r == 0 && stream);
cubeb_stream_destroy(stream);
cubeb_destroy(ctx);
2014-04-04 05:57:30 +04:00
END_TEST;
}
static void
test_init_destroy_multiple_streams(void)
{
2014-09-29 05:04:25 +04:00
size_t i;
int r;
cubeb * ctx;
2014-09-17 09:22:06 +04:00
cubeb_stream * stream[8];
cubeb_stream_params params;
BEGIN_TEST;
2014-04-04 05:57:30 +04:00
r = cubeb_init(&ctx, "test_sanity");
assert(r == 0 && ctx);
params.format = STREAM_FORMAT;
params.rate = STREAM_RATE;
params.channels = STREAM_CHANNELS;
2016-06-09 12:45:25 +03:00
#if defined(__ANDROID__)
2016-06-09 13:31:11 +03:00
params.stream_type = CUBEB_STREAM_TYPE_MUSIC;
2016-06-09 12:45:25 +03:00
#endif
for (i = 0; i < ARRAY_LENGTH(stream); ++i) {
r = cubeb_stream_init(ctx, &stream[i], "test", NULL, NULL, NULL, &params, STREAM_LATENCY,
test_data_callback, test_state_callback, &dummy);
2014-04-04 05:57:30 +04:00
assert(r == 0);
assert(stream[i]);
}
for (i = 0; i < ARRAY_LENGTH(stream); ++i) {
cubeb_stream_destroy(stream[i]);
}
cubeb_destroy(ctx);
2014-04-04 05:57:30 +04:00
END_TEST;
}
static void
test_configure_stream(void)
{
int r;
cubeb * ctx;
cubeb_stream * stream;
cubeb_stream_params params;
BEGIN_TEST;
r = cubeb_init(&ctx, "test_sanity");
assert(r == 0 && ctx);
params.format = STREAM_FORMAT;
params.rate = STREAM_RATE;
params.channels = 2; // panning
2016-06-09 12:45:25 +03:00
#if defined(__ANDROID__)
2016-06-09 13:31:11 +03:00
params.stream_type = CUBEB_STREAM_TYPE_MUSIC;
2016-06-09 12:45:25 +03:00
#endif
r = cubeb_stream_init(ctx, &stream, "test", NULL, NULL, NULL, &params, STREAM_LATENCY,
test_data_callback, test_state_callback, &dummy);
assert(r == 0 && stream);
r = cubeb_stream_set_volume(stream, 1.0f);
assert(r == 0 || r == CUBEB_ERROR_NOT_SUPPORTED);
r = cubeb_stream_set_panning(stream, 0.0f);
assert(r == 0 || r == CUBEB_ERROR_NOT_SUPPORTED);
cubeb_stream_destroy(stream);
cubeb_destroy(ctx);
END_TEST;
}
2012-05-02 08:53:57 +04:00
static void
test_init_start_stop_destroy_multiple_streams(int early, int delay_ms)
2012-05-02 08:53:57 +04:00
{
2014-09-29 05:04:25 +04:00
size_t i;
2012-05-02 08:53:57 +04:00
int r;
cubeb * ctx;
2014-09-17 09:22:06 +04:00
cubeb_stream * stream[8];
2012-05-02 08:53:57 +04:00
cubeb_stream_params params;
BEGIN_TEST;
2014-04-04 05:57:30 +04:00
r = cubeb_init(&ctx, "test_sanity");
2012-05-02 08:53:57 +04:00
assert(r == 0 && ctx);
params.format = STREAM_FORMAT;
params.rate = STREAM_RATE;
params.channels = STREAM_CHANNELS;
2016-06-09 12:45:25 +03:00
#if defined(__ANDROID__)
2016-06-09 13:31:11 +03:00
params.stream_type = CUBEB_STREAM_TYPE_MUSIC;
2016-06-09 12:45:25 +03:00
#endif
2012-05-02 08:53:57 +04:00
for (i = 0; i < ARRAY_LENGTH(stream); ++i) {
r = cubeb_stream_init(ctx, &stream[i], "test", NULL, NULL, NULL, &params, STREAM_LATENCY,
2012-05-02 08:53:57 +04:00
test_data_callback, test_state_callback, &dummy);
2014-04-04 05:57:30 +04:00
assert(r == 0);
assert(stream[i]);
2012-05-02 08:53:57 +04:00
if (early) {
r = cubeb_stream_start(stream[i]);
assert(r == 0);
}
}
if (!early) {
for (i = 0; i < ARRAY_LENGTH(stream); ++i) {
2012-05-02 08:53:57 +04:00
r = cubeb_stream_start(stream[i]);
assert(r == 0);
}
}
if (delay_ms) {
delay(delay_ms);
2012-05-02 08:53:57 +04:00
}
if (!early) {
for (i = 0; i < ARRAY_LENGTH(stream); ++i) {
2012-05-02 08:53:57 +04:00
r = cubeb_stream_stop(stream[i]);
assert(r == 0);
}
}
for (i = 0; i < ARRAY_LENGTH(stream); ++i) {
2012-05-02 08:53:57 +04:00
if (early) {
r = cubeb_stream_stop(stream[i]);
assert(r == 0);
}
cubeb_stream_destroy(stream[i]);
}
cubeb_destroy(ctx);
2014-04-04 05:57:30 +04:00
END_TEST;
}
2012-05-02 08:53:57 +04:00
static void
test_init_destroy_multiple_contexts_and_streams(void)
{
2014-09-29 05:04:25 +04:00
size_t i, j;
int r;
2014-09-17 09:22:06 +04:00
cubeb * ctx[2];
cubeb_stream * stream[8];
cubeb_stream_params params;
2014-09-29 05:04:25 +04:00
size_t streams_per_ctx = ARRAY_LENGTH(stream) / ARRAY_LENGTH(ctx);
assert(ARRAY_LENGTH(ctx) * streams_per_ctx == ARRAY_LENGTH(stream));
BEGIN_TEST;
2014-04-04 05:57:30 +04:00
params.format = STREAM_FORMAT;
params.rate = STREAM_RATE;
params.channels = STREAM_CHANNELS;
2016-06-09 12:45:25 +03:00
#if defined(__ANDROID__)
2016-06-09 13:31:11 +03:00
params.stream_type = CUBEB_STREAM_TYPE_MUSIC;
2016-06-09 12:45:25 +03:00
#endif
for (i = 0; i < ARRAY_LENGTH(ctx); ++i) {
r = cubeb_init(&ctx[i], "test_sanity");
assert(r == 0 && ctx[i]);
for (j = 0; j < streams_per_ctx; ++j) {
r = cubeb_stream_init(ctx[i], &stream[i * streams_per_ctx + j], "test", NULL, NULL, NULL, &params, STREAM_LATENCY,
test_data_callback, test_state_callback, &dummy);
2014-04-04 05:57:30 +04:00
assert(r == 0);
assert(stream[i * streams_per_ctx + j]);
}
}
for (i = 0; i < ARRAY_LENGTH(ctx); ++i) {
for (j = 0; j < streams_per_ctx; ++j) {
cubeb_stream_destroy(stream[i * streams_per_ctx + j]);
}
cubeb_destroy(ctx[i]);
}
2014-04-04 05:57:30 +04:00
END_TEST;
}
static void
test_basic_stream_operations(void)
{
int r;
cubeb * ctx;
cubeb_stream * stream;
cubeb_stream_params params;
uint64_t position;
BEGIN_TEST;
2014-04-04 05:57:30 +04:00
r = cubeb_init(&ctx, "test_sanity");
assert(r == 0 && ctx);
params.format = STREAM_FORMAT;
params.rate = STREAM_RATE;
params.channels = STREAM_CHANNELS;
2016-06-09 12:45:25 +03:00
#if defined(__ANDROID__)
2016-06-09 13:31:11 +03:00
params.stream_type = CUBEB_STREAM_TYPE_MUSIC;
2016-06-09 12:45:25 +03:00
#endif
r = cubeb_stream_init(ctx, &stream, "test", NULL, NULL, NULL, &params, STREAM_LATENCY,
test_data_callback, test_state_callback, &dummy);
assert(r == 0 && stream);
/* position and volume before stream has started */
r = cubeb_stream_get_position(stream, &position);
assert(r == 0 && position == 0);
r = cubeb_stream_start(stream);
assert(r == 0);
/* position and volume after while stream running */
r = cubeb_stream_get_position(stream, &position);
assert(r == 0);
r = cubeb_stream_stop(stream);
assert(r == 0);
/* position and volume after stream has stopped */
r = cubeb_stream_get_position(stream, &position);
assert(r == 0);
cubeb_stream_destroy(stream);
cubeb_destroy(ctx);
2014-04-04 05:57:30 +04:00
END_TEST;
}
static void
test_stream_position(void)
{
2014-09-29 05:04:25 +04:00
size_t i;
int r;
cubeb * ctx;
cubeb_stream * stream;
cubeb_stream_params params;
uint64_t position, last_position;
BEGIN_TEST;
2014-04-04 05:57:30 +04:00
total_frames_written = 0;
r = cubeb_init(&ctx, "test_sanity");
assert(r == 0 && ctx);
params.format = STREAM_FORMAT;
params.rate = STREAM_RATE;
params.channels = STREAM_CHANNELS;
2016-06-09 12:45:25 +03:00
#if defined(__ANDROID__)
2016-06-09 13:31:11 +03:00
params.stream_type = CUBEB_STREAM_TYPE_MUSIC;
2016-06-09 12:45:25 +03:00
#endif
r = cubeb_stream_init(ctx, &stream, "test", NULL, NULL, NULL, &params, STREAM_LATENCY,
test_data_callback, test_state_callback, &dummy);
assert(r == 0 && stream);
/* stream position should not advance before starting playback */
r = cubeb_stream_get_position(stream, &position);
assert(r == 0 && position == 0);
delay(500);
r = cubeb_stream_get_position(stream, &position);
assert(r == 0 && position == 0);
/* stream position should advance during playback */
r = cubeb_stream_start(stream);
assert(r == 0);
/* XXX let start happen */
delay(500);
/* stream should have prefilled */
assert(total_frames_written > 0);
r = cubeb_stream_get_position(stream, &position);
assert(r == 0);
last_position = position;
delay(500);
r = cubeb_stream_get_position(stream, &position);
assert(r == 0);
assert(position >= last_position);
last_position = position;
/* stream position should not exceed total frames written */
for (i = 0; i < 5; ++i) {
r = cubeb_stream_get_position(stream, &position);
assert(r == 0);
assert(position >= last_position);
assert(position <= total_frames_written);
last_position = position;
delay(500);
}
assert(last_position != 0);
/* stream position should not advance after stopping playback */
r = cubeb_stream_stop(stream);
assert(r == 0);
/* XXX allow stream to settle */
delay(500);
r = cubeb_stream_get_position(stream, &position);
assert(r == 0);
last_position = position;
delay(500);
r = cubeb_stream_get_position(stream, &position);
assert(r == 0);
assert(position == last_position);
cubeb_stream_destroy(stream);
cubeb_destroy(ctx);
2014-04-04 05:57:30 +04:00
END_TEST;
}
static int do_drain;
static int got_drain;
static long
2016-08-23 04:20:54 +03:00
test_drain_data_callback(cubeb_stream * stm, void * user_ptr, const void * /*inputbuffer*/, void * outputbuffer, long nframes)
{
assert(stm && user_ptr == &dummy && outputbuffer && nframes > 0);
if (do_drain == 1) {
do_drain = 2;
return 0;
}
/* once drain has started, callback must never be called again */
assert(do_drain != 2);
#if (defined(_WIN32) || defined(__WIN32__))
memset(outputbuffer, 0, nframes * sizeof(float));
#else
memset(outputbuffer, 0, nframes * sizeof(short));
#endif
total_frames_written += nframes;
return nframes;
}
void
2016-08-23 04:20:54 +03:00
test_drain_state_callback(cubeb_stream * /*stm*/, void * /*user_ptr*/, cubeb_state state)
{
if (state == CUBEB_STATE_DRAINED) {
assert(!got_drain);
got_drain = 1;
}
}
static void
test_drain(void)
{
int r;
cubeb * ctx;
cubeb_stream * stream;
cubeb_stream_params params;
uint64_t position;
BEGIN_TEST;
2014-04-04 05:57:30 +04:00
total_frames_written = 0;
r = cubeb_init(&ctx, "test_sanity");
assert(r == 0 && ctx);
params.format = STREAM_FORMAT;
params.rate = STREAM_RATE;
params.channels = STREAM_CHANNELS;
2016-06-09 12:45:25 +03:00
#if defined(__ANDROID__)
2016-06-09 13:31:11 +03:00
params.stream_type = CUBEB_STREAM_TYPE_MUSIC;
2016-06-09 12:45:25 +03:00
#endif
r = cubeb_stream_init(ctx, &stream, "test", NULL, NULL, NULL, &params, STREAM_LATENCY,
test_drain_data_callback, test_drain_state_callback, &dummy);
assert(r == 0 && stream);
r = cubeb_stream_start(stream);
assert(r == 0);
delay(500);
do_drain = 1;
for (;;) {
r = cubeb_stream_get_position(stream, &position);
assert(r == 0);
if (got_drain) {
break;
} else {
assert(position <= total_frames_written);
}
delay(500);
}
r = cubeb_stream_get_position(stream, &position);
assert(r == 0);
assert(got_drain);
2013-09-02 23:16:49 +04:00
// 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.
2014-09-29 04:08:39 +04:00
//assert(position <= total_frames_written);
cubeb_stream_destroy(stream);
cubeb_destroy(ctx);
2014-04-04 05:57:30 +04:00
END_TEST;
}
2014-04-04 05:57:30 +04:00
int is_windows_7()
{
#ifdef __MINGW32__
printf("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__))
OSVERSIONINFOEX osvi;
DWORDLONG condition_mask = 0;
2014-04-04 05:57:30 +04:00
ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
2014-04-15 04:17:42 +04:00
// NT 6.1 is Windows 7
osvi.dwMajorVersion = 6;
osvi.dwMinorVersion = 1;
2014-04-15 04:17:42 +04:00
VER_SET_CONDITION(condition_mask, VER_MAJORVERSION, VER_EQUAL);
VER_SET_CONDITION(condition_mask, VER_MINORVERSION, VER_GREATER_EQUAL);
2014-04-15 04:17:42 +04:00
return VerifyVersionInfo(&osvi, VER_MAJORVERSION | VER_MINORVERSION, condition_mask);
2014-04-04 05:57:30 +04:00
#else
2014-04-15 04:17:42 +04:00
return 0;
2014-04-04 05:57:30 +04:00
#endif
}
int
2016-08-23 04:20:54 +03:00
main(int /*argc*/, char * /*argv*/[])
{
#ifdef CUBEB_GECKO_BUILD
ScopedXPCOM xpcom("test_sanity");
#endif
2014-04-04 05:57:30 +04:00
test_init_destroy_context();
test_init_destroy_multiple_contexts();
test_context_variables();
2014-04-04 05:57:30 +04:00
test_init_destroy_stream();
test_init_destroy_multiple_streams();
test_configure_stream();
2014-04-04 05:57:30 +04:00
test_basic_stream_operations();
test_stream_position();
/* Sometimes, when using WASAPI on windows 7 (vista and 8 are okay), and
* calling Activate a lot on an AudioClient, 0x800700b7 is returned. This is
* the HRESULT value for "Cannot create a file when that file already exists",
* and is not documented as a possible return value for this call. Hence, we
* try to limit the number of streams we create in this test. */
if (!is_windows_7()) {
2014-09-17 09:22:06 +04:00
test_init_destroy_multiple_contexts_and_streams();
2014-04-04 05:57:30 +04:00
delay_callback = 0;
test_init_start_stop_destroy_multiple_streams(0, 0);
test_init_start_stop_destroy_multiple_streams(1, 0);
test_init_start_stop_destroy_multiple_streams(0, 150);
test_init_start_stop_destroy_multiple_streams(1, 150);
delay_callback = 1;
test_init_start_stop_destroy_multiple_streams(0, 0);
test_init_start_stop_destroy_multiple_streams(1, 0);
test_init_start_stop_destroy_multiple_streams(0, 150);
test_init_start_stop_destroy_multiple_streams(1, 150);
}
2012-05-02 08:53:57 +04:00
delay_callback = 0;
test_drain();
/*
to implement:
test_eos_during_prefill();
test_stream_destroy_pending_drain();
*/
printf("\n");
return 0;
}