| @@ -78,7 +78,7 @@ void AudioEffectChorus::update(void) | |||
| // do passthru | |||
| // It stores the unmodified data in the delay line so that | |||
| // it isn't as likely to click | |||
| if(num_chorus < 1) { | |||
| if(num_chorus <= 1) { | |||
| // Just passthrough | |||
| block = receiveWritable(0); | |||
| if(block) { | |||
| @@ -93,6 +93,7 @@ void AudioEffectChorus::update(void) | |||
| transmit(block,0); | |||
| release(block); | |||
| } | |||
| return; | |||
| } | |||
| // L E F T C H A N N E L | |||
| @@ -100,6 +101,7 @@ void AudioEffectChorus::update(void) | |||
| block = receiveWritable(0); | |||
| if(block) { | |||
| bp = block->data; | |||
| uint32_t tmp = delay_length/(num_chorus - 1) - 1; | |||
| for(int i = 0;i < AUDIO_BLOCK_SAMPLES;i++) { | |||
| l_circ_idx++; | |||
| if(l_circ_idx >= delay_length) { | |||
| @@ -110,7 +112,7 @@ void AudioEffectChorus::update(void) | |||
| c_idx = l_circ_idx; | |||
| for(int k = 0; k < num_chorus; k++) { | |||
| 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) { | |||
| c_idx += delay_length; | |||
| } | |||
| @@ -157,6 +157,15 @@ static int16_t testmem[8000]; // testing only | |||
| #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) | |||
| { | |||
| uint32_t addr = memory_begin + offset; | |||
| @@ -77,7 +77,7 @@ if(0) { | |||
| // initial index | |||
| l_delay_rate_index = 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); | |||
| delay_offset_idx = delay_offset; | |||
| @@ -100,7 +100,7 @@ boolean AudioEffectFlange::voices(int delay_offset,int d_depth,float delay_rate) | |||
| 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; | |||
| // Allow the passthru code to go through | |||
| @@ -1053,6 +1053,13 @@ span.mainfunction {color: #993300; font-weight: bolder} | |||
| Disabling the audio library interrupt for too long may cause audible | |||
| dropouts or glitches. | |||
| </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 type="text/x-red" data-template-name="AudioPlaySdWav"> | |||
| <div class="form-row"> | |||
| @@ -32,20 +32,17 @@ | |||
| // compute (a - b) / c | |||
| // handling 32 bit interger overflow at every step | |||
| // 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 | |||
| // compute (a - b) / c ... handling 32 bit interger overflow without slow 64 bit math | |||
| @@ -34,7 +34,7 @@ | |||
| boolean AudioSynthToneSweep::play(float t_amp,int t_lo,int t_hi,float t_time) | |||
| { | |||
| double tone_tmp; | |||
| float tone_tmp; | |||
| if(0) { | |||
| Serial.print("AudioSynthToneSweep.begin(tone_amp = "); | |||
| @@ -52,8 +52,8 @@ if(0) { | |||
| if(t_amp > 1)return false; | |||
| if(t_lo < 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; | |||
| tone_lo = t_lo; | |||
| tone_hi = t_hi; | |||
| @@ -69,7 +69,7 @@ if(0) { | |||
| tone_sign = -1; | |||
| 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); | |||
| sweep_busy = 1; | |||
| return(true); | |||
| @@ -95,22 +95,23 @@ void AudioSynthToneSweep::update(void) | |||
| block = allocate(); | |||
| if(block) { | |||
| 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 | |||
| for(i = 0;i < AUDIO_BLOCK_SAMPLES;i++) { | |||
| *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; | |||
| if(tone_phase & 0x800000000000LL)tone_phase &= 0x7fffffffffffLL; | |||
| if(tone_sign > 0) { | |||
| if((tone_freq >> 32) > tone_hi) { | |||
| if(tmp > tone_hi) { | |||
| sweep_busy = 0; | |||
| break; | |||
| } | |||
| tone_freq += tone_incr; | |||
| } else { | |||
| if((tone_freq >> 32) < tone_hi) { | |||
| if(tmp < tone_hi) { | |||
| sweep_busy = 0; | |||
| break; | |||
| @@ -121,8 +122,7 @@ void AudioSynthToneSweep::update(void) | |||
| while(i < AUDIO_BLOCK_SAMPLES) { | |||
| *bp++ = 0; | |||
| i++; | |||
| } | |||
| //b_count++; | |||
| } | |||
| // send the samples to the left channel | |||
| transmit(block,0); | |||
| release(block); | |||
| @@ -338,7 +338,7 @@ static inline uint32_t get_q_psr(void) __attribute__((always_inline, unused)); | |||
| static inline uint32_t get_q_psr(void) | |||
| { | |||
| uint32_t out; | |||
| asm volatile("mrs %0, APSR" : "=r" (out)); | |||
| asm ("mrs %0, APSR" : "=r" (out)); | |||
| return (out & 0x8000000)>>27; | |||
| } | |||
| @@ -347,9 +347,8 @@ static inline void clr_q_psr(void) __attribute__((always_inline, unused)); | |||
| static inline void clr_q_psr(void) | |||
| { | |||
| 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 | |||