summaryrefslogtreecommitdiffstats
path: root/media/libcubeb/tests
diff options
context:
space:
mode:
Diffstat (limited to 'media/libcubeb/tests')
-rw-r--r--media/libcubeb/tests/common.h61
-rw-r--r--media/libcubeb/tests/moz.build80
-rw-r--r--media/libcubeb/tests/test_audio.cpp294
-rw-r--r--media/libcubeb/tests/test_devices.cpp162
-rw-r--r--media/libcubeb/tests/test_duplex.cpp151
-rw-r--r--media/libcubeb/tests/test_latency.cpp60
-rw-r--r--media/libcubeb/tests/test_record.cpp125
-rw-r--r--media/libcubeb/tests/test_resampler.cpp554
-rw-r--r--media/libcubeb/tests/test_sanity.cpp676
-rw-r--r--media/libcubeb/tests/test_tone.cpp149
-rw-r--r--media/libcubeb/tests/test_utils.cpp80
11 files changed, 2392 insertions, 0 deletions
diff --git a/media/libcubeb/tests/common.h b/media/libcubeb/tests/common.h
new file mode 100644
index 000000000..051f3c42a
--- /dev/null
+++ b/media/libcubeb/tests/common.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright © 2013 Sebastien Alaiwan
+ *
+ * This program is made available under an ISC-style license. See the
+ * accompanying file LICENSE for details.
+ */
+
+#if defined( _WIN32)
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+#include <windows.h>
+#else
+#include <unistd.h>
+#endif
+
+void delay(unsigned int ms)
+{
+#if defined(_WIN32)
+ Sleep(ms);
+#else
+ sleep(ms / 1000);
+ usleep(ms % 1000 * 1000);
+#endif
+}
+
+#if !defined(M_PI)
+#define M_PI 3.14159265358979323846
+#endif
+
+int has_available_input_device(cubeb * ctx)
+{
+ cubeb_device_collection * devices;
+ int input_device_available = 0;
+ int r;
+ /* Bail out early if the host does not have input devices. */
+ r = cubeb_enumerate_devices(ctx, CUBEB_DEVICE_TYPE_INPUT, &devices);
+ if (r != CUBEB_OK) {
+ fprintf(stderr, "error enumerating devices.");
+ return 0;
+ }
+
+ if (devices->count == 0) {
+ fprintf(stderr, "no input device available, skipping test.\n");
+ return 0;
+ }
+
+ for (uint32_t i = 0; i < devices->count; i++) {
+ 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");
+ return 0;
+ }
+
+ return 1;
+}
+
diff --git a/media/libcubeb/tests/moz.build b/media/libcubeb/tests/moz.build
new file mode 100644
index 000000000..1b17c7b1c
--- /dev/null
+++ b/media/libcubeb/tests/moz.build
@@ -0,0 +1,80 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+DEFINES['CUBEB_GECKO_BUILD'] = True
+
+GeckoCppUnitTests([
+ 'test_duplex',
+ 'test_record',
+ 'test_tone',
+ 'test_utils'
+])
+
+if CONFIG['MOZ_PULSEAUDIO'] or CONFIG['OS_TARGET'] in ('Darwin', 'WINNT', 'Android'):
+ GeckoCppUnitTests([
+ 'test_resampler',
+ ])
+
+if CONFIG['OS_TARGET'] != 'Android':
+ GeckoCppUnitTests([
+ 'test_audio',
+ 'test_latency',
+ 'test_sanity'
+ ])
+
+LOCAL_INCLUDES += [
+ '../include',
+ '../src'
+]
+
+USE_LIBS += [
+ 'cubeb',
+ 'speex'
+]
+
+if CONFIG['OS_ARCH'] == 'WINNT':
+ # On windows, the WASAPI backend needs the resampler we have in
+ # /media/libspeex_resampler, so we can't get away with just linking cubeb's
+ # .o
+ USE_LIBS += [
+ 'cubeb',
+ 'speex',
+ ]
+ OS_LIBS += [
+ 'ole32'
+ ]
+else:
+ # Otherwise, we can just grab all the compiled .o and compile against that,
+ # linking the appriopriate libraries.
+ USE_LIBS += [
+ 'cubeb',
+ ]
+ # Don't link gkmedias for it introduces dependencies on Android.
+ if CONFIG['OS_TARGET'] == 'Android':
+ USE_LIBS += [
+ 'speex',
+ ]
+
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+ OS_LIBS += [
+ '-framework AudioUnit',
+ '-framework CoreAudio',
+ ]
+elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'uikit':
+ OS_LIBS += [
+ '-framework CoreFoundation',
+ '-framework AudioToolbox',
+ ]
+elif CONFIG['OS_TARGET'] == 'OpenBSD':
+ OS_LIBS += [
+ 'sndio',
+ ]
+else:
+ OS_LIBS += CONFIG['MOZ_ALSA_LIBS']
+ OS_LIBS += CONFIG['MOZ_PULSEAUDIO_LIBS']
+
+if CONFIG['GNU_CXX']:
+ CXXFLAGS += ['-Wno-error=shadow']
diff --git a/media/libcubeb/tests/test_audio.cpp b/media/libcubeb/tests/test_audio.cpp
new file mode 100644
index 000000000..4943223e6
--- /dev/null
+++ b/media/libcubeb/tests/test_audio.cpp
@@ -0,0 +1,294 @@
+/*
+ * Copyright © 2013 Sebastien Alaiwan <sebastien.alaiwan@gmail.com>
+ *
+ * This program is made available under an ISC-style license. See the
+ * accompanying file LICENSE for details.
+ */
+
+/* libcubeb api/function exhaustive test. Plays a series of tones in different
+ * conditions. */
+#ifdef NDEBUG
+#undef NDEBUG
+#endif
+#define _XOPEN_SOURCE 600
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <assert.h>
+#include <string.h>
+
+#include "cubeb/cubeb.h"
+#include "common.h"
+#ifdef CUBEB_GECKO_BUILD
+#include "TestHarness.h"
+#endif
+
+#define MAX_NUM_CHANNELS 32
+
+#if !defined(M_PI)
+#define M_PI 3.14159265358979323846
+#endif
+
+#define NELEMS(x) ((int) (sizeof(x) / sizeof(x[0])))
+#define VOLUME 0.2
+
+float get_frequency(int channel_index)
+{
+ return 220.0f * (channel_index+1);
+}
+
+/* store the phase of the generated waveform */
+typedef struct {
+ int num_channels;
+ float phase[MAX_NUM_CHANNELS];
+ float sample_rate;
+} synth_state;
+
+synth_state* synth_create(int num_channels, float sample_rate)
+{
+ synth_state* synth = (synth_state *) malloc(sizeof(synth_state));
+ if (!synth)
+ return NULL;
+ for(int i=0;i < MAX_NUM_CHANNELS;++i)
+ synth->phase[i] = 0.0f;
+ synth->num_channels = num_channels;
+ synth->sample_rate = sample_rate;
+ return synth;
+}
+
+void synth_destroy(synth_state* synth)
+{
+ free(synth);
+}
+
+void synth_run_float(synth_state* synth, float* audiobuffer, long nframes)
+{
+ for(int c=0;c < synth->num_channels;++c) {
+ float freq = get_frequency(c);
+ float phase_inc = 2.0 * M_PI * freq / synth->sample_rate;
+ for(long n=0;n < nframes;++n) {
+ audiobuffer[n*synth->num_channels+c] = sin(synth->phase[c]) * VOLUME;
+ synth->phase[c] += phase_inc;
+ }
+ }
+}
+
+long data_cb_float(cubeb_stream * /*stream*/, void * user, const void * /*inputbuffer*/, void * outputbuffer, long nframes)
+{
+ synth_state *synth = (synth_state *)user;
+ synth_run_float(synth, (float*)outputbuffer, nframes);
+ return nframes;
+}
+
+void synth_run_16bit(synth_state* synth, short* audiobuffer, long nframes)
+{
+ for(int c=0;c < synth->num_channels;++c) {
+ float freq = get_frequency(c);
+ float phase_inc = 2.0 * M_PI * freq / synth->sample_rate;
+ for(long n=0;n < nframes;++n) {
+ audiobuffer[n*synth->num_channels+c] = sin(synth->phase[c]) * VOLUME * 32767.0f;
+ synth->phase[c] += phase_inc;
+ }
+ }
+}
+
+long data_cb_short(cubeb_stream * /*stream*/, void * user, const void * /*inputbuffer*/, void * outputbuffer, long nframes)
+{
+ synth_state *synth = (synth_state *)user;
+ synth_run_16bit(synth, (short*)outputbuffer, nframes);
+ return nframes;
+}
+
+void state_cb(cubeb_stream * /*stream*/, void * /*user*/, cubeb_state /*state*/)
+{
+}
+
+/* Our android backends don't support float, only int16. */
+int supports_float32(const char* backend_id)
+{
+ return (strcmp(backend_id, "opensl") != 0 &&
+ strcmp(backend_id, "audiotrack") != 0);
+}
+
+/* The WASAPI backend only supports float. */
+int supports_int16(const char* backend_id)
+{
+ return strcmp(backend_id, "wasapi") != 0;
+}
+
+/* Some backends don't have code to deal with more than mono or stereo. */
+int supports_channel_count(const char* backend_id, int nchannels)
+{
+ return nchannels <= 2 ||
+ (strcmp(backend_id, "opensl") != 0 && strcmp(backend_id, "audiotrack") != 0);
+}
+
+int run_test(int num_channels, int sampling_rate, int is_float)
+{
+ int r = CUBEB_OK;
+
+ cubeb *ctx = NULL;
+ synth_state* synth = NULL;
+ cubeb_stream *stream = NULL;
+ const char * backend_id = NULL;
+
+ r = cubeb_init(&ctx, "Cubeb audio test: channels");
+ if (r != CUBEB_OK) {
+ fprintf(stderr, "Error initializing cubeb library\n");
+ goto cleanup;
+ }
+
+ backend_id = cubeb_get_backend_id(ctx);
+
+ if ((is_float && !supports_float32(backend_id)) ||
+ (!is_float && !supports_int16(backend_id)) ||
+ !supports_channel_count(backend_id, num_channels)) {
+ /* don't treat this as a test failure. */
+ goto cleanup;
+ }
+
+ 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;
+ params.rate = sampling_rate;
+ params.channels = num_channels;
+
+ synth = synth_create(params.channels, params.rate);
+ if (synth == NULL) {
+ fprintf(stderr, "Out of memory\n");
+ goto cleanup;
+ }
+
+ r = cubeb_stream_init(ctx, &stream, "test tone", NULL, NULL, NULL, &params,
+ 4096, is_float ? data_cb_float : data_cb_short, state_cb, synth);
+ if (r != CUBEB_OK) {
+ fprintf(stderr, "Error initializing cubeb stream: %d\n", r);
+ goto cleanup;
+ }
+
+ cubeb_stream_start(stream);
+ delay(200);
+ cubeb_stream_stop(stream);
+
+cleanup:
+ cubeb_stream_destroy(stream);
+ cubeb_destroy(ctx);
+ synth_destroy(synth);
+
+ return r;
+}
+
+int run_panning_volume_test(int is_float)
+{
+ int r = CUBEB_OK;
+
+ cubeb *ctx = NULL;
+ synth_state* synth = NULL;
+ cubeb_stream *stream = NULL;
+ const char * backend_id = NULL;
+
+ r = cubeb_init(&ctx, "Cubeb audio test");
+ if (r != CUBEB_OK) {
+ fprintf(stderr, "Error initializing cubeb library\n");
+ goto cleanup;
+ }
+ backend_id = cubeb_get_backend_id(ctx);
+
+ if ((is_float && !supports_float32(backend_id)) ||
+ (!is_float && !supports_int16(backend_id))) {
+ /* don't treat this as a test failure. */
+ goto cleanup;
+ }
+
+ cubeb_stream_params params;
+ params.format = is_float ? CUBEB_SAMPLE_FLOAT32NE : CUBEB_SAMPLE_S16NE;
+ params.rate = 44100;
+ params.channels = 2;
+
+ synth = synth_create(params.channels, params.rate);
+ if (synth == NULL) {
+ fprintf(stderr, "Out of memory\n");
+ goto cleanup;
+ }
+
+ r = cubeb_stream_init(ctx, &stream, "test tone", NULL, NULL, NULL, &params,
+ 4096, is_float ? data_cb_float : data_cb_short,
+ state_cb, synth);
+ if (r != CUBEB_OK) {
+ fprintf(stderr, "Error initializing cubeb stream: %d\n", r);
+ goto cleanup;
+ }
+
+ fprintf(stderr, "Testing: volume\n");
+ for(int i=0;i <= 4; ++i)
+ {
+ fprintf(stderr, "Volume: %d%%\n", i*25);
+
+ cubeb_stream_set_volume(stream, i/4.0f);
+ cubeb_stream_start(stream);
+ delay(400);
+ cubeb_stream_stop(stream);
+ delay(100);
+ }
+
+ fprintf(stderr, "Testing: panning\n");
+ for(int i=-4;i <= 4; ++i)
+ {
+ fprintf(stderr, "Panning: %.2f%%\n", i/4.0f);
+
+ cubeb_stream_set_panning(stream, i/4.0f);
+ cubeb_stream_start(stream);
+ delay(400);
+ cubeb_stream_stop(stream);
+ delay(100);
+ }
+
+cleanup:
+ cubeb_stream_destroy(stream);
+ cubeb_destroy(ctx);
+ synth_destroy(synth);
+
+ return r;
+}
+
+void run_channel_rate_test()
+{
+ int channel_values[] = {
+ 1,
+ 2,
+ 3,
+ 4,
+ 6,
+ };
+
+ int freq_values[] = {
+ 16000,
+ 24000,
+ 44100,
+ 48000,
+ };
+
+ for(int j = 0; j < NELEMS(channel_values); ++j) {
+ for(int i = 0; i < NELEMS(freq_values); ++i) {
+ assert(channel_values[j] < MAX_NUM_CHANNELS);
+ fprintf(stderr, "--------------------------\n");
+ assert(run_test(channel_values[j], freq_values[i], 0) == CUBEB_OK);
+ assert(run_test(channel_values[j], freq_values[i], 1) == CUBEB_OK);
+ }
+ }
+}
+
+
+int main(int /*argc*/, char * /*argv*/[])
+{
+#ifdef CUBEB_GECKO_BUILD
+ ScopedXPCOM xpcom("test_audio");
+#endif
+
+ assert(run_panning_volume_test(0) == CUBEB_OK);
+ assert(run_panning_volume_test(1) == CUBEB_OK);
+ run_channel_rate_test();
+
+ return CUBEB_OK;
+}
diff --git a/media/libcubeb/tests/test_devices.cpp b/media/libcubeb/tests/test_devices.cpp
new file mode 100644
index 000000000..9c502c0af
--- /dev/null
+++ b/media/libcubeb/tests/test_devices.cpp
@@ -0,0 +1,162 @@
+/*
+ * Copyright © 2015 Haakon Sporsheim <haakon.sporsheim@telenordigital.com>
+ *
+ * This program is made available under an ISC-style license. See the
+ * accompanying file LICENSE for details.
+ */
+
+/* libcubeb enumerate device test/example.
+ * Prints out a list of devices enumerated. */
+#ifdef NDEBUG
+#undef NDEBUG
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "cubeb/cubeb.h"
+
+
+static void
+print_device_info(cubeb_device_info * info, FILE * f)
+{
+ char devfmts[64] = "";
+ 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;
+ };
+
+ 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;
+ };
+
+ 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;
+ };
+
+ if (info->format & CUBEB_DEVICE_FMT_S16LE)
+ strcat(devfmts, " S16LE");
+ if (info->format & CUBEB_DEVICE_FMT_S16BE)
+ strcat(devfmts, " S16BE");
+ if (info->format & CUBEB_DEVICE_FMT_F32LE)
+ strcat(devfmts, " F32LE");
+ if (info->format & CUBEB_DEVICE_FMT_F32BE)
+ 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 %ums, hi %ums\n",
+ info->device_id, info->preferred ? " (PREFERRED)" : "",
+ info->friendly_name, info->group_id, info->vendor_name,
+ devtype, devstate, info->max_channels,
+ (devfmts[0] == ' ') ? &devfmts[1] : devfmts,
+ (unsigned int)info->format, devdeffmt,
+ info->min_rate, info->max_rate, info->default_rate,
+ info->latency_lo_ms, info->latency_hi_ms);
+}
+
+static void
+print_device_collection(cubeb_device_collection * collection, FILE * f)
+{
+ uint32_t i;
+
+ for (i = 0; i < collection->count; i++)
+ print_device_info(collection->device[i], f);
+}
+
+static int
+run_enumerate_devices(void)
+{
+ int r = CUBEB_OK;
+ cubeb * ctx = NULL;
+ cubeb_device_collection * collection = NULL;
+
+ r = cubeb_init(&ctx, "Cubeb audio test");
+ if (r != CUBEB_OK) {
+ fprintf(stderr, "Error initializing cubeb library\n");
+ return r;
+ }
+
+ fprintf(stdout, "Enumerating input devices for backend %s\n",
+ cubeb_get_backend_id(ctx));
+
+ r = cubeb_enumerate_devices(ctx, CUBEB_DEVICE_TYPE_INPUT, &collection);
+ if (r != CUBEB_OK) {
+ fprintf(stderr, "Error enumerating devices %d\n", r);
+ goto cleanup;
+ }
+
+ fprintf(stdout, "Found %u input devices\n", collection->count);
+ print_device_collection(collection, stdout);
+ cubeb_device_collection_destroy(collection);
+
+ fprintf(stdout, "Enumerating output devices for backend %s\n",
+ cubeb_get_backend_id(ctx));
+
+ r = cubeb_enumerate_devices(ctx, CUBEB_DEVICE_TYPE_OUTPUT, &collection);
+ if (r != CUBEB_OK) {
+ fprintf(stderr, "Error enumerating devices %d\n", r);
+ goto cleanup;
+ }
+
+ fprintf(stdout, "Found %u output devices\n", collection->count);
+ print_device_collection(collection, stdout);
+ cubeb_device_collection_destroy(collection);
+
+cleanup:
+ cubeb_destroy(ctx);
+ return r;
+}
+
+int main(int argc, char *argv[])
+{
+ int ret;
+
+ ret = run_enumerate_devices();
+
+ return ret;
+}
diff --git a/media/libcubeb/tests/test_duplex.cpp b/media/libcubeb/tests/test_duplex.cpp
new file mode 100644
index 000000000..c5d02d782
--- /dev/null
+++ b/media/libcubeb/tests/test_duplex.cpp
@@ -0,0 +1,151 @@
+/*
+ * Copyright © 2016 Mozilla Foundation
+ *
+ * This program is made available under an ISC-style license. See the
+ * accompanying file LICENSE for details.
+ */
+
+/* libcubeb api/function test. Loops input back to output and check audio
+ * is flowing. */
+#ifdef NDEBUG
+#undef NDEBUG
+#endif
+#define _XOPEN_SOURCE 600
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <assert.h>
+
+#include "cubeb/cubeb.h"
+#include "common.h"
+#ifdef CUBEB_GECKO_BUILD
+#include "TestHarness.h"
+#endif
+
+#define SAMPLE_FREQUENCY 48000
+#if (defined(_WIN32) || defined(__WIN32__))
+#define STREAM_FORMAT CUBEB_SAMPLE_FLOAT32LE
+#define SILENT_SAMPLE 0.0f
+#else
+#define STREAM_FORMAT CUBEB_SAMPLE_S16LE
+#define SILENT_SAMPLE 0
+#endif
+
+struct user_state
+{
+ bool seen_noise;
+};
+
+
+
+long data_cb(cubeb_stream * stream, void * user, const void * inputbuffer, void * outputbuffer, long nframes)
+{
+ user_state * u = reinterpret_cast<user_state*>(user);
+#if (defined(_WIN32) || defined(__WIN32__))
+ float *ib = (float *)inputbuffer;
+ float *ob = (float *)outputbuffer;
+#else
+ short *ib = (short *)inputbuffer;
+ short *ob = (short *)outputbuffer;
+#endif
+ bool seen_noise = false;
+
+ if (stream == NULL || inputbuffer == NULL || outputbuffer == NULL) {
+ return CUBEB_ERROR;
+ }
+
+ // Loop back: upmix the single input channel to the two output channels,
+ // checking if there is noise in the process.
+ long output_index = 0;
+ for (long i = 0; i < nframes; i++) {
+ if (ib[i] != SILENT_SAMPLE) {
+ seen_noise = true;
+ }
+ ob[output_index] = ob[output_index + 1] = ib[i];
+ output_index += 2;
+ }
+
+ u->seen_noise |= seen_noise;
+
+ return nframes;
+}
+
+void state_cb(cubeb_stream * stream, void * /*user*/, cubeb_state state)
+{
+ if (stream == NULL)
+ return;
+
+ switch (state) {
+ case CUBEB_STATE_STARTED:
+ printf("stream started\n"); break;
+ case CUBEB_STATE_STOPPED:
+ printf("stream stopped\n"); break;
+ case CUBEB_STATE_DRAINED:
+ printf("stream drained\n"); break;
+ default:
+ printf("unknown stream state %d\n", state);
+ }
+
+ return;
+}
+
+int main(int /*argc*/, char * /*argv*/[])
+{
+#ifdef CUBEB_GECKO_BUILD
+ ScopedXPCOM xpcom("test_duplex");
+#endif
+
+ cubeb *ctx;
+ cubeb_stream *stream;
+ cubeb_stream_params input_params;
+ cubeb_stream_params output_params;
+ int r;
+ user_state stream_state = { false };
+ uint32_t latency_frames = 0;
+
+ r = cubeb_init(&ctx, "Cubeb duplex example");
+ if (r != CUBEB_OK) {
+ fprintf(stderr, "Error initializing cubeb library\n");
+ return r;
+ }
+
+ /* This test needs an available input device, skip it if this host does not
+ * have one. */
+ if (!has_available_input_device(ctx)) {
+ return 0;
+ }
+
+ /* typical user-case: mono input, stereo output, low latency. */
+ input_params.format = STREAM_FORMAT;
+ input_params.rate = 48000;
+ input_params.channels = 1;
+ output_params.format = STREAM_FORMAT;
+ output_params.rate = 48000;
+ output_params.channels = 2;
+
+ r = cubeb_get_min_latency(ctx, output_params, &latency_frames);
+
+ if (r != CUBEB_OK) {
+ fprintf(stderr, "Could not get minimal latency\n");
+ return r;
+ }
+
+ r = cubeb_stream_init(ctx, &stream, "Cubeb duplex",
+ NULL, &input_params, NULL, &output_params,
+ latency_frames, data_cb, state_cb, &stream_state);
+ if (r != CUBEB_OK) {
+ fprintf(stderr, "Error initializing cubeb stream\n");
+ return r;
+ }
+
+ cubeb_stream_start(stream);
+ delay(500);
+ cubeb_stream_stop(stream);
+
+ cubeb_stream_destroy(stream);
+ cubeb_destroy(ctx);
+
+ assert(stream_state.seen_noise);
+
+ return CUBEB_OK;
+}
diff --git a/media/libcubeb/tests/test_latency.cpp b/media/libcubeb/tests/test_latency.cpp
new file mode 100644
index 000000000..0586b1a36
--- /dev/null
+++ b/media/libcubeb/tests/test_latency.cpp
@@ -0,0 +1,60 @@
+#ifdef NDEBUG
+#undef NDEBUG
+#endif
+#include <stdlib.h>
+#include "cubeb/cubeb.h"
+#include <assert.h>
+#include <stdio.h>
+#ifdef CUBEB_GECKO_BUILD
+#include "TestHarness.h"
+#endif
+
+#define LOG(msg) fprintf(stderr, "%s\n", msg);
+
+int main(int /*argc*/, char * /*argv*/[])
+{
+#ifdef CUBEB_GECKO_BUILD
+ ScopedXPCOM xpcom("test_latency");
+#endif
+
+ cubeb * ctx = NULL;
+ int r;
+ uint32_t max_channels;
+ uint32_t preferred_rate;
+ uint32_t latency_frames;
+
+ LOG("latency_test start");
+ r = cubeb_init(&ctx, "Cubeb audio test");
+ assert(r == CUBEB_OK && "Cubeb init failed.");
+ LOG("cubeb_init ok");
+
+ r = cubeb_get_max_channel_count(ctx, &max_channels);
+ assert(r == CUBEB_OK || r == CUBEB_ERROR_NOT_SUPPORTED);
+ if (r == CUBEB_OK) {
+ assert(max_channels > 0 && "Invalid max channel count.");
+ LOG("cubeb_get_max_channel_count ok");
+ }
+
+ r = cubeb_get_preferred_sample_rate(ctx, &preferred_rate);
+ assert(r == CUBEB_OK || r == CUBEB_ERROR_NOT_SUPPORTED);
+ if (r == CUBEB_OK) {
+ assert(preferred_rate > 0 && "Invalid preferred sample rate.");
+ LOG("cubeb_get_preferred_sample_rate ok");
+ }
+
+ cubeb_stream_params params = {
+ CUBEB_SAMPLE_FLOAT32NE,
+ preferred_rate,
+ max_channels
+ };
+ r = cubeb_get_min_latency(ctx, params, &latency_frames);
+ assert(r == CUBEB_OK || r == CUBEB_ERROR_NOT_SUPPORTED);
+ if (r == CUBEB_OK) {
+ assert(latency_frames > 0 && "Invalid minimal latency.");
+ LOG("cubeb_get_min_latency ok");
+ }
+
+ cubeb_destroy(ctx);
+ LOG("cubeb_destroy ok");
+ return EXIT_SUCCESS;
+}
diff --git a/media/libcubeb/tests/test_record.cpp b/media/libcubeb/tests/test_record.cpp
new file mode 100644
index 000000000..681e1641e
--- /dev/null
+++ b/media/libcubeb/tests/test_record.cpp
@@ -0,0 +1,125 @@
+/*
+ * Copyright © 2016 Mozilla Foundation
+ *
+ * This program is made available under an ISC-style license. See the
+ * accompanying file LICENSE for details.
+ */
+
+/* libcubeb api/function test. Record the mic and check there is sound. */
+#ifdef NDEBUG
+#undef NDEBUG
+#endif
+#define _XOPEN_SOURCE 600
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <assert.h>
+
+#include "cubeb/cubeb.h"
+#include "common.h"
+#ifdef CUBEB_GECKO_BUILD
+#include "TestHarness.h"
+#endif
+
+#define SAMPLE_FREQUENCY 48000
+#if (defined(_WIN32) || defined(__WIN32__))
+#define STREAM_FORMAT CUBEB_SAMPLE_FLOAT32LE
+#else
+#define STREAM_FORMAT CUBEB_SAMPLE_S16LE
+#endif
+
+struct user_state
+{
+ bool seen_noise;
+};
+
+long data_cb(cubeb_stream * stream, void * user, const void * inputbuffer, void * outputbuffer, long nframes)
+{
+ user_state * u = reinterpret_cast<user_state*>(user);
+#if STREAM_FORMAT != CUBEB_SAMPLE_FLOAT32LE
+ short *b = (short *)inputbuffer;
+#else
+ float *b = (float *)inputbuffer;
+#endif
+
+ if (stream == NULL || inputbuffer == NULL || outputbuffer != NULL) {
+ return CUBEB_ERROR;
+ }
+
+ bool seen_noise = false;
+ for (long i = 0; i < nframes; i++) {
+ if (b[i] != 0.0) {
+ seen_noise = true;
+ }
+ }
+
+ u->seen_noise |= seen_noise;
+
+ return nframes;
+}
+
+void state_cb(cubeb_stream * stream, void * /*user*/, cubeb_state state)
+{
+ if (stream == NULL)
+ return;
+
+ switch (state) {
+ case CUBEB_STATE_STARTED:
+ printf("stream started\n"); break;
+ case CUBEB_STATE_STOPPED:
+ printf("stream stopped\n"); break;
+ case CUBEB_STATE_DRAINED:
+ printf("stream drained\n"); break;
+ default:
+ printf("unknown stream state %d\n", state);
+ }
+
+ return;
+}
+
+int main(int /*argc*/, char * /*argv*/[])
+{
+#ifdef CUBEB_GECKO_BUILD
+ ScopedXPCOM xpcom("test_record");
+#endif
+
+ cubeb *ctx;
+ cubeb_stream *stream;
+ cubeb_stream_params params;
+ int r;
+ user_state stream_state = { false };
+
+ r = cubeb_init(&ctx, "Cubeb record example");
+ if (r != CUBEB_OK) {
+ fprintf(stderr, "Error initializing cubeb library\n");
+ return r;
+ }
+
+ /* This test needs an available input device, skip it if this host does not
+ * have one. */
+ if (!has_available_input_device(ctx)) {
+ return 0;
+ }
+
+ params.format = STREAM_FORMAT;
+ params.rate = SAMPLE_FREQUENCY;
+ params.channels = 1;
+
+ r = cubeb_stream_init(ctx, &stream, "Cubeb record (mono)", NULL, &params, NULL, nullptr,
+ 4096, data_cb, state_cb, &stream_state);
+ if (r != CUBEB_OK) {
+ fprintf(stderr, "Error initializing cubeb stream\n");
+ return r;
+ }
+
+ cubeb_stream_start(stream);
+ delay(500);
+ cubeb_stream_stop(stream);
+
+ cubeb_stream_destroy(stream);
+ cubeb_destroy(ctx);
+
+ assert(stream_state.seen_noise);
+
+ return CUBEB_OK;
+}
diff --git a/media/libcubeb/tests/test_resampler.cpp b/media/libcubeb/tests/test_resampler.cpp
new file mode 100644
index 000000000..7e62a3572
--- /dev/null
+++ b/media/libcubeb/tests/test_resampler.cpp
@@ -0,0 +1,554 @@
+/*
+ * Copyright © 2016 Mozilla Foundation
+ *
+ * This program is made available under an ISC-style license. See the
+ * accompanying file LICENSE for details.
+ */
+#ifndef NOMINMAX
+#define NOMINMAX
+#endif // NOMINMAX
+
+#ifdef NDEBUG
+#undef NDEBUG
+#endif
+#include "cubeb_resampler_internal.h"
+#include <assert.h>
+#include <stdio.h>
+#include <algorithm>
+#include <iostream>
+
+/* Windows cmath USE_MATH_DEFINE thing... */
+const float PI = 3.14159265359f;
+
+/* Testing all sample rates is very long, so if THOROUGH_TESTING is not defined,
+ * only part of the test suite is ran. */
+#ifdef THOROUGH_TESTING
+/* Some standard sample rates we're testing with. */
+const uint32_t sample_rates[] = {
+ 8000,
+ 16000,
+ 32000,
+ 44100,
+ 48000,
+ 88200,
+ 96000,
+ 192000
+};
+/* The maximum number of channels we're resampling. */
+const uint32_t max_channels = 2;
+/* The minimum an maximum number of milliseconds we're resampling for. This is
+ * used to simulate the fact that the audio stream is resampled in chunks,
+ * because audio is delivered using callbacks. */
+const uint32_t min_chunks = 10; /* ms */
+const uint32_t max_chunks = 30; /* ms */
+const uint32_t chunk_increment = 1;
+
+#else
+
+const uint32_t sample_rates[] = {
+ 8000,
+ 44100,
+ 48000,
+};
+const uint32_t max_channels = 2;
+const uint32_t min_chunks = 10; /* ms */
+const uint32_t max_chunks = 30; /* ms */
+const uint32_t chunk_increment = 10;
+#endif
+
+#define DUMP_ARRAYS
+#ifdef DUMP_ARRAYS
+/**
+ * Files produced by dump(...) can be converted to .wave files using:
+ *
+ * sox -c <channel_count> -r <rate> -e float -b 32 file.raw file.wav
+ *
+ * for floating-point audio, or:
+ *
+ * sox -c <channel_count> -r <rate> -e unsigned -b 16 file.raw file.wav
+ *
+ * for 16bit integer audio.
+ */
+
+/* Use the correct implementation of fopen, depending on the platform. */
+void fopen_portable(FILE ** f, const char * name, const char * mode)
+{
+#ifdef WIN32
+ fopen_s(f, name, mode);
+#else
+ *f = fopen(name, mode);
+#endif
+}
+
+template<typename T>
+void dump(const char * name, T * frames, size_t count)
+{
+ FILE * file;
+ fopen_portable(&file, name, "wb");
+
+ if (!file) {
+ fprintf(stderr, "error opening %s\n", name);
+ return;
+ }
+
+ if (count != fwrite(frames, sizeof(T), count, file)) {
+ fprintf(stderr, "error writing to %s\n", name);
+ }
+ fclose(file);
+}
+#else
+template<typename T>
+void dump(const char * name, T * frames, size_t count)
+{ }
+#endif
+
+// The more the ratio is far from 1, the more we accept a big error.
+float epsilon_tweak_ratio(float ratio)
+{
+ return ratio >= 1 ? ratio : 1 / ratio;
+}
+
+// Epsilon values for comparing resampled data to expected data.
+// The bigger the resampling ratio is, the more lax we are about errors.
+template<typename T>
+T epsilon(float ratio);
+
+template<>
+float epsilon(float ratio) {
+ return 0.08f * epsilon_tweak_ratio(ratio);
+}
+
+template<>
+int16_t epsilon(float ratio) {
+ return static_cast<int16_t>(10 * epsilon_tweak_ratio(ratio));
+}
+
+void test_delay_lines(uint32_t delay_frames, uint32_t channels, uint32_t chunk_ms)
+{
+ const size_t length_s = 2;
+ const size_t rate = 44100;
+ const size_t length_frames = rate * length_s;
+ delay_line<float> delay(delay_frames, channels);
+ auto_array<float> input;
+ auto_array<float> output;
+ uint32_t chunk_length = channels * chunk_ms * rate / 1000;
+ uint32_t output_offset = 0;
+ uint32_t channel = 0;
+
+ /** Generate diracs every 100 frames, and check they are delayed. */
+ input.push_silence(length_frames * channels);
+ for (uint32_t i = 0; i < input.length() - 1; i+=100) {
+ input.data()[i + channel] = 0.5;
+ channel = (channel + 1) % channels;
+ }
+ dump("input.raw", input.data(), input.length());
+ while(input.length()) {
+ uint32_t to_pop = std::min<uint32_t>(input.length(), chunk_length * channels);
+ float * in = delay.input_buffer(to_pop / channels);
+ input.pop(in, to_pop);
+ delay.written(to_pop / channels);
+ output.push_silence(to_pop);
+ delay.output(output.data() + output_offset, to_pop / channels);
+ output_offset += to_pop;
+ }
+
+ // Check the diracs have been shifted by `delay_frames` frames.
+ for (uint32_t i = 0; i < output.length() - delay_frames * channels + 1; i+=100) {
+ assert(output.data()[i + channel + delay_frames * channels] == 0.5);
+ channel = (channel + 1) % channels;
+ }
+
+ dump("output.raw", output.data(), output.length());
+}
+/**
+ * This takes sine waves with a certain `channels` count, `source_rate`, and
+ * resample them, by chunk of `chunk_duration` milliseconds, to `target_rate`.
+ * Then a sample-wise comparison is performed against a sine wave generated at
+ * the correct rate.
+ */
+template<typename T>
+void test_resampler_one_way(uint32_t channels, uint32_t source_rate, uint32_t target_rate, float chunk_duration)
+{
+ size_t chunk_duration_in_source_frames = static_cast<uint32_t>(ceil(chunk_duration * source_rate / 1000.));
+ float resampling_ratio = static_cast<float>(source_rate) / target_rate;
+ cubeb_resampler_speex_one_way<T> resampler(channels, source_rate, target_rate, 3);
+ auto_array<T> source(channels * source_rate * 10);
+ auto_array<T> destination(channels * target_rate * 10);
+ auto_array<T> expected(channels * target_rate * 10);
+ uint32_t phase_index = 0;
+ uint32_t offset = 0;
+ const uint32_t buf_len = 2; /* seconds */
+
+ // generate a sine wave in each channel, at the source sample rate
+ source.push_silence(channels * source_rate * buf_len);
+ while(offset != source.length()) {
+ float p = phase_index++ / static_cast<float>(source_rate);
+ for (uint32_t j = 0; j < channels; j++) {
+ source.data()[offset++] = 0.5 * sin(440. * 2 * PI * p);
+ }
+ }
+
+ dump("input.raw", source.data(), source.length());
+
+ expected.push_silence(channels * target_rate * buf_len);
+ // generate a sine wave in each channel, at the target sample rate.
+ // Insert silent samples at the beginning to account for the resampler latency.
+ offset = resampler.latency() * channels;
+ for (uint32_t i = 0; i < offset; i++) {
+ expected.data()[i] = 0.0f;
+ }
+ phase_index = 0;
+ while (offset != expected.length()) {
+ float p = phase_index++ / static_cast<float>(target_rate);
+ for (uint32_t j = 0; j < channels; j++) {
+ expected.data()[offset++] = 0.5 * sin(440. * 2 * PI * p);
+ }
+ }
+
+ dump("expected.raw", expected.data(), expected.length());
+
+ // resample by chunk
+ uint32_t write_offset = 0;
+ destination.push_silence(channels * target_rate * buf_len);
+ while (write_offset < destination.length())
+ {
+ size_t output_frames = static_cast<uint32_t>(floor(chunk_duration_in_source_frames / resampling_ratio));
+ uint32_t input_frames = resampler.input_needed_for_output(output_frames);
+ resampler.input(source.data(), input_frames);
+ source.pop(nullptr, input_frames * channels);
+ resampler.output(destination.data() + write_offset,
+ std::min(output_frames, (destination.length() - write_offset) / channels));
+ write_offset += output_frames * channels;
+ }
+
+ dump("output.raw", destination.data(), expected.length());
+
+ // compare, taking the latency into account
+ bool fuzzy_equal = true;
+ for (uint32_t i = resampler.latency() + 1; i < expected.length(); i++) {
+ float diff = fabs(expected.data()[i] - destination.data()[i]);
+ if (diff > epsilon<T>(resampling_ratio)) {
+ fprintf(stderr, "divergence at %d: %f %f (delta %f)\n", i, expected.data()[i], destination.data()[i], diff);
+ fuzzy_equal = false;
+ }
+ }
+ assert(fuzzy_equal);
+}
+
+template<typename T>
+cubeb_sample_format cubeb_format();
+
+template<>
+cubeb_sample_format cubeb_format<float>()
+{
+ return CUBEB_SAMPLE_FLOAT32NE;
+}
+
+template<>
+cubeb_sample_format cubeb_format<short>()
+{
+ return CUBEB_SAMPLE_S16NE;
+}
+
+struct osc_state {
+ osc_state()
+ : input_phase_index(0)
+ , output_phase_index(0)
+ , output_offset(0)
+ , input_channels(0)
+ , output_channels(0)
+ {}
+ uint32_t input_phase_index;
+ uint32_t max_output_phase_index;
+ uint32_t output_phase_index;
+ uint32_t output_offset;
+ uint32_t input_channels;
+ uint32_t output_channels;
+ uint32_t output_rate;
+ uint32_t target_rate;
+ auto_array<float> input;
+ auto_array<float> output;
+};
+
+uint32_t fill_with_sine(float * buf, uint32_t rate, uint32_t channels,
+ uint32_t frames, uint32_t initial_phase)
+{
+ uint32_t offset = 0;
+ for (uint32_t i = 0; i < frames; i++) {
+ float p = initial_phase++ / static_cast<float>(rate);
+ for (uint32_t j = 0; j < channels; j++) {
+ buf[offset++] = 0.5 * sin(440. * 2 * PI * p);
+ }
+ }
+ return initial_phase;
+}
+
+long data_cb(cubeb_stream * /*stm*/, void * user_ptr,
+ const void * input_buffer, void * output_buffer, long frame_count)
+{
+ osc_state * state = reinterpret_cast<osc_state*>(user_ptr);
+ const float * in = reinterpret_cast<const float*>(input_buffer);
+ float * out = reinterpret_cast<float*>(output_buffer);
+
+
+ state->input.push(in, frame_count * state->input_channels);
+
+ /* Check how much output frames we need to write */
+ uint32_t remaining = state->max_output_phase_index - state->output_phase_index;
+ uint32_t to_write = std::min<uint32_t>(remaining, frame_count);
+ state->output_phase_index = fill_with_sine(out,
+ state->target_rate,
+ state->output_channels,
+ to_write,
+ state->output_phase_index);
+
+ return to_write;
+}
+
+template<typename T>
+bool array_fuzzy_equal(const auto_array<T>& lhs, const auto_array<T>& rhs, T epsi)
+{
+ uint32_t len = std::min(lhs.length(), rhs.length());
+
+ for (uint32_t i = 0; i < len; i++) {
+ if (fabs(lhs.at(i) - rhs.at(i)) > epsi) {
+ std::cout << "not fuzzy equal at index: " << i
+ << " lhs: " << lhs.at(i) << " rhs: " << rhs.at(i)
+ << " delta: " << fabs(lhs.at(i) - rhs.at(i))
+ << " epsilon: "<< epsi << std::endl;
+ return false;
+ }
+ }
+ return true;
+}
+
+template<typename T>
+void test_resampler_duplex(uint32_t input_channels, uint32_t output_channels,
+ uint32_t input_rate, uint32_t output_rate,
+ uint32_t target_rate, float chunk_duration)
+{
+ cubeb_stream_params input_params;
+ cubeb_stream_params output_params;
+ osc_state state;
+
+ input_params.format = output_params.format = cubeb_format<T>();
+ state.input_channels = input_params.channels = input_channels;
+ state.output_channels = output_params.channels = output_channels;
+ input_params.rate = input_rate;
+ state.output_rate = output_params.rate = output_rate;
+ state.target_rate = target_rate;
+ long got;
+
+ cubeb_resampler * resampler =
+ cubeb_resampler_create((cubeb_stream*)nullptr, &input_params, &output_params, target_rate,
+ data_cb, (void*)&state, CUBEB_RESAMPLER_QUALITY_VOIP);
+
+ long latency = cubeb_resampler_latency(resampler);
+
+ const uint32_t duration_s = 2;
+ int32_t duration_frames = duration_s * target_rate;
+ uint32_t input_array_frame_count = ceil(chunk_duration * input_rate / 1000) + ceilf(static_cast<float>(input_rate) / target_rate) * 2;
+ uint32_t output_array_frame_count = chunk_duration * output_rate / 1000;
+ auto_array<float> input_buffer(input_channels * input_array_frame_count);
+ auto_array<float> output_buffer(output_channels * output_array_frame_count);
+ auto_array<float> expected_resampled_input(input_channels * duration_frames);
+ auto_array<float> expected_resampled_output(output_channels * output_rate * duration_s);
+
+ state.max_output_phase_index = duration_s * target_rate;
+
+ expected_resampled_input.push_silence(input_channels * duration_frames);
+ expected_resampled_output.push_silence(output_channels * output_rate * duration_s);
+
+ /* expected output is a 440Hz sine wave at 16kHz */
+ fill_with_sine(expected_resampled_input.data() + latency,
+ target_rate, input_channels, duration_frames - latency, 0);
+ /* expected output is a 440Hz sine wave at 32kHz */
+ fill_with_sine(expected_resampled_output.data() + latency,
+ output_rate, output_channels, output_rate * duration_s - latency, 0);
+
+
+ while (state.output_phase_index != state.max_output_phase_index) {
+ uint32_t leftover_samples = input_buffer.length() * input_channels;
+ input_buffer.reserve(input_array_frame_count);
+ state.input_phase_index = fill_with_sine(input_buffer.data() + leftover_samples,
+ input_rate,
+ input_channels,
+ input_array_frame_count - leftover_samples,
+ state.input_phase_index);
+ long input_consumed = input_array_frame_count;
+ input_buffer.set_length(input_array_frame_count);
+
+ got = cubeb_resampler_fill(resampler,
+ input_buffer.data(), &input_consumed,
+ output_buffer.data(), output_array_frame_count);
+
+ /* handle leftover input */
+ if (input_array_frame_count != static_cast<uint32_t>(input_consumed)) {
+ input_buffer.pop(nullptr, input_consumed * input_channels);
+ } else {
+ input_buffer.clear();
+ }
+
+ state.output.push(output_buffer.data(), got * state.output_channels);
+ }
+
+ dump("input_expected.raw", expected_resampled_input.data(), expected_resampled_input.length());
+ dump("output_expected.raw", expected_resampled_output.data(), expected_resampled_output.length());
+ dump("input.raw", state.input.data(), state.input.length());
+ dump("output.raw", state.output.data(), state.output.length());
+
+ assert(array_fuzzy_equal(state.input, expected_resampled_input, epsilon<T>(input_rate/target_rate)));
+ assert(array_fuzzy_equal(state.output, expected_resampled_output, epsilon<T>(output_rate/target_rate)));
+
+ cubeb_resampler_destroy(resampler);
+}
+
+#define array_size(x) (sizeof(x) / sizeof(x[0]))
+
+void test_resamplers_one_way()
+{
+ /* Test one way resamplers */
+ for (uint32_t channels = 1; channels <= max_channels; channels++) {
+ for (uint32_t source_rate = 0; source_rate < array_size(sample_rates); source_rate++) {
+ for (uint32_t dest_rate = 0; dest_rate < array_size(sample_rates); dest_rate++) {
+ for (uint32_t chunk_duration = min_chunks; chunk_duration < max_chunks; chunk_duration+=chunk_increment) {
+ printf("one_way: channels: %d, source_rate: %d, dest_rate: %d, chunk_duration: %d\n",
+ channels, sample_rates[source_rate], sample_rates[dest_rate], chunk_duration);
+ test_resampler_one_way<float>(channels, sample_rates[source_rate],
+ sample_rates[dest_rate], chunk_duration);
+ }
+ }
+ }
+ }
+}
+
+void test_resamplers_duplex()
+{
+ /* Test duplex resamplers */
+ for (uint32_t input_channels = 1; input_channels <= max_channels; input_channels++) {
+ for (uint32_t output_channels = 1; output_channels <= max_channels; output_channels++) {
+ for (uint32_t source_rate_input = 0; source_rate_input < array_size(sample_rates); source_rate_input++) {
+ for (uint32_t source_rate_output = 0; source_rate_output < array_size(sample_rates); source_rate_output++) {
+ for (uint32_t dest_rate = 0; dest_rate < array_size(sample_rates); dest_rate++) {
+ for (uint32_t chunk_duration = min_chunks; chunk_duration < max_chunks; chunk_duration+=chunk_increment) {
+ printf("input channels:%d output_channels:%d input_rate:%d "
+ "output_rate:%d target_rate:%d chunk_ms:%d\n",
+ input_channels, output_channels,
+ sample_rates[source_rate_input],
+ sample_rates[source_rate_output],
+ sample_rates[dest_rate],
+ chunk_duration);
+ test_resampler_duplex<float>(input_channels, output_channels,
+ sample_rates[source_rate_input],
+ sample_rates[source_rate_output],
+ sample_rates[dest_rate],
+ chunk_duration);
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+void test_delay_line()
+{
+ for (uint32_t channel = 1; channel <= 2; channel++) {
+ for (uint32_t delay_frames = 4; delay_frames <= 40; delay_frames+=chunk_increment) {
+ for (uint32_t chunk_size = 10; chunk_size <= 30; chunk_size++) {
+ printf("channel: %d, delay_frames: %d, chunk_size: %d\n",
+ channel, delay_frames, chunk_size);
+ test_delay_lines(delay_frames, channel, chunk_size);
+ }
+ }
+ }
+}
+
+long test_output_only_noop_data_cb(cubeb_stream * /*stm*/, void * /*user_ptr*/,
+ const void * input_buffer,
+ void * output_buffer, long frame_count)
+{
+ assert(output_buffer);
+ assert(!input_buffer);
+ return frame_count;
+}
+
+void test_output_only_noop()
+{
+ cubeb_stream_params output_params;
+ int target_rate;
+
+ output_params.rate = 44100;
+ output_params.channels = 1;
+ output_params.format = CUBEB_SAMPLE_FLOAT32NE;
+ target_rate = output_params.rate;
+
+ cubeb_resampler * resampler =
+ cubeb_resampler_create((cubeb_stream*)nullptr, nullptr, &output_params, target_rate,
+ test_output_only_noop_data_cb, nullptr,
+ CUBEB_RESAMPLER_QUALITY_VOIP);
+
+ const long out_frames = 128;
+ float out_buffer[out_frames];
+ long got;
+
+ got = cubeb_resampler_fill(resampler, nullptr, nullptr,
+ out_buffer, out_frames);
+
+ assert(got == out_frames);
+
+ cubeb_resampler_destroy(resampler);
+}
+
+long test_drain_data_cb(cubeb_stream * /*stm*/, void * /*user_ptr*/,
+ const void * input_buffer,
+ void * output_buffer, long frame_count)
+{
+ assert(output_buffer);
+ assert(!input_buffer);
+ return frame_count - 10;
+}
+
+void test_resampler_drain()
+{
+ cubeb_stream_params output_params;
+ int target_rate;
+
+ output_params.rate = 44100;
+ output_params.channels = 1;
+ output_params.format = CUBEB_SAMPLE_FLOAT32NE;
+ target_rate = 48000;
+
+ cubeb_resampler * resampler =
+ cubeb_resampler_create((cubeb_stream*)nullptr, nullptr, &output_params, target_rate,
+ test_drain_data_cb, nullptr,
+ CUBEB_RESAMPLER_QUALITY_VOIP);
+
+ const long out_frames = 128;
+ float out_buffer[out_frames];
+ long got;
+
+ do {
+ got = cubeb_resampler_fill(resampler, nullptr, nullptr,
+ out_buffer, out_frames);
+ } while (got == out_frames);
+
+ /* If the above is not an infinite loop, the drain was a success, just mark
+ * this test as such. */
+ assert(true);
+
+ cubeb_resampler_destroy(resampler);
+}
+
+int main()
+{
+ test_resamplers_one_way();
+ test_delay_line();
+ // This is disabled because the latency estimation in the resampler code is
+ // slightly off so we can generate expected vectors.
+ // test_resamplers_duplex();
+ test_output_only_noop();
+ test_resampler_drain();
+
+ return 0;
+}
diff --git a/media/libcubeb/tests/test_sanity.cpp b/media/libcubeb/tests/test_sanity.cpp
new file mode 100644
index 000000000..77973ff15
--- /dev/null
+++ b/media/libcubeb/tests/test_sanity.cpp
@@ -0,0 +1,676 @@
+/*
+ * 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>
+#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__)
+
+#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
+
+template<typename T, size_t N>
+constexpr size_t
+ARRAY_LENGTH(T(&)[N])
+{
+ return N;
+}
+
+static int dummy;
+static 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)
+{
+ 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;
+ if (delay_callback) {
+ delay(10);
+ }
+ return nframes;
+}
+
+void
+test_state_callback(cubeb_stream * /*stm*/, void * /*user_ptr*/, cubeb_state /*state*/)
+{
+}
+
+static void
+test_init_destroy_context(void)
+{
+ int r;
+ cubeb * ctx;
+ char const* backend_id;
+
+ BEGIN_TEST;
+
+ r = cubeb_init(&ctx, "test_sanity");
+ assert(r == 0 && ctx);
+
+
+ backend_id = cubeb_get_backend_id(ctx);
+ assert(backend_id);
+
+ fprintf(stderr, "Backend: %s\n", backend_id);
+
+ cubeb_destroy(ctx);
+
+ END_TEST;
+}
+
+static void
+test_init_destroy_multiple_contexts(void)
+{
+ size_t i;
+ int r;
+ cubeb * ctx[4];
+ int order[4] = {2, 0, 3, 1};
+ assert(ARRAY_LENGTH(ctx) == ARRAY_LENGTH(order));
+
+ BEGIN_TEST;
+
+ 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]]);
+ }
+
+ 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;
+#if defined(__ANDROID__)
+ params.stream_type = CUBEB_STREAM_TYPE_MUSIC;
+#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;
+
+ r = cubeb_init(&ctx, "test_sanity");
+ assert(r == 0 && ctx);
+
+ params.format = STREAM_FORMAT;
+ params.rate = STREAM_RATE;
+ params.channels = STREAM_CHANNELS;
+#if defined(__ANDROID__)
+ params.stream_type = CUBEB_STREAM_TYPE_MUSIC;
+#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);
+
+ END_TEST;
+}
+
+static void
+test_init_destroy_multiple_streams(void)
+{
+ size_t i;
+ int r;
+ cubeb * ctx;
+ cubeb_stream * stream[8];
+ 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 = STREAM_CHANNELS;
+#if defined(__ANDROID__)
+ params.stream_type = CUBEB_STREAM_TYPE_MUSIC;
+#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);
+ assert(r == 0);
+ assert(stream[i]);
+ }
+
+ for (i = 0; i < ARRAY_LENGTH(stream); ++i) {
+ cubeb_stream_destroy(stream[i]);
+ }
+
+ cubeb_destroy(ctx);
+
+ 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
+#if defined(__ANDROID__)
+ params.stream_type = CUBEB_STREAM_TYPE_MUSIC;
+#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;
+}
+
+static void
+test_init_start_stop_destroy_multiple_streams(int early, int delay_ms)
+{
+ size_t i;
+ int r;
+ cubeb * ctx;
+ cubeb_stream * stream[8];
+ 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 = STREAM_CHANNELS;
+#if defined(__ANDROID__)
+ params.stream_type = CUBEB_STREAM_TYPE_MUSIC;
+#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);
+ assert(r == 0);
+ assert(stream[i]);
+ if (early) {
+ r = cubeb_stream_start(stream[i]);
+ assert(r == 0);
+ }
+ }
+
+
+ if (!early) {
+ for (i = 0; i < ARRAY_LENGTH(stream); ++i) {
+ r = cubeb_stream_start(stream[i]);
+ assert(r == 0);
+ }
+ }
+
+ if (delay_ms) {
+ delay(delay_ms);
+ }
+
+ if (!early) {
+ for (i = 0; i < ARRAY_LENGTH(stream); ++i) {
+ r = cubeb_stream_stop(stream[i]);
+ assert(r == 0);
+ }
+ }
+
+ for (i = 0; i < ARRAY_LENGTH(stream); ++i) {
+ if (early) {
+ r = cubeb_stream_stop(stream[i]);
+ assert(r == 0);
+ }
+ cubeb_stream_destroy(stream[i]);
+ }
+
+ cubeb_destroy(ctx);
+
+ END_TEST;
+}
+
+static void
+test_init_destroy_multiple_contexts_and_streams(void)
+{
+ size_t i, j;
+ int r;
+ cubeb * ctx[2];
+ cubeb_stream * stream[8];
+ cubeb_stream_params params;
+ size_t streams_per_ctx = ARRAY_LENGTH(stream) / ARRAY_LENGTH(ctx);
+ assert(ARRAY_LENGTH(ctx) * streams_per_ctx == ARRAY_LENGTH(stream));
+
+ BEGIN_TEST;
+
+ params.format = STREAM_FORMAT;
+ params.rate = STREAM_RATE;
+ params.channels = STREAM_CHANNELS;
+#if defined(__ANDROID__)
+ params.stream_type = CUBEB_STREAM_TYPE_MUSIC;
+#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);
+ 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]);
+ }
+
+ 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;
+
+ r = cubeb_init(&ctx, "test_sanity");
+ assert(r == 0 && ctx);
+
+ params.format = STREAM_FORMAT;
+ params.rate = STREAM_RATE;
+ params.channels = STREAM_CHANNELS;
+#if defined(__ANDROID__)
+ params.stream_type = CUBEB_STREAM_TYPE_MUSIC;
+#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);
+
+ END_TEST;
+}
+
+static void
+test_stream_position(void)
+{
+ size_t i;
+ int r;
+ cubeb * ctx;
+ cubeb_stream * stream;
+ cubeb_stream_params params;
+ uint64_t position, last_position;
+
+ BEGIN_TEST;
+
+ 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;
+#if defined(__ANDROID__)
+ params.stream_type = CUBEB_STREAM_TYPE_MUSIC;
+#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);
+ }
+
+ /* test that the position is valid even when starting and
+ * stopping the stream. */
+ for (i = 0; i < 5; ++i) {
+ r = cubeb_stream_stop(stream);
+ assert(r == 0);
+ r = cubeb_stream_get_position(stream, &position);
+ assert(r == 0);
+ assert(last_position < position);
+ last_position = position;
+ delay(500);
+ r = cubeb_stream_start(stream);
+ assert(r == 0);
+ 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);
+
+ END_TEST;
+}
+
+static int do_drain;
+static int got_drain;
+
+static long
+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
+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;
+
+ 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;
+#if defined(__ANDROID__)
+ params.stream_type = CUBEB_STREAM_TYPE_MUSIC;
+#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);
+
+ // 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(position <= total_frames_written);
+
+ cubeb_stream_destroy(stream);
+ cubeb_destroy(ctx);
+
+ END_TEST;
+}
+
+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;
+
+ ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
+ osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
+
+ // NT 6.1 is Windows 7
+ osvi.dwMajorVersion = 6;
+ osvi.dwMinorVersion = 1;
+
+ 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);
+#else
+ return 0;
+#endif
+}
+
+int
+main(int /*argc*/, char * /*argv*/[])
+{
+#ifdef CUBEB_GECKO_BUILD
+ ScopedXPCOM xpcom("test_sanity");
+#endif
+
+ test_init_destroy_context();
+ test_init_destroy_multiple_contexts();
+ test_context_variables();
+ test_init_destroy_stream();
+ test_init_destroy_multiple_streams();
+ test_configure_stream();
+ 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()) {
+ test_init_destroy_multiple_contexts_and_streams();
+
+ 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);
+ }
+ delay_callback = 0;
+ test_drain();
+/*
+ to implement:
+ test_eos_during_prefill();
+ test_stream_destroy_pending_drain();
+*/
+ printf("\n");
+
+ return 0;
+}
diff --git a/media/libcubeb/tests/test_tone.cpp b/media/libcubeb/tests/test_tone.cpp
new file mode 100644
index 000000000..3c6e0ec54
--- /dev/null
+++ b/media/libcubeb/tests/test_tone.cpp
@@ -0,0 +1,149 @@
+/*
+ * Copyright © 2011 Mozilla Foundation
+ *
+ * This program is made available under an ISC-style license. See the
+ * accompanying file LICENSE for details.
+ */
+
+/* libcubeb api/function test. Plays a simple tone. */
+#ifdef NDEBUG
+#undef NDEBUG
+#endif
+#define _XOPEN_SOURCE 600
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <assert.h>
+#include <limits.h>
+
+#include "cubeb/cubeb.h"
+#include "common.h"
+#ifdef CUBEB_GECKO_BUILD
+#include "TestHarness.h"
+#endif
+
+#define SAMPLE_FREQUENCY 48000
+#if (defined(_WIN32) || defined(__WIN32__))
+#define STREAM_FORMAT CUBEB_SAMPLE_FLOAT32LE
+#else
+#define STREAM_FORMAT CUBEB_SAMPLE_S16LE
+#endif
+
+/* store the phase of the generated waveform */
+struct cb_user_data {
+ long position;
+};
+
+long data_cb(cubeb_stream *stream, void *user, const void* /*inputbuffer*/, void *outputbuffer, long nframes)
+{
+ struct cb_user_data *u = (struct cb_user_data *)user;
+#if (defined(_WIN32) || defined(__WIN32__))
+ float *b = (float *)outputbuffer;
+#else
+ short *b = (short *)outputbuffer;
+#endif
+ float t1, t2;
+ int i;
+
+ if (stream == NULL || u == NULL)
+ return CUBEB_ERROR;
+
+ /* 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);
+#if (defined(_WIN32) || defined(__WIN32__))
+ b[i] = 0.5 * t1;
+ b[i] += 0.5 * t2;
+#else
+ b[i] = (SHRT_MAX / 2) * t1;
+ b[i] += (SHRT_MAX / 2) * t2;
+#endif
+ /* European dial tone */
+ /*
+ t1 = sin(2*M_PI*(i + u->position)*425/SAMPLE_FREQUENCY);
+#if (defined(_WIN32) || defined(__WIN32__))
+ b[i] = t1;
+#else
+ b[i] = SHRT_MAX * t1;
+#endif
+ */
+ }
+ /* remember our phase to avoid clicking on buffer transitions */
+ /* we'll still click if position overflows */
+ u->position += nframes;
+
+ return nframes;
+}
+
+void state_cb(cubeb_stream *stream, void *user, cubeb_state state)
+{
+ struct cb_user_data *u = (struct cb_user_data *)user;
+
+ if (stream == NULL || u == NULL)
+ return;
+
+ switch (state) {
+ case CUBEB_STATE_STARTED:
+ printf("stream started\n"); break;
+ case CUBEB_STATE_STOPPED:
+ printf("stream stopped\n"); break;
+ case CUBEB_STATE_DRAINED:
+ printf("stream drained\n"); break;
+ default:
+ printf("unknown stream state %d\n", state);
+ }
+
+ return;
+}
+
+int main(int /*argc*/, char * /*argv*/[])
+{
+#ifdef CUBEB_GECKO_BUILD
+ ScopedXPCOM xpcom("test_tone");
+#endif
+
+ cubeb *ctx;
+ cubeb_stream *stream;
+ cubeb_stream_params params;
+ struct cb_user_data *user_data;
+ int r;
+
+ r = cubeb_init(&ctx, "Cubeb tone example");
+ if (r != CUBEB_OK) {
+ fprintf(stderr, "Error initializing cubeb library\n");
+ return r;
+ }
+
+ params.format = STREAM_FORMAT;
+ params.rate = SAMPLE_FREQUENCY;
+ params.channels = 1;
+
+ user_data = (struct cb_user_data *) malloc(sizeof(*user_data));
+ if (user_data == NULL) {
+ fprintf(stderr, "Error allocating user data\n");
+ return CUBEB_ERROR;
+ }
+ user_data->position = 0;
+
+ r = cubeb_stream_init(ctx, &stream, "Cubeb tone (mono)", NULL, NULL, NULL, &params,
+ 4096, data_cb, state_cb, user_data);
+ if (r != CUBEB_OK) {
+ fprintf(stderr, "Error initializing cubeb stream\n");
+ return r;
+ }
+
+ cubeb_stream_start(stream);
+ delay(500);
+ cubeb_stream_stop(stream);
+
+ cubeb_stream_destroy(stream);
+ cubeb_destroy(ctx);
+
+ assert(user_data->position);
+
+ free(user_data);
+
+ return CUBEB_OK;
+}
diff --git a/media/libcubeb/tests/test_utils.cpp b/media/libcubeb/tests/test_utils.cpp
new file mode 100644
index 000000000..f52cd3196
--- /dev/null
+++ b/media/libcubeb/tests/test_utils.cpp
@@ -0,0 +1,80 @@
+#include <cassert>
+#include "cubeb_utils.h"
+
+int test_auto_array()
+{
+ auto_array<uint32_t> array;
+ auto_array<uint32_t> array2(10);
+ uint32_t a[10];
+
+ assert(array2.length() == 0);
+ assert(array2.capacity() == 10);
+
+
+ for (uint32_t i = 0; i < 10; i++) {
+ a[i] = i;
+ }
+
+ assert(array.capacity() == 0);
+ assert(array.length() == 0);
+
+ array.push(a, 10);
+
+ assert(!array.reserve(9));
+
+ for (uint32_t i = 0; i < 10; i++) {
+ assert(array.data()[i] == i);
+ }
+
+ assert(array.capacity() == 10);
+ assert(array.length() == 10);
+
+ uint32_t b[10];
+
+ array.pop(b, 5);
+
+ assert(array.capacity() == 10);
+ assert(array.length() == 5);
+ for (uint32_t i = 0; i < 5; i++) {
+ assert(b[i] == i);
+ assert(array.data()[i] == 5 + i);
+ }
+ uint32_t* bb = b + 5;
+ array.pop(bb, 5);
+
+ assert(array.capacity() == 10);
+ assert(array.length() == 0);
+ for (uint32_t i = 0; i < 5; i++) {
+ assert(bb[i] == 5 + i);
+ }
+
+ assert(!array.pop(nullptr, 1));
+
+ array.push(a, 10);
+ array.push(a, 10);
+
+ for (uint32_t j = 0; j < 2; j++) {
+ for (uint32_t i = 0; i < 10; i++) {
+ assert(array.data()[10 * j + i] == i);
+ }
+ }
+ assert(array.length() == 20);
+ assert(array.capacity() == 20);
+ array.pop(nullptr, 5);
+
+ for (uint32_t i = 0; i < 5; i++) {
+ assert(array.data()[i] == 5 + i);
+ }
+
+ assert(array.length() == 15);
+ assert(array.capacity() == 20);
+
+ return 0;
+}
+
+
+int main()
+{
+ test_auto_array();
+ return 0;
+}