Oboe Examples: write_sine_threaded
These will eventually be moved to a separate repo.
Bug: 33347409
Test: these are examples that also serve as tests
Change-Id: I4c2330bb2490f55305cebcd9d3cbdfa26ee837a1
Signed-off-by: Phil Burk <philburk@google.com>
diff --git a/media/liboboe/examples/Android.mk b/media/liboboe/examples/Android.mk
new file mode 100644
index 0000000..5053e7d
--- /dev/null
+++ b/media/liboboe/examples/Android.mk
@@ -0,0 +1 @@
+include $(call all-subdir-makefiles)
diff --git a/media/liboboe/examples/write_sine/Android.mk b/media/liboboe/examples/write_sine/Android.mk
new file mode 100644
index 0000000..b56328b
--- /dev/null
+++ b/media/liboboe/examples/write_sine/Android.mk
@@ -0,0 +1,6 @@
+# include $(call all-subdir-makefiles)
+
+# Just include static/ for now.
+LOCAL_PATH := $(call my-dir)
+#include $(LOCAL_PATH)/jni/Android.mk
+include $(LOCAL_PATH)/static/Android.mk
diff --git a/media/liboboe/examples/write_sine/README.md b/media/liboboe/examples/write_sine/README.md
new file mode 100644
index 0000000..9f7ee87
--- /dev/null
+++ b/media/liboboe/examples/write_sine/README.md
@@ -0,0 +1,7 @@
+# cd to this directory
+mkdir -p jni/include/oboe
+ln -s $PLATFORM/frameworks/av/media/liboboe/include/oboe/*.h jni/include/oboe
+ln -s $PLATFORM/out/target/product/$TARGET_PRODUCT/symbols/out/soong/ndk/platforms/android-current/arch-arm64/usr/lib/liboboe.so jni
+$NDK/ndk-build
+adb push libs/arm64-v8a/write_sine_threaded /data
+adb shell /data/write_sine_threaded
diff --git a/media/liboboe/examples/write_sine/jni/Android.mk b/media/liboboe/examples/write_sine/jni/Android.mk
new file mode 100644
index 0000000..51a5a85
--- /dev/null
+++ b/media/liboboe/examples/write_sine/jni/Android.mk
@@ -0,0 +1,35 @@
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE_TAGS := tests
+LOCAL_C_INCLUDES := \
+ $(call include-path-for, audio-utils) \
+ frameworks/av/media/liboboe/include
+
+LOCAL_SRC_FILES:= frameworks/av/media/liboboe/src/write_sine.cpp
+LOCAL_SHARED_LIBRARIES := libaudioutils libmedia libtinyalsa \
+ libbinder libcutils libutils
+LOCAL_STATIC_LIBRARIES := libsndfile
+LOCAL_MODULE := write_sine_ndk
+LOCAL_SHARED_LIBRARIES += liboboe_prebuilt
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE_TAGS := tests
+LOCAL_C_INCLUDES := \
+ $(call include-path-for, audio-utils) \
+ frameworks/av/media/liboboe/include
+
+LOCAL_SRC_FILES:= frameworks/av/media/liboboe/src/write_sine_threaded.cpp
+LOCAL_SHARED_LIBRARIES := libaudioutils libmedia libtinyalsa \
+ libbinder libcutils libutils
+LOCAL_STATIC_LIBRARIES := libsndfile
+LOCAL_MODULE := write_sine_threaded_ndk
+LOCAL_SHARED_LIBRARIES += liboboe_prebuilt
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := liboboe_prebuilt
+LOCAL_SRC_FILES := liboboe.so
+LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include
+include $(PREBUILT_SHARED_LIBRARY)
diff --git a/media/liboboe/examples/write_sine/jni/Application.mk b/media/liboboe/examples/write_sine/jni/Application.mk
new file mode 100644
index 0000000..e74475c
--- /dev/null
+++ b/media/liboboe/examples/write_sine/jni/Application.mk
@@ -0,0 +1,3 @@
+# TODO remove then when we support other architectures
+APP_ABI := arm64-v8a
+APP_CPPFLAGS += -std=c++11
diff --git a/media/liboboe/examples/write_sine/src/SineGenerator.h b/media/liboboe/examples/write_sine/src/SineGenerator.h
new file mode 100644
index 0000000..ade7527
--- /dev/null
+++ b/media/liboboe/examples/write_sine/src/SineGenerator.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SINE_GENERATOR_H
+#define SINE_GENERATOR_H
+
+#include <math.h>
+
+class SineGenerator
+{
+public:
+ SineGenerator() {}
+ virtual ~SineGenerator() = default;
+
+ void setup(double frequency, double frameRate) {
+ mFrameRate = frameRate;
+ mPhaseIncrement = frequency * M_PI * 2 / frameRate;
+ }
+
+ void setSweep(double frequencyLow, double frequencyHigh, double seconds) {
+ mPhaseIncrementLow = frequencyLow * M_PI * 2 / mFrameRate;
+ mPhaseIncrementHigh = frequencyHigh * M_PI * 2 / mFrameRate;
+
+ double numFrames = seconds * mFrameRate;
+ mUpScaler = pow((frequencyHigh / frequencyLow), (1.0 / numFrames));
+ mDownScaler = 1.0 / mUpScaler;
+ mGoingUp = true;
+ mSweeping = true;
+ }
+
+ void render(int16_t *buffer, int32_t channelStride, int32_t numFrames) {
+ int sampleIndex = 0;
+ for (int i = 0; i < numFrames; i++) {
+ buffer[sampleIndex] = (int16_t) (32767 * sin(mPhase) * mAmplitude);
+ sampleIndex += channelStride;
+ advancePhase();
+ }
+ }
+ void render(float *buffer, int32_t channelStride, int32_t numFrames) {
+ int sampleIndex = 0;
+ for (int i = 0; i < numFrames; i++) {
+ buffer[sampleIndex] = sin(mPhase) * mAmplitude;
+ sampleIndex += channelStride;
+ advancePhase();
+ }
+ }
+
+private:
+ void advancePhase() {
+ mPhase += mPhaseIncrement;
+ if (mPhase > M_PI * 2) {
+ mPhase -= M_PI * 2;
+ }
+ if (mSweeping) {
+ if (mGoingUp) {
+ mPhaseIncrement *= mUpScaler;
+ if (mPhaseIncrement > mPhaseIncrementHigh) {
+ mGoingUp = false;
+ }
+ } else {
+ mPhaseIncrement *= mDownScaler;
+ if (mPhaseIncrement < mPhaseIncrementLow) {
+ mGoingUp = true;
+ }
+ }
+ }
+ }
+
+ double mAmplitude = 0.01;
+ double mPhase = 0.0;
+ double mPhaseIncrement = 440 * M_PI * 2 / 48000;
+ double mFrameRate = 48000;
+ double mPhaseIncrementLow;
+ double mPhaseIncrementHigh;
+ double mUpScaler = 1.0;
+ double mDownScaler = 1.0;
+ bool mGoingUp = false;
+ bool mSweeping = false;
+};
+
+#endif /* SINE_GENERATOR_H */
+
diff --git a/media/liboboe/examples/write_sine/src/write_sine.cpp b/media/liboboe/examples/write_sine/src/write_sine.cpp
new file mode 100644
index 0000000..084665c
--- /dev/null
+++ b/media/liboboe/examples/write_sine/src/write_sine.cpp
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Play sine waves using Oboe.
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <oboe/OboeDefinitions.h>
+#include <oboe/OboeAudio.h>
+#include "SineGenerator.h"
+
+#define SAMPLE_RATE 48000
+#define NUM_SECONDS 10
+
+static const char *getSharingModeText(oboe_sharing_mode_t mode) {
+ const char *modeText = "unknown";
+ switch (mode) {
+ case OBOE_SHARING_MODE_EXCLUSIVE:
+ modeText = "EXCLUSIVE";
+ break;
+ case OBOE_SHARING_MODE_LEGACY:
+ modeText = "LEGACY";
+ break;
+ case OBOE_SHARING_MODE_SHARED:
+ modeText = "SHARED";
+ break;
+ case OBOE_SHARING_MODE_PUBLIC_MIX:
+ modeText = "PUBLIC_MIX";
+ break;
+ default:
+ break;
+ }
+ return modeText;
+}
+
+int main(int argc, char **argv)
+{
+ (void)argc; // unused
+
+ oboe_result_t result = OBOE_OK;
+
+ const int requestedSamplesPerFrame = 2;
+ int actualSamplesPerFrame = 0;
+ const int requestedSampleRate = SAMPLE_RATE;
+ int actualSampleRate = 0;
+ const oboe_audio_format_t requestedDataFormat = OBOE_AUDIO_FORMAT_PCM16;
+ oboe_audio_format_t actualDataFormat = OBOE_AUDIO_FORMAT_PCM16;
+
+ const oboe_sharing_mode_t requestedSharingMode = OBOE_SHARING_MODE_EXCLUSIVE;
+ //const oboe_sharing_mode_t requestedSharingMode = OBOE_SHARING_MODE_LEGACY;
+ oboe_sharing_mode_t actualSharingMode = OBOE_SHARING_MODE_LEGACY;
+
+ OboeStreamBuilder oboeBuilder = OBOE_STREAM_BUILDER_NONE;
+ OboeStream oboeStream = OBOE_STREAM_NONE;
+ oboe_stream_state_t state = OBOE_STREAM_STATE_UNINITIALIZED;
+ oboe_size_frames_t framesPerBurst = 0;
+ oboe_size_frames_t framesToPlay = 0;
+ oboe_size_frames_t framesLeft = 0;
+ int32_t xRunCount = 0;
+ int16_t *data = nullptr;
+
+ SineGenerator sineOsc1;
+ SineGenerator sineOsc2;
+
+ // Make printf print immediately so that debug info is not stuck
+ // in a buffer if we hang or crash.
+ setvbuf(stdout, nullptr, _IONBF, (size_t) 0);
+
+ printf("%s - Play a sine wave using Oboe\n", argv[0]);
+
+ // Use an OboeStreamBuilder to contain requested parameters.
+ result = Oboe_createStreamBuilder(&oboeBuilder);
+ if (result != OBOE_OK) {
+ goto finish;
+ }
+
+ // Request stream properties.
+ result = OboeStreamBuilder_setSampleRate(oboeBuilder, requestedSampleRate);
+ if (result != OBOE_OK) {
+ goto finish;
+ }
+ result = OboeStreamBuilder_setSamplesPerFrame(oboeBuilder, requestedSamplesPerFrame);
+ if (result != OBOE_OK) {
+ goto finish;
+ }
+ result = OboeStreamBuilder_setFormat(oboeBuilder, requestedDataFormat);
+ if (result != OBOE_OK) {
+ goto finish;
+ }
+ result = OboeStreamBuilder_setSharingMode(oboeBuilder, requestedSharingMode);
+ if (result != OBOE_OK) {
+ goto finish;
+ }
+
+ // Create an OboeStream using the Builder.
+ result = OboeStreamBuilder_openStream(oboeBuilder, &oboeStream);
+ printf("oboeStream 0x%08x\n", oboeStream);
+ if (result != OBOE_OK) {
+ goto finish;
+ }
+
+ result = OboeStream_getState(oboeStream, &state);
+ printf("after open, state = %s\n", Oboe_convertStreamStateToText(state));
+
+ // Check to see what kind of stream we actually got.
+ result = OboeStream_getSampleRate(oboeStream, &actualSampleRate);
+ printf("SampleRate: requested = %d, actual = %d\n", requestedSampleRate, actualSampleRate);
+
+ sineOsc1.setup(440.0, actualSampleRate);
+ sineOsc2.setup(660.0, actualSampleRate);
+
+ result = OboeStream_getSamplesPerFrame(oboeStream, &actualSamplesPerFrame);
+ printf("SamplesPerFrame: requested = %d, actual = %d\n",
+ requestedSamplesPerFrame, actualSamplesPerFrame);
+
+ result = OboeStream_getSharingMode(oboeStream, &actualSharingMode);
+ printf("SharingMode: requested = %s, actual = %s\n",
+ getSharingModeText(requestedSharingMode),
+ getSharingModeText(actualSharingMode));
+
+ // This is the number of frames that are read in one chunk by a DMA controller
+ // or a DSP or a mixer.
+ result = OboeStream_getFramesPerBurst(oboeStream, &framesPerBurst);
+ printf("DataFormat: original framesPerBurst = %d\n",framesPerBurst);
+ if (result != OBOE_OK) {
+ fprintf(stderr, "ERROR - OboeStream_getFramesPerBurst() returned %d\n", result);
+ goto finish;
+ }
+ // Some DMA might use very short bursts of 16 frames. We don't need to write such small
+ // buffers. But it helps to use a multiple of the burst size for predictable scheduling.
+ while (framesPerBurst < 48) {
+ framesPerBurst *= 2;
+ }
+ printf("DataFormat: final framesPerBurst = %d\n",framesPerBurst);
+
+ OboeStream_getFormat(oboeStream, &actualDataFormat);
+ printf("DataFormat: requested = %d, actual = %d\n", requestedDataFormat, actualDataFormat);
+ // TODO handle other data formats
+
+ // Allocate a buffer for the audio data.
+ data = new int16_t[framesPerBurst * actualSamplesPerFrame];
+ if (data == nullptr) {
+ fprintf(stderr, "ERROR - could not allocate data buffer\n");
+ result = OBOE_ERROR_NO_MEMORY;
+ goto finish;
+ }
+
+ // Start the stream.
+ printf("call OboeStream_requestStart()\n");
+ result = OboeStream_requestStart(oboeStream);
+ if (result != OBOE_OK) {
+ fprintf(stderr, "ERROR - OboeStream_requestStart() returned %d\n", result);
+ goto finish;
+ }
+
+ result = OboeStream_getState(oboeStream, &state);
+ printf("after start, state = %s\n", Oboe_convertStreamStateToText(state));
+
+ // Play for a while.
+ framesToPlay = actualSampleRate * NUM_SECONDS;
+ framesLeft = framesToPlay;
+ while (framesLeft > 0) {
+ // Render sine waves to left and right channels.
+ sineOsc1.render(&data[0], actualSamplesPerFrame, framesPerBurst);
+ if (actualSamplesPerFrame > 1) {
+ sineOsc2.render(&data[1], actualSamplesPerFrame, framesPerBurst);
+ }
+
+ // Write audio data to the stream.
+ oboe_nanoseconds_t timeoutNanos = 100 * OBOE_NANOS_PER_MILLISECOND;
+ int minFrames = (framesToPlay < framesPerBurst) ? framesToPlay : framesPerBurst;
+ int actual = OboeStream_write(oboeStream, data, minFrames, timeoutNanos);
+ if (actual < 0) {
+ fprintf(stderr, "ERROR - OboeStream_write() returned %zd\n", actual);
+ goto finish;
+ } else if (actual == 0) {
+ fprintf(stderr, "WARNING - OboeStream_write() returned %zd\n", actual);
+ goto finish;
+ }
+ framesLeft -= actual;
+ }
+
+ result = OboeStream_getXRunCount(oboeStream, &xRunCount);
+ printf("OboeStream_getXRunCount %d\n", xRunCount);
+
+finish:
+ delete[] data;
+ OboeStream_close(oboeStream);
+ OboeStreamBuilder_delete(oboeBuilder);
+ printf("exiting - Oboe result = %d = %s\n", result, Oboe_convertResultToText(result));
+ return (result != OBOE_OK) ? EXIT_FAILURE : EXIT_SUCCESS;
+}
+
diff --git a/media/liboboe/examples/write_sine/src/write_sine_threaded.cpp b/media/liboboe/examples/write_sine/src/write_sine_threaded.cpp
new file mode 100644
index 0000000..aedcc6e
--- /dev/null
+++ b/media/liboboe/examples/write_sine/src/write_sine_threaded.cpp
@@ -0,0 +1,315 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Play sine waves using an Oboe background thread.
+
+#include <assert.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+#include <time.h>
+#include <oboe/OboeDefinitions.h>
+#include <oboe/OboeAudio.h>
+#include "SineGenerator.h"
+
+#define NUM_SECONDS 10
+#define SHARING_MODE OBOE_SHARING_MODE_EXCLUSIVE
+//#define SHARING_MODE OBOE_SHARING_MODE_LEGACY
+
+// Prototype for a callback.
+typedef int audio_callback_proc_t(float *outputBuffer,
+ oboe_size_frames_t numFrames,
+ void *userContext);
+
+static void *SimpleOboePlayerThreadProc(void *arg);
+
+/**
+ * Simple wrapper for Oboe that opens a default stream and then calls
+ * a callback function to fill the output buffers.
+ */
+class SimpleOboePlayer {
+public:
+ SimpleOboePlayer() {}
+ virtual ~SimpleOboePlayer() {
+ close();
+ };
+
+ void setSharingMode(oboe_sharing_mode_t requestedSharingMode) {
+ mRequestedSharingMode = requestedSharingMode;
+ }
+
+ /** Also known as "sample rate"
+ */
+ int32_t getFramesPerSecond() {
+ return mFramesPerSecond;
+ }
+
+ int32_t getSamplesPerFrame() {
+ return mSamplesPerFrame;
+ }
+
+ /**
+ * Open a stream
+ */
+ oboe_result_t open(audio_callback_proc_t *proc, void *userContext) {
+ mCallbackProc = proc;
+ mUserContext = userContext;
+ oboe_result_t result = OBOE_OK;
+
+ // Use an OboeStreamBuilder to contain requested parameters.
+ result = Oboe_createStreamBuilder(&mBuilder);
+ if (result != OBOE_OK) return result;
+
+ result = OboeStreamBuilder_setSharingMode(mBuilder, mRequestedSharingMode);
+ if (result != OBOE_OK) goto finish1;
+
+ // Open an OboeStream using the Builder.
+ result = OboeStreamBuilder_openStream(mBuilder, &mStream);
+ if (result != OBOE_OK) goto finish1;
+
+ // Check to see what kind of stream we actually got.
+ result = OboeStream_getSampleRate(mStream, &mFramesPerSecond);
+ printf("open() mFramesPerSecond = %d\n", mFramesPerSecond);
+ if (result != OBOE_OK) goto finish2;
+ result = OboeStream_getSamplesPerFrame(mStream, &mSamplesPerFrame);
+ printf("open() mSamplesPerFrame = %d\n", mSamplesPerFrame);
+ if (result != OBOE_OK) goto finish2;
+
+ // This is the number of frames that are read in one chunk by a DMA controller
+ // or a DSP or a mixer.
+ result = OboeStream_getFramesPerBurst(mStream, &mFramesPerBurst);
+ if (result != OBOE_OK) goto finish2;
+ // Some DMA might use very short bursts. We don't need to write such small
+ // buffers. But it helps to use a multiple of the burst size for predictable scheduling.
+ while (mFramesPerBurst < 48) {
+ mFramesPerBurst *= 2;
+ }
+ printf("DataFormat: final framesPerBurst = %d\n",mFramesPerBurst);
+
+ result = OboeStream_getFormat(mStream, &mDataFormat);
+ if (result != OBOE_OK) {
+ fprintf(stderr, "ERROR - OboeStream_getFormat() returned %d\n", result);
+ goto finish2;
+ }
+
+ // Allocate a buffer for the audio data.
+ mOutputBuffer = new float[mFramesPerBurst * mSamplesPerFrame];
+ if (mOutputBuffer == nullptr) {
+ fprintf(stderr, "ERROR - could not allocate data buffer\n");
+ result = OBOE_ERROR_NO_MEMORY;
+ }
+
+ // If needed allocate a buffer for converting float to int16_t.
+ if (mDataFormat == OBOE_AUDIO_FORMAT_PCM16) {
+ mConversionBuffer = new int16_t[mFramesPerBurst * mSamplesPerFrame];
+ if (mConversionBuffer == nullptr) {
+ fprintf(stderr, "ERROR - could not allocate conversion buffer\n");
+ result = OBOE_ERROR_NO_MEMORY;
+ }
+ }
+ return result;
+
+ finish2:
+ OboeStream_close(mStream);
+ mStream = OBOE_HANDLE_INVALID;
+ finish1:
+ OboeStreamBuilder_delete(mBuilder);
+ mBuilder = OBOE_HANDLE_INVALID;
+ return result;
+ }
+
+ oboe_result_t close() {
+ stop();
+ OboeStream_close(mStream);
+ mStream = OBOE_HANDLE_INVALID;
+ OboeStreamBuilder_delete(mBuilder);
+ mBuilder = OBOE_HANDLE_INVALID;
+ delete mOutputBuffer;
+ mOutputBuffer = nullptr;
+ delete mConversionBuffer;
+ mConversionBuffer = nullptr;
+ return OBOE_OK;
+ }
+
+ // Start a thread that will call the callback proc.
+ oboe_result_t start() {
+ mEnabled = true;
+ oboe_nanoseconds_t nanosPerBurst = mFramesPerBurst * OBOE_NANOS_PER_SECOND
+ / mFramesPerSecond;
+ return OboeStream_createThread(mStream, nanosPerBurst,
+ SimpleOboePlayerThreadProc,
+ this);
+ }
+
+ // Tell the thread to stop.
+ oboe_result_t stop() {
+ mEnabled = false;
+ return OboeStream_joinThread(mStream, nullptr, 2 * OBOE_NANOS_PER_SECOND);
+ }
+
+ oboe_result_t callbackLoop() {
+ int32_t framesWritten = 0;
+ int32_t xRunCount = 0;
+ oboe_result_t result = OBOE_OK;
+
+ result = OboeStream_requestStart(mStream);
+ if (result != OBOE_OK) {
+ fprintf(stderr, "ERROR - OboeStream_requestStart() returned %d\n", result);
+ return result;
+ }
+
+ // Give up after several burst periods have passed.
+ const int burstsPerTimeout = 8;
+ oboe_nanoseconds_t nanosPerTimeout =
+ burstsPerTimeout * mFramesPerBurst * OBOE_NANOS_PER_SECOND
+ / mFramesPerSecond;
+
+ while (mEnabled && result >= 0) {
+ // Call application's callback function to fill the buffer.
+ if (mCallbackProc(mOutputBuffer, mFramesPerBurst, mUserContext)) {
+ mEnabled = false;
+ }
+ // if needed, convert from float to int16_t PCM
+ if (mConversionBuffer != nullptr) {
+ int32_t numSamples = mFramesPerBurst * mSamplesPerFrame;
+ for (int i = 0; i < numSamples; i++) {
+ mConversionBuffer[i] = (int16_t)(32767.0 * mOutputBuffer[i]);
+ }
+ // Write the application data to stream.
+ result = OboeStream_write(mStream, mConversionBuffer, mFramesPerBurst, nanosPerTimeout);
+ } else {
+ // Write the application data to stream.
+ result = OboeStream_write(mStream, mOutputBuffer, mFramesPerBurst, nanosPerTimeout);
+ }
+ framesWritten += result;
+ if (result < 0) {
+ fprintf(stderr, "ERROR - OboeStream_write() returned %zd\n", result);
+ }
+ }
+
+ result = OboeStream_getXRunCount(mStream, &xRunCount);
+ printf("OboeStream_getXRunCount %d\n", xRunCount);
+
+ result = OboeStream_requestStop(mStream);
+ if (result != OBOE_OK) {
+ fprintf(stderr, "ERROR - OboeStream_requestStart() returned %d\n", result);
+ return result;
+ }
+
+ return result;
+ }
+
+private:
+ OboeStreamBuilder mBuilder = OBOE_HANDLE_INVALID;
+ OboeStream mStream = OBOE_HANDLE_INVALID;
+ float * mOutputBuffer = nullptr;
+ int16_t * mConversionBuffer = nullptr;
+
+ audio_callback_proc_t * mCallbackProc = nullptr;
+ void * mUserContext = nullptr;
+ oboe_sharing_mode_t mRequestedSharingMode = SHARING_MODE;
+ int32_t mSamplesPerFrame = 0;
+ int32_t mFramesPerSecond = 0;
+ oboe_size_frames_t mFramesPerBurst = 0;
+ oboe_audio_format_t mDataFormat = OBOE_AUDIO_FORMAT_PCM16;
+
+ volatile bool mEnabled = false; // used to request that callback exit its loop
+};
+
+static void *SimpleOboePlayerThreadProc(void *arg) {
+ SimpleOboePlayer *player = (SimpleOboePlayer *) arg;
+ player->callbackLoop();
+ return nullptr;
+}
+
+// Application data that gets passed to the callback.
+typedef struct SineThreadedData_s {
+ SineGenerator sineOsc1;
+ SineGenerator sineOsc2;
+ int32_t samplesPerFrame = 0;
+} SineThreadedData_t;
+
+// Callback function that fills the audio output buffer.
+int MyCallbackProc(float *outputBuffer, int32_t numFrames, void *userContext) {
+ SineThreadedData_t *data = (SineThreadedData_t *) userContext;
+ // Render sine waves to left and right channels.
+ data->sineOsc1.render(&outputBuffer[0], data->samplesPerFrame, numFrames);
+ if (data->samplesPerFrame > 1) {
+ data->sineOsc2.render(&outputBuffer[1], data->samplesPerFrame, numFrames);
+ }
+ return 0;
+}
+
+int main(int argc, char **argv)
+{
+ (void)argc; // unused
+ SimpleOboePlayer player;
+ SineThreadedData_t myData;
+ oboe_result_t result;
+
+ // Make printf print immediately so that debug info is not stuck
+ // in a buffer if we hang or crash.
+ setvbuf(stdout, nullptr, _IONBF, (size_t) 0);
+ printf("%s - Play a sine wave using an Oboe Thread\n", argv[0]);
+
+ result = player.open(MyCallbackProc, &myData);
+ if (result != OBOE_OK) {
+ fprintf(stderr, "ERROR - player.open() returned %d\n", result);
+ goto error;
+ }
+ printf("player.getFramesPerSecond() = %d\n", player.getFramesPerSecond());
+ printf("player.getSamplesPerFrame() = %d\n", player.getSamplesPerFrame());
+ myData.sineOsc1.setup(440.0, 48000);
+ myData.sineOsc1.setSweep(300.0, 2000.0, 5.0);
+ myData.sineOsc2.setup(660.0, 48000);
+ myData.sineOsc2.setSweep(400.0, 3000.0, 7.0);
+ myData.samplesPerFrame = player.getSamplesPerFrame();
+
+ result = player.start();
+ if (result != OBOE_OK) {
+ fprintf(stderr, "ERROR - player.start() returned %d\n", result);
+ goto error;
+ }
+
+ printf("Sleep for %d seconds while audio plays in a background thread.\n", NUM_SECONDS);
+ {
+ // FIXME sleep is not an NDK API
+ // sleep(NUM_SECONDS);
+ const struct timespec request = { .tv_sec = NUM_SECONDS, .tv_nsec = 0 };
+ (void) clock_nanosleep(CLOCK_MONOTONIC, 0 /*flags*/, &request, NULL /*remain*/);
+ }
+ printf("Woke up now.\n");
+
+ result = player.stop();
+ if (result != OBOE_OK) {
+ fprintf(stderr, "ERROR - player.stop() returned %d\n", result);
+ goto error;
+ }
+ result = player.close();
+ if (result != OBOE_OK) {
+ fprintf(stderr, "ERROR - player.close() returned %d\n", result);
+ goto error;
+ }
+
+ printf("SUCCESS\n");
+ return EXIT_SUCCESS;
+error:
+ player.close();
+ printf("exiting - Oboe result = %d = %s\n", result, Oboe_convertResultToText(result));
+ return EXIT_FAILURE;
+}
+
diff --git a/media/liboboe/examples/write_sine/static/Android.mk b/media/liboboe/examples/write_sine/static/Android.mk
new file mode 100644
index 0000000..7c8d17c
--- /dev/null
+++ b/media/liboboe/examples/write_sine/static/Android.mk
@@ -0,0 +1,34 @@
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE_TAGS := examples
+LOCAL_C_INCLUDES := \
+ $(call include-path-for, audio-utils) \
+ frameworks/av/media/liboboe/include
+
+# TODO reorganize folders to avoid using ../
+LOCAL_SRC_FILES:= ../src/write_sine.cpp
+
+LOCAL_SHARED_LIBRARIES := libaudioutils libmedia \
+ libbinder libcutils libutils \
+ libaudioclient liblog libtinyalsa
+LOCAL_STATIC_LIBRARIES := liboboe
+
+LOCAL_MODULE := write_sine
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE_TAGS := tests
+LOCAL_C_INCLUDES := \
+ $(call include-path-for, audio-utils) \
+ frameworks/av/media/liboboe/include
+
+LOCAL_SRC_FILES:= ../src/write_sine_threaded.cpp
+
+LOCAL_SHARED_LIBRARIES := libaudioutils libmedia \
+ libbinder libcutils libutils \
+ libaudioclient liblog libtinyalsa
+LOCAL_STATIC_LIBRARIES := liboboe
+
+LOCAL_MODULE := write_sine_threaded
+include $(BUILD_EXECUTABLE)
diff --git a/media/liboboe/examples/write_sine/static/README.md b/media/liboboe/examples/write_sine/static/README.md
new file mode 100644
index 0000000..768f4cb
--- /dev/null
+++ b/media/liboboe/examples/write_sine/static/README.md
@@ -0,0 +1,2 @@
+Makefile for building simple command line examples.
+They link with Oboe as a static library.