33
44#if defined(ADAFRUIT_MONSTER_M4SK_EXPRESS)
55
6+ #include " globals.h"
67#include < SPI.h>
78
89#define MIN_PITCH_HZ 65
910#define MAX_PITCH_HZ 1600
1011#define TYP_PITCH_HZ 175
1112
12- // Playback timer stuff - use TC3 on MONSTER M4SK (no TC4 on this board)
13- #define TIMER TC3
14- #define TIMER_IRQN TC3_IRQn
15- #define TIMER_IRQ_HANDLER TC3_Handler
16- #define TIMER_GCLK_ID TC3_GCLK_ID
17- #define TIMER_GCM_ID GCM_TC2_TC3
13+ static void voiceOutCallback (void );
14+ static float actualPlaybackRate;
1815
1916// PDM mic allows 1.0 to 3.25 MHz max clock (2.4 typical).
2017// SPI native max is is 24 MHz, so available speeds are 12, 6, 3 MHz.
@@ -150,38 +147,8 @@ bool voiceSetup(bool modEnable) {
150147
151148 analogWriteResolution (12 );
152149
153- // Feed TIMER off GCLK1 (already set to 48 MHz by Arduino core)
154- GCLK->PCHCTRL [TIMER_GCLK_ID].bit .CHEN = 0 ; // Disable channel
155- while (GCLK->PCHCTRL [TIMER_GCLK_ID].bit .CHEN ); // Wait for disable
156- GCLK_PCHCTRL_Type pchctrl;
157- pchctrl.bit .GEN = GCLK_PCHCTRL_GEN_GCLK1_Val;
158- pchctrl.bit .CHEN = 1 ;
159- GCLK->PCHCTRL [TIMER_GCLK_ID].reg = pchctrl.reg ;
160- while (!GCLK->PCHCTRL [TIMER_GCLK_ID].bit .CHEN ); // Wait for enable
161-
162- // Disable timer before configuring it
163- TIMER->COUNT16 .CTRLA .bit .ENABLE = 0 ;
164- while (TIMER->COUNT16 .SYNCBUSY .bit .ENABLE );
165-
166- // 16-bit counter mode, 1:1 prescale, match-frequency generation mode
167- TIMER->COUNT16 .CTRLA .bit .MODE = TC_CTRLA_MODE_COUNT16;
168- TIMER->COUNT16 .CTRLA .bit .PRESCALER = TC_CTRLA_PRESCALER_DIV1_Val;
169- TIMER->COUNT16 .WAVE .bit .WAVEGEN = TC_WAVE_WAVEGEN_MFRQ_Val;
170-
171- TIMER->COUNT16 .CTRLBCLR .reg = TC_CTRLBCLR_DIR; // Count up
172- while (TIMER->COUNT16 .SYNCBUSY .bit .CTRLB );
173-
174150 voicePitch (1.0 ); // Set timer interval
175151
176- TIMER->COUNT16 .INTENSET .reg = TC_INTENSET_OVF; // Overflow interrupt
177- NVIC_DisableIRQ (TIMER_IRQN);
178- NVIC_ClearPendingIRQ (TIMER_IRQN);
179- NVIC_SetPriority (TIMER_IRQN, 0 ); // Top priority
180- NVIC_EnableIRQ (TIMER_IRQN);
181-
182- TIMER->COUNT16 .CTRLA .bit .ENABLE = 1 ; // Enable timer
183- while (TIMER->COUNT16 .SYNCBUSY .bit .ENABLE ); // Wait for it
184-
185152 return true ; // Success
186153}
187154
@@ -196,12 +163,13 @@ bool voiceSetup(bool modEnable) {
196163// adjustment (after appying constraints) will be returned.
197164float voicePitch (float p) {
198165 float desiredPlaybackRate = sampleRate * p;
199- int32_t period = (int32_t )(48000000.0 / desiredPlaybackRate + 0.5 );
200- if (period > 2500 ) period = 2500 ; // Hard limit is 65536, 2.5K is a practical limit
201- else if (period < 250 ) period = 250 ; // Leave some cycles for IRQ handler
202- TIMER->COUNT16 .CC [0 ].reg = period - 1 ;
203- while (TIMER->COUNT16 .SYNCBUSY .bit .CC0 );
204- float actualPlaybackRate = 48000000.0 / (float )period;
166+ // Clip to sensible range
167+ if (desiredPlaybackRate < 19200 ) desiredPlaybackRate = 19200 ; // ~0.41X
168+ else if (desiredPlaybackRate > 192000 ) desiredPlaybackRate = 192000 ; // ~4.1X
169+ arcada.timerCallback (desiredPlaybackRate, voiceOutCallback);
170+ // Making this assumption here knowing Arcada will use 1:1 prescale:
171+ int32_t period = (int32_t )(48000000.0 / desiredPlaybackRate);
172+ actualPlaybackRate = 48000000.0 / (float )period;
205173 p = (actualPlaybackRate / sampleRate); // New pitch
206174 jumpThreshold = (int )(jump * p + 0.5 );
207175 return p;
@@ -223,10 +191,8 @@ void voiceGain(float g) {
223191void voiceMod (uint32_t freq, uint8_t waveform) {
224192 if (modBuf) { // Ignore if no modulation buffer allocated
225193 if (freq < MOD_MIN) freq = MOD_MIN;
226- uint16_t period = TIMER->COUNT16 .CC [0 ].reg + 1 ; // Audio out timer ticks
227- float playbackRate = 48000000.0 / (float )period; // Audio out samples/sec
228- modLen = (int )(playbackRate / freq + 0.5 );
229- if (modLen < 2 ) modLen = 2 ;
194+ modLen = (int )(actualPlaybackRate / freq + 0.5 );
195+ if (modLen < 2 ) modLen = 2 ;
230196 if (waveform > 4 ) waveform = 4 ;
231197 modWave = waveform;
232198 yield ();
@@ -397,9 +363,7 @@ void PDM_SERCOM_HANDLER(void) {
397363 evenWord ^= 1 ;
398364}
399365
400- // Playback timer interrupt
401- void TIMER_IRQ_HANDLER (void ) {
402- TIMER->COUNT16 .INTFLAG .reg = TC_INTFLAG_OVF;
366+ static void voiceOutCallback (void ) {
403367
404368 // Modulation is done on the output (rather than the input) because
405369 // pitch-shifting modulated input would cause weird waveform
@@ -443,7 +407,7 @@ void TIMER_IRQ_HANDLER(void) {
443407 } else { // Slowed down
444408 // Playback may underflow recording, need to advance periodically
445409 int16_t dist = (playbackIndex >= recIndex) ?
446- (playbackIndex - recIndex) : (recBufSize - (recIndex - playbackIndex));
410+ (playbackIndex - recIndex) : (recBufSize - 1 - (recIndex - playbackIndex));
447411 if (dist <= jumpThreshold) {
448412 playbackIndexJumped = (playbackIndex + jump) % recBufSize;
449413 jumping = true ;
0 commit comments