internal memory AudioDelay seems to work, starton on external support

pull/1/head
Steve Lascos 7 years ago
parent eea5571267
commit eb86b4313f
  1. 140
      src/AudioEffectAnalogDelay.cpp
  2. 15
      src/AudioEffectAnalogDelay.h
  3. 139
      src/LibBasicFunctions.cpp
  4. 44
      src/LibBasicFunctions.h
  5. 265
      src/LibMemoryManagement.cpp
  6. 83
      src/LibMemoryManagement.h

@ -9,38 +9,38 @@
namespace BAGuitar { namespace BAGuitar {
AudioEffectAnalogDelay::AudioEffectAnalogDelay(INTERNAL_MEMORY, float maxDelay) AudioEffectAnalogDelay::AudioEffectAnalogDelay(float maxDelay)
: AudioStream(1, m_inputQueueArray) : AudioStream(1, m_inputQueueArray)
{ {
m_memory = new MemAudioBlock(maxDelay); m_memory = new AudioDelay(maxDelay);
for (int i=0; i<MAX_DELAY_CHANNELS; i++) { for (int i=0; i<MAX_DELAY_CHANNELS; i++) {
m_channelOffsets[i] = 0; m_channelOffsets[i] = 0;
} }
} }
AudioEffectAnalogDelay::AudioEffectAnalogDelay(INTERNAL_MEMORY, size_t numSamples) AudioEffectAnalogDelay::AudioEffectAnalogDelay(size_t numSamples)
: AudioStream(1, m_inputQueueArray) : AudioStream(1, m_inputQueueArray)
{ {
m_memory = new MemAudioBlock(numSamples); m_memory = new AudioDelay(numSamples);
for (int i=0; i<MAX_DELAY_CHANNELS; i++) { for (int i=0; i<MAX_DELAY_CHANNELS; i++) {
m_channelOffsets[i] = 0; m_channelOffsets[i] = 0;
} }
} }
// requires preallocated memory large enough // requires preallocated memory large enough
AudioEffectAnalogDelay::AudioEffectAnalogDelay(EXTERNAL_MEMORY, MemSlot &slot) AudioEffectAnalogDelay::AudioEffectAnalogDelay(ExtMemSlot &slot)
: AudioStream(1, m_inputQueueArray) : AudioStream(1, m_inputQueueArray)
{ {
m_memory = &slot; // m_memory = &slot;
for (int i=0; i<MAX_DELAY_CHANNELS; i++) { // for (int i=0; i<MAX_DELAY_CHANNELS; i++) {
m_channelOffsets[i] = 0; // m_channelOffsets[i] = 0;
} // }
m_memory->clear(); // m_memory->clear();
} }
void AudioEffectAnalogDelay::update(void) void AudioEffectAnalogDelay::update(void)
{ {
audio_block_t *inputAudioBlock = receiveReadOnly(); // get the next block of input samplestransmit(inputAudioBlock, 0); audio_block_t *inputAudioBlock = receiveReadOnly(); // get the next block of input samples
if (!inputAudioBlock) { if (!inputAudioBlock) {
return; return;
@ -66,39 +66,8 @@ void AudioEffectAnalogDelay::update(void)
m_callCount++; m_callCount++;
Serial.println(String("AudioEffectAnalgDelay::update: ") + m_callCount); Serial.println(String("AudioEffectAnalgDelay::update: ") + m_callCount);
audio_block_t *blockToRelease = m_memory->addBlock(inputAudioBlock);
if (m_externalMemory) { if (blockToRelease) release(blockToRelease);
// external mem requires an actual write to memory with the new data block
if (inputAudioBlock) {
// valid block
m_memory->writeAdvance16(inputAudioBlock->data, AUDIO_BLOCK_SAMPLES);
} else {
// write zeros instead
m_memory->zeroAdvance16(AUDIO_BLOCK_SAMPLES);
}
} else {
// internal memory only requires updating the queue of audio block pointers.
MemAudioBlock *memory = reinterpret_cast<MemAudioBlock*>(m_memory);
// check to see if the queue has reached full size yet
//Serial.println(String("memory queues:") + memory->getNumQueues() + String(" m_numQueues:") + m_numQueues);
if (memory->getNumQueues() >= m_numQueues) {
release(memory->pop());
}
// now push in the newest audio block
//Serial.println(String("push ") + (uint32_t)inputAudioBlock);
memory->push(inputAudioBlock); // MemAudioBlock supports nullptrs, no need to check
//Serial.println("done memory->push()");
// audio_block_t *output = memory->getQueueBack();
// Serial.println("got the output");
// if (output) {
// transmit(output, 0);
// }
// Serial.println("Done transmit");
}
// return;
//Serial.print("Active channels: "); Serial.print(m_activeChannels, HEX); Serial.println(""); //Serial.print("Active channels: "); Serial.print(m_activeChannels, HEX); Serial.println("");
@ -111,13 +80,12 @@ void AudioEffectAnalogDelay::update(void)
if (!m_externalMemory) { if (!m_externalMemory) {
// internal memory // internal memory
MemAudioBlock *memory = reinterpret_cast<MemAudioBlock*>(m_memory);
QueuePosition queuePosition = calcQueuePosition(m_channelOffsets[channel]); QueuePosition queuePosition = calcQueuePosition(m_channelOffsets[channel]);
Serial.println(String("Position info: ") + queuePosition.index + " : " + queuePosition.offset); Serial.println(String("Position info: ") + queuePosition.index + " : " + queuePosition.offset);
if (queuePosition.offset == 0) { if (queuePosition.offset == 0) {
// only one queue needs to be read out // only one queue needs to be read out
//Serial.println(String("Directly sending queue offset ") + queuePosition.index); //Serial.println(String("Directly sending queue offset ") + queuePosition.index);
blockToOutput= memory->getQueueBack(queuePosition.index); // could be nullptr! blockToOutput= m_memory->getBlock(queuePosition.index); // could be nullptr!
if (blockToOutput) transmit(blockToOutput); if (blockToOutput) transmit(blockToOutput);
continue; continue;
} else { } else {
@ -130,7 +98,8 @@ void AudioEffectAnalogDelay::update(void)
// copy the output data // copy the output data
if (!blockToOutput) continue; // skip this channel due to failure if (!blockToOutput) continue; // skip this channel due to failure
// copy over data // copy over data
m_memory->read16(blockToOutput->data, 0, m_channelOffsets[channel], AUDIO_BLOCK_SAMPLES); m_memory->getSamples(blockToOutput, m_channelOffsets[channel]);
//m_memory->read16(blockToOutput->data, 0, m_channelOffsets[channel], AUDIO_BLOCK_SAMPLES);
transmit(blockToOutput); transmit(blockToOutput);
release(blockToOutput); release(blockToOutput);
} }
@ -143,25 +112,26 @@ bool AudioEffectAnalogDelay::delay(unsigned channel, float milliseconds)
size_t delaySamples = calcAudioSamples(milliseconds); size_t delaySamples = calcAudioSamples(milliseconds);
if (!m_externalMemory) { // if (!m_externalMemory) {
// Internal memory (AudioStream buffers) // // Internal memory (AudioStream buffers)
MemAudioBlock *memory = reinterpret_cast<MemAudioBlock*>(m_memory); //
// QueuePosition queuePosition = calcQueuePosition(milliseconds);
QueuePosition queuePosition = calcQueuePosition(milliseconds); // Serial.println(String("CONFIG(") + m_memory->getMaxSize() + String("): delay: queue position ") + queuePosition.index + String(":") + queuePosition.offset);
Serial.println(String("CONFIG(") + memory->getMaxSize() + String("): delay: queue position ") + queuePosition.index + String(":") + queuePosition.offset); // // we have to take the queue position and add 1 to get the number of queues. But we need to add one more since this
// we have to take the queue position and add 1 to get the number of queues. But we need to add one more since this // // is the start of the audio buffer, and the AUDIO_BLOCK_SAMPLES will then flow into the next one, so add 2 overall.
// is the start of the audio buffer, and the AUDIO_BLOCK_SAMPLES will then flow into the next one, so add 2 overall. // size_t numQueues = queuePosition.index+2;
size_t numQueues = queuePosition.index+2; // if (numQueues > m_numQueues)
if (numQueues > m_numQueues) // m_numQueues = numQueues;
m_numQueues = numQueues; // } else {
} else { //// // External memory
// External memory //// if (delaySamples > m_memory->getSize()) {
if (delaySamples > m_memory->getSize()) { //// // error, the external memory is not large enough
// error, the external memory is not large enough //// return false;
return false; //// }
} // }
}
QueuePosition queuePosition = calcQueuePosition(milliseconds);
Serial.println(String("CONFIG: delay:") + delaySamples + String(" queue position ") + queuePosition.index + String(":") + queuePosition.offset);
m_channelOffsets[channel] = delaySamples; m_channelOffsets[channel] = delaySamples;
m_activeChannels |= 1<<channel; m_activeChannels |= 1<<channel;
return true; return true;
@ -172,26 +142,28 @@ bool AudioEffectAnalogDelay::delay(unsigned channel, size_t delaySamples)
if (channel > MAX_DELAY_CHANNELS-1) // channel id too high if (channel > MAX_DELAY_CHANNELS-1) // channel id too high
return false; return false;
if (!m_externalMemory) { // if (!m_externalMemory) {
// Internal memory (AudioStream buffers) // // Internal memory (AudioStream buffers)
MemAudioBlock *memory = reinterpret_cast<MemAudioBlock*>(m_memory); // MemAudioBlock *memory = reinterpret_cast<MemAudioBlock*>(m_memory);
//
//QueuePosition queuePosition = calcQueuePosition(milliseconds); // //QueuePosition queuePosition = calcQueuePosition(milliseconds);
QueuePosition queuePosition = calcQueuePosition(delaySamples); // QueuePosition queuePosition = calcQueuePosition(delaySamples);
Serial.println(String("CONFIG(") + memory->getMaxSize() + String("): delay: queue position ") + queuePosition.index + String(":") + queuePosition.offset); // Serial.println(String("CONFIG(") + memory->getMaxSize() + String("): delay: queue position ") + queuePosition.index + String(":") + queuePosition.offset);
// we have to take the queue position and add 1 to get the number of queues. But we need to add one more since this // // we have to take the queue position and add 1 to get the number of queues. But we need to add one more since this
// is the start of the audio buffer, and the AUDIO_BLOCK_SAMPLES will then flow into the next one, so add 2 overall. // // is the start of the audio buffer, and the AUDIO_BLOCK_SAMPLES will then flow into the next one, so add 2 overall.
size_t numQueues = queuePosition.index+2; // size_t numQueues = queuePosition.index+2;
if (numQueues > m_numQueues) // if (numQueues > m_numQueues)
m_numQueues = numQueues; // m_numQueues = numQueues;
} else { // } else {
// External memory //// // External memory
if (delaySamples > m_memory->getSize()) { //// if (delaySamples > m_memory->getSize()) {
// error, the external memory is not large enough //// // error, the external memory is not large enough
return false; //// return false;
} //// }
} // }
QueuePosition queuePosition = calcQueuePosition(delaySamples);
Serial.println(String("CONFIG: delay:") + delaySamples + String(" queue position ") + queuePosition.index + String(":") + queuePosition.offset);
m_channelOffsets[channel] = delaySamples; m_channelOffsets[channel] = delaySamples;
m_activeChannels |= 1<<channel; m_activeChannels |= 1<<channel;
return true; return true;

@ -8,10 +8,10 @@
#ifndef SRC_AUDIOEFFECTANALOGDELAY_H_ #ifndef SRC_AUDIOEFFECTANALOGDELAY_H_
#define SRC_AUDIOEFFECTANALOGDELAY_H_ #define SRC_AUDIOEFFECTANALOGDELAY_H_
#include <vector> //#include <vector>
#include <Audio.h> #include <Audio.h>
#include "LibMemoryManagement.h" #include "LibBasicFunctions.h"
namespace BAGuitar { namespace BAGuitar {
@ -22,9 +22,10 @@ public:
static constexpr int MAX_DELAY_CHANNELS = 8; static constexpr int MAX_DELAY_CHANNELS = 8;
AudioEffectAnalogDelay() = delete; AudioEffectAnalogDelay() = delete;
AudioEffectAnalogDelay(INTERNAL_MEMORY, float maxDelay); AudioEffectAnalogDelay(float maxDelay);
AudioEffectAnalogDelay(INTERNAL_MEMORY, size_t numSamples); AudioEffectAnalogDelay(size_t numSamples);
AudioEffectAnalogDelay(EXTERNAL_MEMORY, MemSlot &slot); // requires sufficiently sized pre-allocated memory
AudioEffectAnalogDelay(ExtMemSlot &slot); // requires sufficiently sized pre-allocated memory
virtual ~AudioEffectAnalogDelay() {} virtual ~AudioEffectAnalogDelay() {}
virtual void update(void); virtual void update(void);
@ -36,9 +37,9 @@ private:
audio_block_t *m_inputQueueArray[1]; audio_block_t *m_inputQueueArray[1];
unsigned m_activeChannels = 0; unsigned m_activeChannels = 0;
bool m_externalMemory = false; bool m_externalMemory = false;
MemBufferIF *m_memory = nullptr; AudioDelay *m_memory = nullptr;
size_t m_numQueues = 0; //size_t m_numQueues = 0;
size_t m_channelOffsets[MAX_DELAY_CHANNELS]; size_t m_channelOffsets[MAX_DELAY_CHANNELS];
size_t m_callCount = 0; size_t m_callCount = 0;

@ -5,57 +5,140 @@
* Author: slascos * Author: slascos
*/ */
#include "Audio.h"
#include "LibBasicFunctions.h" #include "LibBasicFunctions.h"
namespace BAGuitar { namespace BAGuitar {
AudioDelay::AudioDelay(MemType type, size_t maxSamples) size_t calcAudioSamples(float milliseconds)
{ {
m_type = type; return (size_t)((milliseconds*(AUDIO_SAMPLE_RATE_EXACT/1000.0f))+0.5f);
if (type == MemType::INTERNAL) { }
// INTERNAL memory consisting of audio_block_t data structures.
QueuePosition pos = calcQueuePosition(maxSamples);
m_ringBuffer = new RingBuffer<audio_block_t *>(pos.index+2); // If the delay is in queue x, we need to overflow into x+1, thus x+2 total buffers.
} else {
// TODO EXTERNAL memory
} QueuePosition calcQueuePosition(size_t numSamples)
{
QueuePosition queuePosition;
queuePosition.index = (int)(numSamples / AUDIO_BLOCK_SAMPLES);
queuePosition.offset = numSamples % AUDIO_BLOCK_SAMPLES;
return queuePosition;
}
QueuePosition calcQueuePosition(float milliseconds) {
size_t numSamples = (int)((milliseconds*(AUDIO_SAMPLE_RATE_EXACT/1000.0f))+0.5f);
return calcQueuePosition(numSamples);
} }
AudioDelay::AudioDelay(MemType type, float delayTimeMs) size_t calcOffset(QueuePosition position)
: AudioDelay(type, calcAudioSamples(delayTimeMs))
{ {
return (position.index*AUDIO_BLOCK_SAMPLES) + position.offset;
}
AudioDelay::AudioDelay(size_t maxSamples)
: m_slot(nullptr)
{
m_type = MemType::MEM_INTERNAL;
// INTERNAL memory consisting of audio_block_t data structures.
QueuePosition pos = calcQueuePosition(maxSamples);
m_ringBuffer = new RingBuffer<audio_block_t *>(pos.index+2); // If the delay is in queue x, we need to overflow into x+1, thus x+2 total buffers.
} }
AudioDelay::~AudioDelay() AudioDelay::AudioDelay(float maxDelayTimeMs)
: AudioDelay(calcAudioSamples(maxDelayTimeMs))
{ {
if (m_ringBuffer) delete m_ringBuffer;
} }
bool AudioDelay::addBlock(audio_block_t *block) AudioDelay::AudioDelay(ExtMemSlot &slot)
: m_slot(slot)
{ {
if (m_type != MemType::INTERNAL) return false; // ERROR m_type = MemType::MEM_EXTERNAL;
}
// purposefully don't check if block is valid, the ringBuffer can support nullptrs AudioDelay::~AudioDelay()
if ( m_ringBuffer->size() < m_ringBuffer->max_size() ) { {
// pop before adding if (m_ringBuffer) delete m_ringBuffer;
release(m_ringBuffer->front()); }
m_ringBuffer->pop_front();
}
// add the new buffer audio_block_t* AudioDelay::addBlock(audio_block_t *block)
m_ringBuffer->push_back(block); {
if (m_type == MemType::MEM_INTERNAL) {
// INTERNAL memory
audio_block_t *blockToRelease = nullptr;
// purposefully don't check if block is valid, the ringBuffer can support nullptrs
if ( m_ringBuffer->size() >= m_ringBuffer->max_size() ) {
// pop before adding
blockToRelease = m_ringBuffer->front();
m_ringBuffer->pop_front();
}
// add the new buffer
m_ringBuffer->push_back(block);
return blockToRelease;
} else {
// EXTERNAL memory
m_slot.writeAdvance16(block->data, AUDIO_BLOCK_SAMPLES);
return nullptr;
}
} }
void AudioDelay::getSamples(audio_block_t *dest, size_t offset, size_t numSamples = AUDIO_BLOCK_SAMPLES) audio_block_t* AudioDelay::getBlock(size_t index)
{ {
QueuePosition pos = calcQueuePosition(offset); if (m_type == MemType::MEM_INTERNAL) {
size_t readOffset = pos.offset; return m_ringBuffer->at(m_ringBuffer->get_index_from_back(index));
size_t index = pos.index; } else {
return nullptr;
}
}
// Audio is stored in reverse order. That means the first sample (in time) goes in the last location in the audio block. bool AudioDelay::getSamples(audio_block_t *dest, size_t offset, size_t numSamples)
{
if (!dest) return false;
if (m_type == MemType::MEM_INTERNAL) {
QueuePosition position = calcQueuePosition(offset);
size_t index = position.index;
if (position.offset == 0) {
// single transfer
audio_block_t *currentQueue = m_ringBuffer->at(m_ringBuffer->get_index_from_back(index));
memcpy(static_cast<void*>(dest->data), static_cast<void*>(currentQueue->data), numSamples * sizeof(int16_t));
return true;
}
// Otherwise we need to break the transfer into two memcpy because it will go across two source queues.
// Audio is stored in reverse order. That means the first sample (in time) goes in the last location in the audio block.
int16_t *destStart = dest->data;
audio_block_t *currentQueue;
int16_t *srcStart;
// Break the transfer into two. Copy the 'older' data first then the 'newer' data with respect to current time.
currentQueue = m_ringBuffer->at(m_ringBuffer->get_index_from_back(index+1)); // The latest buffer is at the back. We need index+1 counting from the back.
srcStart = (currentQueue->data + AUDIO_BLOCK_SAMPLES - position.offset);
size_t numData = position.offset;
memcpy(static_cast<void*>(destStart), static_cast<void*>(srcStart), numData * sizeof(int16_t));
currentQueue = m_ringBuffer->at(m_ringBuffer->get_index_from_back(index)); // now grab the queue where the 'first' data sample was
destStart += numData; // we already wrote numData so advance by this much.
srcStart = (currentQueue->data);
numData = AUDIO_BLOCK_SAMPLES - numData;
memcpy(static_cast<void*>(destStart), static_cast<void*>(srcStart), numData * sizeof(int16_t));
return true;
} else {
// EXTERNAL Memory
if (numSamples < m_slot.size() ) {
m_slot.read16FromCurrent(dest->data, offset, numSamples);
return true;
} else {
// numSampmles is > than total slot size
return false;
}
}
} }

@ -9,7 +9,8 @@
#include <new> #include <new>
#include "Arduino.h" #include "Arduino.h"
#include "Audio.H" #include "Audio.h"
#include "LibMemoryManagement.h" #include "LibMemoryManagement.h"
#ifndef SRC_LIBBASICFUNCTIONS_H_ #ifndef SRC_LIBBASICFUNCTIONS_H_
@ -17,29 +18,46 @@
namespace BAGuitar { namespace BAGuitar {
struct QueuePosition {
int offset;
int index;
};
QueuePosition calcQueuePosition(float milliseconds);
QueuePosition calcQueuePosition(size_t numSamples);
size_t calcAudioSamples(float milliseconds);
size_t calcOffset(QueuePosition position);
template <class T>
class RingBuffer; // forward declare class RingBuffer; // forward declare
enum MemType : unsigned { enum class MemType : unsigned {
INTERNAL, MEM_INTERNAL = 0,
EXTERNAL MEM_EXTERNAL
}; };
struct INTERNAL_MEMORY {}; struct INTERNAL_MEMORY {};
struct EXTERNAL_MEMORY {}; struct EXTERNAL_MEMORY {};
class AudioDelay { class AudioDelay {
public:
AudioDelay() = delete; AudioDelay() = delete;
AudioDelay(MemType type, size_t maxSamples); AudioDelay(size_t maxSamples);
AudioDelay(MemType type, float delayTimeMs); AudioDelay(float maxDelayTimeMs);
AudioDelay(ExtMemSlot &slot);
~AudioDelay(); ~AudioDelay();
void addBlock(audio_block_t *block); // Internal memory member functions
audio_block_t *addBlock(audio_block_t *blockIn);
audio_block_t *getBlock(size_t index);
bool getSamples(audio_block_t *dest, size_t offset, size_t numSamples = AUDIO_BLOCK_SAMPLES);
// External memory member functions
//bool writeBlock(audio_blocK_t *blockIn);
void getSamples(size_t offset, size_t numSamples);
private: private:
MemType m_type; MemType m_type;
RingBuffer *m_ringBuffer = nullptr; RingBuffer<audio_block_t *> *m_ringBuffer = nullptr;
ExtMemSlot &m_slot;
}; };
template <class T> template <class T>
@ -101,7 +119,7 @@ public:
return m_buffer[m_head-1]; return m_buffer[m_head-1];
} }
size_t getBackIndex(size_t offset = 0) const { size_t get_index_from_back(size_t offset = 0) const {
// the target at m_head - 1 - offset or m_maxSize + m_head -1 - offset; // the target at m_head - 1 - offset or m_maxSize + m_head -1 - offset;
size_t idx = (m_maxSize + m_head -1 - offset); size_t idx = (m_maxSize + m_head -1 - offset);
@ -129,6 +147,10 @@ public:
return m_buffer[index]; return m_buffer[index];
} }
T at(size_t index) const {
return m_buffer[index];
}
void print() const { void print() const {
for (int idx=0; idx<m_maxSize; idx++) { for (int idx=0; idx<m_maxSize; idx++) {
Serial.println(idx + String(" address: ") + (uint32_t)m_buffer[idx] + String(" data: ") + (uint32_t)m_buffer[idx]->data); Serial.println(idx + String(" address: ") + (uint32_t)m_buffer[idx] + String(" data: ") + (uint32_t)m_buffer[idx]->data);

@ -9,242 +9,23 @@ namespace BAGuitar {
bool ExternalSramManager::m_configured = false; bool ExternalSramManager::m_configured = false;
MemConfig ExternalSramManager::m_memConfig[BAGuitar::NUM_MEM_SLOTS]; MemConfig ExternalSramManager::m_memConfig[BAGuitar::NUM_MEM_SLOTS];
size_t calcAudioSamples(float milliseconds)
{
return (size_t)((milliseconds*(AUDIO_SAMPLE_RATE_EXACT/1000.0f))+0.5f);
}
QueuePosition calcQueuePosition(size_t numSamples)
{
QueuePosition queuePosition;
queuePosition.index = (int)(numSamples / AUDIO_BLOCK_SAMPLES);
queuePosition.offset = numSamples % AUDIO_BLOCK_SAMPLES;
return queuePosition;
}
QueuePosition calcQueuePosition(float milliseconds) {
size_t numSamples = (int)((milliseconds*(AUDIO_SAMPLE_RATE_EXACT/1000.0f))+0.5f);
return calcQueuePosition(numSamples);
}
size_t calcOffset(QueuePosition position)
{
return (position.index*AUDIO_BLOCK_SAMPLES) + position.offset;
}
/////////////////////////////////////////////////////////////////////////////
// MEM BUFFER IF
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
// MEM VIRTUAL
/////////////////////////////////////////////////////////////////////////////
MemAudioBlock::MemAudioBlock(size_t numSamples)
: m_queues(((numSamples + AUDIO_BLOCK_SAMPLES - 1)/AUDIO_BLOCK_SAMPLES) +1)
{
// // round up to an integer multiple of AUDIO_BLOCK_SAMPLES
// int numQueues = (numSamples + AUDIO_BLOCK_SAMPLES - 1)/AUDIO_BLOCK_SAMPLES;
//
// // Preload the queue with nullptrs to set the queue depth to the correct size
// for (int i=0; i < numQueues; i++) {
// m_queues.push_back(nullptr);
// }
}
MemAudioBlock::MemAudioBlock(float milliseconds)
: MemAudioBlock(calcAudioSamples(milliseconds))
{
}
MemAudioBlock::~MemAudioBlock()
{
}
// the index is referenced from the head
audio_block_t *MemAudioBlock::getQueueBack(size_t offset)
{
// for (int i=0; i<m_queues.getMaxSize(); i++) {
// Serial.println(i + String(":") + (uint32_t)m_queues[i]->data);
// }
// Serial.println(String("Returning ") + (uint32_t)m_queues[m_queues.getBackIndex(offset)]);
return m_queues[m_queues.getBackIndex(offset)];
}
bool MemAudioBlock::push(audio_block_t *block)
{
//Serial.println("MemAudioBlock::push()");
m_queues.push_back(block);
//Serial.println("MemAudioBlock::push() done");
return true;
}
audio_block_t* MemAudioBlock::pop()
{
//Serial.println("MemAudioBlock::pop()");
audio_block_t* block = m_queues.front();
m_queues.pop_front();
return block;
}
bool MemAudioBlock::clear()
{
for (size_t i=0; i < m_queues.size(); i++) {
if (m_queues[i]) {
memset(m_queues[i]->data, 0, AUDIO_BLOCK_SAMPLES * sizeof(int16_t));
}
}
return true;
}
bool MemAudioBlock::write16(size_t offset, int16_t *srcDataPtr, size_t numData)
{
// Calculate the queue position
auto position = calcQueuePosition(offset);
int writeOffset = position.offset;
size_t index = position.index;
if ( (index+1) > m_queues.size()) return false; // out of range
// loop over a series of memcpys until all data is transferred.
size_t samplesRemaining = numData;
int16_t *srcStart = srcDataPtr; // this will increment during each loop iteration
while (samplesRemaining > 0) {
size_t numSamplesToWrite;
void *destStart = static_cast<void*>(m_queues[index]->data + writeOffset);
// determine if the transfer will complete or will hit the end of a block first
if ( (writeOffset + samplesRemaining) > AUDIO_BLOCK_SAMPLES ) {
// goes past end of the queue
numSamplesToWrite = (AUDIO_BLOCK_SAMPLES - writeOffset);
//writeOffset = 0;
//index++;
} else {
// transfer ends in this audio block
numSamplesToWrite = samplesRemaining;
//writeOffset += numSamplesToWrite;
}
// perform the transfer
if (!m_queues[index]) {
// no allocated audio block, skip the copy
} else {
if (srcDataPtr) {
memcpy(destStart, static_cast<const void*>(srcStart), numSamplesToWrite * sizeof(int16_t));
} else {
memset(destStart, 0, numSamplesToWrite * sizeof(int16_t));
}
}
writeOffset = 0;
index++;
srcStart += numSamplesToWrite;
samplesRemaining -= numSamplesToWrite;
}
m_currentPosition.offset = writeOffset;
m_currentPosition.index = index;
return true;
}
inline bool MemAudioBlock::zero16(size_t offset, size_t numData)
{
return write16(offset, nullptr, numData);
}
bool MemAudioBlock::read16(int16_t *dest, size_t destOffset, size_t srcOffset, size_t numSamples)
{
if (!dest) return false; // destination is not valid
(void)destOffset; // not supported with audio_block_t;
//Serial.println("*************************************************************************");
//Serial.println(String("read16():") + (uint32_t)dest + String(":") + destOffset + String(":") + srcOffset + String(":") + numSamples);
// Calculate the queue position
auto position = calcQueuePosition(srcOffset);
size_t index = position.index;
// Break the transfer in two. Note that the audio is stored first sample (in time) last (in memory).
int16_t *destStart = dest;
audio_block_t *currentQueue;
int16_t *srcStart;
// Break the transfer into two. Note that the audio
//Serial.println("Calling getQueue");
currentQueue = getQueueBack(index+1); // buffer indexes go backwards from the back
//Serial.println(String("Q. Address: ") + (uint32_t)currentQueue + String(" Data: ") + (uint32_t)currentQueue->data);
srcStart = (currentQueue->data + AUDIO_BLOCK_SAMPLES - position.offset);
size_t numData = position.offset;
//Serial.println(String("Source Start1: ") + (uint32_t)currentQueue->data + String(" Dest start1: ") + (uint32_t)dest);
//Serial.println(String("copying to ") + (uint32_t)destStart + String(" from ") + (uint32_t)srcStart + String(" numData= ") + numData);
memcpy(static_cast<void*>(destStart), static_cast<void*>(srcStart), numData * sizeof(int16_t));
currentQueue = getQueueBack(index); // buffer indexes go backwards from the back
//Serial.println(String("Q. Address: ") + (uint32_t)currentQueue + String(" Data: ") + (uint32_t)currentQueue->data);
destStart += numData;
srcStart = (currentQueue->data);
numData = AUDIO_BLOCK_SAMPLES - numData;
//Serial.println(String("Source Start2: ") + (uint32_t)currentQueue->data + String(" Dest start2: ") + (uint32_t)dest);
//Serial.println(String("copying to ") + (uint32_t)destStart + String(" from ") + (uint32_t)srcStart + String(" numData= ") + numData);
memcpy(static_cast<void*>(destStart), static_cast<void*>(srcStart), numData * sizeof(int16_t));
//m_queues.print();
//Serial.println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
return true;
}
// If this function hits the end of the queues it will wrap to the start
bool MemAudioBlock::writeAdvance16(int16_t *dataPtr, size_t numData)
{
auto globalOffset = calcOffset(m_currentPosition);
auto end = globalOffset + numData;
if ( end >= (m_queues.size() * AUDIO_BLOCK_SAMPLES) ) {
// transfer will wrap, so break into two
auto samplesToWrite = end - globalOffset;
// write the first chunk
write16(globalOffset, dataPtr, samplesToWrite);
// write the scond chunk
int16_t *ptr;
if (dataPtr) {
// valid dataptr, advance the pointer
ptr = dataPtr+samplesToWrite;
} else {
// dataPtr was nullptr
ptr = nullptr;
}
write16(0, ptr, numData-samplesToWrite);
} else {
// no wrap
write16(globalOffset, dataPtr, numData);
}
return true;
}
bool MemAudioBlock::zeroAdvance16(size_t numData)
{
return writeAdvance16(nullptr, numData);
}
///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////
// MEM SLOT // MEM SLOT
///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////
bool MemSlot::clear() bool ExtMemSlot::clear()
{ {
if (!m_valid) { return false; } if (!m_valid) { return false; }
m_spi->zero16(m_start, m_size); m_spi->zero16(m_start, m_size);
return true; return true;
} }
bool MemSlot::write16(size_t offset, int16_t *dataPtr, size_t dataSize) bool ExtMemSlot::write16(size_t offset, int16_t *dataPtr, size_t dataSize)
{ {
if (!m_valid) { return false; } if (!m_valid) { return false; }
if ((offset + dataSize-1) <= m_end) { size_t writeStart = m_start + offset;
m_spi->write16(offset, reinterpret_cast<uint16_t*>(dataPtr), dataSize); // cast audio data to uint if ((writeStart + dataSize-1) <= m_end) {
m_spi->write16(writeStart, reinterpret_cast<uint16_t*>(dataPtr), dataSize); // cast audio data to uint
return true; return true;
} else { } else {
// this would go past the end of the memory slot, do not perform the write // this would go past the end of the memory slot, do not perform the write
@ -252,11 +33,12 @@ bool MemSlot::write16(size_t offset, int16_t *dataPtr, size_t dataSize)
} }
} }
bool MemSlot::zero16(size_t offset, size_t dataSize) bool ExtMemSlot::zero16(size_t offset, size_t dataSize)
{ {
if (!m_valid) { return false; } if (!m_valid) { return false; }
if ((offset + dataSize-1) <= m_end) { size_t writeStart = m_start + offset;
m_spi->zero16(offset, dataSize); // cast audio data to uint if ((writeStart + dataSize-1) <= m_end) {
m_spi->zero16(writeStart, dataSize); // cast audio data to uint
return true; return true;
} else { } else {
// this would go past the end of the memory slot, do not perform the write // this would go past the end of the memory slot, do not perform the write
@ -264,11 +46,13 @@ bool MemSlot::zero16(size_t offset, size_t dataSize)
} }
} }
bool MemSlot::read16(int16_t *dest, size_t destOffset, size_t srcOffset, size_t numData) bool ExtMemSlot::read16(int16_t *dest, size_t srcOffset, size_t numData)
{ {
if (!dest) return false; // invalid destination if (!dest) return false; // invalid destination
if ((srcOffset + (numData*sizeof(int16_t))-1) <= m_end) { size_t readOffset = m_start + srcOffset;
m_spi->read16(srcOffset, reinterpret_cast<uint16_t*>(dest), numData);
if ((readOffset + (numData*sizeof(int16_t))-1) <= m_end) {
m_spi->read16(readOffset, reinterpret_cast<uint16_t*>(dest), numData);
return true; return true;
} else { } else {
// this would go past the end of the memory slot, do not perform the write // this would go past the end of the memory slot, do not perform the write
@ -276,7 +60,20 @@ bool MemSlot::read16(int16_t *dest, size_t destOffset, size_t srcOffset, size_t
} }
} }
bool MemSlot::writeAdvance16(int16_t *dataPtr, size_t dataSize) size_t ExtMemSlot::read16FromCurrent(int16_t *dest, size_t currentOffset, size_t numData)
{
size_t readStart;
if (m_currentPosition + currentOffset <= m_end) {
readStart = m_currentPosition + currentOffset;
} else {
// this offset will wrap the memory slot
size_t numBytesToEnd = m_end - m_currentPosition + 1;
readStart = m_start + (currentOffset - numBytesToEnd);
}
m_spi->read16(dest, readStart, numData);
}
bool ExtMemSlot::writeAdvance16(int16_t *dataPtr, size_t dataSize)
{ {
if (!m_valid) { return false; } if (!m_valid) { return false; }
if (m_currentPosition + dataSize-1 <= m_end) { if (m_currentPosition + dataSize-1 <= m_end) {
@ -295,7 +92,7 @@ bool MemSlot::writeAdvance16(int16_t *dataPtr, size_t dataSize)
return true; return true;
} }
bool MemSlot::zeroAdvance16(size_t dataSize) bool ExtMemSlot::zeroAdvance16(size_t dataSize)
{ {
if (!m_valid) { return false; } if (!m_valid) { return false; }
if (m_currentPosition + dataSize-1 <= m_end) { if (m_currentPosition + dataSize-1 <= m_end) {
@ -344,14 +141,14 @@ size_t ExternalSramManager::availableMemory(BAGuitar::MemSelect mem)
return m_memConfig[mem].totalAvailable; return m_memConfig[mem].totalAvailable;
} }
bool ExternalSramManager::requestMemory(MemSlot &slot, float delayMilliseconds, BAGuitar::MemSelect mem) bool ExternalSramManager::requestMemory(ExtMemSlot &slot, float delayMilliseconds, BAGuitar::MemSelect mem)
{ {
// convert the time to numer of samples // convert the time to numer of samples
size_t delayLengthInt = (size_t)((delayMilliseconds*(AUDIO_SAMPLE_RATE_EXACT/1000.0f))+0.5f); size_t delayLengthInt = (size_t)((delayMilliseconds*(AUDIO_SAMPLE_RATE_EXACT/1000.0f))+0.5f);
return requestMemory(slot, delayLengthInt, mem); return requestMemory(slot, delayLengthInt, mem);
} }
bool ExternalSramManager::requestMemory(MemSlot &slot, size_t sizeBytes, BAGuitar::MemSelect mem) bool ExternalSramManager::requestMemory(ExtMemSlot &slot, size_t sizeBytes, BAGuitar::MemSelect mem)
{ {
if (m_memConfig[mem].totalAvailable >= sizeBytes) { if (m_memConfig[mem].totalAvailable >= sizeBytes) {
// there is enough available memory for this request // there is enough available memory for this request

@ -11,7 +11,7 @@
#include <cstddef> #include <cstddef>
#include "Audio.h" //#include "Audio.h"
#include "BAHardware.h" #include "BAHardware.h"
#include "BASpiMemory.h" #include "BASpiMemory.h"
@ -19,14 +19,6 @@
namespace BAGuitar { namespace BAGuitar {
struct QueuePosition {
int offset;
int index;
};
QueuePosition calcQueuePosition(float milliseconds);
QueuePosition calcQueuePosition(size_t numSamples);
size_t calcAudioSamples(float milliseconds);
struct MemConfig { struct MemConfig {
size_t size; size_t size;
size_t totalAvailable; size_t totalAvailable;
@ -34,66 +26,27 @@ struct MemConfig {
BASpiMemory *m_spi = nullptr; BASpiMemory *m_spi = nullptr;
}; };
class ExternalSramManager; // forward declare so MemSlot can setup friendship class ExternalSramManager; // forward declare so ExtMemSlot can setup friendship
class MemBufferIF { class ExtMemSlot {
public: public:
size_t getSize() const { return m_size; }
virtual bool clear() = 0;
virtual bool write16(size_t offset, int16_t *dataPtr, size_t numData) = 0;
virtual bool zero16(size_t offset, size_t numData) = 0;
virtual bool read16(int16_t *dest, size_t destOffset, size_t srcOffset, size_t numData) = 0;
virtual bool writeAdvance16(int16_t *dataPtr, size_t numData) = 0;
virtual bool zeroAdvance16(size_t numData) = 0;
virtual ~MemBufferIF() {}
protected:
bool m_valid = false;
size_t m_size = 0;
};
class MemAudioBlock : public MemBufferIF { bool clear();
public: bool write16(size_t offset, int16_t *dataPtr, size_t numData);
//MemAudioBlock(); bool zero16(size_t offset, size_t numData);
MemAudioBlock() = delete; bool read16(int16_t *dest, size_t srcOffset, size_t numData);
MemAudioBlock(size_t numSamples); bool writeAdvance16(int16_t *dataPtr, size_t numData);
MemAudioBlock(float milliseconds); bool zeroAdvance16(size_t numData);
size_t read16FromCurrent(int16_t *dest, size_t Offset, size_t numData);
virtual ~MemAudioBlock(); size_t size() const { return m_size; }
bool push(audio_block_t *block);
audio_block_t *pop();
audio_block_t *getQueueBack(size_t offset=0);
size_t getNumQueues() const { return m_queues.size(); }
size_t getMaxSize() const { return m_queues.getMaxSize(); }
bool clear() override;
bool write16(size_t offset, int16_t *dataPtr, size_t numData) override;
bool zero16(size_t offset, size_t numSamples) override;
bool read16(int16_t *dest, size_t destOffset, size_t srcOffset, size_t numSamples);
bool writeAdvance16(int16_t *dataPtr, size_t numData) override;
bool zeroAdvance16(size_t numData) override;
private:
//size_t m_numQueues;
BAGuitar::RingBuffer <audio_block_t*> m_queues;
QueuePosition m_currentPosition = {0,0};
};
class MemSlot : public MemBufferIF {
public:
bool clear() override;
bool write16(size_t offset, int16_t *dataPtr, size_t numData) override;
bool zero16(size_t offset, size_t numData) override;
bool read16(int16_t *dest, size_t destOffset, size_t srcOffset, size_t numData);
bool writeAdvance16(int16_t *dataPtr, size_t numData) override;
bool zeroAdvance16(size_t numData) override;
private: private:
friend ExternalSramManager; friend ExternalSramManager;
size_t m_start; bool m_valid = false;
size_t m_end; size_t m_start = 0;
size_t m_currentPosition; size_t m_end = 0;
size_t m_currentPosition = 0;
size_t m_size = 0;
BASpiMemory *m_spi = nullptr; BASpiMemory *m_spi = nullptr;
}; };
@ -105,8 +58,8 @@ public:
virtual ~ExternalSramManager(); virtual ~ExternalSramManager();
size_t availableMemory(BAGuitar::MemSelect mem); size_t availableMemory(BAGuitar::MemSelect mem);
bool requestMemory(MemSlot &slot, float delayMilliseconds, BAGuitar::MemSelect mem = BAGuitar::MemSelect::MEM0); bool requestMemory(ExtMemSlot &slot, float delayMilliseconds, BAGuitar::MemSelect mem = BAGuitar::MemSelect::MEM0);
bool requestMemory(MemSlot &slot, size_t sizeBytes, BAGuitar::MemSelect mem = BAGuitar::MemSelect::MEM0); bool requestMemory(ExtMemSlot &slot, size_t sizeBytes, BAGuitar::MemSelect mem = BAGuitar::MemSelect::MEM0);
private: private:
static bool m_configured; static bool m_configured;

Loading…
Cancel
Save