@@ -76,6 +76,12 @@ static uint16_t dcOffsetNext = 32768; // between these two values
7676
7777static uint16_t micGain = 256 ; // 1:1
7878
79+ #define MOD_MIN 40 // Lowest supported modulation pitch (lower = more RAM use)
80+ static uint8_t modWave = 0 ; // Modulation wave type (none, sine, square, tri, saw)
81+ static uint8_t *modBuf = NULL ; // Modulation waveform buffer
82+ static uint32_t modIndex = 0 ; // Current position in modBuf
83+ static uint32_t modLen = 0 ; // Currently used amount of modBuf based on modFreq
84+
7985// Just playing back directly from the recording circular buffer produces
8086// audible clicks as the waveforms rarely align at the beginning and end of
8187// the buffer. So what we do is advance or push back the playback index a
@@ -100,13 +106,20 @@ float voicePitch(float p);
100106
101107// START PITCH SHIFT (no arguments) ----------------------------------------
102108
103- bool voiceSetup (void ) {
109+ bool voiceSetup (bool modEnable ) {
104110
105111 // Allocate circular buffer for audio
106112 if (NULL == (recBuf = (uint16_t *)malloc (recBufSize * sizeof (uint16_t )))) {
107113 return false ; // Fail
108114 }
109115
116+ // Allocate buffer for voice modulation, if enabled
117+ if (modEnable) {
118+ // 250 comes from min period in voicePitch()
119+ modBuf = (uint8_t *)malloc ((int )(48000000.0 / 250.0 / MOD_MIN + 0.5 ));
120+ // If malloc fails, program will continue without modulation
121+ }
122+
110123 // Set up PDM microphone input -------------------------------------------
111124
112125 PDM_SPI.begin ();
@@ -202,6 +215,47 @@ void voiceGain(float g) {
202215 else micGain = (uint16_t )(g * 256.0 + 0.5 );
203216}
204217
218+ // SET MODULATION ----------------------------------------------------------
219+
220+ // This is a work in progress and NOT FUNCTIONAL YET
221+
222+ void voiceMod (uint32_t freq, uint8_t waveform) {
223+ if (modBuf) { // Ignore if no modulation buffer allocated
224+ if (freq < MOD_MIN) freq = MOD_MIN;
225+ uint16_t period = TIMER->COUNT16 .CC [0 ].reg + 1 ;
226+ float playbackRate = 48000000.0 / (float )period; // samples/sec
227+ modLen = (int )(playbackRate / freq + 0.5 );
228+ if (modLen < 2 ) modLen = 2 ;
229+ if (waveform > 4 ) waveform = 4 ;
230+ modWave = waveform;
231+ switch (modWave) {
232+ case 0 : // None
233+ memset (modBuf, 255 , modLen);
234+ break ;
235+ case 1 : // Square
236+ memset (modBuf, 255 , modLen / 2 );
237+ memset (&modBuf[modLen / 2 ], 0 , modLen - modLen / 2 );
238+ break ;
239+ case 2 : // Sine
240+ for (int i=0 ; i<modLen; i++) {
241+ modBuf[i] = (int )((sin (M_PI * 2.0 * (float )i / (float )modLen) + 1.0 ) * 0.5 * 255.0 + 0.5 );
242+ }
243+ break ;
244+ case 3 : // Triangle
245+ for (int i=0 ; i<modLen; i++) {
246+ modBuf[i] = (int )(fabs (0.5 - (float )i / (float )modLen) * 2.0 * 255.0 + 0.5 );
247+ }
248+ break ;
249+ case 4 : // Sawtooth (increasing)
250+ for (int i=0 ; i<modLen; i++) {
251+ modBuf[i] = (int )((float )i / (float )(modLen - 1 ) * 255.0 + 0.5 );
252+ }
253+ break ;
254+ }
255+ }
256+ }
257+
258+
205259// INTERRUPT HANDLERS ------------------------------------------------------
206260
207261static uint16_t const sincfilter[64 ] = { 0 , 2 , 9 , 21 , 39 , 63 , 94 , 132 , 179 , 236 , 302 , 379 , 467 , 565 , 674 , 792 , 920 , 1055 , 1196 , 1341 , 1487 , 1633 , 1776 , 1913 , 2042 , 2159 , 2263 , 2352 , 2422 , 2474 , 2506 , 2516 , 2506 , 2474 , 2422 , 2352 , 2263 , 2159 , 2042 , 1913 , 1776 , 1633 , 1487 , 1341 , 1196 , 1055 , 920 , 792 , 674 , 565 , 467 , 379 , 302 , 236 , 179 , 132 , 94 , 63 , 39 , 21 , 9 , 2 , 0 , 0 };
@@ -347,6 +401,11 @@ void PDM_SERCOM_HANDLER(void) {
347401void TIMER_IRQ_HANDLER (void ) {
348402 TIMER->COUNT16 .INTFLAG .reg = TC_INTFLAG_OVF;
349403
404+ if (modWave) {
405+ nextOut = (((int32_t )nextOut - 2048 ) * (modBuf[modIndex] + 1 ) / 256 ) + 2048 ;
406+ if (++modIndex >= modLen) modIndex = 0 ;
407+ }
408+
350409 // Do analog writes pronto so output timing is consistent
351410 analogWrite (A0, nextOut);
352411 analogWrite (A1, nextOut);
0 commit comments