parent
6b724a9526
commit
01e018ceac
@ -0,0 +1,41 @@ |
|||||||
|
#ifndef _HX_MEMCPY_H |
||||||
|
#define _HX_MEMCPY_H |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief combine two separate buffers into interleaved one |
||||||
|
* @param sz - samples per output buffer (divisible by 2) |
||||||
|
* @param dst - pointer to source buffer |
||||||
|
* @param srcA - pointer to A source buffer (even samples) |
||||||
|
* @param srcB - pointer to B source buffer (odd samples) |
||||||
|
* @retval none |
||||||
|
*/ |
||||||
|
inline void memcpyInterleave_f32(float32_t *srcA, float32_t *srcB, float32_t *dst, int16_t sz) |
||||||
|
{ |
||||||
|
while(sz) |
||||||
|
{ |
||||||
|
*dst++ = *srcA++; |
||||||
|
*dst++ = *srcB++; |
||||||
|
sz--; |
||||||
|
*dst++ = *srcA++; |
||||||
|
*dst++ = *srcB++; |
||||||
|
sz--;
|
||||||
|
} |
||||||
|
} |
||||||
|
inline void memcpyInterleave_f32(float32_t *srcA, float32_t *srcB, float32_t *dst, int16_t sz); |
||||||
|
|
||||||
|
inline void memcpyDeinterleave_f32(float32_t *src, float32_t *dstA, float32_t *dstB, int16_t sz) |
||||||
|
{ |
||||||
|
while(sz) |
||||||
|
{ |
||||||
|
*dstA++ = *src++; |
||||||
|
*dstB++ = *src++; |
||||||
|
sz--; |
||||||
|
*dstA++ = *src++; |
||||||
|
*dstB++ = *src++; |
||||||
|
sz--;
|
||||||
|
} |
||||||
|
} |
||||||
|
inline void memcpyDeinterleave_f32(float32_t *src, float32_t *dstA, float32_t *dstB, int16_t sz); |
||||||
|
|
||||||
|
|
||||||
|
#endif // _HX_MEMCPY_H
|
@ -0,0 +1,76 @@ |
|||||||
|
/**
|
||||||
|
* @file basic_allpass.h |
||||||
|
* @author Piotr Zapart |
||||||
|
* @brief basic allpass filter |
||||||
|
* @version 1.0 |
||||||
|
* @date 2024-01-09 |
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2024 |
||||||
|
*
|
||||||
|
*/ |
||||||
|
#ifndef _FILTER_ALLPASS_H_ |
||||||
|
#define _FILTER_ALLPASS_H_ |
||||||
|
|
||||||
|
#include "Arduino.h" |
||||||
|
template <int N> |
||||||
|
class AudioFilterAllpass |
||||||
|
{ |
||||||
|
public: |
||||||
|
~AudioFilterAllpass() |
||||||
|
{ |
||||||
|
free(bf); |
||||||
|
} |
||||||
|
/**
|
||||||
|
* @brief Allocate the filter buffer in RAM |
||||||
|
* set the pointer to the allpass coeff |
||||||
|
*
|
||||||
|
* @param coeffPtr pointer to the allpas coeff variable |
||||||
|
*/ |
||||||
|
bool init(float* coeffPtr) |
||||||
|
{ |
||||||
|
bf = (float *)malloc(N*sizeof(float)); // allocate buffer
|
||||||
|
if (!bf) return false; |
||||||
|
kPtr = coeffPtr; |
||||||
|
reset(); |
||||||
|
return true; |
||||||
|
} |
||||||
|
/**
|
||||||
|
* @brief zero the allpass buffer |
||||||
|
*
|
||||||
|
*/ |
||||||
|
void reset() |
||||||
|
{ |
||||||
|
memset(bf, 0, N*sizeof(float)); |
||||||
|
idx = 0; |
||||||
|
} |
||||||
|
/**
|
||||||
|
* @brief process new sample |
||||||
|
*
|
||||||
|
* @param in input sample |
||||||
|
* @return float output sample |
||||||
|
*/ |
||||||
|
float process(float in) |
||||||
|
{ |
||||||
|
float out = bf[idx] + (*kPtr) * in; |
||||||
|
bf[idx] = in - (*kPtr) * out; |
||||||
|
if (++idx >= N) idx = 0; |
||||||
|
return out; |
||||||
|
} |
||||||
|
/**
|
||||||
|
* @brief Set new coeff pointer |
||||||
|
*
|
||||||
|
* @param coeffPtr
|
||||||
|
*/ |
||||||
|
void coeff(float* coeffPtr) |
||||||
|
{ |
||||||
|
kPtr = coeffPtr; |
||||||
|
} |
||||||
|
private: |
||||||
|
float *kPtr; |
||||||
|
float *bf; |
||||||
|
uint32_t idx; |
||||||
|
const uint32_t len = N*sizeof(float); |
||||||
|
}; |
||||||
|
|
||||||
|
|
||||||
|
#endif // _FILTER_ALLPASS_H_
|
@ -0,0 +1,11 @@ |
|||||||
|
#ifndef _BASIC_COMPONENTS_H_ |
||||||
|
#define _BASIC_COMPONENTS_H_ |
||||||
|
|
||||||
|
#include "basic_allpass.h" |
||||||
|
#include "basic_delay.h" |
||||||
|
#include "basic_lfo.h" |
||||||
|
#include "basic_shelvFilter.h" |
||||||
|
#include "basic_pitch.h" |
||||||
|
|
||||||
|
|
||||||
|
#endif // _BASIC_COMPONENTS_H_
|
@ -0,0 +1,88 @@ |
|||||||
|
/**
|
||||||
|
* @file basic_delay.h |
||||||
|
* @author Piotr Zapart www.hexefx.com |
||||||
|
* @brief basic delay line
|
||||||
|
* @version 1.0 |
||||||
|
* @date 2024-01-09 |
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2024 |
||||||
|
*
|
||||||
|
*/ |
||||||
|
#ifndef _BASIC_DELAY_H_ |
||||||
|
#define _BASIC_DELAY_H_ |
||||||
|
|
||||||
|
#include "Arduino.h" |
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Basic delay line with buffer placed in PSRAM |
||||||
|
*
|
||||||
|
* @tparam N delay length in samples (float) |
||||||
|
*/ |
||||||
|
template <int N> |
||||||
|
class AudioBasicDelay |
||||||
|
{ |
||||||
|
public: |
||||||
|
~AudioBasicDelay() |
||||||
|
{ |
||||||
|
free(bf); |
||||||
|
} |
||||||
|
bool init() |
||||||
|
{ |
||||||
|
bf = (float *)malloc(N*sizeof(float)); // allocate buffer
|
||||||
|
if (!bf) return false; |
||||||
|
idx = 0; |
||||||
|
reset(); |
||||||
|
return true; |
||||||
|
} |
||||||
|
void reset() |
||||||
|
{ |
||||||
|
memset(bf, 0, N*sizeof(float)); |
||||||
|
} |
||||||
|
/**
|
||||||
|
* @brief get the tap from the delay buffer |
||||||
|
*
|
||||||
|
* @param offset delay time
|
||||||
|
* @return float
|
||||||
|
*/ |
||||||
|
float getTap(uint32_t offset, float frac=0.0f) |
||||||
|
{ |
||||||
|
int32_t read_idx, read_idx_next;
|
||||||
|
read_idx = idx - offset; |
||||||
|
if (read_idx < 0) read_idx += N; |
||||||
|
if (frac == 0.0f) return bf[read_idx]; |
||||||
|
read_idx_next = read_idx - 1; |
||||||
|
if (read_idx_next < 0) read_idx_next += N; |
||||||
|
return (bf[read_idx]*(1.0f-frac) + bf[read_idx_next]*frac); |
||||||
|
//return bf[read_idx];
|
||||||
|
} |
||||||
|
/**
|
||||||
|
* @brief read last sample and write a new one |
||||||
|
*
|
||||||
|
* @param newSample new sample written to the start address |
||||||
|
* @return float lase sample read from the end of the buffer |
||||||
|
*/ |
||||||
|
float process(float newSample) |
||||||
|
{ |
||||||
|
float out = bf[idx]; |
||||||
|
bf[idx] = newSample; |
||||||
|
|
||||||
|
return out;
|
||||||
|
} |
||||||
|
void write_toOffset(float newSample, uint32_t offset) |
||||||
|
{ |
||||||
|
int32_t write_idx; |
||||||
|
write_idx = idx - offset; |
||||||
|
if (write_idx < 0) write_idx += N; |
||||||
|
bf[write_idx] = newSample; |
||||||
|
} |
||||||
|
void updateIndex() |
||||||
|
{ |
||||||
|
if (++idx >= N) idx = 0; |
||||||
|
} |
||||||
|
private: |
||||||
|
float *bf; |
||||||
|
int32_t idx; |
||||||
|
}; |
||||||
|
|
||||||
|
#endif // _BASIC_DELAY_H_
|
@ -0,0 +1,71 @@ |
|||||||
|
#ifndef _BASIC_LFO_H_ |
||||||
|
#define _BASIC_LFO_H_ |
||||||
|
|
||||||
|
#include <Arduino.h> |
||||||
|
#include "AudioStream_F32.h" |
||||||
|
|
||||||
|
#define BASIC_LFO_PHASE_0 (0) |
||||||
|
#define BASIC_LFO_PHASE_90 (64) |
||||||
|
#define BASIC_LFO_PHASE_180 (128) |
||||||
|
|
||||||
|
extern "C" { |
||||||
|
extern const int16_t AudioWaveformSine[257]; |
||||||
|
} |
||||||
|
|
||||||
|
/*
|
||||||
|
* @brief Basic sin LFO with float oputput |
||||||
|
*
|
||||||
|
*/ |
||||||
|
class AudioBasicLfo |
||||||
|
{ |
||||||
|
public: |
||||||
|
AudioBasicLfo(float rateHz, uint32_t ampl) |
||||||
|
{ |
||||||
|
acc = 0; |
||||||
|
divider = (0x7FFF + (ampl>>1)) / ampl;
|
||||||
|
adder = (uint32_t)(rateHz * rate_mult); |
||||||
|
|
||||||
|
} |
||||||
|
void update() |
||||||
|
{ |
||||||
|
acc += adder; // update the phase acc
|
||||||
|
} |
||||||
|
/**
|
||||||
|
* @brief LFO output split in two parts |
||||||
|
*
|
||||||
|
* @param phase8bit 0-360deg scaled to 8bit value - phase shift (ie 64=90deg) |
||||||
|
* @param intOffset pointer top integer value used as address offset |
||||||
|
* @param fractOffset pointer to fractional part used for interpolation |
||||||
|
*/ |
||||||
|
void get(uint8_t phase8bit, uint32_t *intOffset, float *fractOffset) |
||||||
|
{ |
||||||
|
uint32_t idx; |
||||||
|
uint32_t y0, y1; |
||||||
|
uint64_t y; |
||||||
|
float intOff; |
||||||
|
idx = ((acc >> 24) + phase8bit) & 0xFF; |
||||||
|
y0 = AudioWaveformSine[idx] + 32767; |
||||||
|
y1 = AudioWaveformSine[idx+1] + 32767; |
||||||
|
idx = acc & 0x00FFFFFF; // lower 24 bit = fractional part
|
||||||
|
y = (int64_t)y0 * (0x00FFFFFF - idx); |
||||||
|
y += (int64_t)y1 * idx; |
||||||
|
y0 = (int32_t) (y >> 24); // 16bit output
|
||||||
|
*fractOffset = modff((float)y0 / (float)divider, &intOff); |
||||||
|
*intOffset = (uint32_t)intOff; |
||||||
|
} |
||||||
|
void setRate(float rateHz) |
||||||
|
{ |
||||||
|
adder = (uint32_t)(rateHz * rate_mult); |
||||||
|
} |
||||||
|
void setDepth(uint32_t ampl) |
||||||
|
{ |
||||||
|
divider = (0x7FFF + (ampl>>1)) / ampl;
|
||||||
|
} |
||||||
|
private: |
||||||
|
uint32_t acc; |
||||||
|
uint32_t adder; |
||||||
|
int32_t divider = 1; |
||||||
|
const uint32_t rate_mult = 4294967295.0f / AUDIO_SAMPLE_RATE_EXACT; |
||||||
|
}; |
||||||
|
|
||||||
|
#endif // _BASIC_LFO_H_
|
@ -0,0 +1,121 @@ |
|||||||
|
#ifndef _BASIC_PITCH_H_ |
||||||
|
#define _BASIC_PITCH_H_ |
||||||
|
|
||||||
|
#include <Arduino.h> |
||||||
|
#include "Audio.h" |
||||||
|
|
||||||
|
#define BASIC_PITCH_BUF_BITS (12) |
||||||
|
#define BASIC_PITCH_BUF_SIZE (1<<BASIC_PITCH_BUF_BITS) |
||||||
|
#define BASIC_PITCH_BUF_SIZE_HALF (1<<(BASIC_PITCH_BUF_BITS-1)) |
||||||
|
#define BASIC_PITCH_BUF_MASK (BASIC_PITCH_BUF_SIZE-1) |
||||||
|
#define BASIC_PITCH_BUF_FRAC_MASK ((1<<(32-BASIC_PITCH_BUF_BITS))-1) |
||||||
|
|
||||||
|
#define BASIC_PITCH_XFADE_BITS (10) |
||||||
|
#define BASIC_PITCH_XFADE_LEN (1<<BASIC_PITCH_XFADE_BITS) |
||||||
|
#define BASIC_PITCH_XFADE_LEN_HALF (BASIC_PITCH_XFADE_LEN>>1) |
||||||
|
#define BASIC_PITCH_XFADE_MASK (BASIC_PITCH_XFADE_LEN-1) |
||||||
|
|
||||||
|
extern "C" { |
||||||
|
extern const float AudioWaveformFader_f32[]; // crossfade waveform
|
||||||
|
extern const float music_intevals[]; // semitone intervals -1oct to +2oct
|
||||||
|
} |
||||||
|
|
||||||
|
class AudioBasicPitch |
||||||
|
{ |
||||||
|
public: |
||||||
|
bool init() |
||||||
|
{ |
||||||
|
outFilter.init(hp_f, (float *)&hp_gain, lp_f, &lp_gain); |
||||||
|
bf = (float *)malloc(BASIC_PITCH_BUF_SIZE*sizeof(float)); // allocate buffer
|
||||||
|
if (!bf) return false; |
||||||
|
reset(); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
void setPitch(float ratio) |
||||||
|
{ |
||||||
|
readAdder = (float)pitchDelta0 * ratio; |
||||||
|
} |
||||||
|
void setPitchSemintone(int8_t s) |
||||||
|
{ |
||||||
|
s = constrain(s, -12, +24); // limit to the predefined range
|
||||||
|
setPitch(music_intevals[s + 12]); |
||||||
|
} |
||||||
|
void setTone(float t) |
||||||
|
{ |
||||||
|
//lp_f = constrain(t, 0.01f, 1.0f);
|
||||||
|
lp_gain = constrain(t, 0.0f, 1.0f); |
||||||
|
} |
||||||
|
|
||||||
|
float process(float newSample) |
||||||
|
{ |
||||||
|
uint32_t idx1, idx2; |
||||||
|
uint32_t delta, delta_acc; |
||||||
|
float k_frac, delta_frac, s_n, s_half, xf0, xf1; |
||||||
|
|
||||||
|
bf[writeAddr] = newSample; // write new sample
|
||||||
|
readAddr = readAddr + readAdder; // update read pointer, readAdder controls the pitch
|
||||||
|
// bypass mode is at mix = 0 or if no pitch change
|
||||||
|
if (mix == 0.0f || readAdder == pitchDelta0)
|
||||||
|
{ |
||||||
|
writeAddr = (writeAddr + 1) & BASIC_PITCH_BUF_MASK; |
||||||
|
return newSample; |
||||||
|
} |
||||||
|
// sample end
|
||||||
|
idx1 = (readAddr >> (32-BASIC_PITCH_BUF_BITS)) & BASIC_PITCH_BUF_MASK; // index of the last sample
|
||||||
|
k_frac = (float)(readAddr & BASIC_PITCH_BUF_FRAC_MASK) / (float)BASIC_PITCH_BUF_FRAC_MASK; // fractional part
|
||||||
|
s_n = bf[idx1] * (1.0f-k_frac);
|
||||||
|
s_n += bf[(idx1 + 1) & BASIC_PITCH_BUF_MASK] * k_frac; // interpolated sample
|
||||||
|
// sample half
|
||||||
|
idx2 = ((readAddr + 0x80000000) >> (32-BASIC_PITCH_BUF_BITS)) & BASIC_PITCH_BUF_MASK; |
||||||
|
k_frac = (float)((readAddr+0x80000000) & BASIC_PITCH_BUF_FRAC_MASK) / (float)BASIC_PITCH_BUF_FRAC_MASK; |
||||||
|
s_half = bf[idx2] * (1.0f - k_frac); |
||||||
|
s_half += bf[(idx2 + 1) & BASIC_PITCH_BUF_MASK] * k_frac; |
||||||
|
|
||||||
|
delta_acc = readAddr - (writeAddr<<(32-BASIC_PITCH_BUF_BITS)); // distance between the write and read pointer
|
||||||
|
|
||||||
|
delta = (delta_acc >> (32-9)) & 0x1FF; // 9 bit value = 2x fade table length (fade in + fade out)
|
||||||
|
delta_frac = (float)(delta_acc & ((1<<23)-1)) / (float)((1<<23)-1); // fractional part for the xfade curve
|
||||||
|
idx2 = delta&0xFF; |
||||||
|
xf0 = AudioWaveformFader_f32[idx2];
|
||||||
|
xf1 = AudioWaveformFader_f32[idx2+1]; |
||||||
|
k_frac = xf0 * (1.0f-delta_frac) + xf1 * delta_frac; // interpolated smooth crossfade coeff.
|
||||||
|
|
||||||
|
if (delta > 0xFF) k_frac = 1.0f-k_frac; // invert the curve for the fade out part
|
||||||
|
|
||||||
|
s_n = s_n * k_frac + s_half * (1.0f - k_frac); // crossfade the last and mid sample
|
||||||
|
|
||||||
|
writeAddr = (writeAddr + 1) & BASIC_PITCH_BUF_MASK; // update the write pointer
|
||||||
|
s_n = outFilter.process(s_n); // apply output lowpass
|
||||||
|
return (s_n * mix + newSample * (1.0f-mix)); // do dry/wet mix
|
||||||
|
} |
||||||
|
void setMix(float mixRatio) |
||||||
|
{ |
||||||
|
mix = constrain(mixRatio, 0.0f, 1.0f); |
||||||
|
} |
||||||
|
void reset() |
||||||
|
{ |
||||||
|
memset(bf, 0, BASIC_PITCH_BUF_SIZE*sizeof(float)); |
||||||
|
|
||||||
|
readAddr = 0; |
||||||
|
writeAddr = 0; |
||||||
|
readAdder = pitchDelta0; |
||||||
|
mix = 1.0f; |
||||||
|
} |
||||||
|
private: |
||||||
|
float *bf; |
||||||
|
float mix; |
||||||
|
uint32_t readAddr; |
||||||
|
uint32_t readAdder; |
||||||
|
uint16_t writeAddr; |
||||||
|
static const uint32_t pitchDelta0 = BASIC_PITCH_BUF_FRAC_MASK+1; |
||||||
|
|
||||||
|
AudioFilterShelvingLPHP outFilter; |
||||||
|
static constexpr float hp_f = 0.003f; |
||||||
|
const float hp_gain = 0.0f; |
||||||
|
static constexpr float lp_f = 0.26f; |
||||||
|
float lp_gain = 1.0f; |
||||||
|
}; |
||||||
|
|
||||||
|
|
||||||
|
#endif // _BASIC_PITCH_H_
|
@ -0,0 +1,97 @@ |
|||||||
|
/**
|
||||||
|
* @file basic_shelvFilter.h |
||||||
|
* @author Piotr Zapart www.hexefx.com |
||||||
|
* @brief basic hp/lp filter class |
||||||
|
* @version 1.0 |
||||||
|
* @date 2024-01-09 |
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2024 |
||||||
|
*
|
||||||
|
*/ |
||||||
|
#ifndef _BASIC_SHELVFILTER_H_ |
||||||
|
#define _BASIC_SHELVFILTER_H_ |
||||||
|
|
||||||
|
#include <Arduino.h> |
||||||
|
|
||||||
|
class AudioFilterShelvingLPHP |
||||||
|
{ |
||||||
|
public: |
||||||
|
void init(float hp_freq, float *hp_damp, float lp_freq, float *lp_damp) |
||||||
|
{ |
||||||
|
hidampPtr = lp_damp; |
||||||
|
hidamp = *hidampPtr; |
||||||
|
hp_f = hp_freq; |
||||||
|
lodampPtr = hp_damp; |
||||||
|
lodamp = *lodampPtr; |
||||||
|
lp_f = lp_freq; |
||||||
|
lpreg = 0.0f; |
||||||
|
hpreg = 0.0f; |
||||||
|
} |
||||||
|
float process(float input) |
||||||
|
{ |
||||||
|
float tmp1, tmp2; |
||||||
|
// smoothly update params
|
||||||
|
if (hidamp < (*hidampPtr)) |
||||||
|
{ |
||||||
|
hidamp += upd_step; |
||||||
|
if (hidamp >(*hidampPtr)) hidamp = *hidampPtr; |
||||||
|
} |
||||||
|
if (hidamp > (*hidampPtr)) |
||||||
|
{ |
||||||
|
hidamp -= upd_step; |
||||||
|
if (hidamp < (*hidampPtr)) hidamp = *hidampPtr; |
||||||
|
} |
||||||
|
if (lodamp < (*lodampPtr)) |
||||||
|
{ |
||||||
|
lodamp += upd_step; |
||||||
|
if (lodamp >(*lodampPtr)) lodamp = *lodampPtr; |
||||||
|
} |
||||||
|
if (lodamp > (*lodampPtr)) |
||||||
|
{ |
||||||
|
lodamp -= upd_step; |
||||||
|
if (lodamp < (*lodampPtr)) lodamp = *lodampPtr; |
||||||
|
} |
||||||
|
|
||||||
|
tmp1 = input - lpreg; |
||||||
|
lpreg += tmp1 * lp_f; |
||||||
|
tmp2 = input - lpreg; |
||||||
|
tmp1 = lpreg - hpreg; |
||||||
|
hpreg += tmp1 * hp_f; |
||||||
|
return (lpreg + hidamp*tmp2 + lodamp * hpreg); |
||||||
|
} |
||||||
|
private: |
||||||
|
float lpreg; |
||||||
|
float hpreg; |
||||||
|
float *lodampPtr; |
||||||
|
float *hidampPtr; |
||||||
|
float hidamp;
|
||||||
|
float lodamp; |
||||||
|
float hp_f; |
||||||
|
float lp_f; |
||||||
|
static constexpr float upd_step = 0.02f; |
||||||
|
}; |
||||||
|
|
||||||
|
|
||||||
|
class AudioFilterLP |
||||||
|
{ |
||||||
|
public:
|
||||||
|
void init(float *lp_freq) |
||||||
|
{ |
||||||
|
lp_fPtr = lp_freq; |
||||||
|
lpreg = 0.0f; |
||||||
|
} |
||||||
|
float process(float input) |
||||||
|
{ |
||||||
|
float tmp; |
||||||
|
tmp = input - lpreg; |
||||||
|
lpreg += (*lp_fPtr) *tmp; |
||||||
|
return lpreg; |
||||||
|
} |
||||||
|
private: |
||||||
|
float lpreg; |
||||||
|
float *lp_fPtr; |
||||||
|
}; |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif // _BASIC_SHELVFILTER_H_
|
@ -0,0 +1,118 @@ |
|||||||
|
/* Mono Shephard/Barberpole Phaser/Vibrato effect for Teensy Audio library
|
||||||
|
* |
||||||
|
* Author: Piotr Zapart |
||||||
|
* www.hexefx.com |
||||||
|
* |
||||||
|
* Copyright (c) 2021 by Piotr Zapart |
||||||
|
* |
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy |
||||||
|
* of this software and associated documentation files (the "Software"), to deal |
||||||
|
* in the Software without restriction, including without limitation the rights |
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||||
|
* copies of the Software, and to permit persons to whom the Software is |
||||||
|
* furnished to do so, subject to the following conditions: |
||||||
|
* |
||||||
|
* The above copyright notice and this permission notice shall be included in all |
||||||
|
* copies or substantial portions of the Software. |
||||||
|
* |
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||||
|
* SOFTWARE. |
||||||
|
*/ |
||||||
|
#include "effect_infphaser_F32.h" |
||||||
|
|
||||||
|
// ---------------------------- INFINITE PHASER MODULATION -----------------------
|
||||||
|
#define INF_PHASER_STEP (0x100000000u / INFINITE_PHASER_PATHS) |
||||||
|
|
||||||
|
AudioEffectInfinitePhaser_F32::AudioEffectInfinitePhaser_F32() : AudioStream_F32(1, inputQueueArray_f32) |
||||||
|
{ |
||||||
|
memset(allpass_x, 0, INFINITE_PHASER_STAGES * INFINITE_PHASER_PATHS * sizeof(float32_t)); |
||||||
|
memset(allpass_y, 0, INFINITE_PHASER_STAGES * INFINITE_PHASER_PATHS * sizeof(float32_t)); |
||||||
|
bps = false; |
||||||
|
lfo_top = 1.0f; |
||||||
|
lfo_btm = 0.0f; |
||||||
|
lfo_phase_acc = 0; |
||||||
|
lfo_add = 0; |
||||||
|
feedb = 0.5f; // effect is hard noticable with low feedback settings, hence the range is limited to 0.5-0.999
|
||||||
|
mix_ratio = 0.5f; // start with classic phaser sound
|
||||||
|
stg = INFINITE_PHASER_STAGES; |
||||||
|
} |
||||||
|
AudioEffectInfinitePhaser_F32::~AudioEffectInfinitePhaser_F32() |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
void AudioEffectInfinitePhaser_F32::update() |
||||||
|
{ |
||||||
|
|
||||||
|
#if defined(__ARM_ARCH_7EM__) |
||||||
|
audio_block_f32_t *blockIn;
|
||||||
|
uint16_t i = 0; |
||||||
|
float32_t modSig; |
||||||
|
uint32_t phaseAcc = lfo_phase_acc; |
||||||
|
int32_t phaseAdd = lfo_add; |
||||||
|
float32_t top = lfo_top; |
||||||
|
float32_t btm = lfo_btm; |
||||||
|
uint32_t phase_acc_local; |
||||||
|
uint32_t y0, y1; |
||||||
|
float32_t inSig, drySig, wetSig; |
||||||
|
float32_t fdb = feedb; |
||||||
|
float32_t ampl; |
||||||
|
|
||||||
|
blockIn = AudioStream_F32::receiveWritable_f32(0); // audio data
|
||||||
|
|
||||||
|
if (!blockIn) |
||||||
|
{ |
||||||
|
return; |
||||||
|
} |
||||||
|
if (bps) |
||||||
|
{ |
||||||
|
AudioStream_F32::transmit(blockIn); |
||||||
|
AudioStream_F32::release(blockIn); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
for (i=0; i < blockIn->length; i++)
|
||||||
|
{ |
||||||
|
wetSig = 0.0f; |
||||||
|
drySig = blockIn->data[i] * (1.0f - fdb*0.25f); // attenuate the input if using feedback
|
||||||
|
|
||||||
|
y1 = INFINITE_PHASER_PATHS; |
||||||
|
while (y1) |
||||||
|
{ |
||||||
|
y1--; |
||||||
|
phase_acc_local = phaseAcc + y1*INF_PHASER_STEP; |
||||||
|
modSig = 1.0f - ((float32_t)phase_acc_local / 4294967295.0f); |
||||||
|
ampl = modSig * 2.0f;
|
||||||
|
|
||||||
|
if (ampl > 1.0f) ampl = -2.0f * modSig + 2.0f; |
||||||
|
modSig = modSig*modSig * abs(top - btm) + min(top, btm); |
||||||
|
|
||||||
|
inSig = drySig + last_sample[y1] * fdb; |
||||||
|
y0 = stg; |
||||||
|
while (y0) // process allpass filters in pairs
|
||||||
|
{ |
||||||
|
y0--; |
||||||
|
allpass_y[y1][y0] = modSig * (allpass_y[y1][y0] + inSig) - allpass_x[y1][y0]; |
||||||
|
allpass_x[y1][y0] = inSig; |
||||||
|
y0--; |
||||||
|
allpass_y[y1][y0] = modSig * (allpass_y[y1][y0] + allpass_y[y1][y0+1]) - allpass_x[y1][y0]; |
||||||
|
allpass_x[y1][y0] = allpass_y[y1][y0+1]; |
||||||
|
inSig = allpass_y[y1][y0]; |
||||||
|
} |
||||||
|
last_sample[y1] = inSig; |
||||||
|
wetSig += ((drySig * (1.0f - mix_ratio) + inSig * mix_ratio)* ampl)/2.0f; |
||||||
|
} |
||||||
|
blockIn->data[i] = wetSig; |
||||||
|
|
||||||
|
phaseAcc += phaseAdd;
|
||||||
|
} |
||||||
|
lfo_phase_acc = phaseAcc; |
||||||
|
AudioStream_F32::transmit(blockIn); |
||||||
|
AudioStream_F32::release(blockIn); |
||||||
|
#endif |
||||||
|
} |
||||||
|
|
@ -0,0 +1,180 @@ |
|||||||
|
/* Mono Shephard/Barberpole Phaser/Vibrato effect for Teensy Audio library
|
||||||
|
* |
||||||
|
* Author: Piotr Zapart |
||||||
|
* www.hexefx.com |
||||||
|
* |
||||||
|
* Copyright (c) 2021 by Piotr Zapart |
||||||
|
* |
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy |
||||||
|
* of this software and associated documentation files (the "Software"), to deal |
||||||
|
* in the Software without restriction, including without limitation the rights |
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||||
|
* copies of the Software, and to permit persons to whom the Software is |
||||||
|
* furnished to do so, subject to the following conditions: |
||||||
|
* |
||||||
|
* The above copyright notice and this permission notice shall be included in all |
||||||
|
* copies or substantial portions of the Software. |
||||||
|
* |
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||||
|
* SOFTWARE. |
||||||
|
*/ |
||||||
|
|
||||||
|
#ifndef _EFFECT_INFPHASER_F32_H |
||||||
|
#define _EFFECT_INFPHASER_F32_H |
||||||
|
|
||||||
|
#include <Arduino.h> |
||||||
|
#include "Audio.h" |
||||||
|
#include "AudioStream_F32.h" |
||||||
|
#include "arm_math.h" |
||||||
|
|
||||||
|
// ################ SHEPARD/BARBERPOLE INFINITE PHASER ################
|
||||||
|
#define INFINITE_PHASER_STAGES 6 |
||||||
|
#define INFINITE_PHASER_PATHS 6 // 6 parallel paths
|
||||||
|
|
||||||
|
#define INFINITE_PHASER_MAX_LFO_HZ (0.25f) // maximum LFO rate range
|
||||||
|
|
||||||
|
class AudioEffectInfinitePhaser_F32 : public AudioStream_F32 |
||||||
|
{ |
||||||
|
public: |
||||||
|
AudioEffectInfinitePhaser_F32(); |
||||||
|
~AudioEffectInfinitePhaser_F32(); |
||||||
|
virtual void update(); |
||||||
|
/**
|
||||||
|
* @brief Scale and offset the modulation signal.
|
||||||
|
* LFO will oscillate between these two max and min values.
|
||||||
|
*
|
||||||
|
* @param top top level of the LFO |
||||||
|
* @param bottom bottom level of the LFO |
||||||
|
*/ |
||||||
|
void depth(float32_t top, float32_t bottom) |
||||||
|
{ |
||||||
|
float32_t a, b; |
||||||
|
a = constrain(top, 0.0f, 1.0f); |
||||||
|
b = constrain(bottom, 0.0f, 1.0f); |
||||||
|
__disable_irq(); |
||||||
|
lfo_top = a; |
||||||
|
lfo_btm = b; |
||||||
|
__enable_irq(); |
||||||
|
} |
||||||
|
void depth_top(float32_t top) |
||||||
|
{ |
||||||
|
float32_t a = constrain(top, 0.0f, 1.0f); |
||||||
|
__disable_irq(); |
||||||
|
lfo_top = a; |
||||||
|
__enable_irq(); |
||||||
|
} |
||||||
|
void depth_btm(float32_t btm) |
||||||
|
{ |
||||||
|
float32_t a = constrain(btm, 0.0f, 1.0f); |
||||||
|
__disable_irq(); |
||||||
|
lfo_btm = a; |
||||||
|
__enable_irq(); |
||||||
|
} |
||||||
|
/**
|
||||||
|
* @brief Controls the internal LFO. |
||||||
|
* Use this function to update all lfo parameteres at once |
||||||
|
*
|
||||||
|
* @param rate scaled lfo frequency -1.0f to 1.0f, use 0.0f for manual phaser control |
||||||
|
* @param top lfo top level, range 0.0f to 1.0f |
||||||
|
* @param btm lfo bottm level, range 0.0f to 1.0f |
||||||
|
*/ |
||||||
|
void lfo(float32_t rate, float32_t top, float32_t btm) |
||||||
|
{ |
||||||
|
int32_t add; |
||||||
|
if (rate < 0.0f) rate = rate*rate*(-1.0f); |
||||||
|
else rate = rate*rate; |
||||||
|
top = constrain(top, 0.0f, 1.0f); |
||||||
|
btm = constrain(btm, 0.0f, 1.0f); |
||||||
|
add = rate * (4294967296.0 / AUDIO_SAMPLE_RATE_EXACT); |
||||||
|
__disable_irq(); |
||||||
|
lfo_top = top; |
||||||
|
lfo_btm = btm; |
||||||
|
lfo_add = add; |
||||||
|
__enable_irq(); |
||||||
|
} |
||||||
|
/**
|
||||||
|
* @brief Set the rate of the internal LFO |
||||||
|
*
|
||||||
|
* @param rate lfo frequency, use 0.0f for manual phaser control |
||||||
|
* Range -1.0f to 1.0f for reverse and forward modulation |
||||||
|
*/ |
||||||
|
void lfo_rate(float32_t rate) |
||||||
|
{ |
||||||
|
if (rate < 0.0f) rate = rate*rate*(-1.0f); |
||||||
|
else rate = rate*rate; |
||||||
|
int32_t add; |
||||||
|
rate = map(rate, -1.0f, 1.0f, -INFINITE_PHASER_MAX_LFO_HZ, INFINITE_PHASER_MAX_LFO_HZ); |
||||||
|
add = rate * (4294967296.0f / AUDIO_SAMPLE_RATE_EXACT); |
||||||
|
__disable_irq(); |
||||||
|
lfo_add = add; |
||||||
|
__enable_irq(); |
||||||
|
} |
||||||
|
/**
|
||||||
|
* @brief Controls the feedback parameter |
||||||
|
*
|
||||||
|
* @param fdb feedback value in range 0.0f to 1.0f |
||||||
|
*/ |
||||||
|
void feedback(float32_t fdb) |
||||||
|
{ |
||||||
|
fdb = map(fdb, 0.0f, 1.0f, 0.5f, 0.999f); |
||||||
|
__disable_irq(); |
||||||
|
feedb = fdb; |
||||||
|
__enable_irq(); |
||||||
|
} |
||||||
|
/**
|
||||||
|
* @brief Dry / Wet mixer ratio. Classic Phaser sound uses 0.5f for 50% dry and 50%Wet |
||||||
|
* 1.0f will produce 100% wet signal craeting a vibrato effect |
||||||
|
*
|
||||||
|
* @param ratio mixing ratio, range 0.0f (full dry) to 1.0f (full wet) |
||||||
|
*/ |
||||||
|
void mix(float32_t ratio) |
||||||
|
{ |
||||||
|
ratio = constrain(ratio, 0.0f, 1.0f); |
||||||
|
__disable_irq(); |
||||||
|
mix_ratio = ratio; |
||||||
|
__enable_irq(); |
||||||
|
} |
||||||
|
/**
|
||||||
|
* @brief Sets the number of stages used in the phaser |
||||||
|
* Allowed values are: 2, 4, 6 |
||||||
|
*
|
||||||
|
* @param st number of stages, even value <= 6 |
||||||
|
*/ |
||||||
|
void stages(uint8_t st) |
||||||
|
{ |
||||||
|
if (st && st == ((st >> 1) << 1) && st <= INFINITE_PHASER_STAGES) // only 2, 4, 6, 8, 12 allowed
|
||||||
|
{ |
||||||
|
__disable_irq(); |
||||||
|
stg = st; |
||||||
|
__enable_irq(); |
||||||
|
} |
||||||
|
} |
||||||
|
/**
|
||||||
|
* @brief Use to bypass the effect (true) |
||||||
|
*
|
||||||
|
* @param state true = bypass on, false = phaser on |
||||||
|
*/ |
||||||
|
void set_bypass(bool state) {bps = state;} |
||||||
|
bool get_bypass(void) {return bps;} |
||||||
|
bool tgl_bypass(void) {bps ^= 1; return bps;} |
||||||
|
private: |
||||||
|
uint8_t stg; // number of stages
|
||||||
|
bool bps; // bypass
|
||||||
|
audio_block_f32_t *inputQueueArray_f32[1];
|
||||||
|
float32_t allpass_x[INFINITE_PHASER_PATHS][INFINITE_PHASER_STAGES]; // allpass inputs
|
||||||
|
float32_t allpass_y[INFINITE_PHASER_PATHS][INFINITE_PHASER_STAGES]; // allpass outputs
|
||||||
|
float32_t mix_ratio; // 0 = dry. 1.0 = wet
|
||||||
|
float32_t feedb; // feedback
|
||||||
|
float32_t last_sample[INFINITE_PHASER_PATHS]; |
||||||
|
uint32_t lfo_phase_acc; // interfnal lfo
|
||||||
|
int32_t lfo_add; |
||||||
|
float32_t lfo_top; |
||||||
|
float32_t lfo_btm; |
||||||
|
}; |
||||||
|
|
||||||
|
#endif // _EFFECT_INFPHASER_H
|
@ -0,0 +1,131 @@ |
|||||||
|
/* mono to stereo expander for Teensy 4
|
||||||
|
* 32bit float version for OpenAudio_ArduinoLibrary: |
||||||
|
* https://github.com/chipaudette/OpenAudio_ArduinoLibrary
|
||||||
|
* |
||||||
|
* Author: Piotr Zapart |
||||||
|
* www.hexefx.com |
||||||
|
* |
||||||
|
* Copyright (c) 2021 by Piotr Zapart |
||||||
|
* |
||||||
|
* Development of this audio library was funded by PJRC.COM, LLC by sales of |
||||||
|
* Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop |
||||||
|
* open source software by purchasing Teensy or other PJRC products. |
||||||
|
* |
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy |
||||||
|
* of this software and associated documentation files (the "Software"), to deal |
||||||
|
* in the Software without restriction, including without limitation the rights |
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||||
|
* copies of the Software, and to permit persons to whom the Software is |
||||||
|
* furnished to do so, subject to the following conditions: |
||||||
|
* |
||||||
|
* The above copyright notice, development funding notice, and this permission |
||||||
|
* notice shall be included in all copies or substantial portions of the Software. |
||||||
|
* |
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
||||||
|
* THE SOFTWARE. |
||||||
|
*/ |
||||||
|
#include "effect_monoToStereo_F32.h" |
||||||
|
|
||||||
|
const float32_t allpass_k_table[ALLP_NETWORK_LEN] =
|
||||||
|
{ |
||||||
|
-0.9823311567306519f, -0.9838343858718872f, -0.9838343858718872f,
|
||||||
|
-0.9843953251838684f, -0.9843953251838684f, -0.9850407838821411f,
|
||||||
|
-0.9850407838821411f, -0.9856590032577515f, -0.9856590032577515f,
|
||||||
|
-0.9868955612182617f, -0.9868955612182617f, -0.9894036054611206f,
|
||||||
|
-0.9894036054611206f, -0.9902338981628418f, -0.9902338981628418f,
|
||||||
|
-0.9911531209945679f, -0.9911531209945679f, -0.9911531209945679f,
|
||||||
|
-0.9911531209945679f, -0.9928167462348938f, -0.9928167462348938f |
||||||
|
}; |
||||||
|
|
||||||
|
AudioEffectMonoToStereo_F32::AudioEffectMonoToStereo_F32() : AudioStream_F32(1, inputQueueArray_f32) |
||||||
|
{ |
||||||
|
pancos = 1.0f; |
||||||
|
pansin= 0.0f; |
||||||
|
width = 0.0f; |
||||||
|
bypass = false; |
||||||
|
} |
||||||
|
AudioEffectMonoToStereo_F32::~AudioEffectMonoToStereo_F32() |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
void AudioEffectMonoToStereo_F32::update() |
||||||
|
{ |
||||||
|
#if defined(__ARM_ARCH_7EM__) |
||||||
|
|
||||||
|
audio_block_f32_t *blockIn; |
||||||
|
uint16_t i; |
||||||
|
float32_t _width = width; |
||||||
|
float32_t _pancos = pancos; |
||||||
|
float32_t _pansin = pansin; |
||||||
|
float32_t allp1Out, allp2Out, stereoL, stereoR; |
||||||
|
|
||||||
|
blockIn = AudioStream_F32::receiveReadOnly_f32(0); |
||||||
|
if (!blockIn) return; |
||||||
|
|
||||||
|
audio_block_f32_t *blockOutL = AudioStream_F32::allocate_f32(); |
||||||
|
audio_block_f32_t *blockOutR = AudioStream_F32::allocate_f32(); |
||||||
|
if (!blockOutL || !blockOutR) |
||||||
|
{ |
||||||
|
|
||||||
|
if (blockOutL) |
||||||
|
AudioStream_F32::release(blockOutL); |
||||||
|
if (blockOutR) |
||||||
|
AudioStream_F32::release(blockOutR); |
||||||
|
return; |
||||||
|
} |
||||||
|
if (bypass) |
||||||
|
{ |
||||||
|
AudioStream_F32::transmit(blockIn, 0); // transmit input on both
|
||||||
|
AudioStream_F32::transmit(blockIn, 1); // out channels
|
||||||
|
AudioStream_F32::release(blockIn); |
||||||
|
AudioStream_F32::release(blockOutL); |
||||||
|
AudioStream_F32::release(blockOutR); |
||||||
|
return; |
||||||
|
} |
||||||
|
for (i = 0; i < blockIn->length; i++) |
||||||
|
{ |
||||||
|
allp1Out = do_allp_netw(blockIn->data[i], allpass_netw_1x, allpass_netw_1y); |
||||||
|
allp2Out = do_allp_netw(allp1Out, allpass_netw_2x, allpass_netw_2y); |
||||||
|
stereoL = blockIn->data[i] * _width + allp1Out; |
||||||
|
stereoR = allp1Out - (allp2Out * _width); |
||||||
|
blockOutL->data[i] = (stereoL * _pancos) + (stereoR * _pansin); |
||||||
|
blockOutR->data[i] = (stereoR * _pancos) - (stereoL * _pansin); |
||||||
|
} |
||||||
|
AudioStream_F32::transmit(blockOutL, 0); |
||||||
|
AudioStream_F32::transmit(blockOutR, 1); |
||||||
|
AudioStream_F32::release(blockOutL); |
||||||
|
AudioStream_F32::release(blockOutR); |
||||||
|
AudioStream_F32::release(blockIn); |
||||||
|
|
||||||
|
#endif |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
// y[n] = c*x[n] + x[n-1] - c*y[n-1]
|
||||||
|
// y[n] = c*(x[n] - y[n-1]) + x[n-1]
|
||||||
|
// c = (tan(pi*fc/fs)-1) / (tan(pi*fc/fs)+1)
|
||||||
|
float32_t AudioEffectMonoToStereo_F32::do_allp_netw(float32_t inSig, float32_t *x, float32_t *y) |
||||||
|
{ |
||||||
|
uint32_t stg = ALLP_NETWORK_LEN; |
||||||
|
while (stg) |
||||||
|
{ |
||||||
|
stg--; |
||||||
|
y[stg] = allpass_k_table[stg] * (inSig - y[stg]) + x[stg]; |
||||||
|
x[stg] = inSig; |
||||||
|
inSig = y[stg]; |
||||||
|
stg--; |
||||||
|
y[stg] = allpass_k_table[stg] * (inSig - y[stg]) + x[stg]; |
||||||
|
x[stg] = inSig; |
||||||
|
inSig = y[stg]; |
||||||
|
stg--; |
||||||
|
y[stg] = allpass_k_table[stg] * (inSig - y[stg]) + x[stg]; |
||||||
|
x[stg] = inSig; |
||||||
|
inSig = y[stg]; |
||||||
|
} |
||||||
|
return y[0]; |
||||||
|
} |
@ -0,0 +1,83 @@ |
|||||||
|
/* mono to stereo expander for Teensy 4
|
||||||
|
* 32bit float version for OpenAudio_ArduinoLibrary: |
||||||
|
* https://github.com/chipaudette/OpenAudio_ArduinoLibrary
|
||||||
|
* |
||||||
|
* Author: Piotr Zapart |
||||||
|
* www.hexefx.com |
||||||
|
* |
||||||
|
* Copyright (c) 2021 by Piotr Zapart |
||||||
|
* |
||||||
|
* Development of this audio library was funded by PJRC.COM, LLC by sales of |
||||||
|
* Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop |
||||||
|
* open source software by purchasing Teensy or other PJRC products. |
||||||
|
* |
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy |
||||||
|
* of this software and associated documentation files (the "Software"), to deal |
||||||
|
* in the Software without restriction, including without limitation the rights |
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||||
|
* copies of the Software, and to permit persons to whom the Software is |
||||||
|
* furnished to do so, subject to the following conditions: |
||||||
|
* |
||||||
|
* The above copyright notice, development funding notice, and this permission |
||||||
|
* notice shall be included in all copies or substantial portions of the Software. |
||||||
|
* |
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
||||||
|
* THE SOFTWARE. |
||||||
|
*/ |
||||||
|
#ifndef _EFFECT_MONOTOSTEREO_F32_H |
||||||
|
#define _EFFECT_MONOTOSTEREO_F32_H |
||||||
|
|
||||||
|
#include <Arduino.h> |
||||||
|
#include "AudioStream_F32.h" |
||||||
|
#include "arm_math.h" |
||||||
|
|
||||||
|
#define ALLP_NETWORK_LEN 21 |
||||||
|
|
||||||
|
class AudioEffectMonoToStereo_F32 : public AudioStream_F32 |
||||||
|
{ |
||||||
|
public: |
||||||
|
AudioEffectMonoToStereo_F32(); |
||||||
|
~AudioEffectMonoToStereo_F32(); |
||||||
|
virtual void update(); |
||||||
|
void setSpread(float32_t val) |
||||||
|
{
|
||||||
|
val = constrain(val, 0.0f, 1.0f); |
||||||
|
__disable_irq(); |
||||||
|
width = val; |
||||||
|
__enable_irq(); |
||||||
|
} |
||||||
|
void setPan(float32_t val) |
||||||
|
{ |
||||||
|
float32_t a, b; |
||||||
|
val = constrain(val, -1.0f, 1.0f); |
||||||
|
a = map(val, -1.0f, 1.0f, -0.707f, 0.707f); |
||||||
|
b = 1.0f - abs(val*0.293f); |
||||||
|
__disable_irq(); |
||||||
|
pansin = a; |
||||||
|
pancos = b; |
||||||
|
__enable_irq(); |
||||||
|
} |
||||||
|
void setBypass(bool state) {bypass = state;} |
||||||
|
void tglBypass(void) {bypass ^= 1;} |
||||||
|
bool getBypass(void) { return bypass;} |
||||||
|
private: |
||||||
|
bool bypass; |
||||||
|
float32_t width; |
||||||
|
float32_t pancos, pansin; |
||||||
|
float32_t do_allp_netw(float32_t inSig, float32_t *x, float32_t *y); |
||||||
|
float32_t allpass_netw_1x[ALLP_NETWORK_LEN]; |
||||||
|
float32_t allpass_netw_1y[ALLP_NETWORK_LEN]; |
||||||
|
float32_t allpass_netw_2x[ALLP_NETWORK_LEN]; |
||||||
|
float32_t allpass_netw_2y[ALLP_NETWORK_LEN]; |
||||||
|
|
||||||
|
audio_block_f32_t *inputQueueArray_f32[1];
|
||||||
|
}; |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif // _EFFECT_MONOTOSTEREO_F32_H
|
@ -0,0 +1,166 @@ |
|||||||
|
/* Mono Phaser/Vibrato effect for Teensy Audio library
|
||||||
|
* |
||||||
|
* Author: Piotr Zapart |
||||||
|
* www.hexefx.com |
||||||
|
* |
||||||
|
* Copyright (c) 2021 by Piotr Zapart |
||||||
|
* |
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy |
||||||
|
* of this software and associated documentation files (the "Software"), to deal |
||||||
|
* in the Software without restriction, including without limitation the rights |
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||||
|
* copies of the Software, and to permit persons to whom the Software is |
||||||
|
* furnished to do so, subject to the following conditions: |
||||||
|
* |
||||||
|
* The above copyright notice and this permission notice shall be included in all |
||||||
|
* copies or substantial portions of the Software. |
||||||
|
* |
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||||
|
* SOFTWARE. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <Arduino.h> |
||||||
|
#include "effect_phaserStereo_F32.h" |
||||||
|
|
||||||
|
// ---------------------------- INTERNAL LFO -------------------------------------
|
||||||
|
#define LFO_LUT_BITS 8 |
||||||
|
#define LFO_MAX_F (AUDIO_SAMPLE_RATE_EXACT / 2.0f) |
||||||
|
#define LFO_INTERP_INT_SHIFT (32-LFO_LUT_BITS) |
||||||
|
#define LFO_INTERP_FRACT_MASK ((1<<LFO_INTERP_INT_SHIFT)-1) |
||||||
|
#define LFO_LUT_SIZE_MASK ((1<<LFO_LUT_BITS)-1) |
||||||
|
|
||||||
|
// parabollic/hypertriangular waveform used for the internal LFO
|
||||||
|
extern "C" { |
||||||
|
extern const uint16_t AudioWaveformHyperTri[]; |
||||||
|
} |
||||||
|
// ---------------------------- /INTERNAL LFO ------------------------------------
|
||||||
|
|
||||||
|
AudioEffectPhaserStereo_F32::AudioEffectPhaserStereo_F32() : AudioStream_F32(2, inputQueueArray_f32) |
||||||
|
{ |
||||||
|
memset(allpass_x, 0, PHASER_STEREO_STAGES * sizeof(float32_t) * 2); |
||||||
|
memset(allpass_y, 0, PHASER_STEREO_STAGES * sizeof(float32_t) * 2); |
||||||
|
bps = false; |
||||||
|
lfo_phase_acc = 0; |
||||||
|
lfo_add = 0; |
||||||
|
lfo_lrphase = 0.0f; |
||||||
|
lfo_lroffset = 0; |
||||||
|
feedb = 0.0f; |
||||||
|
mix_ratio = 0.5f; // start with classic phaser sound
|
||||||
|
stg = PHASER_STEREO_STAGES; |
||||||
|
} |
||||||
|
AudioEffectPhaserStereo_F32::~AudioEffectPhaserStereo_F32() |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
void AudioEffectPhaserStereo_F32::update() |
||||||
|
{ |
||||||
|
#if defined(__ARM_ARCH_7EM__) |
||||||
|
audio_block_f32_t *blockL, *blockR;
|
||||||
|
const audio_block_f32_t *blockMod; // inputs
|
||||||
|
bool internalLFO = false; // use internal LFO of no modulation input
|
||||||
|
uint16_t i = 0; |
||||||
|
float32_t modSigL, modSigR; |
||||||
|
uint32_t phaseAcc = lfo_phase_acc; |
||||||
|
uint32_t phaseAdd = lfo_add; |
||||||
|
float32_t _lfo_scaler = lfo_scaler; |
||||||
|
float32_t _lfo_bias = lfo_bias; |
||||||
|
uint32_t y0, y1, fract; |
||||||
|
uint64_t y; |
||||||
|
float32_t inSigL, drySigL, inSigR, drySigR; |
||||||
|
float32_t fdb = feedb; |
||||||
|
|
||||||
|
blockL = AudioStream_F32::receiveWritable_f32(0); // audio data
|
||||||
|
blockR = AudioStream_F32::receiveWritable_f32(1); // audio data
|
||||||
|
blockMod = AudioStream_F32::receiveReadOnly_f32(2); // bipolar/int16_t control input
|
||||||
|
|
||||||
|
if (!blockL || !blockR) |
||||||
|
{ |
||||||
|
if (blockMod) AudioStream_F32::release((audio_block_f32_t *)blockMod); |
||||||
|
return; |
||||||
|
} |
||||||
|
if (!blockMod) internalLFO = true; // no modulation input provided -> use internal LFO
|
||||||
|
if (bps) |
||||||
|
{ |
||||||
|
AudioStream_F32::transmit((audio_block_f32_t *)blockL,0); |
||||||
|
AudioStream_F32::transmit((audio_block_f32_t *)blockR,1); |
||||||
|
AudioStream_F32::release((audio_block_f32_t *)blockL); |
||||||
|
AudioStream_F32::release((audio_block_f32_t *)blockR); |
||||||
|
if (blockMod) AudioStream_F32::release((audio_block_f32_t *)blockMod); |
||||||
|
return; |
||||||
|
} |
||||||
|
for (i=0; i < blockL->length; i++)
|
||||||
|
{ |
||||||
|
if(internalLFO) |
||||||
|
{ |
||||||
|
uint32_t LUTaddr = phaseAcc >> LFO_INTERP_INT_SHIFT; //8 bit address
|
||||||
|
fract = phaseAcc & LFO_INTERP_FRACT_MASK; // fractional part mask
|
||||||
|
y0 = AudioWaveformHyperTri[LUTaddr]; |
||||||
|
y1 = AudioWaveformHyperTri[LUTaddr+1]; |
||||||
|
y = ((int64_t) y0 * (LFO_INTERP_FRACT_MASK - fract)); |
||||||
|
y += ((int64_t) y1 * (fract)); |
||||||
|
modSigL = (float32_t)(y>>LFO_INTERP_INT_SHIFT) / 65535.0f; |
||||||
|
if (lfo_lroffset) |
||||||
|
{ |
||||||
|
LUTaddr = (LUTaddr + lfo_lroffset) & LFO_LUT_SIZE_MASK; |
||||||
|
y0 = AudioWaveformHyperTri[LUTaddr]; |
||||||
|
y1 = AudioWaveformHyperTri[LUTaddr+1]; |
||||||
|
y = ((int64_t) y0 * (LFO_INTERP_FRACT_MASK - fract)); |
||||||
|
y += ((int64_t) y1 * (fract)); |
||||||
|
modSigR = (float32_t)(y>>LFO_INTERP_INT_SHIFT) / 65535.0f;
|
||||||
|
} |
||||||
|
else modSigR = modSigL; |
||||||
|
|
||||||
|
phaseAcc += phaseAdd; |
||||||
|
} |
||||||
|
else // external modulation signal does not use modulation offset between LR
|
||||||
|
{ |
||||||
|
modSigL = ((float32_t)blockMod->data[i] + 32768.0f) / 65535.0f; // mod signal is 0.0 to 1.0
|
||||||
|
modSigR = modSigL;
|
||||||
|
} |
||||||
|
// apply scale/offset to the modulation wave
|
||||||
|
modSigL = modSigL * _lfo_scaler + _lfo_bias; |
||||||
|
modSigR = modSigR * _lfo_scaler + _lfo_bias; |
||||||
|
|
||||||
|
drySigL = blockL->data[i] * (1.0f - abs(fdb)*0.25f); // attenuate the input if using feedback
|
||||||
|
inSigL = drySigL + last_sampleL * fdb; |
||||||
|
drySigR = blockR->data[i] * (1.0f - abs(fdb)*0.25f); |
||||||
|
inSigR = drySigR + last_sampleR * fdb; |
||||||
|
|
||||||
|
y0 = stg; |
||||||
|
while (y0) // process allpass filters in pairs
|
||||||
|
{ |
||||||
|
y0--; |
||||||
|
allpass_y[0][y0] = modSigL * (allpass_y[0][y0] + inSigL) - allpass_x[0][y0]; // left channel
|
||||||
|
allpass_x[0][y0] = inSigL; |
||||||
|
allpass_y[1][y0] = modSigR * (allpass_y[1][y0] + inSigR) - allpass_x[1][y0]; // right channel
|
||||||
|
allpass_x[1][y0] = inSigR; |
||||||
|
y0--; |
||||||
|
allpass_y[0][y0] = modSigL * (allpass_y[0][y0] + allpass_y[0][y0+1]) - allpass_x[0][y0]; |
||||||
|
allpass_x[0][y0] = allpass_y[0][y0+1]; |
||||||
|
inSigL = allpass_y[0][y0]; |
||||||
|
allpass_y[1][y0] = modSigR * (allpass_y[1][y0] + allpass_y[1][y0+1]) - allpass_x[1][y0]; |
||||||
|
allpass_x[1][y0] = allpass_y[1][y0+1]; |
||||||
|
inSigR = allpass_y[1][y0]; |
||||||
|
} |
||||||
|
last_sampleL = inSigL; |
||||||
|
last_sampleR = inSigR; |
||||||
|
blockL->data[i] = drySigL * (1.0f - mix_ratio) + last_sampleL * mix_ratio; // dry/wet mixer
|
||||||
|
blockR->data[i] = drySigR * (1.0f - mix_ratio) + last_sampleR * mix_ratio; // dry/wet mixer
|
||||||
|
|
||||||
|
} |
||||||
|
lfo_phase_acc = phaseAcc; |
||||||
|
AudioStream_F32::transmit(blockL, 0); |
||||||
|
AudioStream_F32::transmit(blockR, 1); |
||||||
|
AudioStream_F32::release(blockL); |
||||||
|
AudioStream_F32::release(blockR); |
||||||
|
if (blockMod) AudioStream_F32::release((audio_block_f32_t *)blockMod); |
||||||
|
#endif |
||||||
|
|
||||||
|
|
||||||
|
} |
@ -0,0 +1,207 @@ |
|||||||
|
/* Stereo Phaser/Vibrato effect for Teensy Audio library
|
||||||
|
* |
||||||
|
* Author: Piotr Zapart |
||||||
|
* www.hexefx.com |
||||||
|
* |
||||||
|
* Copyright (c) 2021 by Piotr Zapart |
||||||
|
* |
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy |
||||||
|
* of this software and associated documentation files (the "Software"), to deal |
||||||
|
* in the Software without restriction, including without limitation the rights |
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||||
|
* copies of the Software, and to permit persons to whom the Software is |
||||||
|
* furnished to do so, subject to the following conditions: |
||||||
|
* |
||||||
|
* The above copyright notice and this permission notice shall be included in all |
||||||
|
* copies or substantial portions of the Software. |
||||||
|
* |
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||||
|
* SOFTWARE. |
||||||
|
*/ |
||||||
|
|
||||||
|
#ifndef _EFFECT_PHASERSTEREO_F32_H |
||||||
|
#define _EFFECT_PHASERSTEREO_F32_H |
||||||
|
|
||||||
|
#include <Arduino.h> |
||||||
|
#include "Audio.h" |
||||||
|
#include "AudioStream.h" |
||||||
|
#include "AudioStream_F32.h" |
||||||
|
#include "arm_math.h" |
||||||
|
|
||||||
|
#define PHASER_STEREO_STAGES 12 |
||||||
|
|
||||||
|
class AudioEffectPhaserStereo_F32 : public AudioStream_F32 |
||||||
|
{ |
||||||
|
public: |
||||||
|
AudioEffectPhaserStereo_F32(); |
||||||
|
~AudioEffectPhaserStereo_F32(); |
||||||
|
virtual void update(); |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Scale and offset the modulation signal. It can be the internal LFO |
||||||
|
* or the incomig routed modulation AudioSignal. |
||||||
|
* LFO will oscillate between these two max and min values.
|
||||||
|
*
|
||||||
|
* @param top top level of the LFO |
||||||
|
* @param bottom bottom level of the LFO |
||||||
|
*/ |
||||||
|
void depth(float32_t top, float32_t bottom) |
||||||
|
{ |
||||||
|
float32_t a, b; |
||||||
|
lfo_top = constrain(top, 0.0f, 1.0f); |
||||||
|
lfo_btm = constrain(bottom, 0.0f, 1.0f); |
||||||
|
|
||||||
|
a = abs(lfo_top - lfo_btm); // scaler
|
||||||
|
b = min(lfo_top, lfo_btm); // bias
|
||||||
|
__disable_irq(); |
||||||
|
lfo_bias = b; |
||||||
|
lfo_scaler = a; |
||||||
|
__enable_irq(); |
||||||
|
} |
||||||
|
/**
|
||||||
|
* @brief classic way of setting the depth: LFO centered around 0.5 |
||||||
|
*
|
||||||
|
* @param dpth modulation wave amplitude |
||||||
|
*/ |
||||||
|
void depth(float32_t value) |
||||||
|
{ |
||||||
|
value = constrain(value, 0.0f, 1.0f); |
||||||
|
value *= 0.5f; |
||||||
|
lfo_top = 0.5f + value; |
||||||
|
lfo_btm = 0.5f - value; |
||||||
|
__disable_irq(); |
||||||
|
lfo_bias = 0.5f; |
||||||
|
lfo_scaler = value; |
||||||
|
__enable_irq();
|
||||||
|
} |
||||||
|
void top(float32_t value) |
||||||
|
{ |
||||||
|
lfo_top = constrain(value, 0.0f, 1.0f); |
||||||
|
depth(lfo_top, lfo_btm);
|
||||||
|
} |
||||||
|
void btm(float32_t value) |
||||||
|
{ |
||||||
|
lfo_btm = constrain(value, 0.0f, 1.0f); |
||||||
|
depth(lfo_top, lfo_btm);
|
||||||
|
} |
||||||
|
/**
|
||||||
|
* @brief Controls the internal LFO, or if a control signal is used, scales it |
||||||
|
* Use this function to update all lfo parameteres at once |
||||||
|
*
|
||||||
|
* @param f_Hz lfo frequency, use 0.0f for manual phaser control |
||||||
|
* @param phase phase shift between the LFOs L and R waveforms, 0.0-1.0 range |
||||||
|
* @param top lfo top level
|
||||||
|
* @param btm lfo bottm level |
||||||
|
*/ |
||||||
|
void lfo(float32_t f_Hz, float32_t phase, float32_t top, float32_t btm) |
||||||
|
{ |
||||||
|
float32_t a, b, c; |
||||||
|
uint32_t add; |
||||||
|
uint8_t bs; |
||||||
|
|
||||||
|
a = constrain(top, 0.0f, 1.0f); |
||||||
|
b = constrain(btm, 0.0f, 1.0f); |
||||||
|
c = abs(a - b); // scaler
|
||||||
|
a = min(a, b); // bias
|
||||||
|
f_Hz = constrain(f_Hz, 0.0f, AUDIO_SAMPLE_RATE_EXACT/2); |
||||||
|
phase = constrain(phase, 0.0f, 1.0f); |
||||||
|
add = f_Hz * (4294967296.0f / AUDIO_SAMPLE_RATE_EXACT); |
||||||
|
bs = (uint8_t)(phase * 128.0f); |
||||||
|
__disable_irq(); |
||||||
|
lfo_scaler = c; |
||||||
|
lfo_bias = a; |
||||||
|
lfo_add = add; |
||||||
|
lfo_lroffset = bs; |
||||||
|
__enable_irq(); |
||||||
|
} |
||||||
|
void stereo(float32_t phase) |
||||||
|
{ |
||||||
|
uint8_t bs; |
||||||
|
phase = constrain(phase, 0.0f, 1.0f); |
||||||
|
bs = (uint8_t)(phase * 128.0f); |
||||||
|
__disable_irq(); |
||||||
|
lfo_lroffset = bs; |
||||||
|
__enable_irq(); |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set the rate of the internal LFO |
||||||
|
*
|
||||||
|
* @param f_Hz lfo frequency, use 0.0f for manual phaser control |
||||||
|
*/ |
||||||
|
void lfo_rate(float32_t f_Hz) |
||||||
|
{ |
||||||
|
float32_t c; |
||||||
|
uint32_t add; |
||||||
|
c = constrain(f_Hz, 0.0f, AUDIO_SAMPLE_RATE_EXACT/2); |
||||||
|
add = c * (4294967296.0 / AUDIO_SAMPLE_RATE_EXACT); |
||||||
|
__disable_irq(); |
||||||
|
lfo_add = add; |
||||||
|
__enable_irq(); |
||||||
|
} |
||||||
|
/**
|
||||||
|
* @brief Controls the feedback parameter |
||||||
|
*
|
||||||
|
* @param fdb ffedback value in range 0.0f to 1.0f |
||||||
|
*/ |
||||||
|
void feedback(float32_t fdb) |
||||||
|
{ |
||||||
|
feedb = constrain(fdb, -1.0f, 1.0f); |
||||||
|
} |
||||||
|
/**
|
||||||
|
* @brief Dry / Wet mixer ratio. Classic Phaser sound uses 0.5f for 50% dry and 50%Wet |
||||||
|
* 1.0f will produce 100% wet signal craeting a vibrato effect |
||||||
|
*
|
||||||
|
* @param ratio mixing ratio, range 0.0f (full dry) to 1.0f (full wet) |
||||||
|
*/ |
||||||
|
void mix(float32_t ratio) |
||||||
|
{ |
||||||
|
mix_ratio = constrain(ratio, 0.0f, 1.0f); |
||||||
|
} |
||||||
|
/**
|
||||||
|
* @brief Sets the number of stages used in the phaser |
||||||
|
* Allowed values are: 2, 4, 6, 8, 10, 12 |
||||||
|
*
|
||||||
|
* @param st number of stages, even value <= 12 |
||||||
|
*/ |
||||||
|
void stages(uint8_t st) |
||||||
|
{ |
||||||
|
if (st && st == ((st >> 1) << 1) && st <= PHASER_STEREO_STAGES) // only 2, 4, 6, 8, 12 allowed
|
||||||
|
{ |
||||||
|
stg = st; |
||||||
|
} |
||||||
|
} |
||||||
|
/**
|
||||||
|
* @brief Use to bypass the effect (true) |
||||||
|
*
|
||||||
|
* @param state true = bypass on, false = phaser on |
||||||
|
*/ |
||||||
|
void bypass_set(bool state) {bps = state;} |
||||||
|
bool bypass_tgl(void) {bps ^= 1; return bps;} |
||||||
|
|
||||||
|
private: |
||||||
|
uint8_t stg; // number of stages
|
||||||
|
bool bps; // bypass
|
||||||
|
audio_block_f32_t *inputQueueArray_f32[3];
|
||||||
|
float32_t allpass_x[2][PHASER_STEREO_STAGES]; // allpass inputs
|
||||||
|
float32_t allpass_y[2][PHASER_STEREO_STAGES]; // allpass outputs
|
||||||
|
float32_t mix_ratio; // 0 = dry. 1.0 = wet
|
||||||
|
float32_t feedb; // feedback
|
||||||
|
float32_t last_sampleL; |
||||||
|
float32_t last_sampleR; |
||||||
|
uint32_t lfo_phase_acc; // interfnal lfo
|
||||||
|
uint32_t lfo_add; |
||||||
|
float32_t lfo_lrphase; |
||||||
|
uint32_t lfo_lroffset; |
||||||
|
float32_t lfo_scaler; |
||||||
|
float32_t lfo_bias; |
||||||
|
float32_t lfo_top; |
||||||
|
float32_t lfo_btm; |
||||||
|
}; |
||||||
|
|
||||||
|
#endif // _EFFECT_PHASER_H
|
@ -0,0 +1,284 @@ |
|||||||
|
/* Stereo plate reverb for Teensy 4
|
||||||
|
* |
||||||
|
* Author: Piotr Zapart |
||||||
|
* www.hexefx.com |
||||||
|
* |
||||||
|
* Copyright (c) 2020 by Piotr Zapart |
||||||
|
* |
||||||
|
* Development of this audio library was funded by PJRC.COM, LLC by sales of |
||||||
|
* Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop |
||||||
|
* open source software by purchasing Teensy or other PJRC products. |
||||||
|
* |
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy |
||||||
|
* of this software and associated documentation files (the "Software"), to deal |
||||||
|
* in the Software without restriction, including without limitation the rights |
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||||
|
* copies of the Software, and to permit persons to whom the Software is |
||||||
|
* furnished to do so, subject to the following conditions: |
||||||
|
* |
||||||
|
* The above copyright notice, development funding notice, and this permission |
||||||
|
* notice shall be included in all copies or substantial portions of the Software. |
||||||
|
* |
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
||||||
|
* THE SOFTWARE. |
||||||
|
*/ |
||||||
|
|
||||||
|
|
||||||
|
#include <Arduino.h> |
||||||
|
#include "effect_platereverb_F32.h" |
||||||
|
|
||||||
|
#define INP_ALLP_COEFF (0.65f) |
||||||
|
#define LOOP_ALLOP_COEFF (0.65f) |
||||||
|
|
||||||
|
#define TREBLE_LOSS_FREQ (0.3f) |
||||||
|
#define TREBLE_LOSS_FREQ_MAX (0.08f) |
||||||
|
#define BASS_LOSS_FREQ (0.06f) |
||||||
|
|
||||||
|
#define RV_MASTER_LOWPASS_F (0.6f) // master lowpass scaled frequency coeff.
|
||||||
|
|
||||||
|
|
||||||
|
AudioEffectPlateReverb_F32::AudioEffectPlateReverb_F32() : AudioStream_F32(2, inputQueueArray_f32) { begin();} |
||||||
|
|
||||||
|
bool AudioEffectPlateReverb_F32::begin() |
||||||
|
{ |
||||||
|
input_attn = 0.5f; |
||||||
|
wet_gain = 1.0f; // default mode: wet signal only
|
||||||
|
dry_gain = 0.0f; |
||||||
|
in_allp_k = INP_ALLP_COEFF; |
||||||
|
loop_allp_k = LOOP_ALLOP_COEFF; |
||||||
|
rv_time_scaler = 1.0f; |
||||||
|
rv_time_k = 0.2f; |
||||||
|
|
||||||
|
if(!in_allp_1L.init(&in_allp_k)) return false; |
||||||
|
if(!in_allp_2L.init(&in_allp_k)) return false; |
||||||
|
if(!in_allp_3L.init(&in_allp_k)) return false; |
||||||
|
if(!in_allp_4L.init(&in_allp_k)) return false; |
||||||
|
|
||||||
|
if(!in_allp_1R.init(&in_allp_k)) return false; |
||||||
|
if(!in_allp_2R.init(&in_allp_k)) return false; |
||||||
|
if(!in_allp_3R.init(&in_allp_k)) return false; |
||||||
|
if(!in_allp_4R.init(&in_allp_k)) return false; |
||||||
|
|
||||||
|
in_allp_out_L = 0.0f; |
||||||
|
in_allp_out_R = 0.0f; |
||||||
|
|
||||||
|
if(!lp_allp_1.init(&loop_allp_k)) return false; |
||||||
|
if(!lp_allp_2.init(&loop_allp_k)) return false; |
||||||
|
if(!lp_allp_3.init(&loop_allp_k)) return false; |
||||||
|
if(!lp_allp_4.init(&loop_allp_k)) return false; |
||||||
|
|
||||||
|
lp_allp_out = 0.0f; |
||||||
|
|
||||||
|
if(!lp_dly1.init()) return false; |
||||||
|
if(!lp_dly2.init()) return false; |
||||||
|
if(!lp_dly3.init()) return false; |
||||||
|
if(!lp_dly4.init()) return false; |
||||||
|
|
||||||
|
lp_hidamp_k = 1.0f; |
||||||
|
lp_lodamp_k = 0.0f; |
||||||
|
flt1.init(BASS_LOSS_FREQ, &lp_lodamp_k, TREBLE_LOSS_FREQ, &lp_hidamp_k); |
||||||
|
flt2.init(BASS_LOSS_FREQ, &lp_lodamp_k, TREBLE_LOSS_FREQ, &lp_hidamp_k); |
||||||
|
flt3.init(BASS_LOSS_FREQ, &lp_lodamp_k, TREBLE_LOSS_FREQ, &lp_hidamp_k); |
||||||
|
flt4.init(BASS_LOSS_FREQ, &lp_lodamp_k, TREBLE_LOSS_FREQ, &lp_hidamp_k); |
||||||
|
|
||||||
|
master_lp_k = 1.0f; |
||||||
|
master_hp_k = 0.0f; |
||||||
|
flt_masterL.init(0.08f, &master_hp_k, 0.1f, &master_lp_k); |
||||||
|
flt_masterR.init(0.08f, &master_hp_k, 0.1f, &master_lp_k); |
||||||
|
|
||||||
|
if(!pitchL.init()) return false; |
||||||
|
if(!pitchR.init()) return false; |
||||||
|
pitchL.setPitch(1.0f); //natural pitch
|
||||||
|
pitchR.setPitch(1.0f); //natural pitch
|
||||||
|
pitchL.setTone(0.36f); |
||||||
|
pitchR.setTone(0.36f); |
||||||
|
pitchL.setMix(0.0f); |
||||||
|
pitchR.setMix(0.0f); |
||||||
|
|
||||||
|
shimmerRatio = 0.0f; |
||||||
|
if(!pitchShimL.init()) return false; |
||||||
|
if(!pitchShimR.init()) return false; |
||||||
|
pitchShimL.setPitch(2.0f); |
||||||
|
pitchShimR.setPitch(2.0f); |
||||||
|
pitchShimL.setTone(0.26f); |
||||||
|
pitchShimR.setTone(0.26f); |
||||||
|
pitchShimL.setMix(0.0f); |
||||||
|
pitchShimR.setMix(0.0f); |
||||||
|
|
||||||
|
flags.bypass = 1; |
||||||
|
flags.freeze = 0; |
||||||
|
initialised = true; |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
void AudioEffectPlateReverb_F32::update() |
||||||
|
{ |
||||||
|
if (!initialised) return; |
||||||
|
audio_block_f32_t *blockL, *blockR; |
||||||
|
int16_t i; |
||||||
|
float acc; |
||||||
|
float rv_time; |
||||||
|
uint32_t offset; |
||||||
|
float lfo_fr; |
||||||
|
// handle bypass, 1st call will clean the buffers to avoid continuing the previous reverb tail
|
||||||
|
if (flags.bypass) |
||||||
|
{ |
||||||
|
if (!flags.cleanup_done) |
||||||
|
{ |
||||||
|
in_allp_1L.reset(); |
||||||
|
in_allp_2L.reset(); |
||||||
|
in_allp_3L.reset(); |
||||||
|
in_allp_4L.reset(); |
||||||
|
in_allp_1R.reset(); |
||||||
|
in_allp_2R.reset(); |
||||||
|
in_allp_3R.reset(); |
||||||
|
in_allp_4R.reset(); |
||||||
|
lp_allp_1.reset(); |
||||||
|
lp_allp_2.reset(); |
||||||
|
lp_allp_3.reset(); |
||||||
|
lp_allp_4.reset(); |
||||||
|
lp_dly1.reset(); |
||||||
|
lp_dly2.reset(); |
||||||
|
lp_dly3.reset(); |
||||||
|
lp_dly4.reset(); |
||||||
|
flags.cleanup_done = 1; |
||||||
|
} |
||||||
|
|
||||||
|
if (dry_gain > 0.0f) // if dry/wet mixer is used
|
||||||
|
{ |
||||||
|
blockL = AudioStream_F32::receiveReadOnly_f32(0); |
||||||
|
blockR = AudioStream_F32::receiveReadOnly_f32(1); |
||||||
|
if (!blockL || !blockR)
|
||||||
|
{ |
||||||
|
if (blockL) AudioStream_F32::release(blockL); |
||||||
|
if (blockR) AudioStream_F32::release(blockR); |
||||||
|
return; |
||||||
|
} |
||||||
|
AudioStream_F32::transmit(blockL, 0);
|
||||||
|
AudioStream_F32::transmit(blockR, 1); |
||||||
|
AudioStream_F32::release(blockL); |
||||||
|
AudioStream_F32::release(blockR); |
||||||
|
} |
||||||
|
blockL = AudioStream_F32::allocate_f32(); |
||||||
|
if (!blockL) return; |
||||||
|
arm_fill_f32(0.0f, blockL->data, blockL->length); |
||||||
|
AudioStream_F32::transmit(blockL, 0);
|
||||||
|
AudioStream_F32::transmit(blockL, 1); |
||||||
|
AudioStream_F32::release(blockL);
|
||||||
|
return; |
||||||
|
} |
||||||
|
flags.cleanup_done = 0; |
||||||
|
|
||||||
|
blockL = AudioStream_F32::receiveWritable_f32(0); |
||||||
|
blockR = AudioStream_F32::receiveWritable_f32(1); |
||||||
|
|
||||||
|
if (!blockL || !blockR)
|
||||||
|
{ |
||||||
|
if (blockL) AudioStream_F32::release(blockL); |
||||||
|
if (blockR) AudioStream_F32::release(blockR); |
||||||
|
return; |
||||||
|
} |
||||||
|
rv_time = rv_time_k; |
||||||
|
|
||||||
|
for (i=0; i < blockL->length; i++)
|
||||||
|
{ |
||||||
|
// do the LFOs
|
||||||
|
lfo1.update(); |
||||||
|
lfo2.update(); |
||||||
|
|
||||||
|
acc = blockL->data[i] * input_attn; |
||||||
|
|
||||||
|
// chained input allpasses, channel L
|
||||||
|
acc = in_allp_1L.process(acc); |
||||||
|
acc = in_allp_2L.process(acc); |
||||||
|
acc = in_allp_3L.process(acc); |
||||||
|
in_allp_out_L = in_allp_4L.process(acc); |
||||||
|
in_allp_out_L = pitchL.process(in_allp_out_L);
|
||||||
|
|
||||||
|
// chained input allpasses, channel R
|
||||||
|
acc = blockR->data[i] * input_attn; |
||||||
|
|
||||||
|
acc = in_allp_1R.process(acc); |
||||||
|
acc = in_allp_2R.process(acc); |
||||||
|
acc = in_allp_3R.process(acc); |
||||||
|
in_allp_out_R = in_allp_4R.process(acc); |
||||||
|
|
||||||
|
acc = pitchShimR.process(lp_allp_out + in_allp_out_R); // shimmer
|
||||||
|
|
||||||
|
acc = lp_dly1.process(acc); |
||||||
|
acc = flt1.process(acc) * rv_time * rv_time_scaler; |
||||||
|
|
||||||
|
acc = lp_allp_2.process(acc + in_allp_out_L); |
||||||
|
acc = lp_dly2.process(acc); |
||||||
|
acc = flt2.process(acc) * rv_time * rv_time_scaler; |
||||||
|
|
||||||
|
acc = pitchShimL.process(acc + in_allp_out_R); // shimmer
|
||||||
|
|
||||||
|
acc = lp_allp_3.process(acc); |
||||||
|
acc = lp_dly3.process(acc); |
||||||
|
acc = flt3.process(acc) * rv_time * rv_time_scaler; |
||||||
|
|
||||||
|
acc = lp_allp_4.process(acc + in_allp_out_L); |
||||||
|
acc = lp_dly4.process(acc); |
||||||
|
|
||||||
|
lp_allp_out = flt4.process(acc) * rv_time * rv_time_scaler;
|
||||||
|
|
||||||
|
acc = lp_dly1.getTap(lp_dly1_offset_L) * 0.8f; |
||||||
|
acc += lp_dly2.getTap(lp_dly2_offset_L) * 0.7f; |
||||||
|
acc += lp_dly3.getTap(lp_dly3_offset_L) * 0.6f; |
||||||
|
acc += lp_dly4.getTap(lp_dly4_offset_L) * 0.5f; |
||||||
|
|
||||||
|
// Master lowpass filter
|
||||||
|
acc = flt_masterL.process(acc); |
||||||
|
|
||||||
|
blockL->data[i] = acc * wet_gain + blockL->data[i] * dry_gain;
|
||||||
|
// ChannelR
|
||||||
|
acc = lp_dly1.getTap(lp_dly1_offset_R) * 0.8f; |
||||||
|
acc += lp_dly2.getTap(lp_dly2_offset_R) * 0.7f; |
||||||
|
acc += lp_dly3.getTap(lp_dly3_offset_R) * 0.6f; |
||||||
|
acc += lp_dly4.getTap(lp_dly4_offset_R) * 0.5f; |
||||||
|
// Master lowpass filter
|
||||||
|
acc = flt_masterR.process(acc); |
||||||
|
blockR->data[i] = acc * wet_gain + blockR->data[i] * dry_gain; |
||||||
|
|
||||||
|
// modulate the delay lines
|
||||||
|
// delay 1
|
||||||
|
lfo1.get(BASIC_LFO_PHASE_0, &offset, &lfo_fr); // lfo1 sin output
|
||||||
|
acc = lp_dly1.getTap(offset, lfo_fr); |
||||||
|
lp_dly1.write_toOffset(acc, LFO_AMPL*2); |
||||||
|
lp_dly1.updateIndex(); |
||||||
|
|
||||||
|
// delay 2
|
||||||
|
lfo1.get(BASIC_LFO_PHASE_90, &offset, &lfo_fr); // lfo1 cos output
|
||||||
|
acc = lp_dly2.getTap(offset, lfo_fr); |
||||||
|
lp_dly2.write_toOffset(acc, LFO_AMPL*2); |
||||||
|
lp_dly2.updateIndex(); |
||||||
|
|
||||||
|
// delay 3
|
||||||
|
lfo2.get(BASIC_LFO_PHASE_0, &offset, &lfo_fr); // lfo2 sin output
|
||||||
|
acc = lp_dly3.getTap(offset, lfo_fr); |
||||||
|
lp_dly3.write_toOffset(acc, LFO_AMPL*2); |
||||||
|
lp_dly3.updateIndex(); |
||||||
|
|
||||||
|
// delay 4
|
||||||
|
lfo2.get(BASIC_LFO_PHASE_90, &offset, &lfo_fr); // lfo2 cos output
|
||||||
|
acc = lp_dly4.getTap(offset, lfo_fr); |
||||||
|
lp_dly4.write_toOffset(acc, LFO_AMPL*2); |
||||||
|
lp_dly4.updateIndex();
|
||||||
|
} |
||||||
|
if (LFO_AMPL != LFO_AMPLset)
|
||||||
|
{ |
||||||
|
lfo1.setDepth(LFO_AMPL); |
||||||
|
lfo2.setDepth(LFO_AMPL); |
||||||
|
LFO_AMPL = LFO_AMPLset; |
||||||
|
} |
||||||
|
AudioStream_F32::transmit(blockL, 0); |
||||||
|
AudioStream_F32::transmit(blockR, 1); |
||||||
|
AudioStream_F32::release(blockL); |
||||||
|
AudioStream_F32::release(blockR); |
||||||
|
} |
@ -0,0 +1,360 @@ |
|||||||
|
/* Stereo plate reverb for ESP32
|
||||||
|
* |
||||||
|
* Author: Piotr Zapart |
||||||
|
* www.hexefx.com |
||||||
|
* |
||||||
|
* Copyright (c) 2021 by Piotr Zapart |
||||||
|
* |
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy |
||||||
|
* of this software and associated documentation files (the "Software"), to deal |
||||||
|
* in the Software without restriction, including without limitation the rights |
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||||
|
* copies of the Software, and to permit persons to whom the Software is |
||||||
|
* furnished to do so, subject to the following conditions: |
||||||
|
* |
||||||
|
* The above copyright notice and this permission notice shall be included in all |
||||||
|
* copies or substantial portions of the Software. |
||||||
|
* |
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||||
|
* SOFTWARE. |
||||||
|
*/ |
||||||
|
|
||||||
|
/***
|
||||||
|
* Algorithm based on plate reverbs developed for SpinSemi FV-1 DSP chip |
||||||
|
*
|
||||||
|
* Allpass + modulated delay line based lush plate reverb |
||||||
|
*
|
||||||
|
* Input parameters are float in range 0.0 to 1.0: |
||||||
|
*
|
||||||
|
* size - reverb time |
||||||
|
* hidamp - hi frequency loss in the reverb tail |
||||||
|
* lodamp - low frequency loss in the reverb tail |
||||||
|
* lowpass - output/master lowpass filter, useful for darkening the reverb sound
|
||||||
|
* diffusion - lower settings will make the reverb tail more "echoey". |
||||||
|
* freeze - infinite reverb tail effect |
||||||
|
*
|
||||||
|
*/ |
||||||
|
|
||||||
|
#ifndef _EFFECT_PLATEREVERB_F32_H_ |
||||||
|
#define _EFFECT_PLATEREVERB_F32_H_ |
||||||
|
|
||||||
|
#include <Arduino.h> |
||||||
|
#include "Audio.h" |
||||||
|
#include "AudioStream.h" |
||||||
|
#include "AudioStream_F32.h" |
||||||
|
#include "arm_math.h" |
||||||
|
#include "basic_components.h" |
||||||
|
|
||||||
|
|
||||||
|
class AudioEffectPlateReverb_F32 : public AudioStream_F32 |
||||||
|
{ |
||||||
|
public: |
||||||
|
AudioEffectPlateReverb_F32(); |
||||||
|
~AudioEffectPlateReverb_F32(){}; |
||||||
|
virtual void update(); |
||||||
|
|
||||||
|
bool begin(void); |
||||||
|
|
||||||
|
void size(float n) |
||||||
|
{ |
||||||
|
n = constrain(n, 0.0f, 1.0f); |
||||||
|
n = 2*n - n*n; |
||||||
|
n = map(n, 0.0f, 1.0f, 0.2f, rv_time_k_max); |
||||||
|
//float attn = map(n, 0.2f, rv_time_k_max, 0.5f, 0.25f);
|
||||||
|
__disable_irq(); |
||||||
|
rv_time_k = n; |
||||||
|
input_attn = 0.5f; |
||||||
|
__enable_irq(); |
||||||
|
} |
||||||
|
|
||||||
|
float size_get(void) {return rv_time_k;} |
||||||
|
|
||||||
|
void hidamp(float n) |
||||||
|
{ |
||||||
|
n = 1.0f - constrain(n, 0.0f, 1.0f); |
||||||
|
__disable_irq(); |
||||||
|
lp_hidamp_k = n; |
||||||
|
__enable_irq(); |
||||||
|
} |
||||||
|
|
||||||
|
void lodamp(float n) |
||||||
|
{ |
||||||
|
n = -constrain(n, 0.0f, 1.0f); |
||||||
|
float32_t tscal = 1.0f + n*0.12f; //n is negativbe here
|
||||||
|
__disable_irq(); |
||||||
|
lp_lodamp_k = n; |
||||||
|
rv_time_scaler = tscal; // limit the max reverb time, otherwise it will clip
|
||||||
|
__enable_irq(); |
||||||
|
} |
||||||
|
|
||||||
|
void lowpass(float n) |
||||||
|
{ |
||||||
|
n = 1.0f - constrain(n, 0.0f, 1.0f); |
||||||
|
__disable_irq(); |
||||||
|
master_lp_k = n; |
||||||
|
__enable_irq(); |
||||||
|
} |
||||||
|
void hipass(float n) |
||||||
|
{ |
||||||
|
n = -constrain(n, 0.0f, 1.0f); |
||||||
|
__disable_irq(); |
||||||
|
master_hp_k = n; |
||||||
|
__enable_irq(); |
||||||
|
} |
||||||
|
void diffusion(float n) |
||||||
|
{ |
||||||
|
n = constrain(n, 0.0f, 1.0f); |
||||||
|
n = map(n, 0.0f, 1.0f, 0.005f, 0.65f); |
||||||
|
__disable_irq(); |
||||||
|
in_allp_k = n; |
||||||
|
loop_allp_k = n; |
||||||
|
__enable_irq(); |
||||||
|
} |
||||||
|
|
||||||
|
void freeze(bool state) |
||||||
|
{ |
||||||
|
flags.freeze = state; |
||||||
|
if (state) |
||||||
|
{ |
||||||
|
rv_time_k_tmp = rv_time_k; // store the settings
|
||||||
|
lp_lodamp_k_tmp = lp_lodamp_k; |
||||||
|
lp_hidamp_k_tmp = lp_hidamp_k; |
||||||
|
__disable_irq(); |
||||||
|
rv_time_k = freeze_rvtime_k;
|
||||||
|
input_attn = freeze_ingain; |
||||||
|
rv_time_scaler = 1.0f; |
||||||
|
lp_lodamp_k = freeze_lodamp_k; |
||||||
|
lp_hidamp_k = freeze_hidamp_k; |
||||||
|
pitchShimL.setMix(0.0f); // shimmer off
|
||||||
|
pitchShimR.setMix(0.0f); |
||||||
|
__enable_irq(); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
//float attn = map(rv_time_k_tmp, 0.0f, rv_time_k_max, 0.5f, 0.25f); // recalc the in attenuation
|
||||||
|
float sc = 1.0f - lp_lodamp_k_tmp * 0.12f; // scale up the reverb time due to bass loss
|
||||||
|
__disable_irq(); |
||||||
|
rv_time_k = rv_time_k_tmp; // restore the value
|
||||||
|
input_attn = 0.5f; |
||||||
|
rv_time_scaler = sc; |
||||||
|
lp_hidamp_k = lp_hidamp_k_tmp; |
||||||
|
lp_lodamp_k = lp_lodamp_k_tmp; |
||||||
|
shimmer(shimmerRatio); |
||||||
|
__enable_irq(); |
||||||
|
} |
||||||
|
} |
||||||
|
/**
|
||||||
|
* @brief Allows to bleed some signal in while in freeze mode |
||||||
|
* has to be relatively low value to avoid oscillation |
||||||
|
*
|
||||||
|
* @param b - amount if input signal injected to the freeze reverb |
||||||
|
* range 0.0 to 1.0 |
||||||
|
*/ |
||||||
|
void freezeBleedIn(float b) |
||||||
|
{ |
||||||
|
b = constrain(b, 0.0f, 1.0f); |
||||||
|
b = map(b, 0.0f, 1.0f, 0.0f, 0.1f); |
||||||
|
freeze_ingain = b; |
||||||
|
if (flags.freeze) input_attn = b; // update input gain if freeze is enabled
|
||||||
|
} |
||||||
|
|
||||||
|
void mix(float wet, float dry=0.0f) |
||||||
|
{ |
||||||
|
wet_level(wet); |
||||||
|
dry_level(dry); |
||||||
|
} |
||||||
|
|
||||||
|
void wet_level(float wet) |
||||||
|
{ |
||||||
|
wet_gain = constrain(wet, 0.0f, 6.0f); |
||||||
|
} |
||||||
|
|
||||||
|
void dry_level(float dry) |
||||||
|
{ |
||||||
|
dry_gain = constrain(dry, 0.0f, 1.0f); |
||||||
|
} |
||||||
|
|
||||||
|
bool freeze_tgl() {flags.freeze ^= 1; freeze(flags.freeze); return flags.freeze;} |
||||||
|
|
||||||
|
bool freeze_get() {return flags.freeze;} |
||||||
|
|
||||||
|
bool bypass_get(void) {return flags.bypass;} |
||||||
|
void bypass_set(bool state)
|
||||||
|
{ |
||||||
|
flags.bypass = state; |
||||||
|
if (state) freeze(false); // disable freeze in bypass mode
|
||||||
|
} |
||||||
|
bool bypass_tgl(void)
|
||||||
|
{ |
||||||
|
flags.bypass ^= 1;
|
||||||
|
if (flags.bypass) freeze(false); // disable freeze in bypass mode
|
||||||
|
return flags.bypass; |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief controls the delay line modulation, higher values create chorus effect |
||||||
|
*
|
||||||
|
* @param c chorus depth, range 0.0f to 1.0f |
||||||
|
*/ |
||||||
|
void chorus(float c) |
||||||
|
{ |
||||||
|
c = map(c, 0.0f, 1.0f, 1.0f, 100.0f); |
||||||
|
LFO_AMPLset = (uint32_t)c;
|
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief
|
||||||
|
*
|
||||||
|
* @param s
|
||||||
|
*/ |
||||||
|
void shimmer(float s) |
||||||
|
{ |
||||||
|
if (flags.freeze) return; // do not update the shimmer if in freeze mode
|
||||||
|
s = constrain(s, 0.0f, 1.0f); |
||||||
|
s = 2*s - s*s;
|
||||||
|
pitchShimL.setMix(s); |
||||||
|
pitchShimR.setMix(s); |
||||||
|
shimmerRatio = s; |
||||||
|
} |
||||||
|
void shimmerPitch(float ratio) |
||||||
|
{ |
||||||
|
pitchShimL.setPitch(ratio); |
||||||
|
pitchShimR.setPitch(ratio); |
||||||
|
} |
||||||
|
void shimmerPitchSemitones(int8_t semitones) |
||||||
|
{ |
||||||
|
pitchShimL.setPitchSemintone(semitones); |
||||||
|
pitchShimR.setPitchSemintone(semitones); |
||||||
|
} |
||||||
|
/**
|
||||||
|
* @brief set the reverb pitch. Range -12 to +24
|
||||||
|
*
|
||||||
|
* @param semitones pitch shift in semitones |
||||||
|
*/ |
||||||
|
void pitchSemitones(int8_t semitones) |
||||||
|
{ |
||||||
|
pitchL.setPitchSemintone(semitones); |
||||||
|
pitchR.setPitchSemintone(semitones); |
||||||
|
} |
||||||
|
void pitchMix(float s) |
||||||
|
{ |
||||||
|
s = constrain(s, 0.0f, 1.0f); |
||||||
|
pitchL.setMix(s); |
||||||
|
pitchR.setMix(s); |
||||||
|
pitchRatio = s; |
||||||
|
} |
||||||
|
|
||||||
|
private: |
||||||
|
struct flags_t |
||||||
|
{ |
||||||
|
unsigned bypass: 1; |
||||||
|
unsigned freeze: 1; |
||||||
|
unsigned shimmer: 1; // maybe will be added at some point
|
||||||
|
unsigned cleanup_done: 1; |
||||||
|
}flags; |
||||||
|
audio_block_f32_t *inputQueueArray_f32[2]; |
||||||
|
|
||||||
|
static const uint16_t IN_ALLP1_BUFL_LEN = 224u; |
||||||
|
static const uint16_t IN_ALLP2_BUFL_LEN = 420u; |
||||||
|
static const uint16_t IN_ALLP3_BUFL_LEN = 856u; |
||||||
|
static const uint16_t IN_ALLP4_BUFL_LEN = 1089u; |
||||||
|
|
||||||
|
static const uint16_t IN_ALLP1_BUFR_LEN = 156u; |
||||||
|
static const uint16_t IN_ALLP2_BUFR_LEN = 520u; |
||||||
|
static const uint16_t IN_ALLP3_BUFR_LEN = 956u; |
||||||
|
static const uint16_t IN_ALLP4_BUFR_LEN = 1289u; |
||||||
|
|
||||||
|
static const uint16_t LP_ALLP1_BUF_LEN = 2303u; |
||||||
|
static const uint16_t LP_ALLP2_BUF_LEN = 2905u; |
||||||
|
static const uint16_t LP_ALLP3_BUF_LEN = 3175u; |
||||||
|
static const uint16_t LP_ALLP4_BUF_LEN = 2398u; |
||||||
|
|
||||||
|
static const uint16_t LP_DLY1_BUF_LEN = 3423u; |
||||||
|
static const uint16_t LP_DLY2_BUF_LEN = 4589u; |
||||||
|
static const uint16_t LP_DLY3_BUF_LEN = 4365u; |
||||||
|
static const uint16_t LP_DLY4_BUF_LEN = 3698u; |
||||||
|
|
||||||
|
const uint16_t lp_dly1_offset_L = 201; |
||||||
|
const uint16_t lp_dly2_offset_L = 145; |
||||||
|
const uint16_t lp_dly3_offset_L = 1897; |
||||||
|
const uint16_t lp_dly4_offset_L = 280; |
||||||
|
|
||||||
|
const uint16_t lp_dly1_offset_R = 1897; |
||||||
|
const uint16_t lp_dly2_offset_R = 1245; |
||||||
|
const uint16_t lp_dly3_offset_R = 487; |
||||||
|
const uint16_t lp_dly4_offset_R = 780;
|
||||||
|
|
||||||
|
AudioFilterAllpass<IN_ALLP1_BUFL_LEN> in_allp_1L; |
||||||
|
AudioFilterAllpass<IN_ALLP2_BUFL_LEN> in_allp_2L; |
||||||
|
AudioFilterAllpass<IN_ALLP3_BUFL_LEN> in_allp_3L; |
||||||
|
AudioFilterAllpass<IN_ALLP4_BUFL_LEN> in_allp_4L; |
||||||
|
|
||||||
|
AudioFilterAllpass<IN_ALLP1_BUFR_LEN> in_allp_1R; |
||||||
|
AudioFilterAllpass<IN_ALLP2_BUFR_LEN> in_allp_2R; |
||||||
|
AudioFilterAllpass<IN_ALLP3_BUFR_LEN> in_allp_3R; |
||||||
|
AudioFilterAllpass<IN_ALLP4_BUFR_LEN> in_allp_4R; |
||||||
|
|
||||||
|
AudioFilterAllpass<LP_ALLP1_BUF_LEN> lp_allp_1; |
||||||
|
AudioFilterAllpass<LP_ALLP2_BUF_LEN> lp_allp_2; |
||||||
|
AudioFilterAllpass<LP_ALLP3_BUF_LEN> lp_allp_3; |
||||||
|
AudioFilterAllpass<LP_ALLP4_BUF_LEN> lp_allp_4; |
||||||
|
|
||||||
|
uint16_t LFO_AMPL = 20u; |
||||||
|
uint16_t LFO_AMPLset = 20u; |
||||||
|
AudioBasicLfo lfo1 = AudioBasicLfo(1.35f, LFO_AMPL); |
||||||
|
AudioBasicLfo lfo2 = AudioBasicLfo(1.57f, LFO_AMPL); |
||||||
|
|
||||||
|
float input_attn; |
||||||
|
float wet_gain; |
||||||
|
float dry_gain; |
||||||
|
|
||||||
|
float in_allp_k; // input allpass coeff (default 0.6)
|
||||||
|
float in_allp_out_L; // L allpass chain output
|
||||||
|
float in_allp_out_R; // R allpass chain output
|
||||||
|
|
||||||
|
float loop_allp_k; // loop allpass coeff (default 0.6)
|
||||||
|
float lp_allp_out; |
||||||
|
|
||||||
|
AudioBasicDelay<LP_DLY1_BUF_LEN> lp_dly1; |
||||||
|
AudioBasicDelay<LP_DLY2_BUF_LEN> lp_dly2; |
||||||
|
AudioBasicDelay<LP_DLY3_BUF_LEN> lp_dly3; |
||||||
|
AudioBasicDelay<LP_DLY4_BUF_LEN> lp_dly4; |
||||||
|
|
||||||
|
float lp_hidamp_k, lp_hidamp_k_tmp; // loop high band damping coeff
|
||||||
|
float lp_lodamp_k, lp_lodamp_k_tmp; // loop low band damping coeff
|
||||||
|
|
||||||
|
AudioFilterShelvingLPHP flt1; |
||||||
|
AudioFilterShelvingLPHP flt2; |
||||||
|
AudioFilterShelvingLPHP flt3; |
||||||
|
AudioFilterShelvingLPHP flt4; |
||||||
|
|
||||||
|
float master_lp_k, master_hp_k; |
||||||
|
AudioFilterShelvingLPHP flt_masterL; |
||||||
|
AudioFilterShelvingLPHP flt_masterR; |
||||||
|
// Shimmer
|
||||||
|
float pitchRatio = 0.0f; |
||||||
|
AudioBasicPitch pitchL; |
||||||
|
AudioBasicPitch pitchR; |
||||||
|
|
||||||
|
float shimmerRatio = 0.0f; |
||||||
|
AudioBasicPitch pitchShimL; |
||||||
|
AudioBasicPitch pitchShimR; |
||||||
|
|
||||||
|
const float rv_time_k_max = 0.97f; |
||||||
|
float rv_time_k, rv_time_k_tmp; // reverb time coeff
|
||||||
|
float rv_time_scaler; // with high lodamp settings lower the max reverb time to avoid clipping
|
||||||
|
|
||||||
|
const float freeze_rvtime_k = 1.0f; |
||||||
|
float freeze_ingain = 0.05f; |
||||||
|
const float freeze_lodamp_k = 0.0f; |
||||||
|
const float freeze_hidamp_k = 1.0f; |
||||||
|
|
||||||
|
bool initialised = false; |
||||||
|
}; |
||||||
|
|
||||||
|
#endif // _EFFECT_PLATERVBSTEREO_20COPY_H_
|
@ -0,0 +1,83 @@ |
|||||||
|
/*
|
||||||
|
TDFII.h |
||||||
|
|
||||||
|
Copyright 2006-7 |
||||||
|
David Yeh <dtyeh@ccrma.stanford.edu> (implementation) |
||||||
|
2006-14 |
||||||
|
Tim Goetze <tim@quitte.de> (cosmetics) |
||||||
|
|
||||||
|
transposed Direct Form II digital filter. |
||||||
|
Assumes order of b = order of a. |
||||||
|
Assumes a0 = 1. |
||||||
|
|
||||||
|
Ported for 32bit float version for OpenAudio_ArduinoLibrary: |
||||||
|
https://github.com/chipaudette/OpenAudio_ArduinoLibrary
|
||||||
|
|
||||||
|
12.2023 Piotr Zapart www.hexefx.com
|
||||||
|
|
||||||
|
*/ |
||||||
|
/*
|
||||||
|
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, write to the Free Software |
||||||
|
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA |
||||||
|
02111-1307, USA or point your web browser to http://www.gnu.org.
|
||||||
|
*/ |
||||||
|
#ifndef _FILTER_TDF2_H_ |
||||||
|
#define _FILTER_TDF2_H_ |
||||||
|
|
||||||
|
#include "arm_math.h" |
||||||
|
|
||||||
|
template <int N> |
||||||
|
class AudioFilterTDF2 |
||||||
|
{ |
||||||
|
public: |
||||||
|
float32_t a[N + 1]; |
||||||
|
float32_t b[N + 1]; |
||||||
|
float32_t h[N + 1]; |
||||||
|
|
||||||
|
void reset() |
||||||
|
{ |
||||||
|
for (int i = 0; i <= N; ++i) |
||||||
|
h[i] = 0; // zero state
|
||||||
|
} |
||||||
|
|
||||||
|
void init() |
||||||
|
{ |
||||||
|
reset(); |
||||||
|
clear(); |
||||||
|
} |
||||||
|
|
||||||
|
void clear() |
||||||
|
{ |
||||||
|
for (int i = 0; i <= N; i++) |
||||||
|
a[i] = b[i] = 0; |
||||||
|
b[0] = 1; |
||||||
|
} |
||||||
|
|
||||||
|
void process(float32_t *src, float32_t *dst, uint32_t blockSize) |
||||||
|
{ |
||||||
|
for (uint16_t i = 0; i<blockSize; i++) |
||||||
|
{ |
||||||
|
float32_t in = *src++; |
||||||
|
float32_t y = h[0] + b[0] * in; |
||||||
|
|
||||||
|
for (uint16_t j = 1; j < N; ++j) |
||||||
|
h[j - 1] = h[j] + b[j] * in- a[j] * y; |
||||||
|
|
||||||
|
h[N - 1] = b[N] * in - a[N] * y; |
||||||
|
*dst++ = y; |
||||||
|
} |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
#endif // _FILTER_TDF2_H_
|
@ -0,0 +1,158 @@ |
|||||||
|
/*
|
||||||
|
ToneStack.h |
||||||
|
|
||||||
|
Copyright 2006-7 |
||||||
|
David Yeh <dtyeh@ccrma.stanford.edu>
|
||||||
|
2006-14 |
||||||
|
Tim Goetze <tim@quitte.de> (cosmetics) |
||||||
|
|
||||||
|
Tone Stack emulation for Teensy 4.x |
||||||
|
Ported for 32bit float version for OpenAudio_ArduinoLibrary: |
||||||
|
https://github.com/chipaudette/OpenAudio_ArduinoLibrary
|
||||||
|
|
||||||
|
12.2023 Piotr Zapart www.hexefx.com |
||||||
|
|
||||||
|
*/ |
||||||
|
/*
|
||||||
|
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, write to the Free Software |
||||||
|
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA |
||||||
|
02111-1307, USA or point your web browser to http://www.gnu.org.
|
||||||
|
*/ |
||||||
|
#include "filter_tonestackStereo_F32.h" |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief EQ models based on various guitar amplifiers |
||||||
|
*/ |
||||||
|
AudioFilterToneStackStereo_F32::toneStackParams_t AudioFilterToneStackStereo_F32::presets[] = { |
||||||
|
/* for convenience, */ |
||||||
|
#define k *1e3 |
||||||
|
#define M *1e6 |
||||||
|
#define nF *1e-9 |
||||||
|
#define pF *1e-12 |
||||||
|
/* parameter order is R1 - R4, C1 - C3 */ |
||||||
|
/* R1=treble R2=Bass R3=Mid, C1-3 related caps, R4 = parallel resistor */ |
||||||
|
/* { 250000, 1000000, 25000, 56000, 0.25e-9, 20e-9, 20e-9 }, DY */ |
||||||
|
{250 k, 1 M, 25 k, 56 k, 250 pF, 20 nF, 20 nF, "Bassman"}, /* 59 Bassman 5F6-A */ |
||||||
|
{250 k, 250 k, 4.8 k, 100 k, 250 pF, 100 nF, 47 nF, "Prince"}, /* 64 Princeton AA1164 */ |
||||||
|
{250 k, 1 M, 25 k, 47 k, 600 pF, 20 nF, 20 nF, "Mesa"}, /* Mesa Dual Rect. 'Orange' */ |
||||||
|
/* Vox -- R3 is fixed (circuit differs anyway) */ |
||||||
|
{1 M, 1 M, 10 k, 100 k, 50 pF, 22 nF, 22 nF, "Vox"}, /* Vox "top boost" */ |
||||||
|
|
||||||
|
{220 k, 1 M, 22 k, 33 k, 470 pF, 22 nF, 22 nF, "JCM800"}, /* 59/81 JCM-800 Lead 100 2203 */ |
||||||
|
{250 k, 250 k, 10 k, 100 k, 120 pF, 100 nF, 47 nF, "Twin"}, /* 69 Twin Reverb AA270 */ |
||||||
|
|
||||||
|
{500 k, 1 M, 25 k, 47 k, 150 pF, 22 nF, 22 nF, "HK"}, /* Hughes & Kettner Tube 20 */ |
||||||
|
{250 k, 250 k, 10 k, 100 k, 150 pF, 82 nF, 47 nF, "Jazz"}, /* Roland Jazz Chorus */ |
||||||
|
{250 k, 1 M, 50 k, 33 k, 100 pF, 22 nF, 22 nF, "Pignose"}, /* Pignose G40V */ |
||||||
|
#undef k |
||||||
|
#undef M |
||||||
|
#undef nF |
||||||
|
#undef pF |
||||||
|
}; |
||||||
|
|
||||||
|
AudioFilterToneStackStereo_F32 :: AudioFilterToneStackStereo_F32() : AudioStream_F32(2, inputQueueArray_f32) |
||||||
|
{ |
||||||
|
gain = 1.0f; |
||||||
|
setModel(TONESTACK_OFF); |
||||||
|
} |
||||||
|
|
||||||
|
void AudioFilterToneStackStereo_F32::setModel(toneStack_presets_e m) |
||||||
|
{ |
||||||
|
if (m >= TONE_STACK_MAX_MODELS) return; |
||||||
|
if (m == TONESTACK_OFF) |
||||||
|
{ |
||||||
|
bp = true; |
||||||
|
filterL.reset(); |
||||||
|
filterR.reset(); |
||||||
|
return; |
||||||
|
} |
||||||
|
bp = false; |
||||||
|
currentModel = m - 1;
|
||||||
|
|
||||||
|
float32_t R1 = presets[currentModel].R1, \
|
||||||
|
R2 = presets[currentModel].R2, \
|
||||||
|
R3 = presets[currentModel].R3, \
|
||||||
|
R4 = presets[currentModel].R4; |
||||||
|
float32_t C1 = presets[currentModel].C1, \
|
||||||
|
C2 = presets[currentModel].C2, \
|
||||||
|
C3 = presets[currentModel].C3; |
||||||
|
|
||||||
|
b1t = C1 * R1; |
||||||
|
b1m = C3 * R3; |
||||||
|
b1l = C1 * R2 + C2 * R2; |
||||||
|
b1d = C1 * R3 + C2 * R3; |
||||||
|
b2t = C1 * C2 * R1 * R4 + C1 * C3 * R1 * R4; |
||||||
|
b2m2 = -(C1 * C3 * R3 * R3 + C2 * C3 * R3 * R3); |
||||||
|
b2m = C1 * C3 * R1 * R3 + C1 * C3 * R3 * R3 + C2 * C3 * R3 * R3; |
||||||
|
b2l = C1 * C2 * R1 * R2 + C1 * C2 * R2 * R4 + C1 * C3 * R2 * R4; |
||||||
|
b2lm = C1 * C3 * R2 * R3 + C2 * C3 * R2 * R3; |
||||||
|
b2d = C1 * C2 * R1 * R3 + C1 * C2 * R3 * R4 + C1 * C3 * R3 * R4; |
||||||
|
b3lm = C1 * C2 * C3 * R1 * R2 * R3 + C1 * C2 * C3 * R2 * R3 * R4; |
||||||
|
b3m2 = -(C1 * C2 * C3 * R1 * R3 * R3 + C1 * C2 * C3 * R3 * R3 * R4); |
||||||
|
b3m = C1 * C2 * C3 * R1 * R3 * R3 + C1 * C2 * C3 * R3 * R3 * R4; |
||||||
|
b3t = C1 * C2 * C3 * R1 * R3 * R4; |
||||||
|
b3tm = -b3t; |
||||||
|
b3tl = C1 * C2 * C3 * R1 * R2 * R4; |
||||||
|
a0 = 1.0f; |
||||||
|
a1d = C1 * R1 + C1 * R3 + C2 * R3 + C2 * R4 + C3 * R4; |
||||||
|
a1m = C3 * R3; |
||||||
|
a1l = C1 * R2 + C2 * R2; |
||||||
|
a2m = C1 * C3 * R1 * R3 - C2 * C3 * R3 * R4 + C1 * C3 * R3 * R3 + C2 * C3 * R3 * R3; |
||||||
|
a2lm = C1 * C3 * R2 * R3 + C2 * C3 * R2 * R3; |
||||||
|
a2m2 = -(C1 * C3 * R3 * R3 + C2 * C3 * R3 * R3); |
||||||
|
a2l = C1 * C2 * R2 * R4 + C1 * C2 * R1 * R2 + C1 * C3 * R2 * R4 + C2 * C3 * R2 * R4; |
||||||
|
a2d = C1 * C2 * R1 * R4 + C1 * C3 * R1 * R4 + C1 * C2 * R3 * R4 + C1 * C2 * R1 * R3 + C1 * C3 * R3 * R4 + C2 * C3 * R3 * R4; |
||||||
|
a3lm = C1 * C2 * C3 * R1 * R2 * R3 + C1 * C2 * C3 * R2 * R3 * R4; |
||||||
|
a3m2 = -(C1 * C2 * C3 * R1 * R3 * R3 + C1 * C2 * C3 * R3 * R3 * R4); |
||||||
|
a3m = C1 * C2 * C3 * R3 * R3 * R4 + C1 * C2 * C3 * R1 * R3 * R3 - C1 * C2 * C3 * R1 * R3 * R4; |
||||||
|
a3l = C1 * C2 * C3 * R1 * R2 * R4; |
||||||
|
a3d = C1 * C2 * C3 * R1 * R3 * R4; |
||||||
|
|
||||||
|
filterL.reset(); |
||||||
|
filterR.reset(); |
||||||
|
} |
||||||
|
|
||||||
|
void AudioFilterToneStackStereo_F32::update() |
||||||
|
{ |
||||||
|
#if defined(__ARM_ARCH_7EM__) |
||||||
|
audio_block_f32_t *blockL, *blockR;
|
||||||
|
blockL = AudioStream_F32::receiveWritable_f32(0); // audio data
|
||||||
|
blockR = AudioStream_F32::receiveWritable_f32(1); // audio data
|
||||||
|
if (!blockL || !blockR) |
||||||
|
{ |
||||||
|
if (blockL) release((audio_block_f32_t *)blockL); |
||||||
|
if (blockR) release((audio_block_f32_t *)blockR);
|
||||||
|
return; |
||||||
|
} |
||||||
|
if (bp) // bypass mode
|
||||||
|
{ |
||||||
|
AudioStream_F32::transmit((audio_block_f32_t *)blockL,0); |
||||||
|
AudioStream_F32::transmit((audio_block_f32_t *)blockR,1); |
||||||
|
AudioStream_F32::release((audio_block_f32_t *)blockL); |
||||||
|
AudioStream_F32::release((audio_block_f32_t *)blockR); |
||||||
|
return;
|
||||||
|
} |
||||||
|
filterL.process(blockL->data, blockL->data, blockL->length); |
||||||
|
filterR.process(blockR->data, blockR->data, blockR->length); |
||||||
|
if (gain != 1.0f) |
||||||
|
{ |
||||||
|
arm_scale_f32(blockL->data, gain, blockL->data, blockL->length); |
||||||
|
arm_scale_f32(blockR->data, gain, blockR->data, blockR->length); |
||||||
|
} |
||||||
|
AudioStream_F32::transmit(blockL, 0); |
||||||
|
AudioStream_F32::transmit(blockR, 1); |
||||||
|
AudioStream_F32::release(blockL); |
||||||
|
AudioStream_F32::release(blockR);
|
||||||
|
#endif |
||||||
|
} |
@ -0,0 +1,187 @@ |
|||||||
|
/*
|
||||||
|
ToneStack.h |
||||||
|
|
||||||
|
Copyright 2006-7 |
||||||
|
David Yeh <dtyeh@ccrma.stanford.edu>
|
||||||
|
2006-14 |
||||||
|
Tim Goetze <tim@quitte.de> (cosmetics) |
||||||
|
|
||||||
|
Tone Stack emulation for Teensy 4.x |
||||||
|
|
||||||
|
Ported for 32bit float version for OpenAudio_ArduinoLibrary: |
||||||
|
https://github.com/chipaudette/OpenAudio_ArduinoLibrary
|
||||||
|
|
||||||
|
12.2023 Piotr Zapart www.hexefx.com |
||||||
|
*/ |
||||||
|
/*
|
||||||
|
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, write to the Free Software |
||||||
|
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA |
||||||
|
02111-1307, USA or point your web browser to http://www.gnu.org.
|
||||||
|
*/ |
||||||
|
|
||||||
|
#ifndef _FILTER_TONESTACK_STEREO_F32_H_ |
||||||
|
#define _FILTER_TONESTACK_STEREO_F32_H_ |
||||||
|
|
||||||
|
#include <Arduino.h> |
||||||
|
#include <Audio.h> |
||||||
|
#include "AudioStream_F32.h" |
||||||
|
#include "filter_tdf2.h" |
||||||
|
#include "arm_math.h" |
||||||
|
|
||||||
|
#define TONE_STACK_MAX_MODELS (10) |
||||||
|
|
||||||
|
typedef enum |
||||||
|
{ |
||||||
|
TONESTACK_OFF, |
||||||
|
TONESTACK_BASSMAN, |
||||||
|
TONESTACK_PRINCE, |
||||||
|
TONESTACK_MESA, |
||||||
|
TONESTACK_VOX, |
||||||
|
TONESTACK_JCM800, |
||||||
|
TONESTACK_TWIN, |
||||||
|
TONESTACK_HK, |
||||||
|
TONESTACK_JAZZ, |
||||||
|
TONESTACK_PIGNOSE |
||||||
|
}toneStack_presets_e; |
||||||
|
|
||||||
|
class AudioFilterToneStackStereo_F32 : public AudioStream_F32 |
||||||
|
{ |
||||||
|
public: |
||||||
|
AudioFilterToneStackStereo_F32(); |
||||||
|
~AudioFilterToneStackStereo_F32(){}; |
||||||
|
virtual void update(void); |
||||||
|
|
||||||
|
typedef struct |
||||||
|
{ |
||||||
|
float32_t R1, R2, R3, R4; |
||||||
|
float32_t C1, C2, C3; |
||||||
|
const char *name; |
||||||
|
} toneStackParams_t; |
||||||
|
/**
|
||||||
|
* @brief preset table |
||||||
|
*/ |
||||||
|
static toneStackParams_t presets[]; |
||||||
|
/**
|
||||||
|
* @brief Set the EQ type from the available pool of models |
||||||
|
* Use TONESTACK_OFF to bypass the module |
||||||
|
*
|
||||||
|
* @param m model defined in toneStack_presets_e |
||||||
|
*/ |
||||||
|
void setModel(toneStack_presets_e m); |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief return the name of the model defined in presets[] |
||||||
|
*
|
||||||
|
* @return const char* pointer to the name char array |
||||||
|
*/ |
||||||
|
const char *getName(){ return presets[currentModel].name;} |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief set all 3 parameters at once |
||||||
|
*
|
||||||
|
* @param b bass setting |
||||||
|
* @param m middle setting |
||||||
|
* @param t treble setting |
||||||
|
*/ |
||||||
|
void setTone(float32_t b, float32_t m, float32_t t) |
||||||
|
{ |
||||||
|
b = constrain(b, 0.0f, 1.0f); bass = b; |
||||||
|
m = constrain(m, 0.0f, 1.0f); mid = m; |
||||||
|
t = constrain(t, 0.0f, 1.0f); treble = t; |
||||||
|
struct |
||||||
|
{ |
||||||
|
float32_t a1, a2, a3; |
||||||
|
float32_t b1, b2, b3; |
||||||
|
} acoef; // analog coefficients
|
||||||
|
|
||||||
|
// digital coefficients
|
||||||
|
float32_t dcoef_a[order + 1]; |
||||||
|
float32_t dcoef_b[order + 1]; |
||||||
|
|
||||||
|
m = (m - 1.0f) * 3.5f; |
||||||
|
m = pow10f(m); |
||||||
|
acoef.a1 = a1d + m * a1m + b * a1l; |
||||||
|
acoef.a2 = m * a2m + b * m * a2lm + m * m * a2m2 + b * a2l + a2d; |
||||||
|
acoef.a3 = b * m * a3lm + m * m * a3m2 + m * a3m + b * a3l + a3d; |
||||||
|
dcoef_a[0] = -1.0f - acoef.a1 * c - acoef.a2 * c * c - acoef.a3 * c * c * c; // sets scale
|
||||||
|
dcoef_a[1] = -3.0f - acoef.a1 * c + acoef.a2 * c * c + 3.0f * acoef.a3 * c * c * c; |
||||||
|
dcoef_a[2] = -3.0f + acoef.a1 * c + acoef.a2 * c * c - 3.0f * acoef.a3 * c * c * c; |
||||||
|
dcoef_a[3] = -1.0f + acoef.a1 * c - acoef.a2 * c * c + acoef.a3 * c * c * c; |
||||||
|
|
||||||
|
acoef.b1 = t * b1t + m * b1m + b * b1l + b1d; |
||||||
|
acoef.b2 = t * b2t + m * m * b2m2 + m * b2m + b * b2l + b * m * b2lm + b2d; |
||||||
|
acoef.b3 = b * m * b3lm + m * m * b3m2 + m * b3m + t * b3t + t * m * b3tm + t * b * b3tl; |
||||||
|
dcoef_b[0] = -acoef.b1 * c - acoef.b2 * c * c - acoef.b3 * c * c * c; |
||||||
|
dcoef_b[1] = -acoef.b1 * c + acoef.b2 * c * c + 3.0f * acoef.b3 * c * c * c; |
||||||
|
dcoef_b[2] = acoef.b1 * c + acoef.b2 * c * c - 3.0f * acoef.b3 * c * c * c; |
||||||
|
dcoef_b[3] = acoef.b1 * c - acoef.b2 * c * c + acoef.b3 * c * c * c; |
||||||
|
|
||||||
|
__disable_irq(); |
||||||
|
for (int i = 1; i <= order; ++i) |
||||||
|
{ |
||||||
|
filterL.a[i] = dcoef_a[i] / dcoef_a[0]; |
||||||
|
filterR.a[i] = filterL.a[i]; |
||||||
|
} |
||||||
|
for (int i = 0; i <= order; ++i) |
||||||
|
{ |
||||||
|
filterL.b[i] = dcoef_b[i] / dcoef_a[0]; |
||||||
|
filterR.b[i] = filterL.b[i];
|
||||||
|
} |
||||||
|
__enable_irq(); |
||||||
|
} |
||||||
|
/**
|
||||||
|
* @brief set the bass range EQ |
||||||
|
*
|
||||||
|
* @param b bass setting |
||||||
|
*/ |
||||||
|
void setBass(float32_t b) { setTone(b, mid, treble);} |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief set the mid range EQ |
||||||
|
*
|
||||||
|
* @param m middle setting |
||||||
|
*/ |
||||||
|
void setMid(float32_t m) { setTone(bass, m, treble);} |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief set the treble range EQ |
||||||
|
*
|
||||||
|
* @param t treble setting |
||||||
|
*/ |
||||||
|
void setTreble(float32_t t) {setTone(bass, mid, t);} |
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Master volume setting |
||||||
|
*
|
||||||
|
* @param g gain value |
||||||
|
*/ |
||||||
|
void setGain(float32_t g) { gain = g;} |
||||||
|
|
||||||
|
private: |
||||||
|
static const uint8_t order = 3; |
||||||
|
AudioFilterTDF2<order> filterL; |
||||||
|
AudioFilterTDF2<order> filterR; |
||||||
|
audio_block_f32_t *inputQueueArray_f32[2]; |
||||||
|
bool bp = false; // bypass
|
||||||
|
uint8_t currentModel; |
||||||
|
float32_t c = 2.0f * AUDIO_SAMPLE_RATE; |
||||||
|
float32_t b1t, b1m, b1l, b1d, |
||||||
|
b2t, b2m2, b2m, b2l, b2lm, b2d, |
||||||
|
b3lm, b3m2, b3m, b3t, b3tm, b3tl, |
||||||
|
a0, a1d, a1m, a1l, a2m, a2lm, a2m2, a2l, a2d, |
||||||
|
a3lm, a3m2, a3m, a3l, a3d; // intermediate calculations
|
||||||
|
float32_t bass, mid, treble, gain; |
||||||
|
}; |
||||||
|
|
||||||
|
#endif // _FILTER_TONESTACK_F32_H_
|
@ -0,0 +1,15 @@ |
|||||||
|
#ifndef _HEXEFX_AUDIO_H |
||||||
|
#define _HEXEFX_AUDIO_H |
||||||
|
|
||||||
|
#include "input_i2s2_F32.h" |
||||||
|
#include "output_i2s2_F32.h" |
||||||
|
|
||||||
|
#include "filter_ir_cabsim_F32.h" |
||||||
|
#include "filter_tonestackStereo_F32.h" |
||||||
|
|
||||||
|
#include "effect_platereverb_F32.h" |
||||||
|
#include "effect_monoToStereo_F32.h" |
||||||
|
#include "effect_infphaser_F32.h" |
||||||
|
#include "effect_phaserStereo_F32.h" |
||||||
|
|
||||||
|
#endif // _HEXEFX_AUDIO_H
|
@ -0,0 +1,260 @@ |
|||||||
|
/*
|
||||||
|
* input_i2s2_f32.cpp |
||||||
|
*
|
||||||
|
* Audio Library for Teensy 3.X |
||||||
|
* Copyright (c) 2014, Paul Stoffregen, paul@pjrc.com |
||||||
|
* |
||||||
|
* Development of this audio library was funded by PJRC.COM, LLC by sales of |
||||||
|
* Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop |
||||||
|
* open source software by purchasing Teensy or other PJRC products. |
||||||
|
* |
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy |
||||||
|
* of this software and associated documentation files (the "Software"), to deal |
||||||
|
* in the Software without restriction, including without limitation the rights |
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||||
|
* copies of the Software, and to permit persons to whom the Software is |
||||||
|
* furnished to do so, subject to the following conditions: |
||||||
|
* |
||||||
|
* The above copyright notice, development funding notice, and this permission |
||||||
|
* notice shall be included in all copies or substantial portions of the Software. |
||||||
|
* |
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
||||||
|
* THE SOFTWARE. |
||||||
|
*/ |
||||||
|
/*
|
||||||
|
* Extended by Chip Audette, OpenAudio, May 2019 |
||||||
|
* Converted to F32 and to variable audio block length |
||||||
|
* The F32 conversion is under the MIT License. Use at your own risk. |
||||||
|
*/ |
||||||
|
// Updated OpenAudio F32 with this version from Chip Audette's Tympan Library Jan 2021 RSL
|
||||||
|
|
||||||
|
#include <Arduino.h> //do we really need this? (Chip: 2020-10-31) |
||||||
|
#include "input_i2s2_F32.h" |
||||||
|
#include "output_i2s2_F32.h" |
||||||
|
|
||||||
|
#include <arm_math.h> |
||||||
|
|
||||||
|
//DMAMEM __attribute__((aligned(32)))
|
||||||
|
static uint32_t i2s2_rx_buffer[AUDIO_BLOCK_SAMPLES]; //good for 16-bit audio samples coming in from teh AIC. 32-bit transfers will need this to be bigger.
|
||||||
|
audio_block_f32_t * AudioInputI2S2_F32::block_left_f32 = NULL; |
||||||
|
audio_block_f32_t * AudioInputI2S2_F32::block_right_f32 = NULL; |
||||||
|
uint16_t AudioInputI2S2_F32::block_offset = 0; |
||||||
|
bool AudioInputI2S2_F32::update_responsibility = false; |
||||||
|
DMAChannel AudioInputI2S2_F32::dma(false); |
||||||
|
|
||||||
|
int AudioInputI2S2_F32::flag_out_of_memory = 0; |
||||||
|
unsigned long AudioInputI2S2_F32::update_counter = 0; |
||||||
|
|
||||||
|
float AudioInputI2S2_F32::sample_rate_Hz = AUDIO_SAMPLE_RATE; |
||||||
|
int AudioInputI2S2_F32::audio_block_samples = AUDIO_BLOCK_SAMPLES; |
||||||
|
|
||||||
|
//#for 16-bit transfers
|
||||||
|
#define I2S2_BUFFER_TO_USE_BYTES (AudioOutputI2S2_F32::audio_block_samples*sizeof(i2s2_rx_buffer[0])) |
||||||
|
|
||||||
|
//#for 32-bit transfers
|
||||||
|
//#define I2S2_BUFFER_TO_USE_BYTES (AudioOutputI2S_F32::audio_block_samples*2*sizeof(i2s_rx_buffer[0]))
|
||||||
|
|
||||||
|
void AudioInputI2S2_F32::begin(void) { |
||||||
|
bool transferUsing32bit = false; |
||||||
|
begin(transferUsing32bit); |
||||||
|
} |
||||||
|
|
||||||
|
void AudioInputI2S2_F32::begin(bool transferUsing32bit) { |
||||||
|
dma.begin(true); // Allocate the DMA channel first
|
||||||
|
|
||||||
|
AudioOutputI2S2_F32::sample_rate_Hz = sample_rate_Hz; //these were given in the AudioSettings in the contructor
|
||||||
|
AudioOutputI2S2_F32::audio_block_samples = audio_block_samples;//these were given in the AudioSettings in the contructor
|
||||||
|
|
||||||
|
//block_left_1st = NULL;
|
||||||
|
//block_right_1st = NULL;
|
||||||
|
|
||||||
|
// TODO: should we set & clear the I2S_RCSR_SR bit here?
|
||||||
|
AudioOutputI2S2_F32::config_i2s(transferUsing32bit); |
||||||
|
|
||||||
|
#if defined(__IMXRT1062__) |
||||||
|
CORE_PIN5_CONFIG = 2; //EMC_08, 2=SAI2_RX_DATA, page 434
|
||||||
|
IOMUXC_SAI2_RX_DATA0_SELECT_INPUT = 0; // 0=GPIO_EMC_08_ALT2, page 876
|
||||||
|
|
||||||
|
dma.TCD->SADDR = (void *)((uint32_t)&I2S2_RDR0 + 2); |
||||||
|
dma.TCD->SOFF = 0; |
||||||
|
dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1); |
||||||
|
dma.TCD->NBYTES_MLNO = 2; |
||||||
|
dma.TCD->SLAST = 0; |
||||||
|
dma.TCD->DADDR = i2s2_rx_buffer; |
||||||
|
dma.TCD->DOFF = 2; |
||||||
|
//dma.TCD->CITER_ELINKNO = sizeof(i2s_rx_buffer) / 2; //original from Teensy Audio Library
|
||||||
|
dma.TCD->CITER_ELINKNO = I2S2_BUFFER_TO_USE_BYTES / 2; |
||||||
|
//dma.TCD->DLASTSGA = -sizeof(i2s_rx_buffer); //original from Teensy Audio Library
|
||||||
|
dma.TCD->DLASTSGA = -I2S2_BUFFER_TO_USE_BYTES; |
||||||
|
//dma.TCD->BITER_ELINKNO = sizeof(i2s_rx_buffer) / 2; //original from Teensy Audio Library
|
||||||
|
dma.TCD->BITER_ELINKNO = I2S2_BUFFER_TO_USE_BYTES / 2; |
||||||
|
dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR; |
||||||
|
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI2_RX); |
||||||
|
|
||||||
|
I2S2_RCSR = I2S_RCSR_RE | I2S_RCSR_BCE | I2S_RCSR_FRDE | I2S_RCSR_FR; // page 2099
|
||||||
|
I2S2_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE; // page 2087
|
||||||
|
#endif |
||||||
|
update_responsibility = update_setup(); |
||||||
|
dma.enable(); |
||||||
|
dma.attachInterrupt(isr); |
||||||
|
|
||||||
|
update_counter = 0; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
void AudioInputI2S2_F32::isr(void) |
||||||
|
{ |
||||||
|
uint32_t daddr, offset; |
||||||
|
const int16_t *src, *end; |
||||||
|
//int16_t *dest_left, *dest_right;
|
||||||
|
//audio_block_t *left, *right;
|
||||||
|
float32_t *dest_left_f32, *dest_right_f32; |
||||||
|
audio_block_f32_t *left_f32, *right_f32; |
||||||
|
|
||||||
|
#if defined(KINETISK) || defined(__IMXRT1062__) |
||||||
|
daddr = (uint32_t)(dma.TCD->DADDR); |
||||||
|
#endif |
||||||
|
dma.clearInterrupt(); |
||||||
|
//Serial.println("isr");
|
||||||
|
|
||||||
|
//if (daddr < (uint32_t)i2s_rx_buffer + sizeof(i2s_rx_buffer) / 2) { //original Teensy Audio Library
|
||||||
|
if (daddr < (uint32_t)i2s2_rx_buffer + I2S2_BUFFER_TO_USE_BYTES / 2) { |
||||||
|
// DMA is receiving to the first half of the buffer
|
||||||
|
// need to remove data from the second half
|
||||||
|
//src = (int16_t *)&i2s_rx_buffer[AUDIO_BLOCK_SAMPLES/2]; //original Teensy Audio Library
|
||||||
|
//end = (int16_t *)&i2s_rx_buffer[AUDIO_BLOCK_SAMPLES]; //original Teensy Audio Library
|
||||||
|
src = (int16_t *)&i2s2_rx_buffer[audio_block_samples/2]; |
||||||
|
end = (int16_t *)&i2s2_rx_buffer[audio_block_samples];
|
||||||
|
update_counter++; //let's increment the counter here to ensure that we get every ISR resulting in audio
|
||||||
|
if (AudioInputI2S2_F32::update_responsibility) AudioStream_F32::update_all(); |
||||||
|
} else { |
||||||
|
// DMA is receiving to the second half of the buffer
|
||||||
|
// need to remove data from the first half
|
||||||
|
src = (int16_t *)&i2s2_rx_buffer[0]; |
||||||
|
//end = (int16_t *)&i2s_rx_buffer[AUDIO_BLOCK_SAMPLES/2]; //original Teensy Audio Library
|
||||||
|
end = (int16_t *)&i2s2_rx_buffer[audio_block_samples/2]; |
||||||
|
} |
||||||
|
left_f32 = AudioInputI2S2_F32::block_left_f32; |
||||||
|
right_f32 = AudioInputI2S2_F32::block_right_f32; |
||||||
|
if (left_f32 != NULL && right_f32 != NULL) { |
||||||
|
offset = AudioInputI2S2_F32::block_offset; |
||||||
|
//if (offset <= (uint32_t)(AUDIO_BLOCK_SAMPLES/2)) { //original Teensy Audio Library
|
||||||
|
if (offset <= ((uint32_t) audio_block_samples/2)) { |
||||||
|
dest_left_f32 = &(left_f32->data[offset]); |
||||||
|
dest_right_f32 = &(right_f32->data[offset]); |
||||||
|
//AudioInputI2S2_F32::block_offset = offset + AUDIO_BLOCK_SAMPLES/2; //original Teensy Audio Library
|
||||||
|
AudioInputI2S2_F32::block_offset = offset + audio_block_samples/2; |
||||||
|
do { |
||||||
|
//Serial.println(*src);
|
||||||
|
//n = *src++;
|
||||||
|
//*dest_left++ = (int16_t)n;
|
||||||
|
//*dest_right++ = (int16_t)(n >> 16);
|
||||||
|
*dest_left_f32++ = (float32_t) *src++; |
||||||
|
*dest_right_f32++ = (float32_t) *src++; |
||||||
|
} while (src < end); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#define I16_TO_F32_NORM_FACTOR (3.051850947599719e-05) //which is 1/32767
|
||||||
|
void AudioInputI2S2_F32::scale_i16_to_f32( float32_t *p_i16, float32_t *p_f32, int len) { |
||||||
|
for (int i=0; i<len; i++) { *p_f32++ = ((*p_i16++) * I16_TO_F32_NORM_FACTOR); } |
||||||
|
} |
||||||
|
#define I24_TO_F32_NORM_FACTOR (1.192093037616377e-07) //which is 1/(2^23 - 1)
|
||||||
|
void AudioInputI2S2_F32::scale_i24_to_f32( float32_t *p_i24, float32_t *p_f32, int len) { |
||||||
|
for (int i=0; i<len; i++) { *p_f32++ = ((*p_i24++) * I24_TO_F32_NORM_FACTOR); } |
||||||
|
} |
||||||
|
#define I32_TO_F32_NORM_FACTOR (4.656612875245797e-10) //which is 1/(2^31 - 1)
|
||||||
|
void AudioInputI2S2_F32::scale_i32_to_f32( float32_t *p_i32, float32_t *p_f32, int len) { |
||||||
|
for (int i=0; i<len; i++) { *p_f32++ = ((*p_i32++) * I32_TO_F32_NORM_FACTOR); } |
||||||
|
} |
||||||
|
|
||||||
|
void AudioInputI2S2_F32::update_1chan(int chan, audio_block_f32_t *&out_f32) { |
||||||
|
if (!out_f32) return; |
||||||
|
|
||||||
|
//scale the float values so that the maximum possible audio values span -1.0 to + 1.0
|
||||||
|
//scale_i32_to_f32(out_f32->data, out_f32->data, audio_block_samples);
|
||||||
|
scale_i16_to_f32(out_f32->data, out_f32->data, audio_block_samples); |
||||||
|
|
||||||
|
//prepare to transmit by setting the update_counter (which helps tell if data is skipped or out-of-order)
|
||||||
|
out_f32->id = update_counter; |
||||||
|
|
||||||
|
//transmit the f32 data!
|
||||||
|
AudioStream_F32::transmit(out_f32,chan); |
||||||
|
|
||||||
|
//release the memory blocks
|
||||||
|
AudioStream_F32::release(out_f32); |
||||||
|
} |
||||||
|
|
||||||
|
void AudioInputI2S2_F32::update(void) |
||||||
|
{ |
||||||
|
static bool flag_beenSuccessfullOnce = false; |
||||||
|
audio_block_f32_t *new_left=NULL, *new_right=NULL, *out_left=NULL, *out_right=NULL; |
||||||
|
|
||||||
|
new_left = AudioStream_F32::allocate_f32(); |
||||||
|
new_right = AudioStream_F32::allocate_f32(); |
||||||
|
if ((!new_left) || (!new_right)) { |
||||||
|
//ran out of memory. Clear and return!
|
||||||
|
if (new_left) AudioStream_F32::release(new_left); |
||||||
|
if (new_right) AudioStream_F32::release(new_right); |
||||||
|
new_left = NULL; new_right = NULL; |
||||||
|
flag_out_of_memory = 1; |
||||||
|
if (flag_beenSuccessfullOnce) Serial.println("Input_I2S_F32: update(): WARNING!!! Out of Memory."); |
||||||
|
} else { |
||||||
|
flag_beenSuccessfullOnce = true; |
||||||
|
} |
||||||
|
|
||||||
|
__disable_irq(); |
||||||
|
if (block_offset >= audio_block_samples) { |
||||||
|
// the DMA filled 2 blocks, so grab them and get the
|
||||||
|
// 2 new blocks to the DMA, as quickly as possible
|
||||||
|
out_left = block_left_f32; |
||||||
|
block_left_f32 = new_left; |
||||||
|
out_right = block_right_f32; |
||||||
|
block_right_f32 = new_right; |
||||||
|
block_offset = 0; |
||||||
|
__enable_irq(); |
||||||
|
|
||||||
|
//update_counter++; //I chose to update it in the ISR instead.
|
||||||
|
update_1chan(0,out_left); //uses audio_block_samples and update_counter
|
||||||
|
update_1chan(1,out_right); //uses audio_block_samples and update_counter
|
||||||
|
|
||||||
|
|
||||||
|
} else if (new_left != NULL) { |
||||||
|
// the DMA didn't fill blocks, but we allocated blocks
|
||||||
|
if (block_left_f32 == NULL) { |
||||||
|
// the DMA doesn't have any blocks to fill, so
|
||||||
|
// give it the ones we just allocated
|
||||||
|
block_left_f32 = new_left; |
||||||
|
block_right_f32 = new_right; |
||||||
|
block_offset = 0; |
||||||
|
__enable_irq(); |
||||||
|
} else { |
||||||
|
// the DMA already has blocks, doesn't need these
|
||||||
|
__enable_irq(); |
||||||
|
AudioStream_F32::release(new_left); |
||||||
|
AudioStream_F32::release(new_right); |
||||||
|
} |
||||||
|
} else { |
||||||
|
// The DMA didn't fill blocks, and we could not allocate
|
||||||
|
// memory... the system is likely starving for memory!
|
||||||
|
// Sadly, there's nothing we can do.
|
||||||
|
__enable_irq(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/******************************************************************/ |
||||||
|
|
||||||
|
|
||||||
|
void AudioInputI2S2slave_F32::begin(void) |
||||||
|
{ |
||||||
|
dma.begin(true); // Allocate the DMA channel first
|
||||||
|
|
||||||
|
AudioOutputI2S2slave_F32::config_i2s(); |
||||||
|
|
||||||
|
} |
@ -0,0 +1,92 @@ |
|||||||
|
/*
|
||||||
|
* ***** input_i2s2_f32.h ****** |
||||||
|
*
|
||||||
|
* Audio Library for Teensy 3.X |
||||||
|
* Copyright (c) 2014, Paul Stoffregen, paul@pjrc.com |
||||||
|
* |
||||||
|
* Development of this audio library was funded by PJRC.COM, LLC by sales of |
||||||
|
* Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop |
||||||
|
* open source software by purchasing Teensy or other PJRC products. |
||||||
|
* |
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy |
||||||
|
* of this software and associated documentation files (the "Software"), to deal |
||||||
|
* in the Software without restriction, including without limitation the rights |
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||||
|
* copies of the Software, and to permit persons to whom the Software is |
||||||
|
* furnished to do so, subject to the following conditions: |
||||||
|
* |
||||||
|
* The above copyright notice, development funding notice, and this permission |
||||||
|
* notice shall be included in all copies or substantial portions of the Software. |
||||||
|
* |
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
||||||
|
* THE SOFTWARE. |
||||||
|
*/ |
||||||
|
/*
|
||||||
|
* Extended by Chip Audette, OpenAudio, May 2019 |
||||||
|
* Converted to F32 and to variable audio block length |
||||||
|
* The F32 conversion is under the MIT License. Use at your own risk. |
||||||
|
*/ |
||||||
|
// Updated OpenAudio F32 with this version from Chip Audette's Tympan Library Jan 2021 RSL
|
||||||
|
|
||||||
|
#ifndef _INPUT_I2S2_F32_H_ |
||||||
|
#define _INPUT_I2S2_F32_H_ |
||||||
|
|
||||||
|
#include <Arduino.h> |
||||||
|
#include <arm_math.h> |
||||||
|
#include "AudioStream_F32.h" |
||||||
|
#include "AudioStream.h" //Do we really need this?? (Chip, 2020-10-31) |
||||||
|
#include "DMAChannel.h" |
||||||
|
|
||||||
|
class AudioInputI2S2_F32 : public AudioStream_F32 |
||||||
|
{ |
||||||
|
//GUI: inputs:0, outputs:2 //this line used for automatic generation of GUI nodes
|
||||||
|
public: |
||||||
|
AudioInputI2S2_F32(void) : AudioStream_F32(0, NULL) { begin(); } //uses default AUDIO_SAMPLE_RATE and BLOCK_SIZE_SAMPLES from AudioStream.h
|
||||||
|
AudioInputI2S2_F32(const AudioSettings_F32 &settings) : AudioStream_F32(0, NULL) {
|
||||||
|
sample_rate_Hz = settings.sample_rate_Hz; |
||||||
|
audio_block_samples = settings.audio_block_samples; |
||||||
|
begin();
|
||||||
|
} |
||||||
|
|
||||||
|
virtual void update(void); |
||||||
|
static void scale_i16_to_f32( float32_t *p_i16, float32_t *p_f32, int len) ; |
||||||
|
static void scale_i24_to_f32( float32_t *p_i24, float32_t *p_f32, int len) ; |
||||||
|
static void scale_i32_to_f32( float32_t *p_i32, float32_t *p_f32, int len); |
||||||
|
void begin(void); |
||||||
|
void begin(bool); |
||||||
|
void sub_begin_i32(void); |
||||||
|
//void sub_begin_i16(void);
|
||||||
|
int get_isOutOfMemory(void) { return flag_out_of_memory; } |
||||||
|
void clear_isOutOfMemory(void) { flag_out_of_memory = 0; } |
||||||
|
//friend class AudioOutputI2S_F32;
|
||||||
|
protected:
|
||||||
|
AudioInputI2S2_F32(int dummy): AudioStream_F32(0, NULL) {} // to be used only inside AudioInputI2Sslave !!
|
||||||
|
static bool update_responsibility; |
||||||
|
static DMAChannel dma; |
||||||
|
static void isr_32(void); |
||||||
|
static void isr(void); |
||||||
|
virtual void update_1chan(int, audio_block_f32_t *&); |
||||||
|
private: |
||||||
|
static audio_block_f32_t *block_left_f32; |
||||||
|
static audio_block_f32_t *block_right_f32; |
||||||
|
static float sample_rate_Hz; |
||||||
|
static int audio_block_samples; |
||||||
|
static uint16_t block_offset; |
||||||
|
static int flag_out_of_memory; |
||||||
|
static unsigned long update_counter; |
||||||
|
}; |
||||||
|
|
||||||
|
class AudioInputI2S2slave_F32 : public AudioInputI2S2_F32 |
||||||
|
{ |
||||||
|
public: |
||||||
|
AudioInputI2S2slave_F32(void) : AudioInputI2S2_F32(0) { begin(); } |
||||||
|
void begin(void); |
||||||
|
friend void dma_ch1_isr(void); |
||||||
|
}; |
||||||
|
|
||||||
|
#endif // _INPUT_I2S_F32_H_
|
@ -0,0 +1,506 @@ |
|||||||
|
/*
|
||||||
|
* ***** output_i2s_f32.cpp ***** |
||||||
|
* |
||||||
|
* Audio Library for Teensy 3.X |
||||||
|
* Copyright (c) 2014, Paul Stoffregen, paul@pjrc.com |
||||||
|
* |
||||||
|
* Development of this audio library was funded by PJRC.COM, LLC by sales of |
||||||
|
* Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop |
||||||
|
* open source software by purchasing Teensy or other PJRC products. |
||||||
|
* |
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy |
||||||
|
* of this software and associated documentation files (the "Software"), to deal |
||||||
|
* in the Software without restriction, including without limitation the rights |
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||||
|
* copies of the Software, and to permit persons to whom the Software is |
||||||
|
* furnished to do so, subject to the following conditions: |
||||||
|
* |
||||||
|
* The above copyright notice, development funding notice, and this permission |
||||||
|
* notice shall be included in all copies or substantial portions of the Software. |
||||||
|
* |
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
||||||
|
* THE SOFTWARE. |
||||||
|
*/ |
||||||
|
/*
|
||||||
|
* Extended by Chip Audette, OpenAudio, May 2019 |
||||||
|
* Converted to F32 and to variable audio block length |
||||||
|
* The F32 conversion is under the MIT License. Use at your own risk. |
||||||
|
*/ |
||||||
|
// Updated OpenAudio F32 with this version from Chip Audette's Tympan Library Jan 2021 RSL
|
||||||
|
// Ported to I2S2, 12.2023 by Piotr Zapart www.hexefx.com - for teensy4.x only!
|
||||||
|
|
||||||
|
#include "output_i2s2_F32.h" |
||||||
|
#include <arm_math.h> |
||||||
|
#include <Audio.h> //to get access to Audio/utlity/imxrt_hw.h...do we really need this??? WEA 2020-10-31 |
||||||
|
|
||||||
|
float AudioOutputI2S2_F32::setI2SFreq_T3(const float freq_Hz) |
||||||
|
{ |
||||||
|
return 0.0f; |
||||||
|
} |
||||||
|
|
||||||
|
audio_block_f32_t *AudioOutputI2S2_F32::block_left_1st = NULL; |
||||||
|
audio_block_f32_t *AudioOutputI2S2_F32::block_right_1st = NULL; |
||||||
|
audio_block_f32_t *AudioOutputI2S2_F32::block_left_2nd = NULL; |
||||||
|
audio_block_f32_t *AudioOutputI2S2_F32::block_right_2nd = NULL; |
||||||
|
uint16_t AudioOutputI2S2_F32::block_left_offset = 0; |
||||||
|
uint16_t AudioOutputI2S2_F32::block_right_offset = 0; |
||||||
|
bool AudioOutputI2S2_F32::update_responsibility = false; |
||||||
|
DMAChannel AudioOutputI2S2_F32::dma(false); |
||||||
|
DMAMEM __attribute__((aligned(32))) static uint32_t i2s2_tx_buffer[AUDIO_BLOCK_SAMPLES]; |
||||||
|
|
||||||
|
float AudioOutputI2S2_F32::sample_rate_Hz = AUDIO_SAMPLE_RATE; |
||||||
|
int AudioOutputI2S2_F32::audio_block_samples = AUDIO_BLOCK_SAMPLES; |
||||||
|
|
||||||
|
#if defined(__IMXRT1062__) |
||||||
|
#include <utility/imxrt_hw.h> //from Teensy Audio library. For set_audioClock() |
||||||
|
#endif |
||||||
|
|
||||||
|
// #for 16-bit transfers
|
||||||
|
#define I2S2_BUFFER_TO_USE_BYTES (AudioOutputI2S2_F32::audio_block_samples * sizeof(i2s2_tx_buffer[0])) |
||||||
|
|
||||||
|
// #for 32-bit transfers
|
||||||
|
// #define I2S_BUFFER_TO_USE_BYTES (AudioOutputI2S2_F32::audio_block_samples*2*sizeof(i2s_tx_buffer[0]))
|
||||||
|
|
||||||
|
void AudioOutputI2S2_F32::begin(void) |
||||||
|
{ |
||||||
|
bool transferUsing32bit = false; |
||||||
|
begin(transferUsing32bit); |
||||||
|
} |
||||||
|
|
||||||
|
void AudioOutputI2S2_F32::begin(bool transferUsing32bit) |
||||||
|
{ |
||||||
|
|
||||||
|
dma.begin(true); // Allocate the DMA channel first
|
||||||
|
|
||||||
|
block_left_1st = NULL; |
||||||
|
block_right_1st = NULL; |
||||||
|
|
||||||
|
AudioOutputI2S2_F32::config_i2s(transferUsing32bit, sample_rate_Hz); |
||||||
|
|
||||||
|
#if defined(__IMXRT1062__) |
||||||
|
CORE_PIN2_CONFIG = 2; // EMC_04, 2=SAI2_TX_DATA, page 428
|
||||||
|
|
||||||
|
dma.TCD->SADDR = i2s2_tx_buffer; |
||||||
|
dma.TCD->SOFF = 2; |
||||||
|
dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1); |
||||||
|
dma.TCD->NBYTES_MLNO = 2; |
||||||
|
// dma.TCD->SLAST = -sizeof(i2s_tx_buffer);//orig from Teensy Audio Library 2020-10-31
|
||||||
|
dma.TCD->SLAST = -I2S2_BUFFER_TO_USE_BYTES; |
||||||
|
dma.TCD->DOFF = 0; |
||||||
|
// dma.TCD->CITER_ELINKNO = sizeof(i2s_tx_buffer) / 2; //orig from Teensy Audio Library 2020-10-31
|
||||||
|
dma.TCD->CITER_ELINKNO = I2S2_BUFFER_TO_USE_BYTES / 2; |
||||||
|
dma.TCD->DLASTSGA = 0; |
||||||
|
// dma.TCD->BITER_ELINKNO = sizeof(i2s_tx_buffer) / 2;//orig from Teensy Audio Library 2020-10-31
|
||||||
|
dma.TCD->BITER_ELINKNO = I2S2_BUFFER_TO_USE_BYTES / 2; |
||||||
|
dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR; |
||||||
|
dma.TCD->DADDR = (void *)((uint32_t)&I2S2_TDR0 + 2); |
||||||
|
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI2_TX); |
||||||
|
dma.enable(); // newer location of this line in Teensy Audio library
|
||||||
|
|
||||||
|
// I2S2_RCSR |= I2S_RCSR_RE | I2S_RCSR_BCE;
|
||||||
|
I2S2_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE | I2S_TCSR_FR; |
||||||
|
#endif |
||||||
|
update_responsibility = update_setup(); |
||||||
|
dma.attachInterrupt(AudioOutputI2S2_F32::isr); |
||||||
|
enabled = 1; |
||||||
|
} |
||||||
|
|
||||||
|
void AudioOutputI2S2_F32::isr(void) |
||||||
|
{ |
||||||
|
#if defined(KINETISK) || defined(__IMXRT1062__) |
||||||
|
int16_t *dest; |
||||||
|
audio_block_f32_t *blockL, *blockR; |
||||||
|
uint32_t saddr, offsetL, offsetR; |
||||||
|
|
||||||
|
saddr = (uint32_t)(dma.TCD->SADDR); |
||||||
|
dma.clearInterrupt(); |
||||||
|
// if (saddr < (uint32_t)i2s_tx_buffer + sizeof(i2s_tx_buffer) / 2) { //original 16-bit
|
||||||
|
if (saddr < (uint32_t)i2s2_tx_buffer + I2S2_BUFFER_TO_USE_BYTES / 2) |
||||||
|
{ // are we transmitting the first half or second half of the buffer?
|
||||||
|
// DMA is transmitting the first half of the buffer
|
||||||
|
// so we must fill the second half
|
||||||
|
// dest = (int16_t *)&i2s_tx_buffer[AUDIO_BLOCK_SAMPLES/2]; //original Teensy Audio
|
||||||
|
dest = (int16_t *)&i2s2_tx_buffer[audio_block_samples / 2]; // this will be diff if we were to do 32-bit samples
|
||||||
|
if (AudioOutputI2S2_F32::update_responsibility) |
||||||
|
AudioStream_F32::update_all(); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
// DMA is transmitting the second half of the buffer
|
||||||
|
// so we must fill the first half
|
||||||
|
dest = (int16_t *)i2s2_tx_buffer; |
||||||
|
} |
||||||
|
|
||||||
|
blockL = AudioOutputI2S2_F32::block_left_1st; |
||||||
|
blockR = AudioOutputI2S2_F32::block_right_1st; |
||||||
|
offsetL = AudioOutputI2S2_F32::block_left_offset; |
||||||
|
offsetR = AudioOutputI2S2_F32::block_right_offset; |
||||||
|
|
||||||
|
int16_t *d = dest; |
||||||
|
if (blockL && blockR) |
||||||
|
{ |
||||||
|
// memcpy_tointerleaveLR(dest, blockL->data + offsetL, blockR->data + offsetR);
|
||||||
|
// memcpy_tointerleaveLRwLen(dest, blockL->data + offsetL, blockR->data + offsetR, audio_block_samples/2);
|
||||||
|
float32_t *pL = blockL->data + offsetL; |
||||||
|
float32_t *pR = blockR->data + offsetR; |
||||||
|
for (int i = 0; i < audio_block_samples / 2; i++) |
||||||
|
{ |
||||||
|
*d++ = (int16_t)*pL++; |
||||||
|
*d++ = (int16_t)*pR++; // interleave
|
||||||
|
//*d++ = 0;
|
||||||
|
//*d++ = 0;
|
||||||
|
} |
||||||
|
offsetL += audio_block_samples / 2; |
||||||
|
offsetR += audio_block_samples / 2; |
||||||
|
} |
||||||
|
else if (blockL) |
||||||
|
{ |
||||||
|
// memcpy_tointerleaveLR(dest, blockL->data + offsetL, blockR->data + offsetR);
|
||||||
|
float32_t *pL = blockL->data + offsetL; |
||||||
|
for (int i = 0; i < audio_block_samples / 2 * 2; i += 2) |
||||||
|
{ |
||||||
|
*(d + i) = (int16_t)*pL++; |
||||||
|
} // interleave
|
||||||
|
offsetL += audio_block_samples / 2; |
||||||
|
} |
||||||
|
else if (blockR) |
||||||
|
{ |
||||||
|
float32_t *pR = blockR->data + offsetR; |
||||||
|
for (int i = 0; i < audio_block_samples / 2 * 2; i += 2) |
||||||
|
{ |
||||||
|
*(d + i) = (int16_t)*pR++; |
||||||
|
} // interleave
|
||||||
|
offsetR += audio_block_samples / 2; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
// memset(dest,0,AUDIO_BLOCK_SAMPLES * 2);
|
||||||
|
memset(dest, 0, audio_block_samples * 2); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
arm_dcache_flush_delete(dest, sizeof(i2s2_tx_buffer) / 2); |
||||||
|
|
||||||
|
// if (offsetL < AUDIO_BLOCK_SAMPLES) { //orig Teensy Audio
|
||||||
|
if (offsetL < (uint16_t)audio_block_samples) |
||||||
|
{ |
||||||
|
AudioOutputI2S2_F32::block_left_offset = offsetL; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
AudioOutputI2S2_F32::block_left_offset = 0; |
||||||
|
AudioStream_F32::release(blockL); |
||||||
|
AudioOutputI2S2_F32::block_left_1st = AudioOutputI2S2_F32::block_left_2nd; |
||||||
|
AudioOutputI2S2_F32::block_left_2nd = NULL; |
||||||
|
} |
||||||
|
// if (offsetR < AUDIO_BLOCK_SAMPLES) { //orig Teensy Audio
|
||||||
|
if (offsetR < (uint16_t)audio_block_samples) |
||||||
|
{ |
||||||
|
AudioOutputI2S2_F32::block_right_offset = offsetR; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
AudioOutputI2S2_F32::block_right_offset = 0; |
||||||
|
AudioStream_F32::release(blockR); |
||||||
|
AudioOutputI2S2_F32::block_right_1st = AudioOutputI2S2_F32::block_right_2nd; |
||||||
|
AudioOutputI2S2_F32::block_right_2nd = NULL; |
||||||
|
} |
||||||
|
#endif |
||||||
|
} |
||||||
|
|
||||||
|
#define F32_TO_I16_NORM_FACTOR (32767) // which is 2^15-1
|
||||||
|
void AudioOutputI2S2_F32::scale_f32_to_i16(float32_t *p_f32, float32_t *p_i16, int len) |
||||||
|
{ |
||||||
|
for (int i = 0; i < len; i++) |
||||||
|
{ |
||||||
|
*p_i16++ = max(-F32_TO_I16_NORM_FACTOR, min(F32_TO_I16_NORM_FACTOR, (*p_f32++) * F32_TO_I16_NORM_FACTOR)); |
||||||
|
} |
||||||
|
} |
||||||
|
#define F32_TO_I24_NORM_FACTOR (8388607) // which is 2^23-1
|
||||||
|
void AudioOutputI2S2_F32::scale_f32_to_i24(float32_t *p_f32, float32_t *p_i24, int len) |
||||||
|
{ |
||||||
|
for (int i = 0; i < len; i++) |
||||||
|
{ |
||||||
|
*p_i24++ = max(-F32_TO_I24_NORM_FACTOR, min(F32_TO_I24_NORM_FACTOR, (*p_f32++) * F32_TO_I24_NORM_FACTOR)); |
||||||
|
} |
||||||
|
} |
||||||
|
#define F32_TO_I32_NORM_FACTOR (2147483647) // which is 2^31-1
|
||||||
|
// define F32_TO_I32_NORM_FACTOR (8388607) //which is 2^23-1
|
||||||
|
void AudioOutputI2S2_F32::scale_f32_to_i32(float32_t *p_f32, float32_t *p_i32, int len) |
||||||
|
{ |
||||||
|
for (int i = 0; i < len; i++) |
||||||
|
{ |
||||||
|
*p_i32++ = max(-F32_TO_I32_NORM_FACTOR, min(F32_TO_I32_NORM_FACTOR, (*p_f32++) * F32_TO_I32_NORM_FACTOR)); |
||||||
|
} |
||||||
|
// for (int i=0; i<len; i++) { *p_i32++ = (*p_f32++) * F32_TO_I32_NORM_FACTOR + 512.f*8388607.f; }
|
||||||
|
} |
||||||
|
|
||||||
|
// update has to be carefully coded so that, if audio_blocks are not available, the code exits
|
||||||
|
// gracefully and won't hang. That'll cause the whole system to hang, which would be very bad.
|
||||||
|
// static int count = 0;
|
||||||
|
void AudioOutputI2S2_F32::update(void) |
||||||
|
{ |
||||||
|
// null audio device: discard all incoming data
|
||||||
|
// if (!active) return;
|
||||||
|
// audio_block_t *block = receiveReadOnly();
|
||||||
|
// if (block) release(block);
|
||||||
|
|
||||||
|
audio_block_f32_t *block_f32; |
||||||
|
audio_block_f32_t *block_f32_scaled = AudioStream_F32::allocate_f32(); |
||||||
|
audio_block_f32_t *block2_f32_scaled = AudioStream_F32::allocate_f32(); |
||||||
|
if ((!block_f32_scaled) || (!block2_f32_scaled)) |
||||||
|
{ |
||||||
|
// couldn't get some working memory. Return.
|
||||||
|
if (block_f32_scaled) |
||||||
|
AudioStream_F32::release(block_f32_scaled); |
||||||
|
if (block2_f32_scaled) |
||||||
|
AudioStream_F32::release(block2_f32_scaled); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
// now that we have our working memory, proceed with getting the audio data and processing
|
||||||
|
block_f32 = receiveReadOnly_f32(0); // input 0 = left channel
|
||||||
|
if (block_f32) |
||||||
|
{ |
||||||
|
if (block_f32->length != audio_block_samples) |
||||||
|
{ |
||||||
|
Serial.print("AudioOutputI2S2_F32: *** WARNING ***: audio_block says len = "); |
||||||
|
Serial.print(block_f32->length); |
||||||
|
Serial.print(", but I2S settings want it to be = "); |
||||||
|
Serial.println(audio_block_samples); |
||||||
|
} |
||||||
|
// Serial.print("AudioOutputI2S2_F32: audio_block_samples = ");
|
||||||
|
// Serial.println(audio_block_samples);
|
||||||
|
|
||||||
|
// scale F32 to Int32
|
||||||
|
// block_f32_scaled = AudioStream_F32::allocate_f32();
|
||||||
|
// scale_f32_to_i32(block_f32->data, block_f32_scaled->data, audio_block_samples);
|
||||||
|
scale_f32_to_i16(block_f32->data, block_f32_scaled->data, audio_block_samples); |
||||||
|
|
||||||
|
// count++;
|
||||||
|
// if (count > 100) {
|
||||||
|
// Serial.print("AudioOutputI2S2_F32::update() orig, scaled = ");
|
||||||
|
// Serial.print(block_f32->data[30]);
|
||||||
|
// Serial.print(", ");
|
||||||
|
// Serial.println(block_f32_scaled->data[30]);
|
||||||
|
// count=0;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// now process the data blocks
|
||||||
|
__disable_irq(); |
||||||
|
if (block_left_1st == NULL) |
||||||
|
{ |
||||||
|
block_left_1st = block_f32_scaled; |
||||||
|
block_left_offset = 0; |
||||||
|
__enable_irq(); |
||||||
|
} |
||||||
|
else if (block_left_2nd == NULL) |
||||||
|
{ |
||||||
|
block_left_2nd = block_f32_scaled; |
||||||
|
__enable_irq(); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
audio_block_f32_t *tmp = block_left_1st; |
||||||
|
block_left_1st = block_left_2nd; |
||||||
|
block_left_2nd = block_f32_scaled; |
||||||
|
block_left_offset = 0; |
||||||
|
__enable_irq(); |
||||||
|
AudioStream_F32::release(tmp); |
||||||
|
} |
||||||
|
AudioStream_F32::transmit(block_f32, 0); |
||||||
|
AudioStream_F32::release(block_f32); // echo the incoming audio out the outputs
|
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
// this branch should never get called, but if it does, let's release the buffer that was never used
|
||||||
|
AudioStream_F32::release(block_f32_scaled); |
||||||
|
} |
||||||
|
|
||||||
|
block_f32_scaled = block2_f32_scaled; // this is simply renaming the pre-allocated buffer
|
||||||
|
block_f32 = receiveReadOnly_f32(1); // input 1 = right channel
|
||||||
|
if (block_f32) |
||||||
|
{ |
||||||
|
// scale F32 to Int32
|
||||||
|
// block_f32_scaled = AudioStream_F32::allocate_f32();
|
||||||
|
// scale_f32_to_i32(block_f32->data, block_f32_scaled->data, audio_block_samples);
|
||||||
|
scale_f32_to_i16(block_f32->data, block_f32_scaled->data, audio_block_samples); |
||||||
|
|
||||||
|
__disable_irq(); |
||||||
|
if (block_right_1st == NULL) |
||||||
|
{ |
||||||
|
block_right_1st = block_f32_scaled; |
||||||
|
block_right_offset = 0; |
||||||
|
__enable_irq(); |
||||||
|
} |
||||||
|
else if (block_right_2nd == NULL) |
||||||
|
{ |
||||||
|
block_right_2nd = block_f32_scaled; |
||||||
|
__enable_irq(); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
audio_block_f32_t *tmp = block_right_1st; |
||||||
|
block_right_1st = block_right_2nd; |
||||||
|
block_right_2nd = block_f32_scaled; |
||||||
|
block_right_offset = 0; |
||||||
|
__enable_irq(); |
||||||
|
AudioStream_F32::release(tmp); |
||||||
|
} |
||||||
|
AudioStream_F32::transmit(block_f32, 1); |
||||||
|
AudioStream_F32::release(block_f32); // echo the incoming audio out the outputs
|
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
// this branch should never get called, but if it does, let's release the buffer that was never used
|
||||||
|
AudioStream_F32::release(block_f32_scaled); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
void AudioOutputI2S2_F32::config_i2s(void) { config_i2s(false, AudioOutputI2S2_F32::sample_rate_Hz); } |
||||||
|
void AudioOutputI2S2_F32::config_i2s(bool transferUsing32bit) { config_i2s(transferUsing32bit, AudioOutputI2S2_F32::sample_rate_Hz); } |
||||||
|
void AudioOutputI2S2_F32::config_i2s(float fs_Hz) { config_i2s(false, fs_Hz); } |
||||||
|
|
||||||
|
void AudioOutputI2S2_F32::config_i2s(bool transferUsing32bit, float fs_Hz) |
||||||
|
{ |
||||||
|
#if defined(__IMXRT1062__) |
||||||
|
CCM_CCGR5 |= CCM_CCGR5_SAI2(CCM_CCGR_ON); |
||||||
|
|
||||||
|
// if either transmitter or receiver is enabled, do nothing
|
||||||
|
if (I2S2_TCSR & I2S_TCSR_TE) |
||||||
|
return; |
||||||
|
if (I2S2_RCSR & I2S_RCSR_RE) |
||||||
|
return; |
||||||
|
// PLL:
|
||||||
|
// int fs = AUDIO_SAMPLE_RATE_EXACT; //original from Teensy Audio Library
|
||||||
|
int fs = fs_Hz; |
||||||
|
|
||||||
|
// PLL between 27*24 = 648MHz und 54*24=1296MHz
|
||||||
|
int n1 = 4; // SAI prescaler 4 => (n1*n2) = multiple of 4
|
||||||
|
int n2 = 1 + (24000000 * 27) / (fs * 256 * n1); |
||||||
|
|
||||||
|
double C = ((double)fs * 256 * n1 * n2) / 24000000; |
||||||
|
int c0 = C; |
||||||
|
int c2 = 10000; |
||||||
|
int c1 = C * c2 - (c0 * c2); |
||||||
|
set_audioClock(c0, c1, c2); |
||||||
|
|
||||||
|
// clear SAI2_CLK register locations
|
||||||
|
CCM_CSCMR1 = (CCM_CSCMR1 & ~(CCM_CSCMR1_SAI2_CLK_SEL_MASK)) | CCM_CSCMR1_SAI2_CLK_SEL(2); // &0x03 // (0,1,2): PLL3PFD0, PLL5, PLL4,
|
||||||
|
CCM_CS2CDR = (CCM_CS2CDR & ~(CCM_CS2CDR_SAI2_CLK_PRED_MASK | CCM_CS2CDR_SAI2_CLK_PODF_MASK)) | CCM_CS2CDR_SAI2_CLK_PRED(n1 - 1) | CCM_CS2CDR_SAI2_CLK_PODF(n2 - 1); |
||||||
|
IOMUXC_GPR_GPR1 = (IOMUXC_GPR_GPR1 & ~(IOMUXC_GPR_GPR1_SAI2_MCLK3_SEL_MASK)) | (IOMUXC_GPR_GPR1_SAI2_MCLK_DIR | IOMUXC_GPR_GPR1_SAI2_MCLK3_SEL(0)); // Select MCLK
|
||||||
|
|
||||||
|
CORE_PIN33_CONFIG = 2; // EMC_07, 2=SAI2_MCLK
|
||||||
|
CORE_PIN4_CONFIG = 2; // EMC_06, 2=SAI2_TX_BCLK
|
||||||
|
CORE_PIN3_CONFIG = 2; // EMC_05, 2=SAI2_TX_SYNC, page 429
|
||||||
|
|
||||||
|
int rsync = 0; |
||||||
|
int tsync = 1; |
||||||
|
|
||||||
|
I2S2_TMR = 0; |
||||||
|
// I2S1_TCSR = (1<<25); //Reset
|
||||||
|
I2S2_TCR1 = I2S_TCR1_RFW(1); |
||||||
|
I2S2_TCR2 = I2S_TCR2_SYNC(tsync) | I2S_TCR2_BCP // sync=0; tx is async;
|
||||||
|
| (I2S_TCR2_BCD | I2S_TCR2_DIV((1)) | I2S_TCR2_MSEL(1)); |
||||||
|
I2S2_TCR3 = I2S_TCR3_TCE; |
||||||
|
I2S2_TCR4 = I2S_TCR4_FRSZ((2 - 1)) | I2S_TCR4_SYWD((32 - 1)) | I2S_TCR4_MF | I2S_TCR4_FSD | I2S_TCR4_FSE | I2S_TCR4_FSP; |
||||||
|
I2S2_TCR5 = I2S_TCR5_WNW((32 - 1)) | I2S_TCR5_W0W((32 - 1)) | I2S_TCR5_FBT((32 - 1)); |
||||||
|
|
||||||
|
I2S2_RMR = 0; |
||||||
|
// I2S1_RCSR = (1<<25); //Reset
|
||||||
|
I2S2_RCR1 = I2S_RCR1_RFW(1); |
||||||
|
I2S2_RCR2 = I2S_RCR2_SYNC(rsync) | I2S_RCR2_BCP // sync=0; rx is async;
|
||||||
|
| (I2S_RCR2_BCD | I2S_RCR2_DIV((1)) | I2S_RCR2_MSEL(1)); |
||||||
|
I2S2_RCR3 = I2S_RCR3_RCE; |
||||||
|
I2S2_RCR4 = I2S_RCR4_FRSZ((2 - 1)) | I2S_RCR4_SYWD((32 - 1)) | I2S_RCR4_MF | I2S_RCR4_FSE | I2S_RCR4_FSP | I2S_RCR4_FSD; |
||||||
|
I2S2_RCR5 = I2S_RCR5_WNW((32 - 1)) | I2S_RCR5_W0W((32 - 1)) | I2S_RCR5_FBT((32 - 1)); |
||||||
|
|
||||||
|
#endif |
||||||
|
} |
||||||
|
|
||||||
|
/******************************************************************/ |
||||||
|
|
||||||
|
// From Chip: The I2SSlave functionality has NOT been extended to allow for different block sizes or sample rates (2020-10-31)
|
||||||
|
|
||||||
|
void AudioOutputI2S2slave_F32::begin(void) |
||||||
|
{ |
||||||
|
|
||||||
|
dma.begin(true); // Allocate the DMA channel first
|
||||||
|
|
||||||
|
// pinMode(2, OUTPUT);
|
||||||
|
block_left_1st = NULL; |
||||||
|
block_right_1st = NULL; |
||||||
|
|
||||||
|
AudioOutputI2S2slave_F32::config_i2s(); |
||||||
|
|
||||||
|
#if defined(__IMXRT1062__) |
||||||
|
CORE_PIN7_CONFIG = 3; // 1:TX_DATA0
|
||||||
|
dma.TCD->SADDR = i2s2_tx_buffer; |
||||||
|
dma.TCD->SOFF = 2; |
||||||
|
dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1); |
||||||
|
dma.TCD->NBYTES_MLNO = 2; |
||||||
|
dma.TCD->SLAST = -sizeof(i2s2_tx_buffer); |
||||||
|
// dma.TCD->DADDR = (void *)((uint32_t)&I2S1_TDR1 + 2);
|
||||||
|
dma.TCD->DOFF = 0; |
||||||
|
dma.TCD->CITER_ELINKNO = sizeof(i2s2_tx_buffer) / 2; |
||||||
|
dma.TCD->DLASTSGA = 0; |
||||||
|
dma.TCD->BITER_ELINKNO = sizeof(i2s2_tx_buffer) / 2; |
||||||
|
// dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI2_TX);
|
||||||
|
dma.TCD->DADDR = (void *)((uint32_t)&I2S2_TDR0 + 2); |
||||||
|
dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR; |
||||||
|
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI2_TX); |
||||||
|
dma.enable(); |
||||||
|
|
||||||
|
I2S2_RCSR |= I2S_RCSR_RE | I2S_RCSR_BCE; |
||||||
|
I2S2_TCSR = I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE; |
||||||
|
|
||||||
|
#endif |
||||||
|
|
||||||
|
update_responsibility = update_setup(); |
||||||
|
// dma.enable();
|
||||||
|
dma.attachInterrupt(AudioOutputI2S2_F32::isr); |
||||||
|
} |
||||||
|
|
||||||
|
void AudioOutputI2S2slave_F32::config_i2s(void) |
||||||
|
{ |
||||||
|
#if defined(__IMXRT1062__) |
||||||
|
|
||||||
|
CCM_CCGR5 |= CCM_CCGR5_SAI2(CCM_CCGR_ON); |
||||||
|
|
||||||
|
// if either transmitter or receiver is enabled, do nothing
|
||||||
|
if (I2S2_TCSR & I2S_TCSR_TE) |
||||||
|
return; |
||||||
|
if (I2S2_RCSR & I2S_RCSR_RE) |
||||||
|
return; |
||||||
|
|
||||||
|
// not using MCLK in slave mode - hope that's ok?
|
||||||
|
// CORE_PIN23_CONFIG = 3; // AD_B1_09 ALT3=SAI2_MCLK
|
||||||
|
CORE_PIN4_CONFIG = 3; // AD_B1_11 ALT3=SAI2_RX_BCLK
|
||||||
|
CORE_PIN3_CONFIG = 3; // AD_B1_10 ALT3=SAI2_RX_SYNC
|
||||||
|
IOMUXC_SAI2_RX_BCLK_SELECT_INPUT = 1; // 1=GPIO_AD_B1_11_ALT3, page 868
|
||||||
|
IOMUXC_SAI2_RX_SYNC_SELECT_INPUT = 1; // 1=GPIO_AD_B1_10_ALT3, page 872
|
||||||
|
|
||||||
|
// configure transmitter
|
||||||
|
I2S2_TMR = 0; |
||||||
|
I2S2_TCR1 = I2S_TCR1_RFW(1); // watermark at half fifo size
|
||||||
|
I2S2_TCR2 = I2S_TCR2_SYNC(1) | I2S_TCR2_BCP; |
||||||
|
I2S2_TCR3 = I2S_TCR3_TCE; |
||||||
|
I2S2_TCR4 = I2S_TCR4_FRSZ(1) | I2S_TCR4_SYWD(31) | I2S_TCR4_MF | I2S_TCR4_FSE | I2S_TCR4_FSP | I2S_RCR4_FSD; |
||||||
|
I2S2_TCR5 = I2S_TCR5_WNW(31) | I2S_TCR5_W0W(31) | I2S_TCR5_FBT(31); |
||||||
|
|
||||||
|
// configure receiver
|
||||||
|
I2S2_RMR = 0; |
||||||
|
I2S2_RCR1 = I2S_RCR1_RFW(1); |
||||||
|
I2S2_RCR2 = I2S_RCR2_SYNC(0) | I2S_TCR2_BCP; |
||||||
|
I2S2_RCR3 = I2S_RCR3_RCE; |
||||||
|
I2S2_RCR4 = I2S_RCR4_FRSZ(1) | I2S_RCR4_SYWD(31) | I2S_RCR4_MF | I2S_RCR4_FSE | I2S_RCR4_FSP; |
||||||
|
I2S2_RCR5 = I2S_RCR5_WNW(31) | I2S_RCR5_W0W(31) | I2S_RCR5_FBT(31); |
||||||
|
|
||||||
|
#endif |
||||||
|
} |
@ -0,0 +1,113 @@ |
|||||||
|
/*
|
||||||
|
* ***** output_i2s_f32.h ***** |
||||||
|
*
|
||||||
|
* Audio Library for Teensy 3.X |
||||||
|
* Copyright (c) 2014, Paul Stoffregen, paul@pjrc.com |
||||||
|
* |
||||||
|
* Development of this audio library was funded by PJRC.COM, LLC by sales of |
||||||
|
* Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop |
||||||
|
* open source software by purchasing Teensy or other PJRC products. |
||||||
|
* |
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy |
||||||
|
* of this software and associated documentation files (the "Software"), to deal |
||||||
|
* in the Software without restriction, including without limitation the rights |
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||||
|
* copies of the Software, and to permit persons to whom the Software is |
||||||
|
* furnished to do so, subject to the following conditions: |
||||||
|
* |
||||||
|
* The above copyright notice, development funding notice, and this permission |
||||||
|
* notice shall be included in all copies or substantial portions of the Software. |
||||||
|
* |
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
||||||
|
* THE SOFTWARE. |
||||||
|
*/ |
||||||
|
/*
|
||||||
|
* Extended by Chip Audette, OpenAudio, May 2019 |
||||||
|
* Converted to F32 and to variable audio block length |
||||||
|
* The F32 conversion is under the MIT License. Use at your own risk. |
||||||
|
*/ |
||||||
|
// Updated OpenAudio F32 with this version from Chip Audette's Tympan Library Jan 2021 RSL
|
||||||
|
|
||||||
|
#ifndef _OUTPUT_I2S2_F32_H_ |
||||||
|
#define _OUTPUT_I2S2_F32_H_ |
||||||
|
|
||||||
|
|
||||||
|
#include <Arduino.h> |
||||||
|
#include <arm_math.h> |
||||||
|
#include "AudioStream_F32.h" |
||||||
|
#include "DMAChannel.h" |
||||||
|
|
||||||
|
class AudioOutputI2S2_F32 : public AudioStream_F32 |
||||||
|
{ |
||||||
|
//GUI: inputs:2, outputs:0 //this line used for automatic generation of GUI node
|
||||||
|
public: |
||||||
|
//uses default AUDIO_SAMPLE_RATE and BLOCK_SIZE_SAMPLES from AudioStream.h:
|
||||||
|
AudioOutputI2S2_F32(void) : AudioStream_F32(2, inputQueueArray) { begin();}
|
||||||
|
// Allow variable sample rate and block size:
|
||||||
|
AudioOutputI2S2_F32(const AudioSettings_F32 &settings) : AudioStream_F32(2, inputQueueArray) |
||||||
|
{
|
||||||
|
sample_rate_Hz = settings.sample_rate_Hz; |
||||||
|
audio_block_samples = settings.audio_block_samples; |
||||||
|
begin();
|
||||||
|
} |
||||||
|
virtual void update(void); |
||||||
|
void begin(void); |
||||||
|
void begin(bool); |
||||||
|
void sub_begin_i32(void); |
||||||
|
void sub_begin_i16(void); |
||||||
|
friend class AudioInputI2S2_F32; |
||||||
|
|
||||||
|
//friend class AudioInputI2S_F32;
|
||||||
|
#if defined(__IMXRT1062__) |
||||||
|
friend class AudioOutputI2SQuad_F32; |
||||||
|
friend class AudioInputI2SQuad_F32; |
||||||
|
|
||||||
|
#endif |
||||||
|
|
||||||
|
static void scale_f32_to_i16( float32_t *p_f32, float32_t *p_i16, int len) ; |
||||||
|
static void scale_f32_to_i24( float32_t *p_f32, float32_t *p_i16, int len) ; |
||||||
|
static void scale_f32_to_i32( float32_t *p_f32, float32_t *p_i32, int len) ; |
||||||
|
|
||||||
|
static float setI2SFreq_T3(const float); // I2S clock for T3,x
|
||||||
|
protected: |
||||||
|
AudioOutputI2S2_F32(int dummy): AudioStream_F32(2, inputQueueArray) {} // to be used only inside AudioOutputI2Sslave !!
|
||||||
|
static void config_i2s(void); |
||||||
|
static void config_i2s(bool); |
||||||
|
static void config_i2s(float); |
||||||
|
static void config_i2s(bool, float); |
||||||
|
|
||||||
|
static audio_block_f32_t *block_left_1st; |
||||||
|
static audio_block_f32_t *block_right_1st; |
||||||
|
static bool update_responsibility; |
||||||
|
static DMAChannel dma; |
||||||
|
static void isr_16(void); |
||||||
|
static void isr_32(void); |
||||||
|
static void isr(void); |
||||||
|
private: |
||||||
|
static audio_block_f32_t *block_left_2nd; |
||||||
|
static audio_block_f32_t *block_right_2nd; |
||||||
|
static uint16_t block_left_offset; |
||||||
|
static uint16_t block_right_offset; |
||||||
|
audio_block_f32_t *inputQueueArray[2]; |
||||||
|
static float sample_rate_Hz; |
||||||
|
static int audio_block_samples; |
||||||
|
volatile uint8_t enabled = 1; |
||||||
|
}; |
||||||
|
|
||||||
|
class AudioOutputI2S2slave_F32 : public AudioOutputI2S2_F32 |
||||||
|
{ |
||||||
|
public: |
||||||
|
AudioOutputI2S2slave_F32(void) : AudioOutputI2S2_F32(0) { begin(); } ; |
||||||
|
void begin(void); |
||||||
|
friend class AudioInputI2S2slave_F32; |
||||||
|
friend void dma_ch0_isr(void); |
||||||
|
protected: |
||||||
|
static void config_i2s(void); |
||||||
|
}; |
||||||
|
|
||||||
|
#endif // _OUTPUT_I2S_F32_H_
|
@ -0,0 +1,84 @@ |
|||||||
|
/*
|
||||||
|
Various waveforms used in the hexefx_audiolib_F32 |
||||||
|
*/ |
||||||
|
#include <arm_math.h> |
||||||
|
const uint16_t AudioWaveformHyperTri[257] =
|
||||||
|
{ |
||||||
|
0, 804, 1608, 2412, 3216, 4019, 4821, 5623, 6424, 7223,
|
||||||
|
8022, 8820, 9616, 10411, 11204, 11996, 12785, 13573, 14359, 15142,
|
||||||
|
15924, 16703, 17479, 18253, 19024, 19792, 20557, 21319, 22078, 22834,
|
||||||
|
23586, 24334, 25079, 25820, 26557, 27291, 28020, 28745, 29465, 30181,
|
||||||
|
30893, 31600, 32302, 32999, 33692, 34379, 35061, 35738, 36409, 37075,
|
||||||
|
37736, 38390, 39039, 39682, 40319, 40950, 41575, 42194, 42806, 43411,
|
||||||
|
44011, 44603, 45189, 45768, 46340, 46905, 47464, 48014, 48558, 49095,
|
||||||
|
49624, 50145, 50659, 51166, 51664, 52155, 52638, 53113, 53580, 54039,
|
||||||
|
54490, 54933, 55367, 55794, 56211, 56620, 57021, 57413, 57797, 58171,
|
||||||
|
58537, 58895, 59243, 59582, 59913, 60234, 60546, 60850, 61144, 61429,
|
||||||
|
61704, 61970, 62227, 62475, 62713, 62942, 63161, 63371, 63571, 63762,
|
||||||
|
63943, 64114, 64276, 64428, 64570, 64703, 64826, 64939, 65042, 65136,
|
||||||
|
65219, 65293, 65357, 65412, 65456, 65491, 65515, 65530, 65535, 65530,
|
||||||
|
65515, 65491, 65456, 65412, 65357, 65293, 65219, 65136, 65042, 64939,
|
||||||
|
64826, 64703, 64570, 64428, 64276, 64114, 63943, 63762, 63571, 63371,
|
||||||
|
63161, 62942, 62713, 62475, 62227, 61970, 61704, 61429, 61144, 60850,
|
||||||
|
60546, 60234, 59913, 59582, 59243, 58895, 58537, 58171, 57797, 57413,
|
||||||
|
57021, 56620, 56211, 55794, 55367, 54933, 54490, 54039, 53580, 53113,
|
||||||
|
52638, 52155, 51664, 51166, 50659, 50145, 49624, 49095, 48558, 48014,
|
||||||
|
47464, 46905, 46340, 45768, 45189, 44603, 44011, 43411, 42806, 42194,
|
||||||
|
41575, 40950, 40319, 39682, 39039, 38390, 37736, 37075, 36409, 35738,
|
||||||
|
35061, 34379, 33692, 32999, 32302, 31600, 30893, 30181, 29465, 28745,
|
||||||
|
28020, 27291, 26557, 25820, 25079, 24334, 23586, 22834, 22078, 21319,
|
||||||
|
20557, 19792, 19024, 18253, 17479, 16703, 15924, 15142, 14359, 13573,
|
||||||
|
12785, 11996, 11204, 10411, 9616, 8820, 8022, 7223, 6424, 5623,
|
||||||
|
4821, 4019, 3216, 2412, 1608, 804, 0 |
||||||
|
}; |
||||||
|
|
||||||
|
const float32_t AudioWaveformFader_f32[257] = |
||||||
|
{ |
||||||
|
0.000000f, 0.003075f, 0.006187f, 0.009336f, 0.012522f, 0.015745f, 0.019004f, 0.022300f, |
||||||
|
0.025633f, 0.029002f, 0.032407f, 0.035848f, 0.039324f, 0.042837f, 0.046384f, 0.049967f, |
||||||
|
0.053585f, 0.057238f, 0.060925f, 0.064647f, 0.068403f, 0.072193f, 0.076016f, 0.079873f, |
||||||
|
0.083764f, 0.087687f, 0.091643f, 0.095632f, 0.099653f, 0.103706f, 0.107791f, 0.111907f, |
||||||
|
0.116055f, 0.120233f, 0.124442f, 0.128681f, 0.132951f, 0.137250f, 0.141578f, 0.145936f, |
||||||
|
0.150322f, 0.154737f, 0.159180f, 0.163650f, 0.168148f, 0.172674f, 0.177226f, 0.181804f, |
||||||
|
0.186409f, 0.191039f, 0.195695f, 0.200375f, 0.205080f, 0.209810f, 0.214563f, 0.219340f, |
||||||
|
0.224139f, 0.228962f, 0.233806f, 0.238673f, 0.243561f, 0.248470f, 0.253400f, 0.258350f, |
||||||
|
0.263320f, 0.268309f, 0.273317f, 0.278343f, 0.283388f, 0.288450f, 0.293530f, 0.298626f, |
||||||
|
0.303738f, 0.308867f, 0.314011f, 0.319169f, 0.324342f, 0.329529f, 0.334730f, 0.339944f, |
||||||
|
0.345170f, 0.350408f, 0.355658f, 0.360920f, 0.366191f, 0.371473f, 0.376765f, 0.382066f, |
||||||
|
0.387375f, 0.392693f, 0.398018f, 0.403351f, 0.408690f, 0.414035f, 0.419386f, 0.424742f, |
||||||
|
0.430103f, 0.435468f, 0.440836f, 0.446208f, 0.451582f, 0.456958f, 0.462336f, 0.467714f, |
||||||
|
0.473093f, 0.478472f, 0.483851f, 0.489228f, 0.494604f, 0.499977f, 0.505348f, 0.510715f, |
||||||
|
0.516079f, 0.521438f, 0.526793f, 0.532142f, 0.537485f, 0.542821f, 0.548151f, 0.553473f, |
||||||
|
0.558787f, 0.564093f, 0.569389f, 0.574675f, 0.579952f, 0.585217f, 0.590471f, 0.595713f, |
||||||
|
0.600943f, 0.606160f, 0.611364f, 0.616553f, 0.621728f, 0.626888f, 0.632032f, 0.637160f, |
||||||
|
0.642271f, 0.647366f, 0.652442f, 0.657500f, 0.662539f, 0.667559f, 0.672559f, 0.677539f, |
||||||
|
0.682498f, 0.687435f, 0.692351f, 0.697244f, 0.702114f, 0.706960f, 0.711782f, 0.716580f, |
||||||
|
0.721353f, 0.726100f, 0.730822f, 0.735516f, 0.740184f, 0.744824f, 0.749436f, 0.754020f, |
||||||
|
0.758574f, 0.763099f, 0.767594f, 0.772058f, 0.776492f, 0.780894f, 0.785264f, 0.789602f, |
||||||
|
0.793907f, 0.798178f, 0.802416f, 0.806619f, 0.810788f, 0.814922f, 0.819020f, 0.823082f, |
||||||
|
0.827107f, 0.831096f, 0.835047f, 0.838960f, 0.842835f, 0.846672f, 0.850469f, 0.854227f, |
||||||
|
0.857945f, 0.861623f, 0.865260f, 0.868856f, 0.872411f, 0.875924f, 0.879394f, 0.882822f, |
||||||
|
0.886206f, 0.889548f, 0.892845f, 0.896099f, 0.899308f, 0.902472f, 0.905591f, 0.908664f, |
||||||
|
0.911692f, 0.914673f, 0.917608f, 0.920496f, 0.923336f, 0.926129f, 0.928875f, 0.931572f, |
||||||
|
0.934221f, 0.936821f, 0.939373f, 0.941875f, 0.944327f, 0.946730f, 0.949082f, 0.951384f, |
||||||
|
0.953636f, 0.955836f, 0.957986f, 0.960084f, 0.962131f, 0.964126f, 0.966069f, 0.967959f, |
||||||
|
0.969797f, 0.971583f, 0.973315f, 0.974995f, 0.976621f, 0.978194f, 0.979714f, 0.981179f, |
||||||
|
0.982591f, 0.983948f, 0.985252f, 0.986501f, 0.987695f, 0.988835f, 0.989920f, 0.990950f, |
||||||
|
0.991925f, 0.992845f, 0.993709f, 0.994519f, 0.995272f, 0.995971f, 0.996614f, 0.997201f, |
||||||
|
0.997732f, 0.998208f, 0.998628f, 0.998992f, 0.999300f, 0.999552f, 0.999748f, 1.000000f, |
||||||
|
1.000000f |
||||||
|
}; |
||||||
|
|
||||||
|
/************************************************************************/ |
||||||
|
/* Pitch intervals in range from -1oct to +2 oct, step 1semitone */ |
||||||
|
/************************************************************************/ |
||||||
|
const float music_intevals[37] = |
||||||
|
{ |
||||||
|
0.500000f, 0.529732f, 0.561231f, 0.594604f, 0.629961f, 0.667420f, 0.707107f, 0.749154f, |
||||||
|
0.793701f, 0.840896f, 0.890899f, 0.943874f, 1.000000f, 1.059463f, 1.122462f, 1.189207f, |
||||||
|
1.259921f, 1.334840f, 1.414214f, 1.498307f, 1.587401f, 1.681793f, 1.781797f, 1.887749f, |
||||||
|
2.000000f, 2.118926f, 2.244924f, 2.378414f, 2.519842f, 2.669680f, 2.828427f, 2.996614f, |
||||||
|
3.174802f, 3.363586f, 3.563595f, 3.775497f, 4.000000f |
||||||
|
}; |
||||||
|
|
||||||
|
|
Loading…
Reference in new issue