incremental cleanup of LibBasicFunction

pull/1/head
Steve Lascos 7 years ago
parent 7cf4b57bc6
commit ed355a03f8
  1. 24
      src/AudioEffectAnalogDelay.cpp
  2. 26
      src/LibBasicFunctions.cpp
  3. 145
      src/LibBasicFunctions.h

@ -47,15 +47,6 @@ void AudioEffectAnalogDelay::update(void)
{ {
audio_block_t *inputAudioBlock = receiveReadOnly(); // get the next block of input samples 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 (m_callCount < 1024) {
if (inputAudioBlock) { if (inputAudioBlock) {
transmit(inputAudioBlock, 0); transmit(inputAudioBlock, 0);
@ -64,15 +55,14 @@ void AudioEffectAnalogDelay::update(void)
m_callCount++; return; m_callCount++; return;
} }
// else if (m_callCount > 1400) {
// if (inputAudioBlock) release(inputAudioBlock);
// return;
// }
m_callCount++; m_callCount++;
Serial.println(String("AudioEffectAnalgDelay::update: ") + m_callCount); Serial.println(String("AudioEffectAnalgDelay::update: ") + m_callCount);
//m_memory->getSlot()->printStatus();
audio_block_t *blockToRelease = m_memory->addBlock(inputAudioBlock); 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) { // if (inputAudioBlock) {
// transmit(inputAudioBlock, 0); // transmit(inputAudioBlock, 0);
@ -80,12 +70,6 @@ void AudioEffectAnalogDelay::update(void)
// } // }
// return; // return;
if (blockToRelease) release(blockToRelease);
Serial.print("Active channels: "); Serial.print(m_activeChannels, HEX); Serial.println("");
// For each active channel, output the delayed audio // For each active channel, output the delayed audio
audio_block_t *blockToOutput = nullptr; audio_block_t *blockToOutput = nullptr;
for (int channel=0; channel<MAX_DELAY_CHANNELS; channel++) { for (int channel=0; channel<MAX_DELAY_CHANNELS; channel++) {

@ -36,7 +36,7 @@ size_t calcOffset(QueuePosition position)
AudioDelay::AudioDelay(size_t maxSamples) AudioDelay::AudioDelay(size_t maxSamples)
: m_slot(nullptr) : m_slot(nullptr)
{ {
m_type = MemType::MEM_INTERNAL; m_type = (MemType::MEM_INTERNAL);
// INTERNAL memory consisting of audio_block_t data structures. // INTERNAL memory consisting of audio_block_t data structures.
QueuePosition pos = calcQueuePosition(maxSamples); QueuePosition pos = calcQueuePosition(maxSamples);
@ -50,9 +50,8 @@ AudioDelay::AudioDelay(float maxDelayTimeMs)
} }
AudioDelay::AudioDelay(ExtMemSlot *slot) AudioDelay::AudioDelay(ExtMemSlot *slot)
//: m_slot(slot)
{ {
m_type = MemType::MEM_EXTERNAL; m_type = (MemType::MEM_EXTERNAL);
m_slot = slot; m_slot = slot;
} }
@ -63,9 +62,11 @@ AudioDelay::~AudioDelay()
audio_block_t* AudioDelay::addBlock(audio_block_t *block) audio_block_t* AudioDelay::addBlock(audio_block_t *block)
{ {
if (m_type == MemType::MEM_INTERNAL) {
// INTERNAL memory
audio_block_t *blockToRelease = nullptr; audio_block_t *blockToRelease = nullptr;
if (m_type == (MemType::MEM_INTERNAL)) {
// INTERNAL memory
// purposefully don't check if block is valid, the ringBuffer can support nullptrs // purposefully don't check if block is valid, the ringBuffer can support nullptrs
if ( m_ringBuffer->size() >= m_ringBuffer->max_size() ) { if ( m_ringBuffer->size() >= m_ringBuffer->max_size() ) {
// pop before adding // 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) audio_block_t* AudioDelay::getBlock(size_t index)
{ {
if (m_type == MemType::MEM_INTERNAL) { audio_block_t *ret = nullptr;
return m_ringBuffer->at(m_ringBuffer->get_index_from_back(index)); if (m_type == (MemType::MEM_INTERNAL)) {
} else { ret = m_ringBuffer->at(m_ringBuffer->get_index_from_back(index));
return nullptr;
} }
return ret;
} }
bool AudioDelay::getSamples(audio_block_t *dest, size_t offset, size_t numSamples) bool AudioDelay::getSamples(audio_block_t *dest, size_t offset, size_t numSamples)
{ {
if (!dest) return false; if (!dest) return false;
if (m_type == MemType::MEM_INTERNAL) { if (m_type == (MemType::MEM_INTERNAL)) {
QueuePosition position = calcQueuePosition(offset); QueuePosition position = calcQueuePosition(offset);
size_t index = position.index; size_t index = position.index;

@ -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 */

Loading…
Cancel
Save