I'm making an electronic drumset with piezo sensors and now I want to change the pitch of the instruments (kick, hi-hat, snare) using rotary encoders.
Everything works just fine. I have no compiler errors and my serial monitor shows the values I want to see. But (almost) nothing happens when I use the setSpeed() or beginPitchShift() methods of my granular effect objects.
I'm using the Teensy 3.5 with Audioshield. Sorry if my code is hard/annoying to read. This is my very first physical computing project and I'm also relatively new to programming as a whole.
I'll also include the whole project as a .zip file, if anyone wants to recreate it.
Everything works just fine. I have no compiler errors and my serial monitor shows the values I want to see. But (almost) nothing happens when I use the setSpeed() or beginPitchShift() methods of my granular effect objects.
I'm using the Teensy 3.5 with Audioshield. Sorry if my code is hard/annoying to read. This is my very first physical computing project and I'm also relatively new to programming as a whole.
I'll also include the whole project as a .zip file, if anyone wants to recreate it.
Code:
#include <Encoder.h>
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>
#include "AudioSampleKick.h" // INDEX = 0
#include "AudioSampleHat.h" // INDEX = 1
#include "AudioSampleSnare.h" // INDEX = 2
AudioPlayMemory sounds[4]; // This and other arrays have a length of 4, because initially 4 instruments were planned
AudioEffectGranular granulars[4]; // Granular Effect for every sound
AudioMixer4 mix;
AudioOutputI2S speakers;
AudioConnection c1(sounds[0], granulars[0]); // Sounds connect to their respective effect
AudioConnection c2(sounds[1], granulars[1]);
AudioConnection c3(sounds[2], granulars[2]);
AudioConnection c4(granulars[0], 0, mix, 0); // After the effects, the sounds get sent into the mixer
AudioConnection c5(granulars[1], 0, mix, 1);
AudioConnection c6(granulars[2], 0, mix, 2);
AudioConnection c7(mix, 0, speakers, 0); // Mixer output goes into both stereo channels
AudioConnection c8(mix, 0, speakers, 1);
//AudioConnection c1(sounds[0], 0, mix, 0); An unsuccessful attempt to solve the problem: "Maybe the effect needs to be applied after the mixer just
//AudioConnection c2(sounds[1], 0, mix, 1); like in the example?"
//AudioConnection c3(sounds[2], 0, mix, 2);
//AudioConnection c4(mix, granulars[2]);
//AudioConnection c5(granulars[2], 0, speakers, 0);
//AudioConnection c6(granulars[2], 0, speakers, 1);
AudioControlSGTL5000 audioShield;
#define GRAN_MEM_SIZE 12800 // This is pretty much copied from the example. I have no idea how many integers I should use...
int16_t granMems[GRAN_MEM_SIZE][3];
Encoder volEnc(26, 27); // Encoder which controls the sgtl5000 volume, see onVolChange()
int vol = 0;
float defaultVol = 0.5;
float gainMult[4] = {0.02, 0.025, 0.025}; // Volume multiplicator for every sound.
Encoder pitchEnc[3] = {Encoder(34, 35), Encoder(29, 28), Encoder(38, 39)}; // Pitch Encoders
int pitchVal[3] = {0, 0, 0};
int count = 3; // Actual count of the instruments. Used for for loops.
int pins[4] = {32, 31, 33, 34}; // Pins used for the instruments. Last one is unused.
int tolerances[4] = {4, 5, 5, 4}; // Tolerance value for each piezo sensor. If a value is surpassed, a sound gets played.
int values[4]; // Piezo sensor values stored each loop
int defaults[4]; // For some reason the default value of my sensors is not always 0. That's why they are taken into account.
int durations[4] = {20000, 20000, 20000, 20000}; // Microsecond durations in which the peak of the piezo sensor gets recorded.
// The sound gets played when the duration is over.
int recordTimer[4]; // Used together with the durations
int recordHigh[4]; // Peak of the piezo sensor
bool isRecording[4] = {false, false, false, false}; // Boolean for each sensor. Recording can only start if the boolean is false.
void setup() {
Serial.begin(4800);
AudioMemory(100);
audioShield.enable();
audioShield.volume(defaultVol);
for(int i = 0; i < count; i++){
granulars[i].begin(granMems[i], GRAN_MEM_SIZE);
pinMode(pins[i], INPUT);
defaults[i] = analogRead(pins[i]);
Serial.println("Piezo " + String(i) + " default value is " + String(defaults[i]));
}
}
void loop() {
if(vol != volEnc.read()) onVolChange();
for(int i = 0; i < count; i++){
int newPitch = round((float)pitchEnc[i].read() / 8.0);
if(newPitch != pitchVal[i]) onPitchChange(i, newPitch);
values[i] = analogRead(pins[i]);
if(!isRecording[i] && values[i] > defaults[i] + tolerances[i]){
isRecording[i] = true;
recordHigh[i] = 0;
recordTimer[i] = micros();
}
if(isRecording[i]){
if(values[i] > recordHigh[i]){
recordHigh[i] = values[i];
}
if(micros() >= recordTimer[i] + durations[i]){
isRecording[i] = false;
mix.gain(i, ((float)recordHigh[i] * gainMult[i]));
switch(i){
case 0:
sounds[i].play(AudioSampleKick);
break;
case 1:
sounds[i].play(AudioSampleHat);
break;
case 2:
sounds[i].play(AudioSampleSnare);
break;
}
Serial.println( "Channel: " + String(i) + " Peak: " + String(recordHigh[i]) + " Volume: " + String(((float)recordHigh[i] * gainMult[i])) + " Length: "
+ String(sounds[i].lengthMillis()));
}
}
}
}
void onVolChange(){
if(volEnc.read() > 50) volEnc.write(50);
if(volEnc.read() < -50) volEnc.write(-50);
vol = volEnc.read();
float newVol = (defaultVol + ((float)vol / 100.0));
audioShield.volume(newVol);
Serial.println(newVol);
}
void onPitchChange(int enc, int newPitch){
pitchVal[enc] = newPitch;
pitchEnc[enc].write(newPitch * 8);
float newSpeed = pow(2.0, ((float)newPitch / 12.0));
granulars[enc].setSpeed(newSpeed);
//granulars[enc].beginPitchShift(newSpeed * 100.0); // I tried setSpeed() first, because I already knew the calculation for the ratio parameter.
Serial.println("Encoder=" + String(enc) + " Value=" + String(pitchEnc[enc].read()) + " Pitch=" + String(newPitch) + " Speed=" + String(newSpeed));
}
Attachments
Last edited: