diff --git a/src/AudioEffectAnalogDelay.cpp b/src/AudioEffectAnalogDelay.cpp new file mode 100644 index 0000000..ff2ec8a --- /dev/null +++ b/src/AudioEffectAnalogDelay.cpp @@ -0,0 +1,118 @@ +/* + * AudioEffectAnalogDelay.cpp + * + * Created on: Jan 7, 2018 + * Author: slascos + */ +#include +#include "AudioEffectAnalogDelay.h" + +namespace BAGuitar { + +AudioEffectAnalogDelay::AudioEffectAnalogDelay(INTERNAL_MEMORY, float maxDelay) +: AudioStream(1, m_inputQueueArray) +{ + m_memory = new MemAudioBlock(maxDelay); + for (int i=0; iclear(); +} + +void AudioEffectAnalogDelay::update(void) +{ + audio_block_t *inputAudioBlock = receiveReadOnly(); // get the next block of input samples + + if (m_externalMemory) { + // 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(m_memory); + memory->push(inputAudioBlock); // MemAudioBlock supports nullptrs, no need to check + + // check to see if the queue has reached full size yet + if (memory->getNumQueues() > m_numQueues) { + release(memory->pop()); + } + } + + // For each active channel, output the delayed audio + audio_block_t *blockToOutput = nullptr; + for (int channel=0; channel(m_memory); + QueuePosition queuePosition = calcQueuePosition(m_channelOffsets[channel]); + if (queuePosition.offset == 0) { + // only one queue needs to be read out + blockToOutput= memory->getQueue(queuePosition.index); // could be nullptr! + transmit(blockToOutput); + } else { + blockToOutput = allocate(); // allocate if spanning 2 queues + } + } else { + blockToOutput = allocate(); // allocate always for external memory + } + + // copy the output data + if (!blockToOutput) continue; // skip this channel due to failure + // copy over data + m_memory->read16(blockToOutput->data, 0, m_channelOffsets[channel], AUDIO_BLOCK_SAMPLES); + transmit(blockToOutput); + release(blockToOutput); + } +} + +bool AudioEffectAnalogDelay::delay(unsigned channel, float milliseconds) +{ + if (channel > MAX_DELAY_CHANNELS-1) // channel id too high + return false; + + size_t delaySamples = calcAudioSamples(milliseconds); + + if (!m_externalMemory) { + // Internal memory (AudioStream buffers) + QueuePosition queuePosition = calcQueuePosition(milliseconds); + size_t numQueues = queuePosition.index+1; + if (numQueues > m_numQueues) + m_numQueues = numQueues; + } else { + // External memory + if (delaySamples > m_memory->getSize()) { + // error, the external memory is not large enough + return false; + } + } + + m_channelOffsets[channel] = delaySamples; + m_activeChannels |= 1< + +#include +#include "LibMemoryManagement.h" + +namespace BAGuitar { + + + +class AudioEffectAnalogDelay : public AudioStream { +public: + static constexpr int MAX_DELAY_CHANNELS = 8; + + AudioEffectAnalogDelay() = delete; + AudioEffectAnalogDelay(INTERNAL_MEMORY, float maxDelay); + AudioEffectAnalogDelay(EXTERNAL_MEMORY, MemSlot &slot); // requires sufficiently sized pre-allocated memory + virtual ~AudioEffectAnalogDelay() {} + + virtual void update(void); + bool delay(unsigned channel, float milliseconds); + void disable(unsigned channel); + +private: + audio_block_t *m_inputQueueArray[1]; + unsigned m_activeChannels = 0; + bool m_externalMemory = false; + MemBufferIF *m_memory = nullptr; + + size_t m_numQueues = 0; + size_t m_channelOffsets[MAX_DELAY_CHANNELS]; +}; + +} + +#endif /* SRC_AUDIOEFFECTANALOGDELAY_H_ */ diff --git a/src/BAGuitar.h b/src/BAGuitar.h index 0e3119e..65de0c9 100644 --- a/src/BAGuitar.h +++ b/src/BAGuitar.h @@ -28,4 +28,6 @@ #include "BAGpio.h" #include "BAAudioEffectDelayExternal.h" +#include "AudioEffectAnalogDelay.h" + #endif /* __SRC_BATGUITAR_H */ diff --git a/src/BAHardware.h b/src/BAHardware.h index 690b1da..100da33 100644 --- a/src/BAHardware.h +++ b/src/BAHardware.h @@ -23,6 +23,8 @@ #ifndef SRC_BAHARDWARE_H_ #define SRC_BAHARDWARE_H_ +#include + /**************************************************************************//** * BAGuitar is a namespace/Library for Guitar processing from Blackaddr Audio. *****************************************************************************/ @@ -62,15 +64,16 @@ enum MemSelect : unsigned { MEM0 = 0, ///< SPI RAM MEM0 MEM1 = 1 ///< SPI RAM MEM1 }; -constexpr int MEM0_MAX_ADDR = 131071; ///< Max address size per chip -constexpr int MEM1_MAX_ADDR = 131071; ///< Max address size per chip +constexpr size_t MEM_MAX_ADDR[NUM_MEM_SLOTS] = { 131071, 131071 }; +//constexpr int MEM0_MAX_ADDR = 131071; ///< Max address size per chip +//constexpr int MEM1_MAX_ADDR = 131071; ///< Max address size per chip /**************************************************************************//** * General Purpose SPI Interfaces *****************************************************************************/ -enum class SpiDeviceId { - SPI_DEVICE0, ///< Arduino SPI device - SPI_DEVICE1 ///< Arduino SPI1 device +enum SpiDeviceId : unsigned { + SPI_DEVICE0 = 0, ///< Arduino SPI device + SPI_DEVICE1 = 1 ///< Arduino SPI1 device }; constexpr int SPI_MAX_ADDR = 131071; ///< Max address size per chip diff --git a/src/BASpiMemory.h b/src/BASpiMemory.h index d62c925..fc5046f 100644 --- a/src/BASpiMemory.h +++ b/src/BASpiMemory.h @@ -57,7 +57,7 @@ public: void zero(size_t address, size_t numBytes); void write16(size_t address, uint16_t data); - void write16(size_t address, uint16_t *data, size_t numBytes); + void write16(size_t address, uint16_t *data, size_t numWords); void zero16(size_t address, size_t numBytes); @@ -71,7 +71,7 @@ public: /// @param address the address in the SPI RAM to read from /// @return the data that was read uint16_t read16(size_t address); - void read16(size_t address, uint16_t *data, size_t numBytes); + void read16(size_t address, uint16_t *data, size_t numWords); private: diff --git a/src/LibBasicFunctions.cpp b/src/LibBasicFunctions.cpp index 59ce791..25e3178 100644 --- a/src/LibBasicFunctions.cpp +++ b/src/LibBasicFunctions.cpp @@ -9,27 +9,6 @@ namespace BAGuitar { -void updateAudioMemorySlot(BASpiMemory *mem, MemSlot slot, audio_block_t *block) -{ - if (block) { - if (slot.currentPosition + AUDIO_BLOCK_SAMPLES-1 <= slot.end) { - // entire block fits in memory slot without wrapping - mem->write16(slot.currentPosition, (uint16_t *)block->data, AUDIO_BLOCK_SAMPLES); // cast audio data to uint. - } else { - // this write will wrap the memory slot - size_t numBytes = slot.end - slot.currentPosition + 1; - mem->write16(slot.currentPosition, (uint16_t *)block->data, numBytes); - size_t remainingBytes = AUDIO_BLOCK_SAMPLES - numBytes; // calculate the remaining bytes - mem->write16(slot.start, (uint16_t *)block->data + numBytes, remainingBytes); // write remaining bytes are start - } - } -} - - -void zeroMemorySlot(BASpiMemory *mem, MemSlot slot) -{ - mem->zero16(slot.start, slot.end-slot.start+1); -} } diff --git a/src/LibBasicFunctions.h b/src/LibBasicFunctions.h index e007cf0..386fabf 100644 --- a/src/LibBasicFunctions.h +++ b/src/LibBasicFunctions.h @@ -5,17 +5,85 @@ * Author: slascos */ +#include +#include + +#include "Arduino.h" + #ifndef SRC_LIBBASICFUNCTIONS_H_ #define SRC_LIBBASICFUNCTIONS_H_ -#include "Audio.h" -#include "LibExtMemoryManagement.h" -#include "BASpiMemory.h" - namespace BAGuitar { -void updateAudioMemorySlot(BAGuitar::BASpiMemory *mem, MemSlot slot, audio_block_t *block); -void zeroMemorySlot(BAGuitar::BASpiMemory *mem, MemSlot slot); +struct INTERNAL_MEMORY {}; +struct EXTERNAL_MEMORY {}; + +template +class RingBuffer { +public: + RingBuffer() = delete; + RingBuffer(const size_t maxSize) : m_maxSize(maxSize) { + m_buffer = new T[maxSize]; + } + virtual ~RingBuffer(){ + if (m_buffer) delete [] m_buffer; + } + + int push_back(T element) { + + if ( (m_head == m_tail) && (m_size > 0) ) { + // overflow + Serial.println("RingBuffer::push_back: overflow"); + return -1; + } + + m_buffer[m_head] = element; + if (m_head < m_maxSize-1) { + m_head++; + } else { + m_head = 0; + } + m_size++; + return 0; + } + + int pop_front() { + if (m_size == 0) { + // buffer is empty + Serial.println("RingBuffer::pop_front: buffer is empty\n"); + return -1; + } + if (m_tail < m_maxSize-1) { + m_tail++; + } else { + m_tail = 0; + } + m_size--; + return 0; + } + + T front() const { + return m_buffer[m_tail]; + } + + size_t size() const { + size_t size = 0; + if (m_head == m_tail) { size = 0; } + else if (m_head > m_tail) { size = (m_head - m_tail); } + else { size = (m_tail - m_head + 1); } + return size; + } + + T& operator[] (size_t index) { + return m_buffer[index]; + } +private: + size_t m_head=0; + size_t m_tail=0; + size_t m_size=0; + T *m_buffer = nullptr; + const size_t m_maxSize; +}; } diff --git a/src/LibExtMemoryManagement.h b/src/LibExtMemoryManagement.h deleted file mode 100644 index 7350d58..0000000 --- a/src/LibExtMemoryManagement.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - * ExtMemoryManagement.h - * - * Created on: Dec 23, 2017 - * Author: slascos - */ - -#ifndef SRC_LIBEXTMEMORYMANAGEMENT_H_ -#define SRC_LIBEXTMEMORYMANAGEMENT_H_ - -#include - -#include "BAHardware.h" - -namespace BAGuitar { - -struct MemConfig { - size_t size; - size_t totalAvailable; - size_t nextAvailable; -}; - -struct MemSlot { - size_t start; - size_t end; - size_t currentPosition; -}; - -class ExternalSramManager { -public: - ExternalSramManager() = delete; - ExternalSramManager(BAGuitar::MemSelect mem) { - - // Initialize the static memory configuration structs - m_memConfig[MEM0].size = MEM0_MAX_ADDR; - m_memConfig[MEM0].totalAvailable = MEM0_MAX_ADDR; - m_memConfig[MEM0].nextAvailable = 0; - - m_memConfig[MEM0].size = MEM0_MAX_ADDR; - m_memConfig[MEM0].totalAvailable = MEM0_MAX_ADDR; - m_memConfig[MEM0].nextAvailable = 0; - } - - - bool getMemory(BAGuitar::MemSelect mem, size_t size, MemSlot &slot) { - if (m_memConfig[mem].totalAvailable >= size) { - slot.start = m_memConfig[mem].nextAvailable; - slot.end = slot.start + size -1; - slot.currentPosition = slot.start; - - // Update the mem config - m_memConfig[mem].nextAvailable = slot.end+1; - m_memConfig[mem].totalAvailable -= size; - return true; - } else { - return false; - } - } - - static MemConfig m_memConfig[BAGuitar::NUM_MEM_SLOTS]; - -}; - -} - -#endif /* SRC_LIBEXTMEMORYMANAGEMENT_H_ */ diff --git a/src/LibMemoryManagement.h b/src/LibMemoryManagement.h new file mode 100644 index 0000000..118fbfd --- /dev/null +++ b/src/LibMemoryManagement.h @@ -0,0 +1,119 @@ +/* + * ExtMemoryManagement.h + * + * Created on: Dec 23, 2017 + * Author: slascos + */ + +#ifndef SRC_LIBMEMORYMANAGEMENT_H_ +#define SRC_LIBMEMORYMANAGEMENT_H_ + +#include + + +#include "Audio.h" + +#include "BAHardware.h" +#include "BASpiMemory.h" +#include "LibBasicFunctions.h" + +namespace BAGuitar { + +struct QueuePosition { + int offset; + int index; +}; +QueuePosition calcQueuePosition(float milliseconds); +QueuePosition calcQueuePosition(size_t numSamples); +size_t calcAudioSamples(float milliseconds); + +struct MemConfig { + size_t size; + size_t totalAvailable; + size_t nextAvailable; + BASpiMemory *m_spi = nullptr; +}; + +class ExternalSramManager; // forward declare so MemSlot can setup friendship + +class MemBufferIF { +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 { +public: + //MemAudioBlock(); + MemAudioBlock() = delete; + MemAudioBlock(size_t numSamples); + MemAudioBlock(float milliseconds); + + virtual ~MemAudioBlock(); + + bool push(audio_block_t *block); + audio_block_t *pop(); + audio_block_t *getQueue(size_t index) { return m_queues[index]; } + size_t getNumQueues() const { return m_queues.size(); } + 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 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: + friend ExternalSramManager; + size_t m_start; + size_t m_end; + size_t m_currentPosition; + BASpiMemory *m_spi = nullptr; +}; + + +class ExternalSramManager final { +public: + ExternalSramManager() = delete; + ExternalSramManager(unsigned numMemories); + virtual ~ExternalSramManager(); + + size_t availableMemory(BAGuitar::MemSelect mem); + bool requestMemory(MemSlot &slot, float delayMilliseconds, BAGuitar::MemSelect mem = BAGuitar::MemSelect::MEM0); + bool requestMemory(MemSlot &slot, size_t sizeBytes, BAGuitar::MemSelect mem = BAGuitar::MemSelect::MEM0); + +private: + static bool m_configured; + static MemConfig m_memConfig[BAGuitar::NUM_MEM_SLOTS]; + +}; + + +} + +#endif /* SRC_LIBMEMORYMANAGEMENT_H_ */ diff --git a/src/LibMemoryManagment.cpp b/src/LibMemoryManagment.cpp new file mode 100644 index 0000000..576ff67 --- /dev/null +++ b/src/LibMemoryManagment.cpp @@ -0,0 +1,360 @@ + +#include +#include + +#include "LibMemoryManagement.h" + +namespace BAGuitar { + + + +bool ExternalSramManager::m_configured = false; +MemConfig ExternalSramManager::m_memConfig[BAGuitar::NUM_MEM_SLOTS]; + +inline 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) +{ +// // 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() +{ + +} + +bool MemAudioBlock::push(audio_block_t *block) +{ + m_queues.push_back(block); + return true; +} + +audio_block_t* 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 *dataPtr, 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; + + while (samplesRemaining > 0) { + size_t numSamplesToWrite; + + void *start = static_cast(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 (dataPtr) { + memcpy(start, dataPtr, numSamplesToWrite * sizeof(int16_t)); + } else { + memset(start, 0, numSamplesToWrite * sizeof(int16_t)); + } + } + 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 + + // Calculate the queue position + auto position = calcQueuePosition(srcOffset); + int readOffset = 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 = numSamples; + + while (samplesRemaining > 0) { + size_t numSamplesToRead; + + void *srcStart = static_cast(m_queues[index]->data + readOffset); + void *destStart = static_cast(dest + destOffset); + + // determine if the transfer will complete or will hit the end of a block first + if ( (readOffset + samplesRemaining) > AUDIO_BLOCK_SAMPLES ) { + // goes past end of the queue + numSamplesToRead = (AUDIO_BLOCK_SAMPLES - readOffset); + readOffset = 0; + index++; + } else { + // transfer ends in this audio block + numSamplesToRead = samplesRemaining; + readOffset += numSamplesToRead; + } + + // perform the transfer + if (!m_queues[index]) { + // no allocated audio block, copy zeros + memset(destStart, 0, numSamplesToRead * sizeof(int16_t)); + } else { + memcpy(srcStart, destStart, numSamplesToRead * sizeof(int16_t)); + } + samplesRemaining -= numSamplesToRead; + } + 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 +///////////////////////////////////////////////////////////////////////////// +bool MemSlot::clear() +{ + if (!m_valid) { return false; } + m_spi->zero16(m_start, m_size); + return true; +} + +bool MemSlot::write16(size_t offset, int16_t *dataPtr, size_t dataSize) +{ + if (!m_valid) { return false; } + if ((offset + dataSize-1) <= m_end) { + m_spi->write16(offset, reinterpret_cast(dataPtr), dataSize); // cast audio data to uint + return true; + } else { + // this would go past the end of the memory slot, do not perform the write + return false; + } +} + +bool MemSlot::zero16(size_t offset, size_t dataSize) +{ + if (!m_valid) { return false; } + if ((offset + dataSize-1) <= m_end) { + m_spi->zero16(offset, dataSize); // cast audio data to uint + return true; + } else { + // this would go past the end of the memory slot, do not perform the write + return false; + } +} + +bool MemSlot::read16(int16_t *dest, size_t destOffset, size_t srcOffset, size_t numData) +{ + if (!dest) return false; // invalid destination + if ((srcOffset + (numData*sizeof(int16_t))-1) <= m_end) { + m_spi->read16(srcOffset, reinterpret_cast(dest), numData); + return true; + } else { + // this would go past the end of the memory slot, do not perform the write + return false; + } +} + +bool MemSlot::writeAdvance16(int16_t *dataPtr, size_t dataSize) +{ + if (!m_valid) { return false; } + if (m_currentPosition + dataSize-1 <= m_end) { + // entire block fits in memory slot without wrapping + m_spi->write16(m_currentPosition, reinterpret_cast(dataPtr), dataSize); // cast audio data to uint. + m_currentPosition += dataSize; + + } else { + // this write will wrap the memory slot + size_t numBytes = m_end - m_currentPosition + 1; + m_spi->write16(m_currentPosition, reinterpret_cast(dataPtr), numBytes); + size_t remainingBytes = dataSize - numBytes; // calculate the remaining bytes + m_spi->write16(m_start, reinterpret_cast(dataPtr + numBytes), remainingBytes); // write remaining bytes are start + m_currentPosition = m_start + remainingBytes; + } + return true; +} + +bool MemSlot::zeroAdvance16(size_t dataSize) +{ + if (!m_valid) { return false; } + if (m_currentPosition + dataSize-1 <= m_end) { + // entire block fits in memory slot without wrapping + m_spi->zero16(m_currentPosition, dataSize); // cast audio data to uint. + m_currentPosition += dataSize; + + } else { + // this write will wrap the memory slot + size_t numBytes = m_end - m_currentPosition + 1; + m_spi->zero16(m_currentPosition, numBytes); + size_t remainingBytes = dataSize - numBytes; // calculate the remaining bytes + m_spi->zero16(m_start, remainingBytes); // write remaining bytes are start + m_currentPosition = m_start + remainingBytes; + } + return true; +} + + +///////////////////////////////////////////////////////////////////////////// +// EXTERNAL SRAM MANAGER +///////////////////////////////////////////////////////////////////////////// +ExternalSramManager::ExternalSramManager(unsigned numMemories) +{ + // Initialize the static memory configuration structs + if (!m_configured) { + for (unsigned i=0; i < NUM_MEM_SLOTS; i++) { + m_memConfig[i].size = MEM_MAX_ADDR[i]; + m_memConfig[i].totalAvailable = MEM_MAX_ADDR[i]; + m_memConfig[i].nextAvailable = 0; + m_memConfig[i].m_spi = new BAGuitar::BASpiMemory(static_cast(i)); + } + m_configured = true; + } +} + +ExternalSramManager::~ExternalSramManager() +{ + for (unsigned i=0; i < NUM_MEM_SLOTS; i++) { + if (m_memConfig[i].m_spi) { delete m_memConfig[i].m_spi; } + } +} + +size_t ExternalSramManager::availableMemory(BAGuitar::MemSelect mem) +{ + return m_memConfig[mem].totalAvailable; +} + +bool ExternalSramManager::requestMemory(MemSlot &slot, float delayMilliseconds, BAGuitar::MemSelect mem) +{ + // convert the time to numer of samples + size_t delayLengthInt = (size_t)((delayMilliseconds*(AUDIO_SAMPLE_RATE_EXACT/1000.0f))+0.5f); + return requestMemory(slot, delayLengthInt, mem); +} + +bool ExternalSramManager::requestMemory(MemSlot &slot, size_t sizeBytes, BAGuitar::MemSelect mem) +{ + if (m_memConfig[mem].totalAvailable >= sizeBytes) { + // there is enough available memory for this request + slot.m_start = m_memConfig[mem].nextAvailable; + slot.m_end = slot.m_start + sizeBytes -1; + slot.m_currentPosition = slot.m_start; // init to start of slot + slot.m_size = sizeBytes; + slot.m_spi = m_memConfig[mem].m_spi; + + // Update the mem config + m_memConfig[mem].nextAvailable = slot.m_end+1; + m_memConfig[mem].totalAvailable -= sizeBytes; + slot.m_valid = true; + return true; + } else { + // there is not enough memory available for the request + return false; + } +} + +} +