Refactor hello-oboe sample to use floats in signal path
diff --git a/samples/hello-oboe/CMakeLists.txt b/samples/hello-oboe/CMakeLists.txt
index b194577..5541dce 100644
--- a/samples/hello-oboe/CMakeLists.txt
+++ b/samples/hello-oboe/CMakeLists.txt
@@ -25,8 +25,8 @@
 # specify a binary directory
 add_subdirectory(${OBOE_DIR} ./oboe-bin)
 
-# Include the Oboe headers
-include_directories(${OBOE_DIR}/include)
+# Include the Oboe headers and shared sample code
+include_directories(${OBOE_DIR}/include ${OBOE_DIR}/samples)
 
 ### END OBOE INCLUDE SECTION ###
 
@@ -40,7 +40,7 @@
 file (GLOB_RECURSE APP_SOURCES
     ${APP_DIR}/jni_bridge.cpp
     ${APP_DIR}/PlayAudioEngine.cpp
-    ${APP_DIR}/SineGenerator.cpp
+    ${APP_DIR}/SoundGenerator.cpp
 )
 
 # Build the libhello-oboe library
diff --git a/samples/hello-oboe/build.gradle b/samples/hello-oboe/build.gradle
index 4c0161a..cdd975d 100644
--- a/samples/hello-oboe/build.gradle
+++ b/samples/hello-oboe/build.gradle
@@ -11,8 +11,8 @@
         versionName '1.0'
         externalNativeBuild {
             cmake {
+                cppFlags "-std=c++14"
                 arguments '-DANDROID_STL=c++_static'
-
                 // armeabi and mips are deprecated in NDK r16 so we don't want to build for them
                 abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
             }
diff --git a/samples/hello-oboe/src/main/cpp/PlayAudioEngine.cpp b/samples/hello-oboe/src/main/cpp/PlayAudioEngine.cpp
index df404d2..a23b5b9 100644
--- a/samples/hello-oboe/src/main/cpp/PlayAudioEngine.cpp
+++ b/samples/hello-oboe/src/main/cpp/PlayAudioEngine.cpp
@@ -16,9 +16,13 @@
 
 #include <trace.h>
 #include <inttypes.h>
+#include <memory>
+
+#include "shared/Oscillator.h"
 
 #include "PlayAudioEngine.h"
 #include "logging_macros.h"
+#include "SoundGenerator.h"
 
 constexpr int64_t kNanosPerMillisecond = 1000000; // Use int64_t to avoid overflows in calculations
 constexpr int32_t kDefaultChannelCount = 2; // Stereo
@@ -80,10 +84,22 @@
 
         // TODO: Implement Oboe_convertStreamToText
         // PrintAudioStreamInfo(mPlayStream);
-        prepareOscillators();
+        if (mPlayStream->getFormat() == oboe::AudioFormat::I16){
+            // create a buffer of floats which we can render our audio data into
+            int conversionBufferSamples = mPlayStream->getBufferCapacityInFrames() * channelCount;
+            LOGD("Stream format is 16-bit integers, creating a temporary buffer of %d samples"
+                 " for float->int16 conversion", conversionBufferSamples);
+            mConversionBuffer = new float[conversionBufferSamples];
+        }
+
+        mSoundGenerator = std::make_unique<SoundGenerator>(
+                mPlayStream->getSampleRate(),
+                mPlayStream->getBufferCapacityInFrames(),
+                mPlayStream->getChannelCount()
+            );
 
         // Create a latency tuner which will automatically tune our buffer size.
-        mLatencyTuner = std::unique_ptr<oboe::LatencyTuner>(new oboe::LatencyTuner(*mPlayStream));
+        mLatencyTuner = std::make_unique<oboe::LatencyTuner>(*mPlayStream);
         // Start the stream - the dataCallback function will start being called
         result = mPlayStream->requestStart();
         if (result != oboe::Result::OK) {
@@ -98,18 +114,6 @@
     }
 }
 
-void PlayAudioEngine::prepareOscillators() {
-
-    double frequency = 440.0;
-    constexpr double interval = 110.0;
-    constexpr float amplitude = 0.25;
-
-    for (SineGenerator &osc : mOscillators){
-        osc.setup(frequency, mSampleRate, amplitude);
-        frequency += interval;
-    }
-}
-
 /**
  * Sets the stream parameters which are specific to playback, including device id and the
  * callback class, which must be set for low latency playback.
@@ -140,10 +144,15 @@
             LOGE("Error closing output stream. %s", oboe::convertToText(result));
         }
     }
+
+    if (mConversionBuffer != nullptr) {
+        delete[] mConversionBuffer;
+        mConversionBuffer = nullptr;
+    }
 }
 
 void PlayAudioEngine::setToneOn(bool isToneOn) {
-    mIsToneOn = isToneOn;
+    mSoundGenerator->setTonesOn(isToneOn);
 }
 
 /**
@@ -178,27 +187,18 @@
     Trace::beginSection("numFrames %d, Underruns %d, buffer size %d",
                         numFrames, underrunCountResult.value(), bufferSize);
 
+    bool is16BitFormat = (audioStream->getFormat() == oboe::AudioFormat::I16);
     int32_t channelCount = audioStream->getChannelCount();
 
-    // If the tone is on we need to use our synthesizer to render the audio data for the sine waves
-    if (audioStream->getFormat() == oboe::AudioFormat::Float) {
-        if (mIsToneOn) {
-            for (int i = 0; i < channelCount; ++i) {
-                mOscillators[i].render(static_cast<float *>(audioData) + i, channelCount, numFrames);
-            }
-        } else {
-            memset(static_cast<uint8_t *>(audioData), 0,
-                   sizeof(float) * channelCount * numFrames);
-        }
-    } else {
-        if (mIsToneOn) {
-            for (int i = 0; i < channelCount; ++i) {
-                mOscillators[i].render(static_cast<int16_t *>(audioData) + i, channelCount, numFrames);
-            }
-        } else {
-            memset(static_cast<uint8_t *>(audioData), 0,
-                   sizeof(int16_t) * channelCount * numFrames);
-        }
+    // If the stream is 16-bit render into a float buffer then convert that buffer to 16-bit ints
+    float *outputBuffer = (is16BitFormat) ? mConversionBuffer : static_cast<float *>(audioData);
+
+    mSoundGenerator->renderAudio(outputBuffer, numFrames);
+
+    if (is16BitFormat){
+        oboe::convertFloatToPcm16(mConversionBuffer,
+                                  static_cast<int16_t *>(audioData),
+                                  numFrames * channelCount);
     }
 
     if (mIsLatencyDetectionSupported) {
diff --git a/samples/hello-oboe/src/main/cpp/PlayAudioEngine.h b/samples/hello-oboe/src/main/cpp/PlayAudioEngine.h
index ff4bce7..a45209b 100644
--- a/samples/hello-oboe/src/main/cpp/PlayAudioEngine.h
+++ b/samples/hello-oboe/src/main/cpp/PlayAudioEngine.h
@@ -20,10 +20,13 @@
 #include <thread>
 #include <array>
 #include <oboe/Oboe.h>
+
+#include "shared/Mixer.h"
+
 #include "SineGenerator.h"
+#include "SoundGenerator.h"
 
 constexpr int32_t kBufferSizeAutomatic = 0;
-constexpr int32_t kMaximumChannelCount = 8;
 
 class PlayAudioEngine : oboe::AudioStreamCallback {
 
@@ -65,9 +68,11 @@
     oboe::AudioStream *mPlayStream;
     std::unique_ptr<oboe::LatencyTuner> mLatencyTuner;
     std::mutex mRestartingLock;
+    std::unique_ptr<SoundGenerator> mSoundGenerator;
+    float *mConversionBuffer = nullptr;
 
     // The SineGenerators generate audio data, feel free to replace with your own audio generators
-    std::array<SineGenerator, kMaximumChannelCount> mOscillators;
+    //std::array<SineGenerator, kMaximumChannelCount> mOscillators;
 
     void createPlaybackStream();
 
@@ -77,7 +82,7 @@
 
     void setupPlaybackStreamParameters(oboe::AudioStreamBuilder *builder);
 
-    void prepareOscillators();
+    void prepareOscillators(oboe::AudioFormat format, int32_t channelCount);
 
     oboe::Result calculateCurrentOutputLatencyMillis(oboe::AudioStream *stream, double *latencyMillis);
 
diff --git a/samples/hello-oboe/src/main/cpp/SoundGenerator.cpp b/samples/hello-oboe/src/main/cpp/SoundGenerator.cpp
new file mode 100644
index 0000000..690bf37
--- /dev/null
+++ b/samples/hello-oboe/src/main/cpp/SoundGenerator.cpp
@@ -0,0 +1,55 @@
+/*
+ * 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 <memory>
+#include "SoundGenerator.h"
+
+SoundGenerator::SoundGenerator(int32_t sampleRate, int32_t maxFrames, int32_t channelCount)
+        : mSampleRate(sampleRate)
+        , mChannelCount(channelCount)
+        , mOscillators(std::make_unique<Oscillator[]>(channelCount))
+        , mBuffer(std::make_unique<float[]>(maxFrames)){
+
+    double frequency = 440.0;
+    constexpr double interval = 110.0;
+    constexpr float amplitude = 1.0;
+
+    // Set up the oscillators
+    for (int i = 0; i < mChannelCount; ++i) {
+        mOscillators[i].setFrequency(frequency);
+        mOscillators[i].setSampleRate(mSampleRate);
+        mOscillators[i].setAmplitude(amplitude);
+        frequency += interval;
+    }
+}
+
+void SoundGenerator::renderAudio(float *audioData, int32_t numFrames) {
+
+    // Render each oscillator into its own channel
+    for (int i = 0; i < mChannelCount; ++i) {
+
+        mOscillators[i].renderAudio(mBuffer.get(), numFrames);
+        for (int j = 0; j < numFrames; ++j) {
+            audioData[(j*mChannelCount)+i] = mBuffer[j];
+        }
+    }
+}
+
+void SoundGenerator::setTonesOn(bool isOn) {
+    for (int i = 0; i < mChannelCount; ++i) {
+        mOscillators[i].setWaveOn(isOn);
+    }
+}
diff --git a/samples/hello-oboe/src/main/cpp/SoundGenerator.h b/samples/hello-oboe/src/main/cpp/SoundGenerator.h
new file mode 100644
index 0000000..7577cdc
--- /dev/null
+++ b/samples/hello-oboe/src/main/cpp/SoundGenerator.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 SAMPLES_SOUNDGENERATOR_H
+#define SAMPLES_SOUNDGENERATOR_H
+
+
+#include <shared/IRenderableAudio.h>
+#include <shared/Oscillator.h>
+
+/**
+ * Generates a fixed frequency tone for each channel.
+ */
+class SoundGenerator : public IRenderableAudio {
+public:
+    /**
+     * Create a new SoundGenerator object.
+     *
+     * @param sampleRate - The output sample rate.
+     * @param maxFrames - The maximum number of audio frames which will be rendered, this is used to
+     * calculate this object's internal buffer size.
+     * @param channelCount - The number of channels in the output, one tone will be created for each
+     * channel, the output will be interlaced.
+     *
+     */
+    SoundGenerator(int32_t sampleRate, int32_t maxFrames, int32_t channelCount);
+    ~SoundGenerator() = default;
+
+    // Switch the tones on
+    void setTonesOn(bool isOn);
+
+    // From IRenderableAudio
+    void renderAudio(float *audioData, int32_t numFrames) override;
+
+private:
+    const int32_t mSampleRate;
+    const int32_t mChannelCount;
+    const std::unique_ptr<Oscillator[]> mOscillators;
+    const std::unique_ptr<float[]> mBuffer;
+};
+
+
+#endif //SAMPLES_SOUNDGENERATOR_H
diff --git a/samples/shared/RenderableAudio.h b/samples/shared/IRenderableAudio.h
similarity index 83%
rename from samples/shared/RenderableAudio.h
rename to samples/shared/IRenderableAudio.h
index 756ed72..eee9872 100644
--- a/samples/shared/RenderableAudio.h
+++ b/samples/shared/IRenderableAudio.h
@@ -17,15 +17,14 @@
 #ifndef MEGADRONE_RENDERABLEAUDIO_H
 #define MEGADRONE_RENDERABLEAUDIO_H
 
-
 #include <cstdint>
+#include <string>
 
-template <typename T>
-class RenderableAudio {
+class IRenderableAudio {
 
 public:
-    virtual ~RenderableAudio() = default;
-    virtual void renderAudio(T *audioData, int32_t numFrames) = 0;
+    virtual ~IRenderableAudio() = default;
+    virtual void renderAudio(float *audioData, int32_t numFrames) = 0;
 };
 
 
diff --git a/samples/shared/Mixer.h b/samples/shared/Mixer.h
index 6969832..d58d192 100644
--- a/samples/shared/Mixer.h
+++ b/samples/shared/Mixer.h
@@ -17,19 +17,19 @@
 #ifndef RHYTHMGAME_MIXER_H
 #define RHYTHMGAME_MIXER_H
 
-#include "RenderableAudio.h"
+#include <array>
+#include "IRenderableAudio.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> {
+class Mixer : public IRenderableAudio {
 
 public:
-    void renderAudio(T *audioData, int32_t numFrames) {
+    void renderAudio(float *audioData, int32_t numFrames) {
 
         // Zero out the incoming container array
-        memset(audioData, 0, sizeof(T) * numFrames);
+        memset(audioData, 0, sizeof(float) * numFrames);
 
         for (int i = 0; i < mNextFreeTrackIndex; ++i) {
             mTracks[i]->renderAudio(mixingBuffer, numFrames);
@@ -40,13 +40,13 @@
         }
     }
 
-    void addTrack(std::shared_ptr<RenderableAudio<T>> renderer){
+    void addTrack(std::shared_ptr<IRenderableAudio> renderer){
         mTracks[mNextFreeTrackIndex++] = renderer;
     }
 
 private:
-    T mixingBuffer[kBufferSize];
-    std::array<std::shared_ptr<RenderableAudio<T>>, kMaxTracks> mTracks;
+    float mixingBuffer[kBufferSize];
+    std::array<std::shared_ptr<IRenderableAudio>, kMaxTracks> mTracks;
     uint8_t mNextFreeTrackIndex = 0;
 };
 
diff --git a/samples/shared/MonoToStereo.h b/samples/shared/MonoToStereo.h
index 80ff9a3..97ba349 100644
--- a/samples/shared/MonoToStereo.h
+++ b/samples/shared/MonoToStereo.h
@@ -14,19 +14,21 @@
  * limitations under the License.
  */
 
-#ifndef MEGADRONE_MONOTOSTEREO_H
-#define MEGADRONE_MONOTOSTEREO_H
+#ifndef SHARED_MONOTOSTEREO_H
+#define SHARED_MONOTOSTEREO_H
 
-#include "RenderableAudio.h"
+#include "IRenderableAudio.h"
 
-template <typename T>
-class MonoToStereo : public RenderableAudio<T> {
+
+class MonoToStereo : public IRenderableAudio {
 
 public:
 
-    MonoToStereo(RenderableAudio<T> *input) : mInput(input){};
+    MonoToStereo(IRenderableAudio *input) : mInput(input){};
 
-    void renderAudio(T *audioData, int32_t numFrames) override {
+    void renderAudio(float *audioData, int32_t numFrames) override {
+
+        constexpr int kChannelCountStereo = 2;
 
         mInput->renderAudio(audioData, numFrames);
 
@@ -35,13 +37,13 @@
         // e.g. 123 => 112233
         for (int i = numFrames - 1; i >= 0; --i) {
 
-            audioData[i * oboe::ChannelCount::Stereo] = audioData[i];
-            audioData[i * oboe::ChannelCount::Stereo + 1] = audioData[i];
+            audioData[i * kChannelCountStereo] = audioData[i];
+            audioData[i * kChannelCountStereo + 1] = audioData[i];
         }
     }
 
-    RenderableAudio<T> *mInput;
+    IRenderableAudio *mInput;
 };
 
 
-#endif //MEGADRONE_MONOTOSTEREO_H
+#endif //SHARED_MONOTOSTEREO_H
diff --git a/samples/shared/Oscillator.cpp b/samples/shared/Oscillator.cpp
index fe590de..cdd77b2 100644
--- a/samples/shared/Oscillator.cpp
+++ b/samples/shared/Oscillator.cpp
@@ -17,7 +17,7 @@
 #include "Oscillator.h"
 
 // We need to calculate the amplitude value differently for each supported output format
-template<>
+/*template<>
 void Oscillator<float>::setAmplitude(float amplitude){
     mAmplitude = amplitude;
 };
@@ -25,4 +25,4 @@
 template<>
 void Oscillator<int16_t>::setAmplitude(float amplitude){
     mAmplitude = amplitude * INT16_MAX;
-};
\ No newline at end of file
+};*/
\ No newline at end of file
diff --git a/samples/shared/Oscillator.h b/samples/shared/Oscillator.h
index 33b2772..4847274 100644
--- a/samples/shared/Oscillator.h
+++ b/samples/shared/Oscillator.h
@@ -15,31 +15,32 @@
  */
 
 
-#ifndef MEGADRONE_OSCILLATOR_H
-#define MEGADRONE_OSCILLATOR_H
+#ifndef SHARED_OSCILLATOR_H
+#define SHARED_OSCILLATOR_H
 
 
 #include <cstdint>
 #include <atomic>
 #include <math.h>
 #include <memory>
-#include "RenderableAudio.h"
+#include "IRenderableAudio.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> {
+class Oscillator : public IRenderableAudio {
 
 public:
 
-    void setWaveOn(bool isWaveOn){
+    ~Oscillator() = default;
+
+    void setWaveOn(bool isWaveOn) {
         mIsWaveOn.store(isWaveOn);
     };
 
-    void setSampleRate(int32_t sampleRate){
+    void setSampleRate(int32_t sampleRate) {
         mSampleRate = sampleRate;
         updatePhaseIncrement();
     };
@@ -49,16 +50,18 @@
         updatePhaseIncrement();
     };
 
-    void setAmplitude(float amplitude);
+    inline void setAmplitude(float amplitude) {
+        mAmplitude = amplitude;
+    };
 
-    // From RenderableAudio<T>
-    void renderAudio(T *audioData, int32_t numFrames) {
+    // From IRenderableAudio
+    void renderAudio(float *audioData, int32_t numFrames) override {
 
         if (mIsWaveOn){
             for (int i = 0; i < numFrames; ++i) {
 
                 // Sine wave (sinf)
-                //audioData[i*kChannelCount] = sinf(mPhase) * mAmplitude;
+                //data[i*kChannelCount] = sinf(mPhase) * mAmplitude;
 
                 // Square wave
                 if (mPhase <= kPi){
@@ -71,14 +74,14 @@
                 if (mPhase > kTwoPi) mPhase -= kTwoPi;
             }
         } else {
-            memset(audioData, 0, sizeof(T) * numFrames);
+            memset(audioData, 0, sizeof(float) * numFrames);
         }
     };
 
 private:
     std::atomic<bool> mIsWaveOn { false };
     float mPhase = 0.0;
-    std::atomic<T> mAmplitude { 0 };
+    std::atomic<float> mAmplitude { 0 };
     std::atomic<double> mPhaseIncrement { 0.0 };
     double mFrequency = kDefaultFrequency;
     int32_t mSampleRate = kDefaultSampleRate;
@@ -88,4 +91,4 @@
     };
 };
 
-#endif //MEGADRONE_OSCILLATOR_H
+#endif //SHARED_OSCILLATOR_H