From ed355a03f8ffaeb1f518598fa32f72e32fc4df59 Mon Sep 17 00:00:00 2001 From: Steve Lascos Date: Fri, 19 Jan 2018 14:34:32 -0500 Subject: [PATCH] incremental cleanup of LibBasicFunction --- src/AudioEffectAnalogDelay.cpp | 24 +----- src/LibBasicFunctions.cpp | 26 +++--- src/LibBasicFunctions.h | 145 +++++++++++++++++++++++++-------- 3 files changed, 129 insertions(+), 66 deletions(-) diff --git a/src/AudioEffectAnalogDelay.cpp b/src/AudioEffectAnalogDelay.cpp index e08ecf7..2298d1c 100644 --- a/src/AudioEffectAnalogDelay.cpp +++ b/src/AudioEffectAnalogDelay.cpp @@ -47,15 +47,6 @@ void AudioEffectAnalogDelay::update(void) { audio_block_t *inputAudioBlock = receiveReadOnly(); // get the next block of input samples - if (!inputAudioBlock) { - return; - } -// else { -// transmit(inputAudioBlock, 0); -// release(inputAudioBlock); -// return; -// } - if (m_callCount < 1024) { if (inputAudioBlock) { transmit(inputAudioBlock, 0); @@ -64,15 +55,14 @@ void AudioEffectAnalogDelay::update(void) m_callCount++; return; } -// else if (m_callCount > 1400) { -// if (inputAudioBlock) release(inputAudioBlock); -// return; -// } + m_callCount++; Serial.println(String("AudioEffectAnalgDelay::update: ") + m_callCount); - //m_memory->getSlot()->printStatus(); audio_block_t *blockToRelease = m_memory->addBlock(inputAudioBlock); + if (blockToRelease) release(blockToRelease); + + Serial.print("Active channels: "); Serial.print(m_activeChannels, HEX); Serial.println(""); // if (inputAudioBlock) { // transmit(inputAudioBlock, 0); @@ -80,12 +70,6 @@ void AudioEffectAnalogDelay::update(void) // } // return; - if (blockToRelease) release(blockToRelease); - - - Serial.print("Active channels: "); Serial.print(m_activeChannels, HEX); Serial.println(""); - - // For each active channel, output the delayed audio audio_block_t *blockToOutput = nullptr; for (int channel=0; channelsize() >= m_ringBuffer->max_size() ) { // pop before adding @@ -97,26 +98,25 @@ audio_block_t* AudioDelay::addBlock(audio_block_t *block) } } - return block; - + blockToRelease = block; } - + return blockToRelease; } audio_block_t* AudioDelay::getBlock(size_t index) { - if (m_type == MemType::MEM_INTERNAL) { - return m_ringBuffer->at(m_ringBuffer->get_index_from_back(index)); - } else { - return nullptr; + audio_block_t *ret = nullptr; + if (m_type == (MemType::MEM_INTERNAL)) { + ret = m_ringBuffer->at(m_ringBuffer->get_index_from_back(index)); } + return ret; } bool AudioDelay::getSamples(audio_block_t *dest, size_t offset, size_t numSamples) { if (!dest) return false; - if (m_type == MemType::MEM_INTERNAL) { + if (m_type == (MemType::MEM_INTERNAL)) { QueuePosition position = calcQueuePosition(offset); size_t index = position.index; diff --git a/src/LibBasicFunctions.h b/src/LibBasicFunctions.h index 139625e..9ff8a1d 100644 --- a/src/LibBasicFunctions.h +++ b/src/LibBasicFunctions.h @@ -1,9 +1,24 @@ -/* - * LibBasicFunctions.h +/**************************************************************************//** + * @file + * @author Steve Lascos + * @company Blackaddr Audio * - * Created on: Dec 23, 2017 - * Author: slascos - */ + * LibBasicFunctions is a collection of helpful functions and classes that make + * it easier to perform common tasks in Audio applications. + * + * @copyright This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version.* + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + *****************************************************************************/ #include #include @@ -13,60 +28,131 @@ #include "LibMemoryManagement.h" -#ifndef SRC_LIBBASICFUNCTIONS_H_ -#define SRC_LIBBASICFUNCTIONS_H_ +#ifndef __LIBBASICFUNCTIONS_H +#define __LIBBASICFUNCTIONS_H namespace BAGuitar { +/**************************************************************************//** + * QueuePosition is used for storing the index (in an array of queues) and the + * offset within an audio_block_t data buffer. Useful for dealing with large + * windows of audio spread across multiple audio data blocks. + *****************************************************************************/ struct QueuePosition { - int offset; - int index; + int offset; ///< offset in samples within an audio_block_t data buffer + int index; ///< index in an array of audio data blocks }; + +/// Calculate the exact sample position in an array of audio blocks that corresponds +/// to a particular offset given as time. +/// @param milliseconds length of the interval in milliseconds +/// @returns a struct containing the index and offset QueuePosition calcQueuePosition(float milliseconds); + +/// Calculate the exact sample position in an array of audio blocks that corresponds +/// to a particular offset given as a number of samples +/// @param milliseconds length of the interval in milliseconds +/// @returns a struct containing the index and offset QueuePosition calcQueuePosition(size_t numSamples); -size_t calcAudioSamples(float milliseconds); -size_t calcOffset(QueuePosition position); -template -class RingBuffer; // forward declare +/// Calculate the number of audio samples (rounded up) that correspond to a +/// given length of time. +/// @param milliseconds length of the interval in milliseconds +/// @returns the number of corresonding audio samples. +size_t calcAudioSamples(float milliseconds); -enum class MemType : unsigned { - MEM_INTERNAL = 0, - MEM_EXTERNAL -}; +/// Calculate the number of audio samples (usually an offset) from +/// a queue position. +/// @param position specifies the index and offset within a queue +/// @returns the number of samples from the start of the queue array to the +/// specified position. +size_t calcOffset(QueuePosition position); -struct INTERNAL_MEMORY {}; -struct EXTERNAL_MEMORY {}; +template +class RingBuffer; // forward declare so AudioDelay can use it. + + +/**************************************************************************//** + * Audio delays are a very common function in audio processing. In addition to + * being used for simply create a delay effect, it can also be used for buffering + * a sliding window in time of audio samples. This is useful when combining + * several audio_block_t data buffers together to form one large buffer for + * FFTs, etc. + * @details The buffer works like a queue. You add new audio_block_t when available, + * and the class will return an old buffer when it is to be discarded from the queue.
+ * Note that using INTERNAL memory means the class will only store a queue + * of pointers to audio_block_t buffers, since the Teensy Audio uses a shared memory + * approach. When using EXTERNAL memory, data is actually copyied to/from an external + * SRAM device. + *****************************************************************************/ class AudioDelay { public: AudioDelay() = delete; + + /// Construct an audio buffer using INTERNAL memory by specifying the max number + /// of audio samples you will want. + /// @param maxSamples equal or greater than your longest delay requirement AudioDelay(size_t maxSamples); + + /// Construct an audio buffer using INTERNAL memory by specifying the max amount of + /// time you will want available in the buffer. + /// @param maxDelayTimeMs max length of time you want in the buffer specified in milliseconds AudioDelay(float maxDelayTimeMs); + + /// Construct an audio buffer using a slot configured with the BAGuitar::ExternalSramManager + /// @param slot a pointer to the slot representing the memory you wish to use for the buffer. AudioDelay(ExtMemSlot *slot); + ~AudioDelay(); - // Internal memory member functions + /// Add a new audio block into the buffer. When the buffer is filled, + /// adding a new block will push out the oldest once which is returned. + /// @param blockIn pointer to the most recent block of audio + /// @returns the buffer to be discarded, or nullptr if not filled (INTERNAL), or + /// not applicable (EXTERNAL). audio_block_t *addBlock(audio_block_t *blockIn); + + /// When using INTERNAL memory, returns the pointer for the specified index into buffer. + /// @details, the most recent block is 0, 2nd most recent is 1, ..., etc. + /// @param index the specifies how many buffers older than the current to retrieve + /// @returns a pointer to the requested audio_block_t audio_block_t *getBlock(size_t index); + + /// Retrieve an audio block (or samples) from the buffer. + /// @details when using INTERNAL memory, only supported size is AUDIO_BLOCK_SAMPLES. When using + /// EXTERNAL, a size smaller than AUDIO_BLOCK_SAMPLES can be requested. + /// @param dest pointer to the target audio block to write the samples to. + /// @param offset data will start being transferred offset samples from the start of the audio buffer + /// @param numSamples default value is AUDIO_BLOCK_SAMPLES, so typically you don't have to specify this parameter. + /// @returns true on success, false on error. bool getSamples(audio_block_t *dest, size_t offset, size_t numSamples = AUDIO_BLOCK_SAMPLES); - // External memory member functions + /// When using EXTERNAL memory, this function can return a pointer to the underlying ExtMemSlot object associated + /// with the buffer. + /// @returns pointer to the underlying ExtMemSlot. ExtMemSlot *getSlot() const { return m_slot; } private: - MemType m_type; - RingBuffer *m_ringBuffer = nullptr; - ExtMemSlot *m_slot = nullptr; + + /// enumerates whether the underlying memory buffer uses INTERNAL or EXTERNAL memory + enum class MemType : unsigned { + MEM_INTERNAL = 0, ///< internal audio_block_t from the Teensy Audio Library is used + MEM_EXTERNAL ///< external SPI based ram is used + }; + + MemType m_type; ///< when 0, INTERNAL memory, when 1, external MEMORY. + RingBuffer *m_ringBuffer = nullptr; ///< When using INTERNAL memory, a RingBuffer will be created. + ExtMemSlot *m_slot = nullptr; ///< When using EXTERNAL memory, an ExtMemSlot must be provided. }; + template class RingBuffer { public: RingBuffer() = delete; RingBuffer(const size_t maxSize) : m_maxSize(maxSize) { m_buffer = new T[maxSize]; - //Serial.println(String("New RingBuffer: max size is ") + m_maxSize); } virtual ~RingBuffer(){ if (m_buffer) delete [] m_buffer; @@ -78,7 +164,6 @@ public: if ( (m_head == m_tail) && (m_size > 0) ) { // overflow Serial.println("RingBuffer::push_back: overflow"); - while(1) {} // TODO REMOVE return -1; } @@ -89,7 +174,6 @@ public: m_head = 0; } m_size++; - //Serial.println(" ...Done push"); return 0; } @@ -127,15 +211,10 @@ public: idx -= m_maxSize; } - //Serial.println(String("BackIndex is ") + idx + String(" address: ") + (uint32_t)m_buffer[idx] + String(" data: ") + (uint32_t)m_buffer[idx]->data); -// for (int i=0; idata); -// } return idx; } size_t size() const { - //Serial.println(String("RingBuffer::size: ") + m_head + String(":") + m_tail + String(":") + m_size); return m_size; } @@ -167,4 +246,4 @@ private: } -#endif /* SRC_LIBBASICFUNCTIONS_H_ */ +#endif /* __LIBBASICFUNCTIONS_H */