| // 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 |