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