You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
BALibrary_HW/src/effects/AudioEffectDelayExternal.cpp

294 lines
7.5 KiB

/*
* BAAudioEffectDelayExternal.cpp
*
* Created on: November 1, 2017
* Author: slascos
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.*
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "BAAudioEffectDelayExternal.h"
using namespace BALibrary;
namespace BAEffects {
#define SPISETTING SPISettings(20000000, MSBFIRST, SPI_MODE0)
struct MemSpiConfig {
unsigned mosiPin;
unsigned misoPin;
unsigned sckPin;
unsigned csPin;
unsigned memSize;
};
constexpr MemSpiConfig Mem0Config = {7, 8, 14, 15, 65536 };
constexpr MemSpiConfig Mem1Config = {21, 5, 20, 31, 65536 };
unsigned BAAudioEffectDelayExternal::m_usingSPICount[2] = {0,0};
BAAudioEffectDelayExternal::BAAudioEffectDelayExternal()
: AudioStream(1, m_inputQueueArray)
{
initialize(MemSelect::MEM0);
}
BAAudioEffectDelayExternal::BAAudioEffectDelayExternal(MemSelect mem)
: AudioStream(1, m_inputQueueArray)
{
initialize(mem);
}
BAAudioEffectDelayExternal::BAAudioEffectDelayExternal(BALibrary::MemSelect type, float delayLengthMs)
: AudioStream(1, m_inputQueueArray)
{
unsigned delayLengthInt = (delayLengthMs*(AUDIO_SAMPLE_RATE_EXACT/1000.0f))+0.5f;
initialize(type, delayLengthInt);
}
BAAudioEffectDelayExternal::~BAAudioEffectDelayExternal()
{
if (m_spi) delete m_spi;
}
void BAAudioEffectDelayExternal::delay(uint8_t channel, float milliseconds) {
if (channel >= 8) return;
if (milliseconds < 0.0) milliseconds = 0.0;
uint32_t n = (milliseconds*(AUDIO_SAMPLE_RATE_EXACT/1000.0f))+0.5f;
n += AUDIO_BLOCK_SAMPLES;
if (n > m_memoryLength - AUDIO_BLOCK_SAMPLES)
n = m_memoryLength - AUDIO_BLOCK_SAMPLES;
m_channelDelayLength[channel] = n;
unsigned mask = m_activeMask;
if (m_activeMask == 0) m_startUsingSPI(m_spiChannel);
m_activeMask = mask | (1<<channel);
}
void BAAudioEffectDelayExternal::disable(uint8_t channel) {
if (channel >= 8) return;
uint8_t mask = m_activeMask & ~(1<<channel);
m_activeMask = mask;
if (mask == 0) m_stopUsingSPI(m_spiChannel);
}
void BAAudioEffectDelayExternal::update(void)
{
audio_block_t *block;
uint32_t n, channel, read_offset;
// grab incoming data and put it into the memory
block = receiveReadOnly();
if (block) {
if (m_headOffset + AUDIO_BLOCK_SAMPLES <= m_memoryLength) {
// a single write is enough
write(m_headOffset, AUDIO_BLOCK_SAMPLES, block->data);
m_headOffset += AUDIO_BLOCK_SAMPLES;
} else {
// write wraps across end-of-memory
n = m_memoryLength - m_headOffset;
write(m_headOffset, n, block->data);
m_headOffset = AUDIO_BLOCK_SAMPLES - n;
write(0, m_headOffset, block->data + n);
}
release(block);
} else {
// if no input, store zeros, so later playback will
// not be random garbage previously stored in memory
if (m_headOffset + AUDIO_BLOCK_SAMPLES <= m_memoryLength) {
zero(m_headOffset, AUDIO_BLOCK_SAMPLES);
m_headOffset += AUDIO_BLOCK_SAMPLES;
} else {
n = m_memoryLength - m_headOffset;
zero(m_headOffset, n);
m_headOffset = AUDIO_BLOCK_SAMPLES - n;
zero(0, m_headOffset);
}
}
// transmit the delayed outputs
for (channel = 0; channel < 8; channel++) {
if (!(m_activeMask & (1<<channel))) continue;
block = allocate();
if (!block) continue;
// compute the delayed location where we read
if (m_channelDelayLength[channel] <= m_headOffset) {
read_offset = m_headOffset - m_channelDelayLength[channel];
} else {
read_offset = m_memoryLength + m_headOffset - m_channelDelayLength[channel];
}
if (read_offset + AUDIO_BLOCK_SAMPLES <= m_memoryLength) {
// a single read will do it
read(read_offset, AUDIO_BLOCK_SAMPLES, block->data);
} else {
// read wraps across end-of-memory
n = m_memoryLength - read_offset;
read(read_offset, n, block->data);
read(0, AUDIO_BLOCK_SAMPLES - n, block->data + n);
}
transmit(block, channel);
release(block);
}
}
unsigned BAAudioEffectDelayExternal::m_allocated[2] = {0, 0};
void BAAudioEffectDelayExternal::initialize(MemSelect mem, unsigned delayLength)
{
unsigned samples = 0;
unsigned memsize, avail;
m_activeMask = 0;
m_headOffset = 0;
m_mem = mem;
switch (mem) {
case MemSelect::MEM0 :
{
memsize = Mem0Config.memSize;
m_spi = &SPI;
m_spiChannel = 0;
m_misoPin = Mem0Config.misoPin;
m_mosiPin = Mem0Config.mosiPin;
m_sckPin = Mem0Config.sckPin;
m_csPin = Mem0Config.csPin;
m_spi->setMOSI(m_mosiPin);
m_spi->setMISO(m_misoPin);
m_spi->setSCK(m_sckPin);
m_spi->begin();
break;
}
case MemSelect::MEM1 :
{
#if defined(__MK64FX512__) || defined(__MK66FX1M0__)
memsize = Mem1Config.memSize;
m_spi = &SPI1;
m_spiChannel = 1;
m_misoPin = Mem1Config.misoPin;
m_mosiPin = Mem1Config.mosiPin;
m_sckPin = Mem1Config.sckPin;
m_csPin = Mem1Config.csPin;
m_spi->setMOSI(m_mosiPin);
m_spi->setMISO(m_misoPin);
m_spi->setSCK(m_sckPin);
m_spi->begin();
#endif
break;
}
}
pinMode(m_csPin, OUTPUT);
digitalWriteFast(m_csPin, HIGH);
avail = memsize - m_allocated[mem];
if (delayLength > avail) samples = avail;
m_memoryStart = m_allocated[mem];
m_allocated[mem] += samples;
m_memoryLength = samples;
zero(0, m_memoryLength);
}
void BAAudioEffectDelayExternal::read(uint32_t offset, uint32_t count, int16_t *data)
{
uint32_t addr = m_memoryStart + offset;
addr *= 2;
m_spi->beginTransaction(SPISETTING);
digitalWriteFast(m_csPin, LOW);
m_spi->transfer16((0x03 << 8) | (addr >> 16));
m_spi->transfer16(addr & 0xFFFF);
while (count) {
*data++ = (int16_t)(m_spi->transfer16(0));
count--;
}
digitalWriteFast(m_csPin, HIGH);
m_spi->endTransaction();
}
void BAAudioEffectDelayExternal::write(uint32_t offset, uint32_t count, const int16_t *data)
{
uint32_t addr = m_memoryStart + offset;
addr *= 2;
m_spi->beginTransaction(SPISETTING);
digitalWriteFast(m_csPin, LOW);
m_spi->transfer16((0x02 << 8) | (addr >> 16));
m_spi->transfer16(addr & 0xFFFF);
while (count) {
int16_t w = 0;
if (data) w = *data++;
m_spi->transfer16(w);
count--;
}
digitalWriteFast(m_csPin, HIGH);
m_spi->endTransaction();
}
///////////////////////////////////////////////////////////////////
// PRIVATE METHODS
///////////////////////////////////////////////////////////////////
void BAAudioEffectDelayExternal::zero(uint32_t address, uint32_t count) {
write(address, count, NULL);
}
#ifdef SPI_HAS_NOTUSINGINTERRUPT
inline void BAAudioEffectDelayExternal::m_startUsingSPI(int spiBus) {
if (spiBus == 0) {
m_spi->usingInterrupt(IRQ_SOFTWARE);
} else if (spiBus == 1) {
m_spi->usingInterrupt(IRQ_SOFTWARE);
}
m_usingSPICount[spiBus]++;
}
inline void BAAudioEffectDelayExternal::m_stopUsingSPI(int spiBus) {
if (m_usingSPICount[spiBus] == 0 || --m_usingSPICount[spiBus] == 0)
{
if (spiBus == 0) {
m_spi->notUsingInterrupt(IRQ_SOFTWARE);
} else if (spiBus == 1) {
m_spi->notUsingInterrupt(IRQ_SOFTWARE);
}
}
}
#else
inline void BAAudioEffectDelayExternal::m_startUsingSPI(int spiBus) {
if (spiBus == 0) {
m_spi->usingInterrupt(IRQ_SOFTWARE);
} else if (spiBus == 1) {
m_spi->usingInterrupt(IRQ_SOFTWARE);
}
}
inline void BAAudioEffectDelayExternal::m_stopUsingSPI(int spiBus) {
}
#endif
} /* namespace BAEffects */