|
|
@ -1,9 +1,24 @@ |
|
|
|
/*
|
|
|
|
/**************************************************************************//**
|
|
|
|
* LibBasicFunctions.h |
|
|
|
* @file |
|
|
|
|
|
|
|
* @author Steve Lascos |
|
|
|
|
|
|
|
* @company Blackaddr Audio |
|
|
|
* |
|
|
|
* |
|
|
|
* Created on: Dec 23, 2017 |
|
|
|
* LibBasicFunctions is a collection of helpful functions and classes that make |
|
|
|
* Author: slascos |
|
|
|
* 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 <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
*****************************************************************************/ |
|
|
|
|
|
|
|
|
|
|
|
#include <cstddef> |
|
|
|
#include <cstddef> |
|
|
|
#include <new> |
|
|
|
#include <new> |
|
|
@ -13,60 +28,131 @@ |
|
|
|
|
|
|
|
|
|
|
|
#include "LibMemoryManagement.h" |
|
|
|
#include "LibMemoryManagement.h" |
|
|
|
|
|
|
|
|
|
|
|
#ifndef SRC_LIBBASICFUNCTIONS_H_ |
|
|
|
#ifndef __LIBBASICFUNCTIONS_H |
|
|
|
#define SRC_LIBBASICFUNCTIONS_H_ |
|
|
|
#define __LIBBASICFUNCTIONS_H |
|
|
|
|
|
|
|
|
|
|
|
namespace BAGuitar { |
|
|
|
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 { |
|
|
|
struct QueuePosition { |
|
|
|
int offset; |
|
|
|
int offset; ///< offset in samples within an audio_block_t data buffer
|
|
|
|
int index; |
|
|
|
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); |
|
|
|
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); |
|
|
|
QueuePosition calcQueuePosition(size_t numSamples); |
|
|
|
size_t calcAudioSamples(float milliseconds); |
|
|
|
|
|
|
|
size_t calcOffset(QueuePosition position); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
template <class T> |
|
|
|
/// Calculate the number of audio samples (rounded up) that correspond to a
|
|
|
|
class RingBuffer; // forward declare
|
|
|
|
/// 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 { |
|
|
|
/// Calculate the number of audio samples (usually an offset) from
|
|
|
|
MEM_INTERNAL = 0, |
|
|
|
/// a queue position.
|
|
|
|
MEM_EXTERNAL |
|
|
|
/// @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 T> |
|
|
|
|
|
|
|
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.<br> |
|
|
|
|
|
|
|
* 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 { |
|
|
|
class AudioDelay { |
|
|
|
public: |
|
|
|
public: |
|
|
|
AudioDelay() = delete; |
|
|
|
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); |
|
|
|
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); |
|
|
|
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(ExtMemSlot *slot); |
|
|
|
|
|
|
|
|
|
|
|
~AudioDelay(); |
|
|
|
~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); |
|
|
|
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); |
|
|
|
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); |
|
|
|
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; } |
|
|
|
ExtMemSlot *getSlot() const { return m_slot; } |
|
|
|
|
|
|
|
|
|
|
|
private: |
|
|
|
private: |
|
|
|
MemType m_type; |
|
|
|
|
|
|
|
RingBuffer<audio_block_t *> *m_ringBuffer = nullptr; |
|
|
|
/// enumerates whether the underlying memory buffer uses INTERNAL or EXTERNAL memory
|
|
|
|
ExtMemSlot *m_slot = nullptr; |
|
|
|
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<audio_block_t *> *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 T> |
|
|
|
template <class T> |
|
|
|
class RingBuffer { |
|
|
|
class RingBuffer { |
|
|
|
public: |
|
|
|
public: |
|
|
|
RingBuffer() = delete; |
|
|
|
RingBuffer() = delete; |
|
|
|
RingBuffer(const size_t maxSize) : m_maxSize(maxSize) { |
|
|
|
RingBuffer(const size_t maxSize) : m_maxSize(maxSize) { |
|
|
|
m_buffer = new T[maxSize]; |
|
|
|
m_buffer = new T[maxSize]; |
|
|
|
//Serial.println(String("New RingBuffer: max size is ") + m_maxSize);
|
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
virtual ~RingBuffer(){ |
|
|
|
virtual ~RingBuffer(){ |
|
|
|
if (m_buffer) delete [] m_buffer; |
|
|
|
if (m_buffer) delete [] m_buffer; |
|
|
@ -78,7 +164,6 @@ public: |
|
|
|
if ( (m_head == m_tail) && (m_size > 0) ) { |
|
|
|
if ( (m_head == m_tail) && (m_size > 0) ) { |
|
|
|
// overflow
|
|
|
|
// overflow
|
|
|
|
Serial.println("RingBuffer::push_back: overflow"); |
|
|
|
Serial.println("RingBuffer::push_back: overflow"); |
|
|
|
while(1) {} // TODO REMOVE
|
|
|
|
|
|
|
|
return -1; |
|
|
|
return -1; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -89,7 +174,6 @@ public: |
|
|
|
m_head = 0; |
|
|
|
m_head = 0; |
|
|
|
} |
|
|
|
} |
|
|
|
m_size++; |
|
|
|
m_size++; |
|
|
|
//Serial.println(" ...Done push");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return 0; |
|
|
|
return 0; |
|
|
|
} |
|
|
|
} |
|
|
@ -127,15 +211,10 @@ public: |
|
|
|
idx -= m_maxSize; |
|
|
|
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; i<m_maxSize; i++) {
|
|
|
|
|
|
|
|
// Serial.println(i + String(":") + (uint32_t)m_buffer[i]->data);
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
return idx; |
|
|
|
return idx; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
size_t size() const { |
|
|
|
size_t size() const { |
|
|
|
//Serial.println(String("RingBuffer::size: ") + m_head + String(":") + m_tail + String(":") + m_size);
|
|
|
|
|
|
|
|
return m_size; |
|
|
|
return m_size; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -167,4 +246,4 @@ private: |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#endif /* SRC_LIBBASICFUNCTIONS_H_ */ |
|
|
|
#endif /* __LIBBASICFUNCTIONS_H */ |
|
|
|