// do passthru | // do passthru | ||||
// It stores the unmodified data in the delay line so that | // It stores the unmodified data in the delay line so that | ||||
// it isn't as likely to click | // it isn't as likely to click | ||||
if(num_chorus < 1) { | |||||
if(num_chorus <= 1) { | |||||
// Just passthrough | // Just passthrough | ||||
block = receiveWritable(0); | block = receiveWritable(0); | ||||
if(block) { | if(block) { | ||||
transmit(block,0); | transmit(block,0); | ||||
release(block); | release(block); | ||||
} | } | ||||
return; | |||||
} | } | ||||
// L E F T C H A N N E L | // L E F T C H A N N E L | ||||
block = receiveWritable(0); | block = receiveWritable(0); | ||||
if(block) { | if(block) { | ||||
bp = block->data; | bp = block->data; | ||||
uint32_t tmp = delay_length/(num_chorus - 1) - 1; | |||||
for(int i = 0;i < AUDIO_BLOCK_SAMPLES;i++) { | for(int i = 0;i < AUDIO_BLOCK_SAMPLES;i++) { | ||||
l_circ_idx++; | l_circ_idx++; | ||||
if(l_circ_idx >= delay_length) { | if(l_circ_idx >= delay_length) { | ||||
c_idx = l_circ_idx; | c_idx = l_circ_idx; | ||||
for(int k = 0; k < num_chorus; k++) { | for(int k = 0; k < num_chorus; k++) { | ||||
sum += l_delayline[c_idx]; | sum += l_delayline[c_idx]; | ||||
if(num_chorus > 1)c_idx -= delay_length/(num_chorus - 1) - 1; | |||||
if(num_chorus > 1)c_idx -= tmp; | |||||
if(c_idx < 0) { | if(c_idx < 0) { | ||||
c_idx += delay_length; | c_idx += delay_length; | ||||
} | } |
#define SPISETTING SPISettings(20000000, MSBFIRST, SPI_MODE0) | #define SPISETTING SPISettings(20000000, MSBFIRST, SPI_MODE0) | ||||
// While 20 MHz (Teensy actually uses 16 MHz in most cases) and even 24 MHz | |||||
// have worked well in testing at room temperature with 3.3V power, to fully | |||||
// meet all the worst case timing specs, the SPI clock low time would need | |||||
// to be 40ns (12.5 MHz clock) for the single chip case and 51ns (9.8 MHz | |||||
// clock) for the 6-chip memoryboard with 74LCX126 buffers. | |||||
// | |||||
// Timing analysis and info is here: | |||||
// https://forum.pjrc.com/threads/29276-Limits-of-delay-effect-in-audio-library?p=97506&viewfull=1#post97506 | |||||
void AudioEffectDelayExternal::read(uint32_t offset, uint32_t count, int16_t *data) | void AudioEffectDelayExternal::read(uint32_t offset, uint32_t count, int16_t *data) | ||||
{ | { | ||||
uint32_t addr = memory_begin + offset; | uint32_t addr = memory_begin + offset; |
// initial index | // initial index | ||||
l_delay_rate_index = 0; | l_delay_rate_index = 0; | ||||
l_circ_idx = 0; | l_circ_idx = 0; | ||||
delay_rate_incr = delay_rate/44100.*2147483648.; | |||||
delay_rate_incr = delay_rate / (2147483648.0 * AUDIO_SAMPLE_RATE_EXACT); | |||||
//Serial.println(delay_rate_incr,HEX); | //Serial.println(delay_rate_incr,HEX); | ||||
delay_offset_idx = delay_offset; | delay_offset_idx = delay_offset; | ||||
delay_depth = d_depth; | delay_depth = d_depth; | ||||
delay_rate_incr = delay_rate/44100.*2147483648.; | |||||
delay_rate_incr = delay_rate / (2147483648.0 * AUDIO_SAMPLE_RATE_EXACT); | |||||
delay_offset_idx = delay_offset; | delay_offset_idx = delay_offset; | ||||
// Allow the passthru code to go through | // Allow the passthru code to go through |
Disabling the audio library interrupt for too long may cause audible | Disabling the audio library interrupt for too long may cause audible | ||||
dropouts or glitches. | dropouts or glitches. | ||||
</p> | </p> | ||||
<p>An experimental SD library optimization exists, which can remove these | |||||
SD library restrictions. It also allows reliable playback of more | |||||
files at the same time. To enable this special code, find and edit | |||||
the SD_t3.h file within your Arduino folder. See the comments within | |||||
that file for details. | |||||
</p> | |||||
</script> | </script> | ||||
<script type="text/x-red" data-template-name="AudioPlaySdWav"> | <script type="text/x-red" data-template-name="AudioPlaySdWav"> | ||||
<div class="form-row"> | <div class="form-row"> |
// compute (a - b) / c | // compute (a - b) / c | ||||
// handling 32 bit interger overflow at every step | // handling 32 bit interger overflow at every step | ||||
// without resorting to slow 64 bit math | // without resorting to slow 64 bit math | ||||
#if 0 | |||||
// TODO: write this in assembly.... | |||||
static inline int32_t substract_32_then_divide(int32_t a, int32_t b, int32_t c) __attribute__((always_inline, unused)); | |||||
static inline int32_t substract_32_then_divide(int32_t a, int32_t b, int32_t c) | |||||
#if defined(KINETISK) | |||||
static inline int32_t substract_int32_then_divide_int32(int32_t a, int32_t b, int32_t c) __attribute__((always_inline, unused)); | |||||
static inline int32_t substract_int32_then_divide_int32(int32_t a, int32_t b, int32_t c) | |||||
{ | { | ||||
int32_t diff = substract_32_saturate(a, b); | |||||
// if only C language provided a way to test Q status bit.... | |||||
if (diff != 0x7FFFFFFF && diff != 0x80000000) { | |||||
return diff / c; | |||||
} else { | |||||
diff = substract_32_saturate(a/2, b/2); | |||||
if (c == 1 || c == -1) c *= 2; // <-- horrible, incorrect hack | |||||
return (diff / c) * 2; | |||||
} | |||||
int r; | |||||
r = substract_32_saturate(a,b); | |||||
if ( !get_q_psr() ) return (r/c); | |||||
clr_q_psr(); | |||||
if ( c==0 ) r=0; | |||||
if (__builtin_abs(c)<=1) return r; | |||||
return (a/c)-(b/c); | |||||
} | } | ||||
#else | #else | ||||
// compute (a - b) / c ... handling 32 bit interger overflow without slow 64 bit math | // compute (a - b) / c ... handling 32 bit interger overflow without slow 64 bit math |
boolean AudioSynthToneSweep::play(float t_amp,int t_lo,int t_hi,float t_time) | boolean AudioSynthToneSweep::play(float t_amp,int t_lo,int t_hi,float t_time) | ||||
{ | { | ||||
double tone_tmp; | |||||
float tone_tmp; | |||||
if(0) { | if(0) { | ||||
Serial.print("AudioSynthToneSweep.begin(tone_amp = "); | Serial.print("AudioSynthToneSweep.begin(tone_amp = "); | ||||
if(t_amp > 1)return false; | if(t_amp > 1)return false; | ||||
if(t_lo < 1)return false; | if(t_lo < 1)return false; | ||||
if(t_hi < 1)return false; | if(t_hi < 1)return false; | ||||
if(t_hi >= 44100/2)return false; | |||||
if(t_lo >= 44100/2)return false; | |||||
if(t_hi >= (int) AUDIO_SAMPLE_RATE_EXACT / 2)return false; | |||||
if(t_lo >= (int) AUDIO_SAMPLE_RATE_EXACT / 2)return false; | |||||
if(t_time < 0)return false; | if(t_time < 0)return false; | ||||
tone_lo = t_lo; | tone_lo = t_lo; | ||||
tone_hi = t_hi; | tone_hi = t_hi; | ||||
tone_sign = -1; | tone_sign = -1; | ||||
tone_tmp = tone_lo - tone_hi; | tone_tmp = tone_lo - tone_hi; | ||||
} | } | ||||
tone_tmp = tone_tmp/t_time/44100.; | |||||
tone_tmp = tone_tmp / t_time / AUDIO_SAMPLE_RATE_EXACT; | |||||
tone_incr = (tone_tmp * 0x100000000LL); | tone_incr = (tone_tmp * 0x100000000LL); | ||||
sweep_busy = 1; | sweep_busy = 1; | ||||
return(true); | return(true); | ||||
block = allocate(); | block = allocate(); | ||||
if(block) { | if(block) { | ||||
bp = block->data; | bp = block->data; | ||||
uint32_t tmp = tone_freq >> 32; | |||||
uint64_t tone_tmp = (0x400000000000LL * (int)(tmp&0x7fffffff)) / (int) AUDIO_SAMPLE_RATE_EXACT; | |||||
// Generate the sweep | // Generate the sweep | ||||
for(i = 0;i < AUDIO_BLOCK_SAMPLES;i++) { | for(i = 0;i < AUDIO_BLOCK_SAMPLES;i++) { | ||||
*bp++ = (short)(( (short)(arm_sin_q31((uint32_t)((tone_phase >> 15)&0x7fffffff))>>16) *tone_amp) >> 16); | *bp++ = (short)(( (short)(arm_sin_q31((uint32_t)((tone_phase >> 15)&0x7fffffff))>>16) *tone_amp) >> 16); | ||||
uint64_t tone_tmp = (0x400000000000LL * (int)((tone_freq >> 32)&0x7fffffff))/44100; | |||||
tone_phase += tone_tmp; | tone_phase += tone_tmp; | ||||
if(tone_phase & 0x800000000000LL)tone_phase &= 0x7fffffffffffLL; | if(tone_phase & 0x800000000000LL)tone_phase &= 0x7fffffffffffLL; | ||||
if(tone_sign > 0) { | if(tone_sign > 0) { | ||||
if((tone_freq >> 32) > tone_hi) { | |||||
if(tmp > tone_hi) { | |||||
sweep_busy = 0; | sweep_busy = 0; | ||||
break; | break; | ||||
} | } | ||||
tone_freq += tone_incr; | tone_freq += tone_incr; | ||||
} else { | } else { | ||||
if((tone_freq >> 32) < tone_hi) { | |||||
if(tmp < tone_hi) { | |||||
sweep_busy = 0; | sweep_busy = 0; | ||||
break; | break; | ||||
while(i < AUDIO_BLOCK_SAMPLES) { | while(i < AUDIO_BLOCK_SAMPLES) { | ||||
*bp++ = 0; | *bp++ = 0; | ||||
i++; | i++; | ||||
} | |||||
//b_count++; | |||||
} | |||||
// send the samples to the left channel | // send the samples to the left channel | ||||
transmit(block,0); | transmit(block,0); | ||||
release(block); | release(block); |
static inline uint32_t get_q_psr(void) | static inline uint32_t get_q_psr(void) | ||||
{ | { | ||||
uint32_t out; | uint32_t out; | ||||
asm volatile("mrs %0, APSR" : "=r" (out)); | |||||
asm ("mrs %0, APSR" : "=r" (out)); | |||||
return (out & 0x8000000)>>27; | return (out & 0x8000000)>>27; | ||||
} | } | ||||
static inline void clr_q_psr(void) | static inline void clr_q_psr(void) | ||||
{ | { | ||||
uint32_t t; | uint32_t t; | ||||
asm volatile("mrs %0,APSR " : "=r" (t)); | |||||
asm volatile("bfc %0, #27, #1" : "=r" (t)); | |||||
asm volatile("msr APSR_nzcvq,%0" : "=r" (t)); | |||||
asm ("mov %[t],#0\n" | |||||
"msr APSR_nzcvq,%0\n" : [t] "=&r" (t)::"cc"); | |||||
} | } | ||||
#endif | #endif |