Adding MegaDrone sample (from AES talk)
diff --git a/samples/MegaDrone/.gitignore b/samples/MegaDrone/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/samples/MegaDrone/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/samples/MegaDrone/CMakeLists.txt b/samples/MegaDrone/CMakeLists.txt
new file mode 100644
index 0000000..ed9a7df
--- /dev/null
+++ b/samples/MegaDrone/CMakeLists.txt
@@ -0,0 +1,28 @@
+cmake_minimum_required(VERSION 3.4.1)
+
+
+### INCLUDE OBOE LIBRARY ###
+
+# Set the path to the Oboe library directory
+set (OBOE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../..)
+
+# Add the Oboe library as a subproject. Since Oboe is an out-of-tree source library we must also
+# specify a binary directory
+add_subdirectory(${OBOE_DIR} ./oboe-bin)
+
+# Include the Oboe headers
+include_directories(${OBOE_DIR}/include)
+
+### END OBOE INCLUDE SECTION ###
+
+
+add_library( megadrone SHARED
+ src/main/cpp/native-lib.cpp
+ src/main/cpp/Synth.cpp)
+
+target_link_libraries( megadrone log oboe )
+
+# Make the Oboe public headers available to our app
+include_directories (${OBOE_DIR}/include)
+
+set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Ofast")
diff --git a/samples/MegaDrone/README.md b/samples/MegaDrone/README.md
new file mode 100644
index 0000000..4aed5de
--- /dev/null
+++ b/samples/MegaDrone/README.md
@@ -0,0 +1,43 @@
+Mega Drone
+==========
+Ever wondered what 100 square waves sound like when played together? Well now you can find out!
+
+Mega Drone is an app which creates 100 oscillators, combines their output in a mixer and plays the resulting sound.
+
+This sample demonstrates how to obtain the lowest latency by:
+
+1) Leaving Oboe to choose the best default stream properties for the current device
+2) Setting performance mode to LowLatency
+3) Setting sharing mode to Exclusive
+4) Setting the buffer size to 2 bursts
+
+This code was presented at [AES Milan](http://www.aes.org/events/144/) as part of a live coding demonstration.
+
+The following article explaining how to debug CPU performance problems may also be useful when looking at this code
+
+Implementation details
+---
+The stream properties are left to Oboe as such the app must output audio data in a format which matches that of the stream.
+
+Four different formats are supported:
+
+|Channel count|Format|
+|-------------|------|
+|1 - Mono|16-bit int|
+|2 - Stereo|16-bit int|
+|1 - Mono|Float|
+|1 - Stereo|Float|
+
+The signal chain for mono streams is:
+
+ Oscillators->Mixer
+
+For stereo chains a mono to stereo converter is added to the end of the chain:
+
+ Oscillators->Mixer->MonoToStereo
+
+The compiler optimization flag `-Ofast` can be found in [CMakeLists.txt](CMakeLists.txt).
+
+Screenshots
+-----------
+![mega-drone-screenshot](mega-drone-screenshot.png)
\ No newline at end of file
diff --git a/samples/MegaDrone/build.gradle b/samples/MegaDrone/build.gradle
new file mode 100644
index 0000000..cbe4ffc
--- /dev/null
+++ b/samples/MegaDrone/build.gradle
@@ -0,0 +1,49 @@
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion 27
+ defaultConfig {
+ applicationId "com.example.oboe.megadrone"
+ minSdkVersion 16
+ targetSdkVersion 27
+ versionCode 1
+ versionName "1.0"
+ testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+ externalNativeBuild {
+ cmake {
+ cppFlags "-std=c++14"
+ }
+ }
+ }
+ signingConfigs {
+ release {
+ storeFile new File("${System.properties['user.home']}/.android/debug.keystore")
+ storePassword 'android'
+ storeType "jks"
+ keyAlias 'androiddebugkey'
+ keyPassword 'android'
+ }
+ }
+ buildTypes {
+ release {
+ signingConfig signingConfigs.release
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ debuggable false
+ }
+ }
+ externalNativeBuild {
+ cmake {
+ path "CMakeLists.txt"
+ }
+ }
+}
+
+dependencies {
+ implementation fileTree(dir: 'libs', include: ['*.jar'])
+ implementation 'com.android.support:appcompat-v7:27.1.1'
+ implementation 'com.android.support.constraint:constraint-layout:1.1.0'
+ testImplementation 'junit:junit:4.12'
+ androidTestImplementation 'com.android.support.test:runner:1.0.2'
+ androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
+}
diff --git a/samples/MegaDrone/megadrone-screenshot.png b/samples/MegaDrone/megadrone-screenshot.png
new file mode 100644
index 0000000..acdb98e
--- /dev/null
+++ b/samples/MegaDrone/megadrone-screenshot.png
Binary files differ
diff --git a/samples/MegaDrone/proguard-rules.pro b/samples/MegaDrone/proguard-rules.pro
new file mode 100644
index 0000000..f1b4245
--- /dev/null
+++ b/samples/MegaDrone/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
diff --git a/samples/MegaDrone/src/main/AndroidManifest.xml b/samples/MegaDrone/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..ba26543
--- /dev/null
+++ b/samples/MegaDrone/src/main/AndroidManifest.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.example.oboe.megadrone">
+
+ <application
+ android:allowBackup="true"
+ android:icon="@mipmap/ic_launcher"
+ android:label="@string/app_name"
+ android:roundIcon="@mipmap/ic_launcher_round"
+ android:supportsRtl="true"
+ android:theme="@style/AppTheme">
+ <activity android:name="com.example.oboe.megadrone.MainActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+
+</manifest>
\ No newline at end of file
diff --git a/samples/MegaDrone/src/main/cpp/Constants.h b/samples/MegaDrone/src/main/cpp/Constants.h
new file mode 100644
index 0000000..6011f35
--- /dev/null
+++ b/samples/MegaDrone/src/main/cpp/Constants.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2018 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 MEGADRONE_CONSTANTS_H
+#define MEGADRONE_CONSTANTS_H
+
+constexpr int kMonoChannelCount = 1;
+constexpr int kStereoChannelCount = 2;
+
+#endif //MEGADRONE_CONSTANTS_H
diff --git a/samples/MegaDrone/src/main/cpp/Mixer.h b/samples/MegaDrone/src/main/cpp/Mixer.h
new file mode 100644
index 0000000..2d45dbc
--- /dev/null
+++ b/samples/MegaDrone/src/main/cpp/Mixer.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2018 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 RHYTHMGAME_MIXER_H
+#define RHYTHMGAME_MIXER_H
+
+#include "RenderableAudio.h"
+
+constexpr int32_t kBufferSize = 192*10; // Temporary buffer is used for mixing
+constexpr uint8_t kMaxTracks = 100;
+
+template <typename T>
+class Mixer : public RenderableAudio<T> {
+
+public:
+
+ void renderAudio(T *audioData, int32_t numFrames) {
+
+ // Zero out the incoming container array
+ for (int j = 0; j < numFrames; ++j) {
+ audioData[j] = 0;
+ }
+
+ for (int i = 0; i < mNextFreeTrackIndex; ++i) {
+ mTracks[i]->renderAudio(mixingBuffer, numFrames);
+
+ for (int j = 0; j < numFrames; ++j) {
+ audioData[j] += mixingBuffer[j];
+ }
+ }
+ }
+
+ void addTrack(RenderableAudio<T> *renderer){
+ mTracks[mNextFreeTrackIndex++] = renderer;
+ }
+
+private:
+ T mixingBuffer[kBufferSize]; // TODO: smart pointer
+ RenderableAudio<T>* mTracks[kMaxTracks]; // TODO: this might be better as a linked list for easy track removal
+ uint8_t mNextFreeTrackIndex = 0;
+};
+
+
+#endif //RHYTHMGAME_MIXER_H
diff --git a/samples/MegaDrone/src/main/cpp/MonoToStereo.h b/samples/MegaDrone/src/main/cpp/MonoToStereo.h
new file mode 100644
index 0000000..85cce4d
--- /dev/null
+++ b/samples/MegaDrone/src/main/cpp/MonoToStereo.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2018 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 MEGADRONE_MONOTOSTEREO_H
+#define MEGADRONE_MONOTOSTEREO_H
+
+#include "RenderableAudio.h"
+#include "Constants.h"
+
+template <typename T>
+class MonoToStereo : public RenderableAudio<T> {
+
+public:
+
+ MonoToStereo(RenderableAudio<T> *input) : mInput(input){};
+
+ void renderAudio(T *audioData, int32_t numFrames) override {
+
+ // We assume that audioData has sufficient frames to hold the stereo output, so copy each
+ // frame in the input to the output twice
+ // e.g. 123 => 112233
+ for (int i = 0; i < numFrames; ++i) {
+ mInput->renderAudio(audioData+(i*kStereoChannelCount), 1);
+ audioData[(i*kStereoChannelCount)+1] = audioData[i*kStereoChannelCount];
+ }
+ }
+
+ RenderableAudio<T> *mInput;
+};
+
+
+#endif //MEGADRONE_MONOTOSTEREO_H
diff --git a/samples/MegaDrone/src/main/cpp/Oscillator.h b/samples/MegaDrone/src/main/cpp/Oscillator.h
new file mode 100644
index 0000000..a4e9cbb
--- /dev/null
+++ b/samples/MegaDrone/src/main/cpp/Oscillator.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2018 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 MEGADRONE_OSCILLATOR_H
+#define MEGADRONE_OSCILLATOR_H
+
+
+#include <cstdint>
+#include <atomic>
+#include <math.h>
+#include "RenderableAudio.h"
+
+constexpr double kDefaultFrequency = 440.0;
+constexpr int32_t kDefaultSampleRate = 48000;
+constexpr double kPi = M_PI;
+constexpr double kTwoPi = kPi * 2;
+
+template <typename T>
+class Oscillator : public RenderableAudio<T> {
+
+public:
+ ~Oscillator(){};
+
+ void setWaveOn(bool isWaveOn){
+ mIsWaveOn.store(isWaveOn);
+ };
+
+ void setSampleRate(int32_t sampleRate){
+ mSampleRate = sampleRate;
+ updatePhaseIncrement();
+ };
+
+ void setFrequency(double frequency) {
+ mFrequency = frequency;
+ updatePhaseIncrement();
+ };
+
+ void setAmplitude(T amplitude){
+ mAmplitude = amplitude;
+ };
+
+ // From RenderableAudio<T>
+ void renderAudio(T *audioData, int32_t numFrames) {
+
+ if (mIsWaveOn){
+ for (int i = 0; i < numFrames; ++i) {
+
+ // Sine wave (sinf)
+ //audioData[i*kChannelCount] = sinf(mPhase) * mAmplitude;
+
+ // Square wave
+ if (mPhase <= kPi){
+ audioData[i] = -mAmplitude;
+ } else {
+ audioData[i] = mAmplitude;
+ }
+
+ mPhase += mPhaseIncrement;
+ if (mPhase > kTwoPi) mPhase -= kTwoPi;
+ }
+ } else {
+ for (int i = 0; i < numFrames; ++i) {
+ audioData[i] = 0;
+ }
+ }
+ };
+
+private:
+ std::atomic<bool> mIsWaveOn{false};
+ float mPhase = 0.0;
+ std::atomic<float> mAmplitude { 0.0 };
+ std::atomic<double> mPhaseIncrement { 0.0 };
+ double mFrequency = kDefaultFrequency;
+ int32_t mSampleRate = kDefaultSampleRate;
+
+ void updatePhaseIncrement(){
+ mPhaseIncrement.store((kTwoPi * mFrequency) / (double) mSampleRate);
+ };
+};
+
+
+#endif //MEGADRONE_OSCILLATOR_H
diff --git a/samples/MegaDrone/src/main/cpp/RenderableAudio.h b/samples/MegaDrone/src/main/cpp/RenderableAudio.h
new file mode 100644
index 0000000..756ed72
--- /dev/null
+++ b/samples/MegaDrone/src/main/cpp/RenderableAudio.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2018 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 MEGADRONE_RENDERABLEAUDIO_H
+#define MEGADRONE_RENDERABLEAUDIO_H
+
+
+#include <cstdint>
+
+template <typename T>
+class RenderableAudio {
+
+public:
+ virtual ~RenderableAudio() = default;
+ virtual void renderAudio(T *audioData, int32_t numFrames) = 0;
+};
+
+
+#endif //MEGADRONE_RENDERABLEAUDIO_H
diff --git a/samples/MegaDrone/src/main/cpp/Synth.cpp b/samples/MegaDrone/src/main/cpp/Synth.cpp
new file mode 100644
index 0000000..d3fde2c
--- /dev/null
+++ b/samples/MegaDrone/src/main/cpp/Synth.cpp
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2018 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.
+ */
+
+#include "Synth.h"
+#include "Constants.h"
+#include "MonoToStereo.h"
+
+void Synth::start() {
+
+ AudioStreamBuilder builder;
+ builder.setCallback(this);
+ builder.setPerformanceMode(PerformanceMode::LowLatency);
+ builder.setSharingMode(SharingMode::Exclusive);
+
+ builder.openStream(&mStream);
+
+ const float baseOscFrequency = 116.0;
+ const float divisor = 33;
+ const float amplitude = 0.009;
+
+ if (mStream->getFormat() == AudioFormat::Float){
+
+ mMixerFloat = std::make_unique<Mixer<float>>();
+
+ // Spin up the oscillators (float versions)
+ for (int i = 0; i < kNumOscillators; ++i) {
+ mOscsFloat[i].setSampleRate(mStream->getSampleRate());
+ mOscsFloat[i].setFrequency(baseOscFrequency+((float)i/divisor));
+ mOscsFloat[i].setAmplitude(amplitude);
+ mMixerFloat->addTrack(&mOscsFloat[i]);
+ }
+
+ if (mStream->getChannelCount() == kStereoChannelCount){
+ mOutputStageFloat = std::make_shared<MonoToStereo<float>>(mMixerFloat.get());
+ } else if (mStream->getChannelCount() == kMonoChannelCount){
+ mOutputStageFloat = mMixerFloat;
+ }
+
+ } else {
+
+ mMixerInt16 = std::make_unique<Mixer<int16_t>>();
+
+ // Spin up the oscillators (16-bit int versions)
+ for (int i = 0; i < kNumOscillators; ++i) {
+ mOscsInt16[i].setSampleRate(mStream->getSampleRate());
+ mOscsInt16[i].setFrequency(baseOscFrequency+((float)i/divisor));
+ mOscsInt16[i].setAmplitude((int16_t)(amplitude * INT16_MAX));
+ mMixerInt16->addTrack(&mOscsInt16[i]);
+ }
+
+ if (mStream->getChannelCount() == kStereoChannelCount){
+ mOutputStageInt16 = std::make_shared<MonoToStereo<int16_t>>(mMixerInt16.get());
+ } else if (mStream->getChannelCount() == kMonoChannelCount){
+ mOutputStageInt16 = mMixerInt16;
+ }
+ }
+
+ mStream->setBufferSizeInFrames(mStream->getFramesPerBurst() * 2);
+ mStream->requestStart();
+}
+
+void Synth::tap(bool isOn) {
+
+ if (mStream){
+ if (mStream->getFormat() == AudioFormat::Float){
+ for (auto &osc : mOscsFloat) osc.setWaveOn(isOn);
+ } else {
+ for (auto &osc : mOscsInt16) osc.setWaveOn(isOn);
+ }
+ }
+}
+
+DataCallbackResult
+Synth::onAudioReady(AudioStream *oboeStream, void *audioData, int32_t numFrames) {
+
+ if (mStream->getFormat() == AudioFormat::Float){
+ mOutputStageFloat->renderAudio(static_cast<float *>(audioData), numFrames);
+ } else {
+ mOutputStageInt16->renderAudio(static_cast<int16_t *>(audioData), numFrames);
+ }
+
+ return DataCallbackResult::Continue;
+}
diff --git a/samples/MegaDrone/src/main/cpp/Synth.h b/samples/MegaDrone/src/main/cpp/Synth.h
new file mode 100644
index 0000000..d83d50a
--- /dev/null
+++ b/samples/MegaDrone/src/main/cpp/Synth.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2018 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 MEGADRONE_AUDIOENGINE_H
+#define MEGADRONE_AUDIOENGINE_H
+
+#include <array>
+
+#include <oboe/Oboe.h>
+#include "Oscillator.h"
+#include "Mixer.h"
+
+constexpr int kNumOscillators = 100;
+
+using namespace oboe;
+
+class Synth : public AudioStreamCallback {
+public:
+ void start();
+ void tap(bool isOn);
+
+ DataCallbackResult
+ onAudioReady(AudioStream *oboeStream, void *audioData, int32_t numFrames) override;
+
+private:
+ AudioStream *mStream;
+
+ // Rendering objects for 16-bit integer output
+ std::array<Oscillator<int16_t>, kNumOscillators> mOscsInt16;
+ std::shared_ptr<Mixer<int16_t>> mMixerInt16;
+ std::shared_ptr<RenderableAudio<int16_t>> mOutputStageInt16;
+
+ // Rendering objects for floating point output
+ std::array<Oscillator<float>, kNumOscillators> mOscsFloat;
+ std::shared_ptr<Mixer<float>> mMixerFloat;
+ std::shared_ptr<RenderableAudio<float>> mOutputStageFloat;
+
+
+
+};
+
+
+#endif //MEGADRONE_AUDIOENGINE_H
diff --git a/samples/MegaDrone/src/main/cpp/native-lib.cpp b/samples/MegaDrone/src/main/cpp/native-lib.cpp
new file mode 100644
index 0000000..c7e55b4
--- /dev/null
+++ b/samples/MegaDrone/src/main/cpp/native-lib.cpp
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2018 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.
+ */
+
+#include <jni.h>
+#include <string>
+#include "Synth.h"
+
+Synth engine;
+
+extern "C"
+JNIEXPORT void JNICALL
+Java_com_example_oboe_megadrone_MainActivity_startEngine(JNIEnv *env, jobject instance) {
+
+ engine.start();
+}
+
+extern "C"
+JNIEXPORT void JNICALL
+Java_com_example_oboe_megadrone_MainActivity_tap(JNIEnv *env, jobject instance, jboolean b) {
+
+ engine.tap(b);
+}
\ No newline at end of file
diff --git a/samples/MegaDrone/src/main/java/com/example/oboe/megadrone/MainActivity.java b/samples/MegaDrone/src/main/java/com/example/oboe/megadrone/MainActivity.java
new file mode 100644
index 0000000..efe67e2
--- /dev/null
+++ b/samples/MegaDrone/src/main/java/com/example/oboe/megadrone/MainActivity.java
@@ -0,0 +1,58 @@
+package com.example.oboe.megadrone;
+
+/*
+ * Copyright 2018 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.
+ */
+
+import android.support.v7.app.AppCompatActivity;
+import android.os.Bundle;
+import android.view.MotionEvent;
+
+public class MainActivity extends AppCompatActivity {
+
+ // Used to load the 'native-lib' library on application startup.
+ static {
+ System.loadLibrary("megadrone");
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+
+ startEngine();
+ }
+
+ private native void startEngine();
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+
+ if (event.getAction() == MotionEvent.ACTION_DOWN){
+ tap(true);
+ } else if (event.getAction() == MotionEvent.ACTION_UP){
+ tap(false);
+ }
+
+
+ return super.onTouchEvent(event);
+ }
+
+ private native void tap(boolean b);
+
+
+}
+
+
diff --git a/samples/MegaDrone/src/main/res/drawable-v24/ic_launcher_foreground.xml b/samples/MegaDrone/src/main/res/drawable-v24/ic_launcher_foreground.xml
new file mode 100644
index 0000000..1f6bb29
--- /dev/null
+++ b/samples/MegaDrone/src/main/res/drawable-v24/ic_launcher_foreground.xml
@@ -0,0 +1,34 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt"
+ android:width="108dp"
+ android:height="108dp"
+ android:viewportWidth="108"
+ android:viewportHeight="108">
+ <path
+ android:fillType="evenOdd"
+ android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z"
+ android:strokeWidth="1"
+ android:strokeColor="#00000000">
+ <aapt:attr name="android:fillColor">
+ <gradient
+ android:endX="78.5885"
+ android:endY="90.9159"
+ android:startX="48.7653"
+ android:startY="61.0927"
+ android:type="linear">
+ <item
+ android:color="#44000000"
+ android:offset="0.0" />
+ <item
+ android:color="#00000000"
+ android:offset="1.0" />
+ </gradient>
+ </aapt:attr>
+ </path>
+ <path
+ android:fillColor="#FFFFFF"
+ android:fillType="nonZero"
+ android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z"
+ android:strokeWidth="1"
+ android:strokeColor="#00000000" />
+</vector>
diff --git a/samples/MegaDrone/src/main/res/drawable/ic_launcher_background.xml b/samples/MegaDrone/src/main/res/drawable/ic_launcher_background.xml
new file mode 100644
index 0000000..0d025f9
--- /dev/null
+++ b/samples/MegaDrone/src/main/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,170 @@
+<?xml version="1.0" encoding="utf-8"?>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="108dp"
+ android:height="108dp"
+ android:viewportWidth="108"
+ android:viewportHeight="108">
+ <path
+ android:fillColor="#008577"
+ android:pathData="M0,0h108v108h-108z" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M9,0L9,108"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M19,0L19,108"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M29,0L29,108"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M39,0L39,108"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M49,0L49,108"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M59,0L59,108"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M69,0L69,108"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M79,0L79,108"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M89,0L89,108"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M99,0L99,108"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M0,9L108,9"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M0,19L108,19"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M0,29L108,29"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M0,39L108,39"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M0,49L108,49"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M0,59L108,59"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M0,69L108,69"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M0,79L108,79"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M0,89L108,89"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M0,99L108,99"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M19,29L89,29"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M19,39L89,39"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M19,49L89,49"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M19,59L89,59"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M19,69L89,69"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M19,79L89,79"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M29,19L29,89"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M39,19L39,89"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M49,19L49,89"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M59,19L59,89"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M69,19L69,89"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+ <path
+ android:fillColor="#00000000"
+ android:pathData="M79,19L79,89"
+ android:strokeWidth="0.8"
+ android:strokeColor="#33FFFFFF" />
+</vector>
diff --git a/samples/MegaDrone/src/main/res/layout/activity_main.xml b/samples/MegaDrone/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000..2d6f544
--- /dev/null
+++ b/samples/MegaDrone/src/main/res/layout/activity_main.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ tools:context=".MainActivity">
+
+ <TextView
+ android:id="@+id/sample_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Tap anywhere to play"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintLeft_toLeftOf="parent"
+ app:layout_constraintRight_toRightOf="parent"
+ app:layout_constraintTop_toTopOf="parent" />
+
+</android.support.constraint.ConstraintLayout>
\ No newline at end of file
diff --git a/samples/MegaDrone/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/samples/MegaDrone/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 0000000..eca70cf
--- /dev/null
+++ b/samples/MegaDrone/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+ <background android:drawable="@drawable/ic_launcher_background" />
+ <foreground android:drawable="@drawable/ic_launcher_foreground" />
+</adaptive-icon>
\ No newline at end of file
diff --git a/samples/MegaDrone/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/samples/MegaDrone/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 0000000..eca70cf
--- /dev/null
+++ b/samples/MegaDrone/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+ <background android:drawable="@drawable/ic_launcher_background" />
+ <foreground android:drawable="@drawable/ic_launcher_foreground" />
+</adaptive-icon>
\ No newline at end of file
diff --git a/samples/MegaDrone/src/main/res/mipmap-hdpi/ic_launcher.png b/samples/MegaDrone/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..898f3ed
--- /dev/null
+++ b/samples/MegaDrone/src/main/res/mipmap-hdpi/ic_launcher.png
Binary files differ
diff --git a/samples/MegaDrone/src/main/res/mipmap-hdpi/ic_launcher_round.png b/samples/MegaDrone/src/main/res/mipmap-hdpi/ic_launcher_round.png
new file mode 100644
index 0000000..dffca36
--- /dev/null
+++ b/samples/MegaDrone/src/main/res/mipmap-hdpi/ic_launcher_round.png
Binary files differ
diff --git a/samples/MegaDrone/src/main/res/mipmap-mdpi/ic_launcher.png b/samples/MegaDrone/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..64ba76f
--- /dev/null
+++ b/samples/MegaDrone/src/main/res/mipmap-mdpi/ic_launcher.png
Binary files differ
diff --git a/samples/MegaDrone/src/main/res/mipmap-mdpi/ic_launcher_round.png b/samples/MegaDrone/src/main/res/mipmap-mdpi/ic_launcher_round.png
new file mode 100644
index 0000000..dae5e08
--- /dev/null
+++ b/samples/MegaDrone/src/main/res/mipmap-mdpi/ic_launcher_round.png
Binary files differ
diff --git a/samples/MegaDrone/src/main/res/mipmap-xhdpi/ic_launcher.png b/samples/MegaDrone/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..e5ed465
--- /dev/null
+++ b/samples/MegaDrone/src/main/res/mipmap-xhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/MegaDrone/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/samples/MegaDrone/src/main/res/mipmap-xhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..14ed0af
--- /dev/null
+++ b/samples/MegaDrone/src/main/res/mipmap-xhdpi/ic_launcher_round.png
Binary files differ
diff --git a/samples/MegaDrone/src/main/res/mipmap-xxhdpi/ic_launcher.png b/samples/MegaDrone/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..b0907ca
--- /dev/null
+++ b/samples/MegaDrone/src/main/res/mipmap-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/MegaDrone/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/samples/MegaDrone/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..d8ae031
--- /dev/null
+++ b/samples/MegaDrone/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
Binary files differ
diff --git a/samples/MegaDrone/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/samples/MegaDrone/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..2c18de9
--- /dev/null
+++ b/samples/MegaDrone/src/main/res/mipmap-xxxhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/MegaDrone/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/samples/MegaDrone/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..beed3cd
--- /dev/null
+++ b/samples/MegaDrone/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
Binary files differ
diff --git a/samples/MegaDrone/src/main/res/values/colors.xml b/samples/MegaDrone/src/main/res/values/colors.xml
new file mode 100644
index 0000000..69b2233
--- /dev/null
+++ b/samples/MegaDrone/src/main/res/values/colors.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <color name="colorPrimary">#008577</color>
+ <color name="colorPrimaryDark">#00574B</color>
+ <color name="colorAccent">#D81B60</color>
+</resources>
diff --git a/samples/MegaDrone/src/main/res/values/strings.xml b/samples/MegaDrone/src/main/res/values/strings.xml
new file mode 100644
index 0000000..d13fb4c
--- /dev/null
+++ b/samples/MegaDrone/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+<resources>
+ <string name="app_name">Mega Drone</string>
+</resources>
diff --git a/samples/MegaDrone/src/main/res/values/styles.xml b/samples/MegaDrone/src/main/res/values/styles.xml
new file mode 100644
index 0000000..5885930
--- /dev/null
+++ b/samples/MegaDrone/src/main/res/values/styles.xml
@@ -0,0 +1,11 @@
+<resources>
+
+ <!-- Base application theme. -->
+ <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
+ <!-- Customize your theme here. -->
+ <item name="colorPrimary">@color/colorPrimary</item>
+ <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
+ <item name="colorAccent">@color/colorAccent</item>
+ </style>
+
+</resources>