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; for (i = 0; i < ARRAY_LENGTH(stream); ++i) { r = cubeb_stream_init(ctx, &stream[i], "test", 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; }
TEST(cubeb, configure_stream) { int r; cubeb * ctx; cubeb_stream * stream; cubeb_stream_params params; r = common_init(&ctx, "test_sanity"); ASSERT_EQ(r, CUBEB_OK); ASSERT_NE(ctx, nullptr); params.format = STREAM_FORMAT; params.rate = STREAM_RATE; params.channels = 2; // panning params.layout = CUBEB_LAYOUT_STEREO; 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); r = cubeb_stream_set_volume(stream, 1.0f); ASSERT_TRUE(r == 0 || r == CUBEB_ERROR_NOT_SUPPORTED); r = cubeb_stream_set_panning(stream, 0.0f); ASSERT_TRUE(r == 0 || r == CUBEB_ERROR_NOT_SUPPORTED); cubeb_stream_destroy(stream); cubeb_destroy(ctx); }
TEST(cubeb, configure_stream_undefined_layout) { int r; cubeb * ctx; cubeb_stream * stream; cubeb_stream_params params; r = common_init(&ctx, "test_sanity"); ASSERT_EQ(r, CUBEB_OK); ASSERT_NE(ctx, nullptr); params.format = STREAM_FORMAT; params.rate = STREAM_RATE; params.channels = 2; // panning params.layout = CUBEB_LAYOUT_UNDEFINED; 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); r = cubeb_stream_start(stream); ASSERT_EQ(r, CUBEB_OK); delay(100); r = cubeb_stream_stop(stream); ASSERT_EQ(r, CUBEB_OK); cubeb_stream_destroy(stream); cubeb_destroy(ctx); }
TEST(cubeb, init_destroy_multiple_streams) { size_t i; int r; cubeb * ctx; cubeb_stream * stream[8]; cubeb_stream_params params; r = common_init(&ctx, "test_sanity"); ASSERT_EQ(r, CUBEB_OK); ASSERT_NE(ctx, nullptr); params.format = STREAM_FORMAT; params.rate = STREAM_RATE; params.channels = STREAM_CHANNELS; params.layout = STREAM_LAYOUT; 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); ASSERT_EQ(r, CUBEB_OK); ASSERT_NE(stream[i], nullptr); } for (i = 0; i < ARRAY_LENGTH(stream); ++i) { cubeb_stream_destroy(stream[i]); } cubeb_destroy(ctx); }
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; }
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 r = cubeb_stream_init(ctx, &stream, "test", 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_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; r = cubeb_stream_init(ctx, &stream, "test", 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_start_stop_destroy_multiple_streams(int early, int delay_ms) { int 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; for (i = 0; i < 8; ++i) { r = cubeb_stream_init(ctx, &stream[i], "test", 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 < 8; ++i) { r = cubeb_stream_start(stream[i]); assert(r == 0); } } if (delay_ms) { delay(delay_ms); } if (!early) { for (i = 0; i < 8; ++i) { r = cubeb_stream_stop(stream[i]); assert(r == 0); } } for (i = 0; i < 8; ++i) { if (early) { r = cubeb_stream_stop(stream[i]); assert(r == 0); } cubeb_stream_destroy(stream[i]); } cubeb_destroy(ctx); END_TEST }
TEST(cubeb, drain) { int r; cubeb * ctx; cubeb_stream * stream; cubeb_stream_params params; uint64_t position; delay_callback = 0; total_frames_written = 0; r = common_init(&ctx, "test_sanity"); ASSERT_EQ(r, CUBEB_OK); ASSERT_NE(ctx, nullptr); params.format = STREAM_FORMAT; params.rate = STREAM_RATE; params.channels = STREAM_CHANNELS; params.layout = STREAM_LAYOUT; 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); r = cubeb_stream_start(stream); ASSERT_EQ(r, CUBEB_OK); delay(500); do_drain = 1; for (;;) { r = cubeb_stream_get_position(stream, &position); ASSERT_EQ(r, CUBEB_OK); if (got_drain) { break; } else { ASSERT_LE(position, total_frames_written.load()); } delay(500); } r = cubeb_stream_get_position(stream, &position); 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()); cubeb_stream_destroy(stream); cubeb_destroy(ctx); got_drain = 0; do_drain = 0; }
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; r = cubeb_stream_init(ctx, &stream, "test", 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 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, ¶ms, 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); 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; }
TEST(cubeb, record) { if (cubeb_set_log_callback(CUBEB_LOG_DISABLED, nullptr /*print_log*/) != CUBEB_OK) { printf("Set log callback failed\n"); } cubeb *ctx; cubeb_stream *stream; cubeb_stream_params params; int r; user_state_record stream_state = { false }; r = cubeb_init(&ctx, "Cubeb record example"); if (r != CUBEB_OK) { fprintf(stderr, "Error initializing cubeb library\n"); ASSERT_EQ(r, CUBEB_OK); } /* This test needs an available input device, skip it if this host does not * have one. */ if (!has_available_input_device(ctx)) { return; } params.format = STREAM_FORMAT; params.rate = SAMPLE_FREQUENCY; params.channels = 1; params.layout = CUBEB_LAYOUT_MONO; r = cubeb_stream_init(ctx, &stream, "Cubeb record (mono)", NULL, ¶ms, NULL, nullptr, 4096, data_cb_record, state_cb_record, &stream_state); if (r != CUBEB_OK) { fprintf(stderr, "Error initializing cubeb stream\n"); ASSERT_EQ(r, CUBEB_OK); } cubeb_stream_start(stream); delay(500); cubeb_stream_stop(stream); cubeb_stream_destroy(stream); cubeb_destroy(ctx); #ifdef __linux__ // user callback does not arrive in Linux, silence the error printf("Check is disabled in Linux\n"); #else ASSERT_TRUE(stream_state.seen_audio); #endif }
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, ¶ms, NULL, nullptr, 250, 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; }
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; r = cubeb_stream_init(ctx, &stream, "test", 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 }
TEST(cubeb, basic_stream_operations) { int r; cubeb * ctx; cubeb_stream * stream; cubeb_stream_params params; uint64_t position; r = common_init(&ctx, "test_sanity"); ASSERT_EQ(r, CUBEB_OK); ASSERT_NE(ctx, nullptr); params.format = STREAM_FORMAT; params.rate = STREAM_RATE; params.channels = STREAM_CHANNELS; params.layout = STREAM_LAYOUT; 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); /* position and volume before stream has started */ r = cubeb_stream_get_position(stream, &position); ASSERT_EQ(r, CUBEB_OK); ASSERT_EQ(position, 0u); r = cubeb_stream_start(stream); ASSERT_EQ(r, CUBEB_OK); /* position and volume after while stream running */ r = cubeb_stream_get_position(stream, &position); ASSERT_EQ(r, CUBEB_OK); r = cubeb_stream_stop(stream); ASSERT_EQ(r, CUBEB_OK); /* position and volume after stream has stopped */ r = cubeb_stream_get_position(stream, &position); ASSERT_EQ(r, CUBEB_OK); cubeb_stream_destroy(stream); cubeb_destroy(ctx); }
TEST(cubeb, init_destroy_multiple_contexts_and_streams) { 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_EQ(ARRAY_LENGTH(ctx) * streams_per_ctx, ARRAY_LENGTH(stream)); /* 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()) return; params.format = STREAM_FORMAT; params.rate = STREAM_RATE; params.channels = STREAM_CHANNELS; params.layout = STREAM_LAYOUT; for (i = 0; i < ARRAY_LENGTH(ctx); ++i) { r = common_init(&ctx[i], "test_sanity"); ASSERT_EQ(r, CUBEB_OK); 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, test_data_callback, test_state_callback, &dummy); ASSERT_EQ(r, CUBEB_OK); ASSERT_NE(stream[i * streams_per_ctx + j], nullptr); } } 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]); } }
TEST(cubeb, record) { cubeb *ctx; cubeb_stream *stream; cubeb_stream_params params; int r; user_state_record stream_state = { false }; r = cubeb_init(&ctx, "Cubeb record example"); if (r != CUBEB_OK) { fprintf(stderr, "Error initializing cubeb library\n"); ASSERT_EQ(r, CUBEB_OK); } /* This test needs an available input device, skip it if this host does not * have one. */ if (!has_available_input_device(ctx)) { return; } params.format = STREAM_FORMAT; params.rate = SAMPLE_FREQUENCY; params.channels = 1; r = cubeb_stream_init(ctx, &stream, "Cubeb record (mono)", NULL, ¶ms, NULL, nullptr, 4096, data_cb_record, state_cb_record, &stream_state); if (r != CUBEB_OK) { fprintf(stderr, "Error initializing cubeb stream\n"); ASSERT_EQ(r, CUBEB_OK); } cubeb_stream_start(stream); delay(500); cubeb_stream_stop(stream); cubeb_stream_destroy(stream); cubeb_destroy(ctx); ASSERT_TRUE(stream_state.seen_noise); }
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; 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", 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; }
TEST(cubeb, init_destroy_stream) { int r; cubeb * ctx; cubeb_stream * stream; cubeb_stream_params params; r = common_init(&ctx, "test_sanity"); ASSERT_EQ(r, CUBEB_OK); ASSERT_NE(ctx, nullptr); params.format = STREAM_FORMAT; params.rate = STREAM_RATE; params.channels = STREAM_CHANNELS; params.layout = STREAM_LAYOUT; 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); cubeb_stream_destroy(stream); cubeb_destroy(ctx); }
static void test_init_destroy_multiple_contexts_and_streams(void) { int i, j; int r; cubeb * ctx[2]; cubeb_stream * stream[8]; cubeb_stream_params params; BEGIN_TEST params.format = STREAM_FORMAT; params.rate = STREAM_RATE; params.channels = STREAM_CHANNELS; for (i = 0; i < 2; ++i) { r = cubeb_init(&ctx[i], "test_sanity"); assert(r == 0 && ctx[i]); for (j = 0; j < 4; ++j) { r = cubeb_stream_init(ctx[i], &stream[i * 4 + j], "test", params, STREAM_LATENCY, test_data_callback, test_state_callback, &dummy); assert(r == 0); assert(stream[i * 4 + j]); } } for (i = 0; i < 2; ++i) { for (j = 0; j < 4; ++j) { cubeb_stream_destroy(stream[i * 4 + j]); } cubeb_destroy(ctx[i]); } END_TEST }
int cubeb_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_name, cubeb_stream_params stream_params, unsigned int latency, cubeb_data_callback data_callback, cubeb_state_callback state_callback, void * user_ptr) { MMRESULT r; WAVEFORMATEXTENSIBLE wfx; cubeb_stream * stm; int i; size_t bufsz; assert(context); assert(stream); *stream = NULL; if (stream_params.rate < 1 || stream_params.rate > 192000 || stream_params.channels < 1 || stream_params.channels > 32 || latency < 1 || latency > 2000) { return CUBEB_ERROR_INVALID_FORMAT; } memset(&wfx, 0, sizeof(wfx)); if (stream_params.channels > 2) { wfx.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; wfx.Format.cbSize = sizeof(wfx) - sizeof(wfx.Format); } else { wfx.Format.wFormatTag = WAVE_FORMAT_PCM; if (stream_params.format == CUBEB_SAMPLE_FLOAT32LE) { wfx.Format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT; } wfx.Format.cbSize = 0; } wfx.Format.nChannels = stream_params.channels; wfx.Format.nSamplesPerSec = stream_params.rate; /* XXX fix channel mappings */ wfx.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT; switch (stream_params.format) { case CUBEB_SAMPLE_S16LE: wfx.Format.wBitsPerSample = 16; wfx.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; break; case CUBEB_SAMPLE_FLOAT32LE: wfx.Format.wBitsPerSample = 32; wfx.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; break; default: return CUBEB_ERROR_INVALID_FORMAT; } wfx.Format.nBlockAlign = (wfx.Format.wBitsPerSample * wfx.Format.nChannels) / 8; wfx.Format.nAvgBytesPerSec = wfx.Format.nSamplesPerSec * wfx.Format.nBlockAlign; wfx.Samples.wValidBitsPerSample = 0; wfx.Samples.wSamplesPerBlock = 0; wfx.Samples.wReserved = 0; EnterCriticalSection(&context->lock); /* CUBEB_STREAM_MAX is a horrible hack to avoid a situation where, when many streams are active at once, a subset of them will not consume (via playback) or release (via waveOutReset) their buffers. */ if (context->active_streams >= CUBEB_STREAM_MAX) { LeaveCriticalSection(&context->lock); return CUBEB_ERROR; } context->active_streams += 1; LeaveCriticalSection(&context->lock); stm = calloc(1, sizeof(*stm)); assert(stm); stm->context = context; stm->params = stream_params; stm->data_callback = data_callback; stm->state_callback = state_callback; stm->user_ptr = user_ptr; bufsz = (size_t) (stm->params.rate / 1000.0 * latency * bytes_per_frame(stm->params) / NBUFS); if (bufsz % bytes_per_frame(stm->params) != 0) { bufsz += bytes_per_frame(stm->params) - (bufsz % bytes_per_frame(stm->params)); } assert(bufsz % bytes_per_frame(stm->params) == 0); stm->buffer_size = bufsz; InitializeCriticalSection(&stm->lock); stm->event = CreateEvent(NULL, FALSE, FALSE, NULL); if (!stm->event) { cubeb_stream_destroy(stm); return CUBEB_ERROR; } /* cubeb_buffer_callback will be called during waveOutOpen, so all other initialization must be complete before calling it. */ r = waveOutOpen(&stm->waveout, WAVE_MAPPER, &wfx.Format, (DWORD_PTR) cubeb_buffer_callback, (DWORD_PTR) stm, CALLBACK_FUNCTION); if (r != MMSYSERR_NOERROR) { cubeb_stream_destroy(stm); return CUBEB_ERROR; } r = waveOutPause(stm->waveout); if (r != MMSYSERR_NOERROR) { cubeb_stream_destroy(stm); return CUBEB_ERROR; } for (i = 0; i < NBUFS; ++i) { WAVEHDR * hdr = &stm->buffers[i]; hdr->lpData = calloc(1, bufsz); assert(hdr->lpData); hdr->dwBufferLength = bufsz; hdr->dwFlags = 0; r = waveOutPrepareHeader(stm->waveout, hdr, sizeof(*hdr)); if (r != MMSYSERR_NOERROR) { cubeb_stream_destroy(stm); return CUBEB_ERROR; } cubeb_refill_stream(stm); } *stream = stm; return CUBEB_OK; }
int cubeb_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_name, cubeb_stream_params stream_params, unsigned int latency, cubeb_data_callback data_callback, cubeb_state_callback state_callback, void * user_ptr) { cubeb_stream * stm; int r; snd_pcm_format_t format; snd_pcm_uframes_t buffer_size; snd_pcm_uframes_t period_size; assert(context); assert(stream); if (stream_params.rate < 1 || stream_params.rate > 192000 || stream_params.channels < 1 || stream_params.channels > 32 || latency < 1 || latency > 2000) { return CUBEB_ERROR_INVALID_FORMAT; } switch (stream_params.format) { case CUBEB_SAMPLE_S16LE: format = SND_PCM_FORMAT_S16_LE; break; case CUBEB_SAMPLE_S16BE: format = SND_PCM_FORMAT_S16_BE; break; case CUBEB_SAMPLE_FLOAT32LE: format = SND_PCM_FORMAT_FLOAT_LE; break; case CUBEB_SAMPLE_FLOAT32BE: format = SND_PCM_FORMAT_FLOAT_BE; break; default: return CUBEB_ERROR_INVALID_FORMAT; } stm = calloc(1, sizeof(*stm)); assert(stm); stm->context = context; stm->data_callback = data_callback; stm->state_callback = state_callback; stm->user_ptr = user_ptr; stm->params = stream_params; r = pthread_mutex_init(&stm->lock, NULL); assert(r == 0); r = pthread_cond_init(&stm->cond, NULL); assert(r == 0); r = cubeb_locked_pcm_open(&stm->pcm, SND_PCM_STREAM_PLAYBACK); if (r < 0) { cubeb_stream_destroy(stm); return CUBEB_ERROR; } r = snd_pcm_nonblock(stm->pcm, 1); assert(r == 0); r = snd_pcm_set_params(stm->pcm, format, SND_PCM_ACCESS_RW_INTERLEAVED, stm->params.channels, stm->params.rate, 1, latency * 1000); if (r < 0) { /* XXX: return format error if necessary */ cubeb_stream_destroy(stm); return CUBEB_ERROR; } r = snd_pcm_get_params(stm->pcm, &buffer_size, &period_size); assert(r == 0); fprintf(stderr, "b=%u p=%u\n", buffer_size, period_size); /* set up poll infrastructure */ stm->n_descriptors = snd_pcm_poll_descriptors_count(stm->pcm); assert(stm->n_descriptors > 0); stm->descriptors = calloc(stm->n_descriptors, sizeof(*stm->descriptors)); assert(stm->descriptors); r = snd_pcm_poll_descriptors(stm->pcm, stm->descriptors, stm->n_descriptors); assert(r == stm->n_descriptors); r = snd_pcm_pause(stm->pcm, 1); #if 0 assert(r == 0); #endif stm->state = CUBEB_STREAM_STATE_INACTIVE; *stream = stm; return CUBEB_OK; }
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, ¶ms, 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); 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; }
int cubeb_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_name, cubeb_stream_params stream_params, unsigned int latency, cubeb_data_callback data_callback, cubeb_state_callback state_callback, void * user_ptr) { AudioStreamBasicDescription ss; #if MAC_OS_X_VERSION_MIN_REQUIRED < 1060 ComponentDescription desc; Component comp; #else AudioComponentDescription desc; AudioComponent comp; #endif cubeb_stream * stm; AURenderCallbackStruct input; unsigned int buffer_size; OSStatus r; assert(context == (void *) 0xdeadbeef); *stream = NULL; if (stream_params.rate < 1 || stream_params.rate > 192000 || stream_params.channels < 1 || stream_params.channels > 32 || latency < 1 || latency > 2000) { return CUBEB_ERROR_INVALID_FORMAT; } memset(&ss, 0, sizeof(ss)); ss.mFormatFlags = 0; switch (stream_params.format) { case CUBEB_SAMPLE_S16LE: ss.mBitsPerChannel = 16; ss.mFormatFlags |= kAudioFormatFlagIsSignedInteger; break; case CUBEB_SAMPLE_S16BE: ss.mBitsPerChannel = 16; ss.mFormatFlags |= kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsBigEndian; break; case CUBEB_SAMPLE_FLOAT32LE: ss.mBitsPerChannel = 32; ss.mFormatFlags |= kAudioFormatFlagIsFloat; break; case CUBEB_SAMPLE_FLOAT32BE: ss.mBitsPerChannel = 32; ss.mFormatFlags |= kAudioFormatFlagIsFloat | kAudioFormatFlagIsBigEndian; break; default: return CUBEB_ERROR_INVALID_FORMAT; } ss.mFormatID = kAudioFormatLinearPCM; ss.mFormatFlags |= kLinearPCMFormatFlagIsPacked; ss.mSampleRate = stream_params.rate; ss.mChannelsPerFrame = stream_params.channels; ss.mBytesPerFrame = (ss.mBitsPerChannel / 8) * ss.mChannelsPerFrame; ss.mFramesPerPacket = 1; ss.mBytesPerPacket = ss.mBytesPerFrame * ss.mFramesPerPacket; desc.componentType = kAudioUnitType_Output; desc.componentSubType = kAudioUnitSubType_DefaultOutput; desc.componentManufacturer = kAudioUnitManufacturer_Apple; desc.componentFlags = 0; desc.componentFlagsMask = 0; #if MAC_OS_X_VERSION_MIN_REQUIRED < 1060 comp = FindNextComponent(NULL, &desc); #else comp = AudioComponentFindNext(NULL, &desc); #endif assert(comp); stm = calloc(1, sizeof(*stm)); assert(stm); stm->data_callback = data_callback; stm->state_callback = state_callback; stm->user_ptr = user_ptr; stm->sample_spec = ss; r = pthread_mutex_init(&stm->mutex, NULL); assert(r == 0); stm->frames_played = 0; stm->frames_queued = 0; #if MAC_OS_X_VERSION_MIN_REQUIRED < 1060 r = OpenAComponent(comp, &stm->unit); #else r = AudioComponentInstanceNew(comp, &stm->unit); #endif if (r != 0) { cubeb_stream_destroy(stm); return CUBEB_ERROR; } input.inputProc = audio_unit_output_callback; input.inputProcRefCon = stm; r = AudioUnitSetProperty(stm->unit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Global, 0, &input, sizeof(input)); if (r != 0) { cubeb_stream_destroy(stm); return CUBEB_ERROR; } r = AudioUnitSetProperty(stm->unit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &ss, sizeof(ss)); if (r != 0) { cubeb_stream_destroy(stm); return CUBEB_ERROR; } buffer_size = ss.mSampleRate / 1000.0 * latency * ss.mBytesPerFrame / NBUFS; if (buffer_size % ss.mBytesPerFrame != 0) { buffer_size += ss.mBytesPerFrame - (buffer_size % ss.mBytesPerFrame); } assert(buffer_size % ss.mBytesPerFrame == 0); r = AudioUnitInitialize(stm->unit); if (r != 0) { cubeb_stream_destroy(stm); return CUBEB_ERROR; } *stream = stm; return CUBEB_OK; }
int cubeb_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_name, cubeb_stream_params stream_params, unsigned int latency, cubeb_data_callback data_callback, cubeb_state_callback state_callback, void * user_ptr) { cubeb_stream * stm; assert(ctx); *stream = NULL; if (stream_params.rate < 8000 || stream_params.rate > 48000 || stream_params.channels < 1 || stream_params.channels > 32 || latency < 1 || latency > 2000) { return CUBEB_ERROR_INVALID_FORMAT; } SLDataFormat_PCM format; format.formatType = SL_DATAFORMAT_PCM; format.numChannels = stream_params.channels; // samplesPerSec is in milliHertz format.samplesPerSec = stream_params.rate * 1000; format.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_16; format.containerSize = SL_PCMSAMPLEFORMAT_FIXED_16; format.channelMask = stream_params.channels == 1 ? SL_SPEAKER_FRONT_CENTER : SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT; switch (stream_params.format) { case CUBEB_SAMPLE_S16LE: format.endianness = SL_BYTEORDER_LITTLEENDIAN; break; case CUBEB_SAMPLE_S16BE: format.endianness = SL_BYTEORDER_BIGENDIAN; break; default: return CUBEB_ERROR_INVALID_FORMAT; } stm = calloc(1, sizeof(*stm)); assert(stm); stm->context = ctx; stm->data_callback = data_callback; stm->state_callback = state_callback; stm->user_ptr = user_ptr; stm->framesize = stream_params.channels * sizeof(int16_t); stm->bytespersec = stream_params.rate * stm->framesize; stm->queuebuf_len = (stm->bytespersec * latency) / (1000 * NBUFS); stm->queuebuf_len += stm->framesize - (stm->queuebuf_len % stm->framesize); int i; for (i = 0; i < NBUFS; i++) { stm->queuebuf[i] = malloc(stm->queuebuf_len); assert(stm->queuebuf[i]); } SLDataLocator_BufferQueue loc_bufq; loc_bufq.locatorType = SL_DATALOCATOR_BUFFERQUEUE; loc_bufq.numBuffers = NBUFS; SLDataSource source; source.pLocator = &loc_bufq; source.pFormat = &format; SLDataLocator_OutputMix loc_outmix; loc_outmix.locatorType = SL_DATALOCATOR_OUTPUTMIX; loc_outmix.outputMix = ctx->outmixObj; SLDataSink sink; sink.pLocator = &loc_outmix; sink.pFormat = NULL; const SLInterfaceID ids[] = {ctx->SL_IID_BUFFERQUEUE}; const SLboolean req[] = {SL_BOOLEAN_TRUE}; SLresult res = (*ctx->eng)->CreateAudioPlayer(ctx->eng, &stm->playerObj, &source, &sink, 1, ids, req); if (res != SL_RESULT_SUCCESS) { cubeb_stream_destroy(stm); return CUBEB_ERROR; } res = (*stm->playerObj)->Realize(stm->playerObj, SL_BOOLEAN_FALSE); if (res != SL_RESULT_SUCCESS) { cubeb_stream_destroy(stm); return CUBEB_ERROR; } res = (*stm->playerObj)->GetInterface(stm->playerObj, ctx->SL_IID_PLAY, &stm->play); if (res != SL_RESULT_SUCCESS) { cubeb_stream_destroy(stm); return CUBEB_ERROR; } res = (*stm->playerObj)->GetInterface(stm->playerObj, ctx->SL_IID_BUFFERQUEUE, &stm->bufq); if (res != SL_RESULT_SUCCESS) { cubeb_stream_destroy(stm); return CUBEB_ERROR; } res = (*stm->bufq)->RegisterCallback(stm->bufq, bufferqueue_callback, stm); if (res != SL_RESULT_SUCCESS) { cubeb_stream_destroy(stm); return CUBEB_ERROR; } *stream = stm; return CUBEB_OK; }
TEST(cubeb, stream_position) { size_t i; int r; cubeb * ctx; cubeb_stream * stream; cubeb_stream_params params; uint64_t position, last_position; total_frames_written = 0; r = common_init(&ctx, "test_sanity"); ASSERT_EQ(r, CUBEB_OK); ASSERT_NE(ctx, nullptr); params.format = STREAM_FORMAT; params.rate = STREAM_RATE; params.channels = STREAM_CHANNELS; params.layout = STREAM_LAYOUT; 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); /* stream position should not advance before starting playback */ r = cubeb_stream_get_position(stream, &position); ASSERT_EQ(r, CUBEB_OK); ASSERT_EQ(position, 0u); delay(500); r = cubeb_stream_get_position(stream, &position); ASSERT_EQ(r, CUBEB_OK); ASSERT_EQ(position, 0u); /* stream position should advance during playback */ r = cubeb_stream_start(stream); ASSERT_EQ(r, CUBEB_OK); /* XXX let start happen */ delay(500); /* stream should have prefilled */ ASSERT_TRUE(total_frames_written.load() > 0); r = cubeb_stream_get_position(stream, &position); ASSERT_EQ(r, CUBEB_OK); last_position = position; delay(500); r = cubeb_stream_get_position(stream, &position); ASSERT_EQ(r, CUBEB_OK); ASSERT_GE(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_EQ(r, CUBEB_OK); ASSERT_GE(position, last_position); ASSERT_LE(position, total_frames_written.load()); 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_EQ(r, CUBEB_OK); r = cubeb_stream_get_position(stream, &position); ASSERT_EQ(r, CUBEB_OK); ASSERT_TRUE(last_position < position); last_position = position; delay(500); r = cubeb_stream_start(stream); ASSERT_EQ(r, CUBEB_OK); delay(500); } ASSERT_NE(last_position, 0u); /* stream position should not advance after stopping playback */ r = cubeb_stream_stop(stream); ASSERT_EQ(r, CUBEB_OK); /* XXX allow stream to settle */ delay(500); r = cubeb_stream_get_position(stream, &position); ASSERT_EQ(r, CUBEB_OK); last_position = position; delay(500); r = cubeb_stream_get_position(stream, &position); ASSERT_EQ(r, CUBEB_OK); ASSERT_EQ(position, last_position); cubeb_stream_destroy(stream); cubeb_destroy(ctx); }
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; r = cubeb_stream_init(ctx, &stream, "test", 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 { uint32_t i, skip = 0; /* Latency passed to cubeb_stream_init is not really honored on OSX, win32/winmm and android, skip this test. */ const char * backend_id = cubeb_get_backend_id(ctx); const char * latency_not_honored_bakends[] = { "audiounit", "winmm", "audiotrack", "opensl" }; for (i = 0; i < ARRAY_LENGTH(latency_not_honored_bakends); i++) { if (!strcmp(backend_id, latency_not_honored_bakends[i])) { skip = 1; } } if (!skip) { /* Position should roughly be equal to the number of written frames. We * need to take the latency into account. */ int latency = (STREAM_LATENCY * STREAM_RATE) / 1000; assert(position + latency <= total_frames_written); } } delay(500); } r = cubeb_stream_get_position(stream, &position); assert(r == 0); assert(got_drain); // Disabled due to failures in the ALSA backend. //assert(position == total_frames_written); cubeb_stream_destroy(stream); cubeb_destroy(ctx); END_TEST }
static void test_stream_position(void) { int 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; r = cubeb_stream_init(ctx, &stream, "test", 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); END_TEST }
static void Release(cubeb_stream* aStream) { cubeb_stream_destroy(aStream); }
CubebStream::~CubebStream() { SetRunning(false); cubeb_stream_destroy(m_stream); m_ctx.reset(); }