Skip to content

Commit 5d9ccca

Browse files
Merge pull request #855 from adafruit/philb-branch
Modulation config added. Not great, but just enough for Dalek voice.
2 parents 5301078 + aa8c55b commit 5d9ccca

4 files changed

Lines changed: 25 additions & 8 deletions

File tree

M4_Eyes/M4_Eyes.ino

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -420,12 +420,13 @@ void setup() {
420420

421421
#if defined(ADAFRUIT_MONSTER_M4SK_EXPRESS)
422422
if(voiceOn) {
423-
if(!voiceSetup(false)) {
423+
if(!voiceSetup((waveform > 0))) {
424424
Serial.println("Voice init fail, continuing without");
425425
voiceOn = false;
426426
} else {
427427
voiceGain(gain);
428428
currentPitch = voicePitch(currentPitch);
429+
if(waveform) voiceMod(modulate, waveform);
429430
digitalWrite(20, HIGH); // Speaker on
430431
}
431432
}
@@ -933,6 +934,7 @@ void loop() {
933934
}
934935
if(newlyPressed) {
935936
currentPitch = voicePitch(currentPitch);
937+
if(waveform) voiceMod(modulate, waveform);
936938
Serial.print("Voice pitch: ");
937939
Serial.println(currentPitch);
938940
}

M4_Eyes/file.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,15 @@ void loadConfig(char *filename) {
377377
if(v.is<bool>()) voiceOn = v.as<bool>();
378378
currentPitch = defaultPitch = doc["pitch"] | defaultPitch;
379379
gain = doc["gain"] | gain;
380+
modulate = doc["modulate"] | modulate;
381+
v = doc["waveform"];
382+
if(v.is<char*>()) { // If string...
383+
if(!strncasecmp( v, "sq", 2)) waveform = 1;
384+
else if(!strncasecmp(v, "si", 2)) waveform = 2;
385+
else if(!strncasecmp(v, "t" , 1)) waveform = 3;
386+
else if(!strncasecmp(v, "sa", 2)) waveform = 4;
387+
else waveform = 0;
388+
}
380389
#endif // ADAFRUIT_MONSTER_M4SK_EXPRESS
381390
}
382391
file.close();

M4_Eyes/globals.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,8 @@ GLOBAL_VAR bool voiceOn GLOBAL_INIT(false);
8989
GLOBAL_VAR float currentPitch GLOBAL_INIT(1.0);
9090
GLOBAL_VAR float defaultPitch GLOBAL_INIT(1.0);
9191
GLOBAL_VAR float gain GLOBAL_INIT(1.0);
92+
GLOBAL_VAR uint8_t waveform GLOBAL_INIT(0);
93+
GLOBAL_VAR uint32_t modulate GLOBAL_INIT(30); // Dalek pitch
9294
#endif
9395

9496
// EYE-RELATED STRUCTURES --------------------------------------------------

M4_Eyes/pdmvoice.cpp

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ static uint16_t dcOffsetNext = 32768; // between these two values
7676

7777
static uint16_t micGain = 256; // 1:1
7878

79-
#define MOD_MIN 40 // Lowest supported modulation pitch (lower = more RAM use)
79+
#define MOD_MIN 20 // Lowest supported modulation frequency (lower = more RAM use)
8080
static uint8_t modWave = 0; // Modulation wave type (none, sine, square, tri, saw)
8181
static uint8_t *modBuf = NULL; // Modulation waveform buffer
8282
static uint32_t modIndex = 0; // Current position in modBuf
@@ -217,20 +217,21 @@ void voiceGain(float g) {
217217

218218
// SET MODULATION ----------------------------------------------------------
219219

220-
// This is a work in progress and NOT FUNCTIONAL YET
220+
// This needs to be called after any call to voicePitch() -- the modulation
221+
// table is not currently auto-regenerated. Maybe that'll change.
221222

222223
void voiceMod(uint32_t freq, uint8_t waveform) {
223224
if(modBuf) { // Ignore if no modulation buffer allocated
224225
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
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
227228
modLen = (int)(playbackRate / freq + 0.5);
228229
if(modLen < 2) modLen = 2;
229230
if(waveform > 4) waveform = 4;
230231
modWave = waveform;
231-
switch(modWave) {
232+
yield();
233+
switch(waveform) {
232234
case 0: // None
233-
memset(modBuf, 255, modLen);
234235
break;
235236
case 1: // Square
236237
memset(modBuf, 255, modLen / 2);
@@ -255,7 +256,6 @@ void voiceMod(uint32_t freq, uint8_t waveform) {
255256
}
256257
}
257258

258-
259259
// INTERRUPT HANDLERS ------------------------------------------------------
260260

261261
static 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 };
@@ -401,6 +401,10 @@ void PDM_SERCOM_HANDLER(void) {
401401
void TIMER_IRQ_HANDLER(void) {
402402
TIMER->COUNT16.INTFLAG.reg = TC_INTFLAG_OVF;
403403

404+
// Modulation is done on the output (rather than the input) because
405+
// pitch-shifting modulated input would cause weird waveform
406+
// discontinuities. This does require recalculating the modulation table
407+
// any time the pitch changes though.
404408
if(modWave) {
405409
nextOut = (((int32_t)nextOut - 2048) * (modBuf[modIndex] + 1) / 256) + 2048;
406410
if(++modIndex >= modLen) modIndex = 0;

0 commit comments

Comments
 (0)