/* * AudioEffectSimpleChorus.cpp * * Created on: Jan 7, 2018 * Author: slascos */ #include #include // std::roundf #include "AudioEffectAnalogDelayFilters.h" #include "AudioEffectSimpleChorus.h" using namespace BALibrary; namespace BAEffects { constexpr int MIDI_CHANNEL = 0; constexpr int MIDI_CONTROL = 1; AudioEffectSimpleChorus::AudioEffectSimpleChorus(float maxDelayMs) : AudioStream(1, m_inputQueueArray) { delay(maxDelayMs); m_memory = new AudioDelay(maxDelayMs); m_maxDelaySamples = calcAudioSamples(maxDelayMs); lfo.setRateAudio(m_frequency); } AudioEffectSimpleChorus::~AudioEffectSimpleChorus() { if (m_memory) delete m_memory; } void AudioEffectSimpleChorus::update(void) { audio_block_t *inputAudioBlock = receiveReadOnly(); // get the next block of input samples /* // Check is block is disablee if (m_enable == false) { // do not transmit or process any audio, return as quickly as possible. if (inputAudioBlock) release(inputAudioBlock); // when using internal memory we have to release all references in the ring buffer while (m_memory->getRingBuffer()->size() > 0) { audio_block_t *releaseBlock = m_memory->getRingBuffer()->front(); m_memory->getRingBuffer()->pop_front(); if (releaseBlock) release(releaseBlock); } return; } // Check is block is bypassed, if so either transmit input directly or create silence if (m_bypass == true) { // transmit the input directly if (!inputAudioBlock) { // create silence inputAudioBlock = allocate(); if (!inputAudioBlock) { return; } // failed to allocate else { clearAudioBlock(inputAudioBlock); } } transmit(inputAudioBlock, 0); release(inputAudioBlock); return; } */ // Otherwise perform normal processing // In order to make use of the SPI DMA, we need to request the read from memory first, // then do other processing while it fills in the back. audio_block_t *blockToOutput = nullptr; // this will hold the output audio blockToOutput = allocate(); if (!blockToOutput) return; // skip this update cycle due to failure memset(blockToOutput->data,0,AUDIO_BLOCK_SAMPLES * sizeof(int16_t)); audio_block_t *blockToRelease = m_memory->addBlock(inputAudioBlock); // Chorus size_t half_delay_samples=size_t(float(m_delaySamples)/2+0.5); float *mod = lfo.getNextVector(); audio_block_t *lfoData = nullptr; lfoData = allocate(); if (!lfoData) return; for(uint8_t i=0;igetSamples(blockToOutput,m_delaySamples); //blockToOutput->data[i]=inputAudioBlock->data[i]; m_memory->getSamples(lfoData,half_delay_samples+size_t((float(half_delay_samples)*mod[i]+0.5)),1); blockToOutput->data[i]=lfoData[0]; } // perform the wet/dry mix mix //m_postProcessing(blockToOutput, inputAudioBlock, blockToOutput); transmit(blockToOutput); release(inputAudioBlock); release(lfoData); if(m_previousBlock) release(m_previousBlock); m_previousBlock = blockToOutput; release(blockToOutput); if (m_blockToRelease) release(m_blockToRelease); m_blockToRelease = blockToRelease; } void AudioEffectSimpleChorus::delay(float milliseconds) { size_t delaySamples = calcAudioSamples(milliseconds); if (delaySamples > m_memory->getMaxDelaySamples()) { // this exceeds max delay value, limit it. delaySamples = m_memory->getMaxDelaySamples(); } if (!m_memory) { Serial.println("delay(): m_memory is not valid"); } m_delaySamples = delaySamples; } void AudioEffectSimpleChorus::delay(size_t delaySamples) { if (!m_memory) { Serial.println("delay(): m_memory is not valid"); } m_delaySamples = delaySamples; } void AudioEffectSimpleChorus::delayFractionMax(float delayFraction) { size_t delaySamples = static_cast(static_cast(m_memory->getMaxDelaySamples()) * delayFraction); if (delaySamples > m_memory->getMaxDelaySamples()) { // this exceeds max delay value, limit it. delaySamples = m_memory->getMaxDelaySamples(); } if (!m_memory) { Serial.println("delay(): m_memory is not valid"); } m_delaySamples = delaySamples; } void AudioEffectSimpleChorus::m_postProcessing(audio_block_t *out, audio_block_t *dry, audio_block_t *wet) { if (!out) return; // no valid output buffer if ( out && dry && wet) { // Simulate the LPF IIR nature of the analog systems alphaBlend(out, dry, wet, m_mix); } else if (dry) { memcpy(out->data, dry->data, sizeof(int16_t) * AUDIO_BLOCK_SAMPLES); } } void AudioEffectSimpleChorus::processMidi(int channel, int control, int value) { float val = (float)value / 127.0f; if ((m_midiConfig[FREQUENCY][MIDI_CHANNEL] == channel) && (m_midiConfig[FREQUENCY][MIDI_CONTROL] == control)) { // Frequency frequency(value/10); Serial.println(String("AudioEffectSimpleChorus::frequency (Hz): ") + calcAudioTimeMs(value/10)); return; } if ((m_midiConfig[BYPASS][MIDI_CHANNEL] == channel) && (m_midiConfig[BYPASS][MIDI_CONTROL] == control)) { // Bypass if (value >= 65) { bypass(false); Serial.println(String("AudioEffectSimpleChorus::not bypassed -> ON") + value); } else { bypass(true); Serial.println(String("AudioEffectSimpleChorus::bypassed -> OFF") + value); } return; } if ((m_midiConfig[MIX][MIDI_CHANNEL] == channel) && (m_midiConfig[MIX][MIDI_CONTROL] == control)) { // Mix Serial.println(String("AudioEffectSimpleChorus::mix: Dry: ") + 100*(1-val) + String("% Wet: ") + 100*val ); mix(val); return; } } void AudioEffectSimpleChorus::mapMidiControl(int parameter, int midiCC, int midiChannel) { if (parameter >= NUM_CONTROLS) { return ; // Invalid midi parameter } m_midiConfig[parameter][MIDI_CHANNEL] = midiChannel; m_midiConfig[parameter][MIDI_CONTROL] = midiCC; } }