diff --git a/libraries/audio/src/AudioRingBuffer.cpp b/libraries/audio/src/AudioRingBuffer.cpp
index ab0948e328..4f64d4a4b0 100644
--- a/libraries/audio/src/AudioRingBuffer.cpp
+++ b/libraries/audio/src/AudioRingBuffer.cpp
@@ -26,8 +26,8 @@
 static const QString RING_BUFFER_OVERFLOW_DEBUG { "AudioRingBuffer::writeData has overflown the buffer. Overwriting old data." };
 static const QString DROPPED_SILENT_DEBUG { "AudioRingBuffer::addSilentSamples dropping silent samples to prevent overflow." };
 
-template<>
-AudioRingBufferTemplate<int16_t>::AudioRingBufferTemplate(int numFrameSamples, int numFramesCapacity) :
+template <class T>
+AudioRingBufferTemplate<T>::AudioRingBufferTemplate(int numFrameSamples, int numFramesCapacity) :
     _numFrameSamples(numFrameSamples),
     _frameCapacity(numFramesCapacity),
     _sampleCapacity(numFrameSamples * numFramesCapacity),
@@ -44,25 +44,25 @@ AudioRingBufferTemplate<int16_t>::AudioRingBufferTemplate(int numFrameSamples, i
     static QString repeatedDroppedMessage = LogHandler::getInstance().addRepeatedMessageRegex(DROPPED_SILENT_DEBUG);
 }
 
-template<>
-AudioRingBufferTemplate<int16_t>::~AudioRingBufferTemplate() {
+template <class T>
+AudioRingBufferTemplate<T>::~AudioRingBufferTemplate() {
     delete[] _buffer;
 }
 
-template<>
-void AudioRingBufferTemplate<int16_t>::clear() {
+template <class T>
+void AudioRingBufferTemplate<T>::clear() {
     _endOfLastWrite = _buffer;
     _nextOutput = _buffer;
 }
 
-template<>
-void AudioRingBufferTemplate<int16_t>::reset() {
+template <class T>
+void AudioRingBufferTemplate<T>::reset() {
     clear();
     _overflowCount = 0;
 }
 
-template<>
-void AudioRingBufferTemplate<int16_t>::resizeForFrameSize(int numFrameSamples) {
+template <class T>
+void AudioRingBufferTemplate<T>::resizeForFrameSize(int numFrameSamples) {
     delete[] _buffer;
     _numFrameSamples = numFrameSamples;
     _sampleCapacity = numFrameSamples * _frameCapacity;
@@ -78,255 +78,13 @@ void AudioRingBufferTemplate<int16_t>::resizeForFrameSize(int numFrameSamples) {
     reset();
 }
 
-template<>
-int AudioRingBufferTemplate<int16_t>::readSamples(Sample* destination, int maxSamples) {
+template <class T>
+int AudioRingBufferTemplate<T>::readSamples(Sample* destination, int maxSamples) {
     return readData((char*)destination, maxSamples * SampleSize) / SampleSize;
 }
 
-template<>
-int AudioRingBufferTemplate<int16_t>::writeSamples(const Sample* source, int maxSamples) {
-    return writeData((char*)source, maxSamples * SampleSize) / SampleSize;
-}
-
-template<>
-int AudioRingBufferTemplate<int16_t>::readData(char *data, int maxSize) {
-    // only copy up to the number of samples we have available
-    int maxSamples = maxSize / SampleSize;
-    int numReadSamples = std::min(maxSamples, samplesAvailable());
-
-    if (_nextOutput + numReadSamples > _buffer + _bufferLength) {
-        // we're going to need to do two reads to get this data, it wraps around the edge
-        int numSamplesToEnd = (_buffer + _bufferLength) - _nextOutput;
-
-        // read to the end of the buffer
-        memcpy(data, _nextOutput, numSamplesToEnd * SampleSize);
-
-        // read the rest from the beginning of the buffer
-        memcpy(data + (numSamplesToEnd * SampleSize), _buffer, (numReadSamples - numSamplesToEnd) * SampleSize);
-    } else {
-        memcpy(data, _nextOutput, numReadSamples * SampleSize);
-    }
-
-    shiftReadPosition(numReadSamples);
-
-    return numReadSamples * SampleSize;
-}
-
-template<>
-int AudioRingBufferTemplate<int16_t>::writeData(const char* data, int maxSize) {
-    // only copy up to the number of samples we have capacity for
-    int maxSamples = maxSize / SampleSize;
-    int numWriteSamples = std::min(maxSamples, _sampleCapacity);
-    int samplesRoomFor = _sampleCapacity - samplesAvailable();
-
-    if (numWriteSamples > samplesRoomFor) {
-        // there's not enough room for this write. erase old data to make room for this new data
-        int samplesToDelete = numWriteSamples - samplesRoomFor;
-        _nextOutput = shiftedPositionAccomodatingWrap(_nextOutput, samplesToDelete);
-        _overflowCount++;
-
-        qCDebug(audio) << qPrintable(RING_BUFFER_OVERFLOW_DEBUG);
-    }
-
-    if (_endOfLastWrite + numWriteSamples > _buffer + _bufferLength) {
-        // we're going to need to do two writes to set this data, it wraps around the edge
-        int numSamplesToEnd = (_buffer + _bufferLength) - _endOfLastWrite;
-
-        // write to the end of the buffer
-        memcpy(_endOfLastWrite, data, numSamplesToEnd * SampleSize);
-
-        // write the rest to the beginning of the buffer
-        memcpy(_buffer, data + (numSamplesToEnd * SampleSize), (numWriteSamples - numSamplesToEnd) * SampleSize);
-    } else {
-        memcpy(_endOfLastWrite, data, numWriteSamples * SampleSize);
-    }
-
-    _endOfLastWrite = shiftedPositionAccomodatingWrap(_endOfLastWrite, numWriteSamples);
-
-    return numWriteSamples * SampleSize;
-}
-
-template<>
-int AudioRingBufferTemplate<int16_t>::samplesAvailable() const {
-    if (!_endOfLastWrite) {
-        return 0;
-    }
-
-    int sampleDifference = _endOfLastWrite - _nextOutput;
-    if (sampleDifference < 0) {
-        sampleDifference += _bufferLength;
-    }
-    return sampleDifference;
-}
-
-template<>
-int16_t* AudioRingBufferTemplate<int16_t>::shiftedPositionAccomodatingWrap(Sample* position, int numSamplesShift) const {
-    // NOTE: It is possible to shift out-of-bounds if (|numSamplesShift| > 2 * _bufferLength), but this should not occur
-    if (numSamplesShift > 0 && position + numSamplesShift >= _buffer + _bufferLength) {
-        // this shift will wrap the position around to the beginning of the ring
-        return position + numSamplesShift - _bufferLength;
-    } else if (numSamplesShift < 0 && position + numSamplesShift < _buffer) {
-        // this shift will go around to the end of the ring
-        return position + numSamplesShift + _bufferLength;
-    } else {
-        return position + numSamplesShift;
-    }
-}
-
-template<>
-int AudioRingBufferTemplate<int16_t>::addSilentSamples(int silentSamples) {
-    // NOTE: This implementation is nearly identical to writeData save for s/memcpy/memset, refer to comments there
-    int numWriteSamples = std::min(silentSamples, _sampleCapacity);
-    int samplesRoomFor = _sampleCapacity - samplesAvailable();
-
-    if (numWriteSamples > samplesRoomFor) {
-        numWriteSamples = samplesRoomFor;
-
-        qCDebug(audio) << qPrintable(DROPPED_SILENT_DEBUG);
-    }
-
-    if (_endOfLastWrite + numWriteSamples > _buffer + _bufferLength) {
-        int numSamplesToEnd = (_buffer + _bufferLength) - _endOfLastWrite;
-        memset(_endOfLastWrite, 0, numSamplesToEnd * SampleSize);
-        memset(_buffer, 0, (numWriteSamples - numSamplesToEnd) * SampleSize);
-    } else {
-        memset(_endOfLastWrite, 0, numWriteSamples * SampleSize);
-    }
-
-    _endOfLastWrite = shiftedPositionAccomodatingWrap(_endOfLastWrite, numWriteSamples);
-
-    return numWriteSamples;
-}
-
-template<>
-float AudioRingBufferTemplate<int16_t>::getFrameLoudness(const Sample* frameStart) const {
-    // FIXME: This is a bad measure of loudness - normal estimation uses sqrt(sum(x*x))
-    float loudness = 0.0f;
-    const Sample* sampleAt = frameStart;
-    const Sample* bufferLastAt = _buffer + _bufferLength - 1;
-
-    for (int i = 0; i < _numFrameSamples; ++i) {
-        loudness += (float) std::abs(*sampleAt);
-        // wrap if necessary
-        sampleAt = sampleAt == bufferLastAt ? _buffer : sampleAt + 1;
-    }
-    loudness /= _numFrameSamples;
-    loudness /= AudioConstants::MAX_SAMPLE_VALUE;
-
-    return loudness;
-}
-
-template<>
-float AudioRingBufferTemplate<int16_t>::getFrameLoudness(ConstIterator frameStart) const {
-    if (frameStart.isNull()) {
-        return 0.0f;
-    }
-    return getFrameLoudness(&(*frameStart));
-}
-
-template<>
-int AudioRingBufferTemplate<int16_t>::writeSamples(ConstIterator source, int maxSamples) {
-    int samplesToCopy = std::min(maxSamples, _sampleCapacity);
-    int samplesRoomFor = _sampleCapacity - samplesAvailable();
-    if (samplesToCopy > samplesRoomFor) {
-        // there's not enough room for this write.  erase old data to make room for this new data
-        int samplesToDelete = samplesToCopy - samplesRoomFor;
-        _nextOutput = shiftedPositionAccomodatingWrap(_nextOutput, samplesToDelete);
-        _overflowCount++;
-        qCDebug(audio) << qPrintable(RING_BUFFER_OVERFLOW_DEBUG);
-    }
-
-    Sample* bufferLast = _buffer + _bufferLength - 1;
-    for (int i = 0; i < samplesToCopy; i++) {
-        *_endOfLastWrite = *source;
-        _endOfLastWrite = (_endOfLastWrite == bufferLast) ? _buffer : _endOfLastWrite + 1;
-        ++source;
-    }
-
-    return samplesToCopy;
-}
-
-template<>
-int AudioRingBufferTemplate<int16_t>::writeSamplesWithFade(ConstIterator source, int maxSamples, float fade) {
-    int samplesToCopy = std::min(maxSamples, _sampleCapacity);
-    int samplesRoomFor = _sampleCapacity - samplesAvailable();
-    if (samplesToCopy > samplesRoomFor) {
-        // there's not enough room for this write.  erase old data to make room for this new data
-        int samplesToDelete = samplesToCopy - samplesRoomFor;
-        _nextOutput = shiftedPositionAccomodatingWrap(_nextOutput, samplesToDelete);
-        _overflowCount++;
-        qCDebug(audio) << qPrintable(RING_BUFFER_OVERFLOW_DEBUG);
-    }
-
-    Sample* bufferLast = _buffer + _bufferLength - 1;
-    for (int i = 0; i < samplesToCopy; i++) {
-        *_endOfLastWrite = (Sample)((float)(*source) * fade);
-        _endOfLastWrite = (_endOfLastWrite == bufferLast) ? _buffer : _endOfLastWrite + 1;
-        ++source;
-    }
-
-    return samplesToCopy;
-}
-
-template<>
-AudioRingBufferTemplate<float>::AudioRingBufferTemplate(int numFrameSamples, int numFramesCapacity) :
-    _numFrameSamples(numFrameSamples),
-    _frameCapacity(numFramesCapacity),
-    _sampleCapacity(numFrameSamples * numFramesCapacity),
-    _bufferLength(numFrameSamples * (numFramesCapacity + 1))
-{
-    if (numFrameSamples) {
-        _buffer = new Sample[_bufferLength];
-        memset(_buffer, 0, _bufferLength * SampleSize);
-        _nextOutput = _buffer;
-        _endOfLastWrite = _buffer;
-    }
-
-    static QString repeatedOverflowMessage = LogHandler::getInstance().addRepeatedMessageRegex(RING_BUFFER_OVERFLOW_DEBUG);
-    static QString repeatedDroppedMessage = LogHandler::getInstance().addRepeatedMessageRegex(DROPPED_SILENT_DEBUG);
-}
-
-template<>
-AudioRingBufferTemplate<float>::~AudioRingBufferTemplate() {
-    delete[] _buffer;
-}
-
-template<>
-void AudioRingBufferTemplate<float>::clear() {
-    _endOfLastWrite = _buffer;
-    _nextOutput = _buffer;
-}
-
-template<>
-void AudioRingBufferTemplate<float>::reset() {
-    clear();
-    _overflowCount = 0;
-}
-
-template<>
-void AudioRingBufferTemplate<float>::resizeForFrameSize(int numFrameSamples) {
-    delete[] _buffer;
-    _numFrameSamples = numFrameSamples;
-    _sampleCapacity = numFrameSamples * _frameCapacity;
-    _bufferLength = numFrameSamples * (_frameCapacity + 1);
-
-    if (numFrameSamples) {
-        _buffer = new Sample[_bufferLength];
-        memset(_buffer, 0, _bufferLength * SampleSize);
-    } else {
-        _buffer = nullptr;
-    }
-
-    reset();
-}
-
-template<>
-int AudioRingBufferTemplate<float>::readSamples(Sample* destination, int maxSamples) {
-    return readData((char*)destination, maxSamples * SampleSize) / SampleSize;
-}
-
-template<>
-int AudioRingBufferTemplate<float>::appendSamples(Sample* destination, int maxSamples, bool append) {
+template <class T>
+int AudioRingBufferTemplate<T>::appendSamples(Sample* destination, int maxSamples, bool append) {
     if (append) {
         return appendData((char*)destination, maxSamples * SampleSize) / SampleSize;
     } else {
@@ -334,13 +92,13 @@ int AudioRingBufferTemplate<float>::appendSamples(Sample* destination, int maxSa
     }
 }
 
-template<>
-int AudioRingBufferTemplate<float>::writeSamples(const Sample* source, int maxSamples) {
+template <class T>
+int AudioRingBufferTemplate<T>::writeSamples(const Sample* source, int maxSamples) {
     return writeData((char*)source, maxSamples * SampleSize) / SampleSize;
 }
 
-template<>
-int AudioRingBufferTemplate<float>::readData(char *data, int maxSize) {
+template <class T>
+int AudioRingBufferTemplate<T>::readData(char *data, int maxSize) {
     // only copy up to the number of samples we have available
     int maxSamples = maxSize / SampleSize;
     int numReadSamples = std::min(maxSamples, samplesAvailable());
@@ -363,8 +121,8 @@ int AudioRingBufferTemplate<float>::readData(char *data, int maxSize) {
     return numReadSamples * SampleSize;
 }
 
-template<>
-int AudioRingBufferTemplate<float>::appendData(char *data, int maxSize) {
+template <class T>
+int AudioRingBufferTemplate<T>::appendData(char *data, int maxSize) {
     // only copy up to the number of samples we have available
     int maxSamples = maxSize / SampleSize;
     int numReadSamples = std::min(maxSamples, samplesAvailable());
@@ -396,8 +154,8 @@ int AudioRingBufferTemplate<float>::appendData(char *data, int maxSize) {
     return numReadSamples * SampleSize;
 }
 
-template<>
-int AudioRingBufferTemplate<float>::writeData(const char* data, int maxSize) {
+template <class T>
+int AudioRingBufferTemplate<T>::writeData(const char* data, int maxSize) {
     // only copy up to the number of samples we have capacity for
     int maxSamples = maxSize / SampleSize;
     int numWriteSamples = std::min(maxSamples, _sampleCapacity);
@@ -430,8 +188,8 @@ int AudioRingBufferTemplate<float>::writeData(const char* data, int maxSize) {
     return numWriteSamples * SampleSize;
 }
 
-template<>
-int AudioRingBufferTemplate<float>::samplesAvailable() const {
+template <class T>
+int AudioRingBufferTemplate<T>::samplesAvailable() const {
     if (!_endOfLastWrite) {
         return 0;
     }
@@ -443,8 +201,8 @@ int AudioRingBufferTemplate<float>::samplesAvailable() const {
     return sampleDifference;
 }
 
-template<>
-float* AudioRingBufferTemplate<float>::shiftedPositionAccomodatingWrap(Sample* position, int numSamplesShift) const {
+template <class T>
+typename AudioRingBufferTemplate<T>::Sample* AudioRingBufferTemplate<T>::shiftedPositionAccomodatingWrap(Sample* position, int numSamplesShift) const {
     // NOTE: It is possible to shift out-of-bounds if (|numSamplesShift| > 2 * _bufferLength), but this should not occur
     if (numSamplesShift > 0 && position + numSamplesShift >= _buffer + _bufferLength) {
         // this shift will wrap the position around to the beginning of the ring
@@ -455,4 +213,103 @@ float* AudioRingBufferTemplate<float>::shiftedPositionAccomodatingWrap(Sample* p
     } else {
         return position + numSamplesShift;
     }
-}
\ No newline at end of file
+}
+
+template <class T>
+int AudioRingBufferTemplate<T>::addSilentSamples(int silentSamples) {
+    // NOTE: This implementation is nearly identical to writeData save for s/memcpy/memset, refer to comments there
+    int numWriteSamples = std::min(silentSamples, _sampleCapacity);
+    int samplesRoomFor = _sampleCapacity - samplesAvailable();
+
+    if (numWriteSamples > samplesRoomFor) {
+        numWriteSamples = samplesRoomFor;
+
+        qCDebug(audio) << qPrintable(DROPPED_SILENT_DEBUG);
+    }
+
+    if (_endOfLastWrite + numWriteSamples > _buffer + _bufferLength) {
+        int numSamplesToEnd = (_buffer + _bufferLength) - _endOfLastWrite;
+        memset(_endOfLastWrite, 0, numSamplesToEnd * SampleSize);
+        memset(_buffer, 0, (numWriteSamples - numSamplesToEnd) * SampleSize);
+    } else {
+        memset(_endOfLastWrite, 0, numWriteSamples * SampleSize);
+    }
+
+    _endOfLastWrite = shiftedPositionAccomodatingWrap(_endOfLastWrite, numWriteSamples);
+
+    return numWriteSamples;
+}
+
+template <class T>
+float AudioRingBufferTemplate<T>::getFrameLoudness(const Sample* frameStart) const {
+    // FIXME: This is a bad measure of loudness - normal estimation uses sqrt(sum(x*x))
+    float loudness = 0.0f;
+    const Sample* sampleAt = frameStart;
+    const Sample* bufferLastAt = _buffer + _bufferLength - 1;
+
+    for (int i = 0; i < _numFrameSamples; ++i) {
+        loudness += (float) std::abs(*sampleAt);
+        // wrap if necessary
+        sampleAt = sampleAt == bufferLastAt ? _buffer : sampleAt + 1;
+    }
+    loudness /= _numFrameSamples;
+    loudness /= AudioConstants::MAX_SAMPLE_VALUE;
+
+    return loudness;
+}
+
+template <class T>
+float AudioRingBufferTemplate<T>::getFrameLoudness(ConstIterator frameStart) const {
+    if (frameStart.isNull()) {
+        return 0.0f;
+    }
+    return getFrameLoudness(&(*frameStart));
+}
+
+template <class T>
+int AudioRingBufferTemplate<T>::writeSamples(ConstIterator source, int maxSamples) {
+    int samplesToCopy = std::min(maxSamples, _sampleCapacity);
+    int samplesRoomFor = _sampleCapacity - samplesAvailable();
+    if (samplesToCopy > samplesRoomFor) {
+        // there's not enough room for this write.  erase old data to make room for this new data
+        int samplesToDelete = samplesToCopy - samplesRoomFor;
+        _nextOutput = shiftedPositionAccomodatingWrap(_nextOutput, samplesToDelete);
+        _overflowCount++;
+        qCDebug(audio) << qPrintable(RING_BUFFER_OVERFLOW_DEBUG);
+    }
+
+    Sample* bufferLast = _buffer + _bufferLength - 1;
+    for (int i = 0; i < samplesToCopy; i++) {
+        *_endOfLastWrite = *source;
+        _endOfLastWrite = (_endOfLastWrite == bufferLast) ? _buffer : _endOfLastWrite + 1;
+        ++source;
+    }
+
+    return samplesToCopy;
+}
+
+template <class T>
+int AudioRingBufferTemplate<T>::writeSamplesWithFade(ConstIterator source, int maxSamples, float fade) {
+    int samplesToCopy = std::min(maxSamples, _sampleCapacity);
+    int samplesRoomFor = _sampleCapacity - samplesAvailable();
+    if (samplesToCopy > samplesRoomFor) {
+        // there's not enough room for this write.  erase old data to make room for this new data
+        int samplesToDelete = samplesToCopy - samplesRoomFor;
+        _nextOutput = shiftedPositionAccomodatingWrap(_nextOutput, samplesToDelete);
+        _overflowCount++;
+        qCDebug(audio) << qPrintable(RING_BUFFER_OVERFLOW_DEBUG);
+    }
+
+    Sample* bufferLast = _buffer + _bufferLength - 1;
+    for (int i = 0; i < samplesToCopy; i++) {
+        *_endOfLastWrite = (Sample)((float)(*source) * fade);
+        _endOfLastWrite = (_endOfLastWrite == bufferLast) ? _buffer : _endOfLastWrite + 1;
+        ++source;
+    }
+
+    return samplesToCopy;
+}
+
+// explicit instantiations for scratch/mix buffers
+template class AudioRingBufferTemplate<int16_t>;
+template class AudioRingBufferTemplate<float>;
diff --git a/libraries/audio/src/AudioRingBuffer.h b/libraries/audio/src/AudioRingBuffer.h
index 92c6dcc336..0208c8686e 100644
--- a/libraries/audio/src/AudioRingBuffer.h
+++ b/libraries/audio/src/AudioRingBuffer.h
@@ -100,8 +100,16 @@ public:
 
     class ConstIterator {
     public:
-        ConstIterator();
-        ConstIterator(Sample* bufferFirst, int capacity, Sample* at);
+        ConstIterator() :
+            _bufferLength(0),
+            _bufferFirst(NULL),
+            _bufferLast(NULL),
+            _at(NULL) {}
+        ConstIterator(Sample* bufferFirst, int capacity, Sample* at) :
+            _bufferLength(capacity),
+            _bufferFirst(bufferFirst),
+            _bufferLast(bufferFirst + capacity - 1),
+            _at(at) {}
         ConstIterator(const ConstIterator& rhs) = default;
 
         bool isNull() const { return _at == NULL; }
@@ -110,22 +118,73 @@ public:
         bool operator!=(const ConstIterator& rhs) { return _at != rhs._at; }
         const Sample& operator*() { return *_at; }
 
-        ConstIterator& operator=(const ConstIterator& rhs);
-        ConstIterator& operator++();
-        ConstIterator operator++(int);
-        ConstIterator& operator--();
-        ConstIterator operator--(int);
-        const Sample& operator[] (int i);
-        ConstIterator operator+(int i);
-        ConstIterator operator-(int i);
+        ConstIterator& operator=(const ConstIterator& rhs) {
+            _bufferLength = rhs._bufferLength;
+            _bufferFirst = rhs._bufferFirst;
+            _bufferLast = rhs._bufferLast;
+            _at = rhs._at;
+            return *this;
+        }
+        ConstIterator& operator++() {
+            _at = (_at == _bufferLast) ? _bufferFirst : _at + 1;
+            return *this;
+        }
+        ConstIterator operator++(int) {
+            ConstIterator tmp(*this);
+            ++(*this);
+            return tmp;
+        }
+        ConstIterator& operator--() {
+            _at = (_at == _bufferFirst) ? _bufferLast : _at - 1;
+            return *this;
+        }
+        ConstIterator operator--(int) {
+            ConstIterator tmp(*this);
+            --(*this);
+            return tmp;
+        }
+        const Sample& operator[] (int i) {
+            return *atShiftedBy(i);
+        }
+        ConstIterator operator+(int i) {
+            return ConstIterator(_bufferFirst, _bufferLength, atShiftedBy(i));
+        }
+        ConstIterator operator-(int i) {
+            return ConstIterator(_bufferFirst, _bufferLength, atShiftedBy(-i));
+        }
+
+        void readSamples(Sample* dest, int numSamples) {
+            auto samplesToEnd = _bufferLast - _at + 1;
+
+            if (samplesToEnd >= numSamples) {
+                memcpy(dest, _at, numSamples * SampleSize);
+                _at += numSamples;
+            } else {
+                auto samplesFromStart = numSamples - samplesToEnd;
+                memcpy(dest, _at, samplesToEnd * SampleSize);
+                memcpy(dest + samplesToEnd, _bufferFirst, samplesFromStart * SampleSize);
+
+                _at = _bufferFirst + samplesFromStart;
+            }
+        }
+        void readSamplesWithFade(Sample* dest, int numSamples, float fade) {
+            Sample* at = _at;
+            for (int i = 0; i < numSamples; i++) {
+                *dest = (float)*at * fade;
+                ++dest;
+                at = (at == _bufferLast) ? _bufferFirst : at + 1;
+            }
+        }
 
-        void readSamples(Sample* dest, int numSamples);
-        void readSamplesWithFade(Sample* dest, int numSamples, float fade);
-        void readSamplesWithUpmix(Sample* dest, int numSamples, int numExtraChannels);
-        void readSamplesWithDownmix(Sample* dest, int numSamples);
 
     private:
-        Sample* atShiftedBy(int i);
+        Sample* atShiftedBy(int i) {
+            i = (_at - _bufferFirst + i) % _bufferLength;
+            if (i < 0) {
+                i += _bufferLength;
+            }
+            return _bufferFirst + i;
+        }
 
         int _bufferLength;
         Sample* _bufferFirst;
@@ -133,8 +192,12 @@ public:
         Sample* _at;
     };
 
-    ConstIterator nextOutput() const;
-    ConstIterator lastFrameWritten() const;
+    ConstIterator nextOutput() const {
+        return ConstIterator(_buffer, _bufferLength, _nextOutput);
+    }
+    ConstIterator lastFrameWritten() const {
+        return ConstIterator(_buffer, _bufferLength, _endOfLastWrite) - _numFrameSamples;
+    }
 
     int writeSamples(ConstIterator source, int maxSamples);
     int writeSamplesWithFade(ConstIterator source, int maxSamples, float fade);
@@ -156,136 +219,8 @@ protected:
     Sample* _buffer{ nullptr };
 };
 
+// expose explicit instantiations for scratch/mix buffers
 using AudioRingBuffer = AudioRingBufferTemplate<int16_t>;
 using AudioRingMixBuffer = AudioRingBufferTemplate<float>;
 
-// inline the iterator:
-template<> inline AudioRingBufferTemplate<int16_t>::ConstIterator::ConstIterator() :
-    _bufferLength(0),
-    _bufferFirst(NULL),
-    _bufferLast(NULL),
-    _at(NULL) {}
-
-template<> inline AudioRingBufferTemplate<int16_t>::ConstIterator::ConstIterator(Sample* bufferFirst, int capacity, Sample* at) :
-    _bufferLength(capacity),
-    _bufferFirst(bufferFirst),
-    _bufferLast(bufferFirst + capacity - 1),
-    _at(at) {}
-
-template<> inline AudioRingBufferTemplate<int16_t>::ConstIterator& AudioRingBufferTemplate<int16_t>::ConstIterator::operator=(const ConstIterator& rhs) {
-    _bufferLength = rhs._bufferLength;
-    _bufferFirst = rhs._bufferFirst;
-    _bufferLast = rhs._bufferLast;
-    _at = rhs._at;
-    return *this;
-}
-
-template<> inline AudioRingBufferTemplate<int16_t>::ConstIterator& AudioRingBufferTemplate<int16_t>::ConstIterator::operator++() {
-    _at = (_at == _bufferLast) ? _bufferFirst : _at + 1;
-    return *this;
-}
-
-template<> inline AudioRingBufferTemplate<int16_t>::ConstIterator AudioRingBufferTemplate<int16_t>::ConstIterator::operator++(int) {
-    ConstIterator tmp(*this);
-    ++(*this);
-    return tmp;
-}
-
-template<> inline AudioRingBufferTemplate<int16_t>::ConstIterator& AudioRingBufferTemplate<int16_t>::ConstIterator::operator--() {
-    _at = (_at == _bufferFirst) ? _bufferLast : _at - 1;
-    return *this;
-}
-
-template<> inline AudioRingBufferTemplate<int16_t>::ConstIterator AudioRingBufferTemplate<int16_t>::ConstIterator::operator--(int) {
-    ConstIterator tmp(*this);
-    --(*this);
-    return tmp;
-}
-
-template<> inline const int16_t& AudioRingBufferTemplate<int16_t>::ConstIterator::operator[] (int i) {
-    return *atShiftedBy(i);
-}
-
-template<> inline AudioRingBufferTemplate<int16_t>::ConstIterator AudioRingBufferTemplate<int16_t>::ConstIterator::operator+(int i) {
-    return ConstIterator(_bufferFirst, _bufferLength, atShiftedBy(i));
-}
-
-template<> inline AudioRingBufferTemplate<int16_t>::ConstIterator AudioRingBufferTemplate<int16_t>::ConstIterator::operator-(int i) {
-    return ConstIterator(_bufferFirst, _bufferLength, atShiftedBy(-i));
-}
-
-template<> inline int16_t* AudioRingBufferTemplate<int16_t>::ConstIterator::atShiftedBy(int i) {
-    i = (_at - _bufferFirst + i) % _bufferLength;
-    if (i < 0) {
-        i += _bufferLength;
-    }
-    return _bufferFirst + i;
-}
-
-template<> inline void AudioRingBufferTemplate<int16_t>::ConstIterator::readSamples(Sample* dest, int numSamples) {
-    auto samplesToEnd = _bufferLast - _at + 1;
-
-    if (samplesToEnd >= numSamples) {
-        memcpy(dest, _at, numSamples * SampleSize);
-        _at += numSamples;
-    } else {
-        auto samplesFromStart = numSamples - samplesToEnd;
-        memcpy(dest, _at, samplesToEnd * SampleSize);
-        memcpy(dest + samplesToEnd, _bufferFirst, samplesFromStart * SampleSize);
-
-        _at = _bufferFirst + samplesFromStart;
-    }
-}
-
-template<> inline void AudioRingBufferTemplate<int16_t>::ConstIterator::readSamplesWithFade(Sample* dest, int numSamples, float fade) {
-    Sample* at = _at;
-    for (int i = 0; i < numSamples; i++) {
-        *dest = (float)*at * fade;
-        ++dest;
-        at = (at == _bufferLast) ? _bufferFirst : at + 1;
-    }
-}
-
-template<> inline void AudioRingBufferTemplate<int16_t>::ConstIterator::readSamplesWithUpmix(Sample* dest, int numSamples, int numExtraChannels) {
-    Sample* at = _at;
-    for (int i = 0; i < numSamples/2; i++) {
-
-        // read 2 samples
-        Sample left = *at;
-        at = (at == _bufferLast) ? _bufferFirst : at + 1;
-        Sample right = *at;
-        at = (at == _bufferLast) ? _bufferFirst : at + 1;
-
-        // write 2 + N samples
-        *dest++ = left;
-        *dest++ = right;
-        for (int n = 0; n < numExtraChannels; n++) {
-            *dest++ = 0;
-        }
-    }
-}
-
-template<> inline void AudioRingBufferTemplate<int16_t>::ConstIterator::readSamplesWithDownmix(Sample* dest, int numSamples) {
-    Sample* at = _at;
-    for (int i = 0; i < numSamples/2; i++) {
-
-        // read 2 samples
-        Sample left = *at;
-        at = (at == _bufferLast) ? _bufferFirst : at + 1;
-        Sample right = *at;
-        at = (at == _bufferLast) ? _bufferFirst : at + 1;
-
-        // write 1 sample
-        *dest++ = (Sample)((left + right) / 2);
-    }
-}
-
-template<> inline AudioRingBufferTemplate<int16_t>::ConstIterator AudioRingBufferTemplate<int16_t>::nextOutput() const {
-    return ConstIterator(_buffer, _bufferLength, _nextOutput);
-}
-
-template<> inline AudioRingBufferTemplate<int16_t>::ConstIterator AudioRingBufferTemplate<int16_t>::lastFrameWritten() const {
-    return ConstIterator(_buffer, _bufferLength, _endOfLastWrite) - _numFrameSamples;
-}
-
 #endif // hifi_AudioRingBuffer_h