forked from wirtz/BALibrary
parent
988174912b
commit
d2a8227f91
@ -0,0 +1,118 @@ |
||||
/*
|
||||
* AudioEffectAnalogDelay.cpp |
||||
* |
||||
* Created on: Jan 7, 2018 |
||||
* Author: slascos |
||||
*/ |
||||
#include <new> |
||||
#include "AudioEffectAnalogDelay.h" |
||||
|
||||
namespace BAGuitar { |
||||
|
||||
AudioEffectAnalogDelay::AudioEffectAnalogDelay(INTERNAL_MEMORY, float maxDelay) |
||||
: AudioStream(1, m_inputQueueArray) |
||||
{ |
||||
m_memory = new MemAudioBlock(maxDelay); |
||||
for (int i=0; i<MAX_DELAY_CHANNELS; i++) { |
||||
m_channelOffsets[i] = 0; |
||||
} |
||||
} |
||||
|
||||
// requires preallocated memory large enough
|
||||
AudioEffectAnalogDelay::AudioEffectAnalogDelay(EXTERNAL_MEMORY, MemSlot &slot) |
||||
: AudioStream(1, m_inputQueueArray) |
||||
{ |
||||
m_memory = &slot; |
||||
for (int i=0; i<MAX_DELAY_CHANNELS; i++) { |
||||
m_channelOffsets[i] = 0; |
||||
} |
||||
m_memory->clear(); |
||||
} |
||||
|
||||
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<MemAudioBlock*>(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<MAX_DELAY_CHANNELS; channel++) { |
||||
if (!(m_activeChannels & (1<<channel))) continue; // If this channel is disable, skip to the next;
|
||||
|
||||
if (!m_externalMemory) { |
||||
// internal memory
|
||||
MemAudioBlock *memory = reinterpret_cast<MemAudioBlock*>(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<<channel; |
||||
return true; |
||||
} |
||||
|
||||
void AudioEffectAnalogDelay::disable(unsigned channel) |
||||
{ |
||||
m_activeChannels &= ~(1<<channel); |
||||
} |
||||
|
||||
} |
||||
|
||||
|
@ -0,0 +1,45 @@ |
||||
/*
|
||||
* AudioEffectAnalogDelay.h |
||||
* |
||||
* Created on: Jan 7, 2018 |
||||
* Author: slascos |
||||
*/ |
||||
|
||||
#ifndef SRC_AUDIOEFFECTANALOGDELAY_H_ |
||||
#define SRC_AUDIOEFFECTANALOGDELAY_H_ |
||||
|
||||
#include <vector> |
||||
|
||||
#include <Audio.h> |
||||
#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_ */ |
@ -1,66 +0,0 @@ |
||||
/*
|
||||
* ExtMemoryManagement.h |
||||
* |
||||
* Created on: Dec 23, 2017 |
||||
* Author: slascos |
||||
*/ |
||||
|
||||
#ifndef SRC_LIBEXTMEMORYMANAGEMENT_H_ |
||||
#define SRC_LIBEXTMEMORYMANAGEMENT_H_ |
||||
|
||||
#include <cstddef> |
||||
|
||||
#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_ */ |
@ -0,0 +1,119 @@ |
||||
/*
|
||||
* ExtMemoryManagement.h |
||||
* |
||||
* Created on: Dec 23, 2017 |
||||
* Author: slascos |
||||
*/ |
||||
|
||||
#ifndef SRC_LIBMEMORYMANAGEMENT_H_ |
||||
#define SRC_LIBMEMORYMANAGEMENT_H_ |
||||
|
||||
#include <cstddef> |
||||
|
||||
|
||||
#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 <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: |
||||
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_ */ |
@ -0,0 +1,360 @@ |
||||
|
||||
#include <cstring> |
||||
#include <new> |
||||
|
||||
#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<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 (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<void*>(m_queues[index]->data + readOffset); |
||||
void *destStart = static_cast<void *>(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<uint16_t*>(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<uint16_t*>(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<uint16_t*>(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<uint16_t*>(dataPtr), numBytes); |
||||
size_t remainingBytes = dataSize - numBytes; // calculate the remaining bytes
|
||||
m_spi->write16(m_start, reinterpret_cast<uint16_t*>(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<BAGuitar::SpiDeviceId>(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; |
||||
} |
||||
} |
||||
|
||||
} |
||||
|
Loading…
Reference in new issue