Addressing code reviews for echo sample
diff --git a/samples/common/audio_common.cpp b/samples/common/audio_common.cpp
index 82dc8a4..3d296af 100644
--- a/samples/common/audio_common.cpp
+++ b/samples/common/audio_common.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright 2017 The Android Open Source Project
+ * 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.
@@ -14,99 +14,52 @@
  * limitations under the License.
  */
 
-#include <string>
 #include "audio_common.h"
 #include <logging_macros.h>
+#include <string>
+#include <cinttypes>
 
-static const oboe::AudioFormat audioFormatEnum[] = {
-    oboe::AudioFormat::Invalid,
-    oboe::AudioFormat::Unspecified,
-    oboe::AudioFormat::I16,
-    oboe::AudioFormat::Float,
-};
-static const int32_t audioFormatCount = sizeof(audioFormatEnum)/
-                                        sizeof(audioFormatEnum[0]);
-
-static const uint32_t sampleFormatBPP[] = {
-    0xffff,
-    0xffff,
-    16, //I16
-    32, //FLOAT
-};
 uint16_t SampleFormatToBpp(oboe::AudioFormat format) {
-    for (int32_t i = 0; i < audioFormatCount; ++i) {
-      if (audioFormatEnum[i] == format)
-        return sampleFormatBPP[i];
+    switch (format) {
+      case oboe::AudioFormat::I16:
+        return 16;
+      case oboe::AudioFormat::Float:
+        return 32;
+      default:
+        return 0;
     }
-    return 0xffff;
-}
-static const char * audioFormatStr[] = {
-    "AAUDIO_FORMAT_INVALID", // = -1,
-    "AAUDIO_FORMAT_UNSPECIFIED", // = 0,
-    "AAUDIO_FORMAT_PCM_I16",
-    "AAUDIO_FORMAT_PCM_FLOAT",
-};
-const char* FormatToString(oboe::AudioFormat format) {
-    for (int32_t i = 0; i < audioFormatCount; ++i) {
-        if (audioFormatEnum[i] == format)
-            return audioFormatStr[i];
-    }
-    return "UNKNOW_AUDIO_FORMAT";
 }
 
 void PrintAudioStreamInfo(const oboe::AudioStream *stream) {
-#define STREAM_CALL(func) (stream)->func()
-    LOGI("StreamID: %p", stream);
+  LOGI("StreamID: %p", stream);
+  oboe::Direction  dir = stream->getDirection();
+  LOGI("Direction: %s", oboe::convertToText(dir));
+  LOGI("API type: %s", oboe::convertToText(stream->getAudioApi()));
+  LOGI("BufferCapacity: %d", stream->getBufferCapacityInFrames());
+  LOGI("BufferSize: %d", stream->getBufferSizeInFrames());
+  LOGI("FramesPerBurst: %d", stream->getFramesPerBurst());
+  LOGI("XRunCount: %d", stream->getXRunCount().value());
+  LOGI("SampleRate: %d", stream->getSampleRate());
+  LOGI("SamplesPerFrame: %d", stream->getChannelCount());
+  LOGI("DeviceId: %d", stream->getDeviceId());
+  LOGI("Format: %s",  oboe::convertToText(stream->getFormat()));
+  LOGI("SharingMode: %s", oboe::convertToText(stream->getSharingMode()));
+  LOGI("PerformanceMode: %s", oboe::convertToText(stream->getPerformanceMode()));
 
-    LOGI("API type: %s", oboe::convertToText(stream->getAudioApi()));
-    LOGI("BufferCapacity: %d", STREAM_CALL(getBufferCapacityInFrames));
-    LOGI("BufferSize: %d", STREAM_CALL(getBufferSizeInFrames));
-//  Question: does this one have to non-constant function?
-//    LOGI("FramesPerBurst: %d", STREAM_CALL(getFramesPerBurst));
-    LOGI("FramesPerBurst: %d", const_cast<oboe::AudioStream* >(stream)->getFramesPerBurst());
-    LOGI("XRunCount: %d", STREAM_CALL(getXRunCount));
-    LOGI("SampleRate: %d", STREAM_CALL(getSampleRate));
-    LOGI("SamplesPerFrame: %d", STREAM_CALL(getChannelCount));
-    LOGI("DeviceId: %d", STREAM_CALL(getDeviceId));
-    LOGI("Format: %s",  FormatToString(STREAM_CALL(getFormat)));
-    LOGI("SharingMode: %s", (STREAM_CALL(getSharingMode)) == oboe::SharingMode::Exclusive?
-                          "EXCLUSIVE" : "SHARED");
-
-    oboe::PerformanceMode perfMode = STREAM_CALL(getPerformanceMode);
-    std::string perfModeDescription;
-    switch (perfMode){
-      case oboe::PerformanceMode ::None:
-        perfModeDescription = "NONE";
-        break;
-      case oboe::PerformanceMode::LowLatency:
-        perfModeDescription = "LOW_LATENCY";
-        break;
-      case oboe::PerformanceMode::PowerSaving:
-        perfModeDescription = "POWER_SAVING";
-        break;
-      default:
-        perfModeDescription = "UNKNOWN";
-        break;
-    }
-    LOGI("PerformanceMode: %s", perfModeDescription.c_str());
-
-    oboe::Direction  dir = STREAM_CALL(getDirection);
-    LOGI("Direction: %s", (dir == oboe::Direction ::Output ? "OUTPUT" : "INPUT"));
-    if (dir == oboe::Direction ::Output) {
-        LOGI("FramesReadByDevice: %d", (int32_t)STREAM_CALL(getFramesRead));
-        LOGI("FramesWriteByApp: %d", (int32_t)STREAM_CALL(getFramesWritten));
-    } else {
-        LOGI("FramesReadByApp: %d", (int32_t)STREAM_CALL(getFramesRead));
-        LOGI("FramesWriteByDevice: %d", (int32_t)STREAM_CALL(getFramesWritten));
-    }
-#undef STREAM_CALL
+  if (dir == oboe::Direction ::Output) {
+      LOGI("FramesReadByDevice: %" PRIx64, stream->getFramesRead());
+      LOGI("FramesWriteByApp: %d", (int32_t)stream->getFramesWritten());
+  } else {
+      LOGI("FramesReadByApp: %d", (int32_t)stream->getFramesRead());
+      LOGI("FramesWriteByDevice: %d", (int32_t)stream->getFramesWritten());
+  }
 }
 
-int64_t timestamp_to_nanoseconds(timespec ts){
-  return (ts.tv_sec * (int64_t) NANOS_PER_SECOND) + ts.tv_nsec;
+int64_t timestamp_to_nanoseconds(timespec ts) {
+  return (ts.tv_sec * oboe::kNanosPerSecond) + ts.tv_nsec;
 }
 
-int64_t get_time_nanoseconds(clockid_t clockid){
+int64_t get_time_nanoseconds(clockid_t clockid) {
   timespec ts;
   clock_gettime(clockid, &ts);
   return timestamp_to_nanoseconds(ts);
diff --git a/samples/common/audio_common.h b/samples/common/audio_common.h
index 36a4b5e..83d3fda 100644
--- a/samples/common/audio_common.h
+++ b/samples/common/audio_common.h
@@ -1,5 +1,5 @@
 /*
- * Copyright 2017 The Android Open Source Project
+ * 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.
@@ -21,9 +21,6 @@
 #include <chrono>
 #include <oboe/Oboe.h>
 
-// Time constants
-#define NANOS_PER_SECOND 1000000000L
-#define NANOS_PER_MILLISECOND 1000000L
 
 constexpr int kMonoChannelCount = 1;
 constexpr int kStereoChannelCount = 2;
@@ -36,7 +33,7 @@
     struct timeval Time;
     gettimeofday( &Time, NULL );
 
-    return (static_cast<uint64_t>(1000000) * Time.tv_sec + Time.tv_usec);
+    return (static_cast<uint64_t>(oboe::kNanosPerMillisecond) * Time.tv_sec + Time.tv_usec);
 }
 
 /*
diff --git a/samples/echo/src/main/cpp/AudioEffect.cpp b/samples/echo/src/main/cpp/AudioEffect.cpp
index 3f7c842..3080237 100644
--- a/samples/echo/src/main/cpp/AudioEffect.cpp
+++ b/samples/echo/src/main/cpp/AudioEffect.cpp
@@ -19,6 +19,9 @@
 #include <cstring>
 #include <audio_common.h>
 
+#define CLAMP_FOR_I16(x) ((x)>SHRT_MAX ? SHRT_MAX : \
+                         ((x)<SHRT_MIN ? SHRT_MIN : (x)))
+
 /*
  * Mixing Audio in integer domain to avoid FP calculation
  *   (FG * ( MixFactor * 16 ) + BG * ( (1.0f-MixFactor) * 16 )) / 16
@@ -28,7 +31,6 @@
 AudioMixer::AudioMixer() :
     AudioFormat(48000, 2, oboe::AudioFormat::I16) {
 
-  busy_ = false;
   bgMixFactorInt_ = (int32_t)
                    (bgMixFactor_ * kFloatToIntMapFactor + 0.5f);
   fgMixFactorInt_ = kFloatToIntMapFactor - bgMixFactorInt_;
@@ -49,8 +51,13 @@
 void  AudioMixer::setBackgroundMixer(float mixer) {
   if (mixer >= 0.0f && mixer <= 1.0f) {
     bgMixFactor_ = mixer;
-    bgMixFactorInt_ = (int32_t)
-      (bgMixFactor_ * kFloatToIntMapFactor + 0.5f);
+    if (bgMixFactor_ < 0.05f) {
+      bgMixFactor_ = 0.0f;
+      bgMixFactorInt_ = 0;
+    } else {
+      bgMixFactorInt_ = (int32_t)
+        (bgMixFactor_ * kFloatToIntMapFactor + 0.5f);
+    }
     fgMixFactorInt_ = kFloatToIntMapFactor - bgMixFactorInt_;
   }
 }
@@ -63,12 +70,10 @@
  * @param channelCount channels for PCM audio pointed by samples
  * @param freq is PCM audio frequency (48000hz for this sample)
  */
-void  AudioMixer::addStream(std::unique_ptr<int16_t[]>samples, size_t sampleCount,
-          int32_t sampleRate, int32_t channelCount, oboe::AudioFormat format){
-  if (busy_) {
-    LOGW("filtering in progress, filter configuration is IGNORED");
-    return;
-  }
+bool  AudioMixer::addStream(std::unique_ptr<int16_t[]>samples, size_t sampleCount,
+          int32_t sampleRate, int32_t channelCount, oboe::AudioFormat format) {
+  // Wait for lock, from user context.
+  std::lock_guard<std::mutex> lock(lock_);
   bgAudio_ = std::move(samples);
   bgAudioSampleCount_ = sampleCount;
   sampleRate_ = sampleRate;
@@ -76,6 +81,8 @@
   channelCount_ = channelCount;
 
   curPosition_ = 0;
+
+  return true;
 }
 
 /**
@@ -87,35 +94,67 @@
  */
 void AudioMixer::process(int16_t *liveAudio, int32_t channelCount,
                           int32_t numFrames) {
-  assert(bgAudio_ && liveAudio);
-  if (numFrames > bgAudioSampleCount_ || channelCount != channelCount_ ||
+  if(!bgAudio_ || !liveAudio) {
+    return;
+  }
+
+  if ((numFrames * channelCount) > bgAudioSampleCount_ ||
+      channelCount != channelCount_ ||
       bgMixFactorInt_ == 0) {
     return;
   }
 
-  busy_ = true;
+  if (!lock_.try_lock()) {
+    // UI thread still updating the stream, skip blending
+    return;
+  }
+
+  size_t sampleCount  = numFrames * channelCount;
   int32_t curSample;
-  for (int i = 0; i < (numFrames * channelCount); i++) {
+  for (int i = 0; i < sampleCount ; i++) {
     curSample = liveAudio[i];
     curSample = curSample * fgMixFactorInt_ +
                 bgAudio_[curPosition_] * bgMixFactorInt_;
     curSample /= kFloatToIntMapFactor;
 
-    curSample = (curSample > SHRT_MAX ? SHRT_MAX : curSample);
-    liveAudio[i] = (int16_t)(curSample < SHRT_MIN ? SHRT_MIN : curSample);
+    curSample = CLAMP_FOR_I16(curSample);
+    liveAudio[i] = (int16_t)curSample;
     curPosition_ = (curPosition_ + 1 ) % bgAudioSampleCount_;
   }
-  busy_ = false;
+
+  lock_.unlock();
 }
 
 /**
  * query for audio format supportability
  */
-bool AudioMixer::AudioFormatSupported(int32_t frequency,
-                  int32_t channels, oboe::AudioFormat format) const {
-  return (frequency == sampleRate_ &&
-          channels == channelCount_ &&
-          format == format_);
+bool AudioMixer::AudioFormatSupported(int32_t sampleRate,
+                  int32_t channels, oboe::AudioFormat format) {
+  if (sampleRate != sampleRate_ || format != format_) {
+    return false;
+  }
+
+  if (channels  == channelCount_ ) {
+    return true;
+  }
+
+  if(channelCount_ == channels * 2) {
+    size_t dst = 0, src = 0;
+    size_t totalFrames = bgAudioSampleCount_ / channelCount_;
+    for(size_t frame = 0; frame < totalFrames; frame++) {
+      for (int32_t c = 0; c < channelCount_; c += 2) {
+        int32_t sample = bgAudio_[src] + bgAudio_[src + 1];
+        src += 2;
+        sample /= 2;
+        bgAudio_[dst++] = static_cast<int16_t>(CLAMP_FOR_I16(sample));
+      }
+    }
+    channelCount_ >>= 1;
+    bgAudioSampleCount_ >>= 1;
+    return true;
+  }
+
+  return false;
 }
 
 /**
@@ -143,11 +182,11 @@
  * Destructor
  */
 AudioDelay::~AudioDelay() {
-  if(buffer_) delete static_cast<uint8_t*>(buffer_);
+  delete buffer_;
 }
 
 /**
- * Configure for delay time ( in miliseconds ). It is possible to dynamically
+ * Configure for delay time ( in second ). It is possible to dynamically
  * adjust the value
  * @param delay in seconds
  * @return true if delay time is set successfully
@@ -160,10 +199,7 @@
 
   std::lock_guard<std::mutex> lock(lock_);
 
-  if(buffer_) {
-    delete static_cast<uint8_t*>(buffer_);
-    buffer_ = nullptr;
-  }
+  delete (buffer_);
 
   delay_  = delay;
   allocateBuffer();
@@ -194,7 +230,9 @@
   buffer_ = new uint8_t[bufCapacity_];
   assert(buffer_);
 
-  memset(buffer_, 0, bufCapacity_);
+  if (buffer_) {
+    memset(buffer_, 0, bufCapacity_);
+  }
   curPos_ = 0;
 
   // bufSize_ is in Frames ( not samples, not bytes )
@@ -237,7 +275,9 @@
 void AudioDelay::process(int16_t *liveAudio,
                          int32_t channelCount,
                          int32_t numFrames) {
-  if (feedbackFactor_ == 0 ||
+
+  if (!buffer_ || !liveAudio ||
+      feedbackFactor_ == 0 ||
       channelCount != channelCount_ ||
       bufSize_ < numFrames) {
     return;
@@ -253,22 +293,13 @@
 
   // process every sample
   int32_t sampleCount = channelCount * numFrames;
-  int16_t* samples =  & static_cast<int16_t*>(buffer_)[curPos_];
-  int32_t curSample;
+  int16_t* samples =  & reinterpret_cast<int16_t*>(buffer_)[curPos_ * channelCount_];
   for (size_t idx = 0; idx < sampleCount; idx++) {
-#if 1
-    curSample = (samples[idx] * feedbackFactor_ +
+    int32_t curSample = (samples[idx] * feedbackFactor_ +
                 liveAudio[idx] * liveAudioFactor_) / kFloatToIntMapFactor;
-#else
-    curSample = (samples[idx] * feedbackFactor_) / kFloatToIntMapFactor +
-                 liveAudio[idx];
-#endif
-    if(curSample > SHRT_MAX)
-      curSample = SHRT_MAX;
-    else if (curSample < SHRT_MIN)
-      curSample = SHRT_MAX;
-    samples[idx] = static_cast<int16_t>(curSample);
+    CLAMP_FOR_I16(curSample);
     liveAudio[idx] = samples[idx];
+    samples[idx] = static_cast<int16_t>(curSample);
   }
 
   curPos_ += numFrames;
diff --git a/samples/echo/src/main/cpp/AudioEffect.h b/samples/echo/src/main/cpp/AudioEffect.h
index 9119507..8ca5769 100644
--- a/samples/echo/src/main/cpp/AudioEffect.h
+++ b/samples/echo/src/main/cpp/AudioEffect.h
@@ -1,5 +1,5 @@
 /*
- * Copyright 2017 The Android Open Source Project
+ * 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.
@@ -48,20 +48,20 @@
     ~AudioMixer();
     void process(int16_t *liveAudio, int32_t channelCount,
                  int32_t numFrames);
-    void addStream(std::unique_ptr<int16_t[]>samples, size_t sampleCount,
+    bool addStream(std::unique_ptr<int16_t[]>samples, size_t sampleCount,
                    int32_t sampleRate, int32_t channelCount,
                    oboe::AudioFormat format);
     void setBackgroundMixer(float bgMix);
     bool AudioFormatSupported(int32_t sampleRate, int32_t channels,
-                              oboe::AudioFormat format) const;
+                              oboe::AudioFormat format);
   private:
-    std::unique_ptr<int16_t[]> bgAudio_ = nullptr;
+    std::unique_ptr<int16_t[]> bgAudio_;
     size_t bgAudioSampleCount_ = 0;
     size_t curPosition_ = 0;
-    std::atomic_bool busy_;
     float bgMixFactor_ = 0.5f;
     int32_t  fgMixFactorInt_;
     int32_t  bgMixFactorInt_;
+    std::mutex lock_;
 };
 
 /**
@@ -93,7 +93,7 @@
   private:
     float delay_ = 0.0f;
     float decay_ = 0.1f;
-    void *buffer_ = nullptr;
+    uint8_t *buffer_ = nullptr;
     size_t bufCapacity_ = 0;
     size_t bufSize_ = 0;
     size_t curPos_ = 0;
diff --git a/samples/echo/src/main/cpp/CMakeLists.txt b/samples/echo/src/main/cpp/CMakeLists.txt
index ed2764b..fe6db7c 100644
--- a/samples/echo/src/main/cpp/CMakeLists.txt
+++ b/samples/echo/src/main/cpp/CMakeLists.txt
@@ -44,4 +44,4 @@
 
 target_compile_options(echo
     PRIVATE
-        -Wall)
+        -Wall -Werror)
diff --git a/samples/echo/src/main/cpp/EchoAudioEngine.cpp b/samples/echo/src/main/cpp/EchoAudioEngine.cpp
index 10267f8..d2775b2 100644
--- a/samples/echo/src/main/cpp/EchoAudioEngine.cpp
+++ b/samples/echo/src/main/cpp/EchoAudioEngine.cpp
@@ -1,5 +1,5 @@
 /**
- * Copyright 2017 The Android Open Source Project
+ * 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.
@@ -20,6 +20,14 @@
 #include <climits>
 #include <assert.h>
 
+/**
+ * Duplex is not very stable right after starting up:
+ *   the callbacks may not be happening at the right times
+ * The time to get it stable varies on different systems. Half second
+ * is used for this sample, during the time this sample plays silence.
+ */
+const float kSystemWarmupTime = 0.5f;
+
 EchoAudioEngine::EchoAudioEngine() {
   assert(outputChannelCount_ == inputChannelCount_);
   mixerEffect_ = std::unique_ptr<AudioMixer>(new AudioMixer);
@@ -30,7 +38,6 @@
   stopStream(recordingStream_);
 
   closeStream(playStream_);
-  frameCallbackCount_ = 0;
 
   closeStream(recordingStream_);
 }
@@ -158,20 +165,16 @@
     assert(playStream_->getFormat() == oboe::AudioFormat::I16);
     assert(outputChannelCount_ == playStream_->getChannelCount());
 
+    systemStartupFrames_ = static_cast<uint64_t>
+                             (sampleRate_ * kSystemWarmupTime);
+    processedFrameCount_ = 0;
+
     framesPerBurst_ = playStream_->getFramesPerBurst();
 
-    // Read blocking timeout value: half of the burst size
-    audioBlockingReadTimeout_ = static_cast<uint64_t>(.5f * framesPerBurst_
-                                          / sampleRate_ * NANOS_PER_SECOND);
-
-    latencyTuner_ = std::unique_ptr<oboe::LatencyTuner>
-                    (new oboe::LatencyTuner(*playStream_));
-
     delayEffect_ = std::unique_ptr<AudioDelay>(new AudioDelay(
       sampleRate_,outputChannelCount_, format_, echoDelay_, echoDecay_));
     assert(delayEffect_ && mixerEffect_);
 
-    frameCallbackCount_ = 0;
     warnIfNotLowLatency(playStream_);
 
     PrintAudioStreamInfo(playStream_);
@@ -211,7 +214,8 @@
       ->setDeviceId(playbackDeviceId_)
       ->setDirection(oboe::Direction::Output)
       ->setChannelCount(outputChannelCount_)
-      ->setSampleRate(sampleRate_);
+      ->setSampleRate(sampleRate_)
+      ->setFramesPerCallback(framesPerBurst_);
 
   return setupCommonStreamParameters(builder);
 }
@@ -318,23 +322,29 @@
 
   assert(oboeStream == playStream_);
 
-  if (frameCallbackCount_) {
-    latencyTuner_->tune();
+  int32_t prevFrameRead = 0, framesRead = 0;
+  if (processedFrameCount_ < systemStartupFrames_) {
+    do {
+      // Drain the audio for the starting up period, half second for
+      // this sample.
+      prevFrameRead = framesRead;
+
+      oboe::ResultWithValue<int32_t> status =
+        recordingStream_->read(audioData, numFrames, 0);
+      framesRead = (!status) ? 0 : status.value();
+      if (framesRead == 0)
+        break;
+
+    } while (framesRead);
+
+    framesRead = prevFrameRead;
+  } else {
+    oboe::ResultWithValue<int32_t> status =
+      recordingStream_->read(audioData, numFrames, 0);
+
+    framesRead = (!status) ? 0 : status.value();
   }
-  frameCallbackCount_++;
 
-  // blocking read with timeout:
-  //     recorder may not have data ready, specifically
-  //     at the very beginning; in this case, simply play
-  //     silent audio. The timeout is equivalent to
-  //       framesPerBurst()/2
-  //     Do not make it too long, otherwise player would underrun
-  //     and if tuning is in process, player will increase
-  //     FramesPerBurst.
-  oboe::ErrorOrValue<int32_t> status =
-    recordingStream_->read(audioData, numFrames, audioBlockingReadTimeout_);
-
-  int32_t framesRead = (!status) ? 0 : status.value();
   if (framesRead < numFrames) {
     int32_t bytesPerFrame = recordingStream_->getChannelCount() *
                             SampleFormatToBpp(oboeStream->getFormat()) / 8;
@@ -343,12 +353,16 @@
     memset(padPos, 0, (size_t)(numFrames - framesRead) * bytesPerFrame);
   }
 
+  // Processing audio: padded silence audio treated as valid audio
+  //                   glitch would be felt by turning off mixer
   delayEffect_->process(static_cast<int16_t *>(audioData),
                         outputChannelCount_, numFrames);
   if (mixAudio_) {
     mixerEffect_->process(static_cast<int16_t *>(audioData),
-                         outputChannelCount_, numFrames);
+                          outputChannelCount_, numFrames);
   }
+  processedFrameCount_ += numFrames;
+
   return oboe::DataCallbackResult::Continue;
 }
 
diff --git a/samples/echo/src/main/cpp/EchoAudioEngine.h b/samples/echo/src/main/cpp/EchoAudioEngine.h
index c937d6c..6a7a628 100644
--- a/samples/echo/src/main/cpp/EchoAudioEngine.h
+++ b/samples/echo/src/main/cpp/EchoAudioEngine.h
@@ -1,5 +1,5 @@
 /*
- * Copyright 2017 The Android Open Source Project
+ * 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.
@@ -53,7 +53,8 @@
 
  private:
   bool isEchoOn_ = false;
-  uint64_t frameCallbackCount_ = 0;
+  uint64_t processedFrameCount_ = 0;
+  uint64_t systemStartupFrames_ = 0;
   int32_t recordingDeviceId_ = oboe::kUnspecified;
   int32_t playbackDeviceId_ = oboe::kUnspecified;
   oboe::AudioFormat format_ = oboe::AudioFormat::I16;
@@ -64,15 +65,13 @@
   oboe::AudioStream *playStream_ = nullptr;
   int32_t framesPerBurst_;
   std::mutex restartingLock_;
-  std::unique_ptr<AudioMixer> mixerEffect_ = nullptr;
-  std::unique_ptr<AudioDelay> delayEffect_ = nullptr;
-  uint64_t audioBlockingReadTimeout_ = NANOS_PER_MILLISECOND;
+  std::unique_ptr<AudioMixer> mixerEffect_;
+  std::unique_ptr<AudioDelay> delayEffect_;
   oboe::AudioApi audioApi_ = oboe::AudioApi::AAudio;
   float echoDelay_ = 0.5f;
   float echoDecay_ = 0.1f;
 
   bool mixAudio_ = false;
-  std::unique_ptr<oboe::LatencyTuner> latencyTuner_;
 
   void openRecordingStream();
   void openPlaybackStream();
diff --git a/samples/echo/src/main/cpp/jni_bridge.cpp b/samples/echo/src/main/cpp/jni_bridge.cpp
index fe55fca..742b381 100644
--- a/samples/echo/src/main/cpp/jni_bridge.cpp
+++ b/samples/echo/src/main/cpp/jni_bridge.cpp
@@ -1,5 +1,5 @@
 /**
- * Copyright 2017 The Android Open Source Project
+ * 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.
@@ -21,6 +21,10 @@
 #include <android/asset_manager_jni.h>
 
 static EchoAudioEngine *engine = nullptr;
+
+static const int OBOE_API_AAUDIO = 0;
+static const int OBOE_API_OPENSL_ES = 1;
+
 extern "C" {
 
 JNIEXPORT bool JNICALL
@@ -132,9 +136,6 @@
 }
 
 
-static const int OBOE_API_AAUDIO = 0;
-static const int OBOE_API_OPENSL_ES = 1;
-
 JNIEXPORT jboolean JNICALL
 Java_com_google_sample_oboe_echo_EchoEngine_setAPI(JNIEnv *env, jclass type, jint apiType) {
   if (engine == nullptr) {
diff --git a/samples/echo/src/main/res/layout/activity_main.xml b/samples/echo/src/main/res/layout/activity_main.xml
index cbfc51f..de5b51b 100644
--- a/samples/echo/src/main/res/layout/activity_main.xml
+++ b/samples/echo/src/main/res/layout/activity_main.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-  ~ Copyright (C) 2017 The Android Open Source Project
+  ~ Copyright (C) 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.