Adding MegaDrone sample (from AES talk)
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