cleand up AudioEffectAnalogDelay, added wet/dry mix and feedback controls, removed independent channels

master
Steve Lascos 7 years ago
parent 12fbdcff8d
commit 6b59f022b3
  1. 115
      src/AudioEffectAnalogDelay.cpp
  2. 22
      src/AudioEffectAnalogDelay.h
  3. 12
      src/LibBasicFunctions.cpp
  4. 7
      src/LibBasicFunctions.h

@ -13,18 +13,12 @@ AudioEffectAnalogDelay::AudioEffectAnalogDelay(float maxDelay)
: AudioStream(1, m_inputQueueArray) : AudioStream(1, m_inputQueueArray)
{ {
m_memory = new AudioDelay(maxDelay); m_memory = new AudioDelay(maxDelay);
for (int i=0; i<MAX_DELAY_CHANNELS; i++) {
m_channelOffsets[i] = 0;
}
} }
AudioEffectAnalogDelay::AudioEffectAnalogDelay(size_t numSamples) AudioEffectAnalogDelay::AudioEffectAnalogDelay(size_t numSamples)
: AudioStream(1, m_inputQueueArray) : AudioStream(1, m_inputQueueArray)
{ {
m_memory = new AudioDelay(numSamples); m_memory = new AudioDelay(numSamples);
for (int i=0; i<MAX_DELAY_CHANNELS; i++) {
m_channelOffsets[i] = 0;
}
} }
// requires preallocated memory large enough // requires preallocated memory large enough
@ -33,9 +27,6 @@ AudioEffectAnalogDelay::AudioEffectAnalogDelay(ExtMemSlot *slot)
{ {
m_memory = new AudioDelay(slot); m_memory = new AudioDelay(slot);
m_externalMemory = true; m_externalMemory = true;
for (int i=0; i<MAX_DELAY_CHANNELS; i++) {
m_channelOffsets[i] = 0;
}
} }
AudioEffectAnalogDelay::~AudioEffectAnalogDelay() AudioEffectAnalogDelay::~AudioEffectAnalogDelay()
@ -47,6 +38,32 @@ void AudioEffectAnalogDelay::update(void)
{ {
audio_block_t *inputAudioBlock = receiveReadOnly(); // get the next block of input samples audio_block_t *inputAudioBlock = receiveReadOnly(); // get the next block of input samples
if (!inputAudioBlock) {
// create silence
inputAudioBlock = allocate();
if (!inputAudioBlock) { return; } // failed to allocate
else {
clearAudioBlock(inputAudioBlock);
}
}
if (m_enable == false) {
// release all held memory resources
transmit(inputAudioBlock);
release(inputAudioBlock); inputAudioBlock = nullptr;
if (m_previousBlock) {
release(m_previousBlock); m_previousBlock = nullptr;
}
if (!m_externalMemory) {
while (m_memory->getRingBuffer()->size() > 0) {
audio_block_t *releaseBlock = m_memory->getRingBuffer()->front();
m_memory->getRingBuffer()->pop_front();
if (releaseBlock) release(releaseBlock);
}
}
}
if (m_callCount < 1024) { if (m_callCount < 1024) {
if (inputAudioBlock) { if (inputAudioBlock) {
transmit(inputAudioBlock, 0); transmit(inputAudioBlock, 0);
@ -59,10 +76,12 @@ 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); // Preprocessing
if (blockToRelease) release(blockToRelease); audio_block_t *preProcessed = allocate();
m_preProcessing(preProcessed, inputAudioBlock, m_previousBlock);
Serial.print("Active channels: "); Serial.print(m_activeChannels, HEX); Serial.println(""); audio_block_t *blockToRelease = m_memory->addBlock(preProcessed);
if (blockToRelease) release(blockToRelease);
// if (inputAudioBlock) { // if (inputAudioBlock) {
// transmit(inputAudioBlock, 0); // transmit(inputAudioBlock, 0);
@ -70,44 +89,25 @@ void AudioEffectAnalogDelay::update(void)
// } // }
// return; // return;
// For each active channel, output the delayed audio // OUTPUT PROCESSING
audio_block_t *blockToOutput = nullptr; audio_block_t *blockToOutput = nullptr;
for (int channel=0; channel<MAX_DELAY_CHANNELS; channel++) { blockToOutput = allocate();
if (!(m_activeChannels & (1<<channel))) continue; // If this channel is disable, skip to the next;
if (!m_externalMemory) {
// internal memory
QueuePosition queuePosition = calcQueuePosition(m_channelOffsets[channel]);
Serial.println(String("Position info: ") + queuePosition.index + " : " + queuePosition.offset);
if (queuePosition.offset == 0) {
// only one queue needs to be read out
//Serial.println(String("Directly sending queue offset ") + queuePosition.index);
blockToOutput= m_memory->getBlock(queuePosition.index); // could be nullptr!
if (blockToOutput) transmit(blockToOutput);
continue;
} else {
blockToOutput = allocate(); // allocate if spanning 2 queues
}
} else {
// external memory
blockToOutput = allocate(); // allocate always for external memory
}
// copy the output data // copy the output data
if (!blockToOutput) continue; // skip this channel due to failure if (!blockToOutput) return; // skip this time due to failure
// copy over data // copy over data
m_memory->getSamples(blockToOutput, m_channelOffsets[channel]); m_memory->getSamples(blockToOutput, m_delaySamples);
// perform the mix
m_postProcessing(blockToOutput, inputAudioBlock, blockToOutput);
transmit(blockToOutput); transmit(blockToOutput);
release(blockToOutput);
} release(inputAudioBlock);
release(m_previousBlock);
m_previousBlock = blockToOutput;
} }
bool AudioEffectAnalogDelay::delay(unsigned channel, float milliseconds) void AudioEffectAnalogDelay::delay(float milliseconds)
{ {
if (channel > MAX_DELAY_CHANNELS-1) // channel id too high
return false;
size_t delaySamples = calcAudioSamples(milliseconds); size_t delaySamples = calcAudioSamples(milliseconds);
if (!m_memory) { Serial.println("delay(): m_memory is not valid"); } if (!m_memory) { Serial.println("delay(): m_memory is not valid"); }
@ -128,16 +128,11 @@ bool AudioEffectAnalogDelay::delay(unsigned channel, float milliseconds)
} }
} }
m_channelOffsets[channel] = delaySamples; m_delaySamples = delaySamples;
m_activeChannels |= 1<<channel;
return true;
} }
bool AudioEffectAnalogDelay::delay(unsigned channel, size_t delaySamples) void AudioEffectAnalogDelay::delay(size_t delaySamples)
{ {
if (channel > MAX_DELAY_CHANNELS-1) // channel id too high
return false;
if (!m_memory) { Serial.println("delay(): m_memory is not valid"); } if (!m_memory) { Serial.println("delay(): m_memory is not valid"); }
if (!m_externalMemory) { if (!m_externalMemory) {
@ -152,14 +147,26 @@ bool AudioEffectAnalogDelay::delay(unsigned channel, size_t delaySamples)
slot->enable(); slot->enable();
} }
} }
m_channelOffsets[channel] = delaySamples; m_delaySamples= delaySamples;
m_activeChannels |= 1<<channel; }
return true;
void AudioEffectAnalogDelay::m_preProcessing(audio_block_t *out, audio_block_t *dry, audio_block_t *wet)
{
if ( out && dry && wet) {
alphaBlend(out, dry, wet, m_feedback);
} else if (dry) {
memcpy(out->data, dry->data, sizeof(int16_t) * AUDIO_BLOCK_SAMPLES);
}
} }
void AudioEffectAnalogDelay::disable(unsigned channel) void AudioEffectAnalogDelay::m_postProcessing(audio_block_t *out, audio_block_t *dry, audio_block_t *wet)
{ {
m_activeChannels &= ~(1<<channel); if ( out && dry && wet) {
alphaBlend(out, dry, wet, m_mix);
} else if (dry) {
memcpy(out->data, dry->data, sizeof(int16_t) * AUDIO_BLOCK_SAMPLES);
}
} }
} }

@ -15,8 +15,6 @@
namespace BAGuitar { namespace BAGuitar {
class AudioEffectAnalogDelay : public AudioStream { class AudioEffectAnalogDelay : public AudioStream {
public: public:
static constexpr int MAX_DELAY_CHANNELS = 8; static constexpr int MAX_DELAY_CHANNELS = 8;
@ -28,20 +26,28 @@ public:
virtual ~AudioEffectAnalogDelay(); virtual ~AudioEffectAnalogDelay();
virtual void update(void); virtual void update(void);
bool delay(unsigned channel, float milliseconds); void delay(float milliseconds);
bool delay(unsigned channel, size_t delaySamples); void delay(size_t delaySamples);
void disable(unsigned channel); void feedback(float feedback) { m_feedback = feedback; }
void mix(float mix) { m_mix = mix; }
void enable() { m_enable = true; }
void disable() { m_enable = false; }
private: private:
audio_block_t *m_inputQueueArray[1]; audio_block_t *m_inputQueueArray[1];
unsigned m_activeChannels = 0; bool m_enable = false;
bool m_externalMemory = false; bool m_externalMemory = false;
AudioDelay *m_memory = nullptr; AudioDelay *m_memory = nullptr;
//size_t m_numQueues = 0; size_t m_delaySamples = 0;
size_t m_channelOffsets[MAX_DELAY_CHANNELS]; float m_feedback = 0.0f;
float m_mix = 0.0f;
size_t m_callCount = 0; size_t m_callCount = 0;
audio_block_t *m_previousBlock = nullptr;
void m_preProcessing(audio_block_t *out, audio_block_t *dry, audio_block_t *wet);
void m_postProcessing(audio_block_t *out, audio_block_t *dry, audio_block_t *wet);
}; };
} }

@ -32,6 +32,18 @@ size_t calcOffset(QueuePosition position)
return (position.index*AUDIO_BLOCK_SAMPLES) + position.offset; return (position.index*AUDIO_BLOCK_SAMPLES) + position.offset;
} }
audio_block_t alphaBlend(audio_block_t *out, audio_block_t *dry, audio_block_t* wet, float mix)
{
for (int i=0; i< AUDIO_BLOCK_SAMPLES; i++) {
out->data[i] = (dry->data[i] * (1 - mix)) + (wet->data[i] * mix);
}
}
void clearAudioBlock(audio_block_t *block)
{
memset(block->data, 0, sizeof(int16_t)*AUDIO_BLOCK_SAMPLES);
}
AudioDelay::AudioDelay(size_t maxSamples) AudioDelay::AudioDelay(size_t maxSamples)
: m_slot(nullptr) : m_slot(nullptr)

@ -68,6 +68,11 @@ size_t calcAudioSamples(float milliseconds);
/// specified position. /// specified position.
size_t calcOffset(QueuePosition position); size_t calcOffset(QueuePosition position);
void clearAudioBlock(audio_block_t *block);
audio_block_t alphaBlend(audio_block_t *out, audio_block_t *dry, audio_block_t* wet, float mix);
template <class T> template <class T>
class RingBuffer; // forward declare so AudioDelay can use it. class RingBuffer; // forward declare so AudioDelay can use it.
@ -133,6 +138,8 @@ public:
/// @returns pointer to the underlying ExtMemSlot. /// @returns pointer to the underlying ExtMemSlot.
ExtMemSlot *getSlot() const { return m_slot; } ExtMemSlot *getSlot() const { return m_slot; }
RingBuffer<audio_block_t*> *getRingBuffer() const { return m_ringBuffer; }
private: private:
/// enumerates whether the underlying memory buffer uses INTERNAL or EXTERNAL memory /// enumerates whether the underlying memory buffer uses INTERNAL or EXTERNAL memory

Loading…
Cancel
Save