I've been working on a synth on an off for a few months now and I've finally gotten to the point where my salvaged keyboard is hooked up to the teensy (with the help of a pair of MCP23017s, Adafruit's library, a lot of readying about diode matrices, and of course, this forum).
Unfortunately, I don't seem to be reading all keystrokes. When playing fast (say two side by side keys being alternated quickly) I'll get some of the notes, but not all.
Below I've posted an "abridged" version of my code that produces the same results. In this code, there's only one voice (monophonic) and one waveform object. In my actual code, there are 10 voices, each with two waveform objects and an FM object from the audio library.
Can anyone see anythingthing that I may have overlooked in the code causing a delay when polling the keys, hence missing those quick notes? Or perhaps my salvaged keyboard isn't in tip top shape (I don't really have a way to test this).
On the hardware side, I have the ROWs of the diode matrix of the keyboard going into one MCP, and the COLUMN going into the other MCP, one reading and one writing. I2C clock and data are connected to pin 20 and 21.
Please excuse my semi-unorganized code
Unfortunately, I don't seem to be reading all keystrokes. When playing fast (say two side by side keys being alternated quickly) I'll get some of the notes, but not all.
Below I've posted an "abridged" version of my code that produces the same results. In this code, there's only one voice (monophonic) and one waveform object. In my actual code, there are 10 voices, each with two waveform objects and an FM object from the audio library.
Can anyone see anythingthing that I may have overlooked in the code causing a delay when polling the keys, hence missing those quick notes? Or perhaps my salvaged keyboard isn't in tip top shape (I don't really have a way to test this).
On the hardware side, I have the ROWs of the diode matrix of the keyboard going into one MCP, and the COLUMN going into the other MCP, one reading and one writing. I2C clock and data are connected to pin 20 and 21.
Please excuse my semi-unorganized code
Code:
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>
#include <Bounce.h>
#include <Encoder.h>
#include <Adafruit_MCP23017.h>
// GUItool: begin automatically generated code
AudioSynthWaveform waveform1; //xy=389,247
AudioSynthWaveformDc dc1; //xy=397,295
AudioEffectMultiply multiply1; //xy=551,283
AudioMixer4 mixer1; //xy=723,294
AudioOutputI2S i2s1; //xy=878,293
AudioConnection patchCord1(waveform1, 0, multiply1, 0);
AudioConnection patchCord2(dc1, 0, multiply1, 1);
AudioConnection patchCord3(multiply1, 0, mixer1, 0);
AudioConnection patchCord4(mixer1, 0, i2s1, 0);
AudioConnection patchCord5(mixer1, 0, i2s1, 1);
AudioControlSGTL5000 sgtl5000_1;
// GUItool: end automatically generated code
// convert midi note to frequency and detune it by a specified amount (in cents).
float midi2freq (int midi, int detune){
float val = 440.0 * powf(2.0, (float)(((float)midi- (float)69 - ((float)detune/(float)100)) * 0.08333333));
return (val);
}
class voice{
public:
AudioSynthWaveform *waveA; // Create a class named Voice which will store the wave and dc
AudioSynthWaveformDc *dc;
int midi; // Other variables: Midi note, to change note when needed, what key
int keyAssigned; // (or button) this voice is assigned to: a bool to know when the
bool wavePlaying; // voice is playing, the millis timestamp of when the note stated playing
unsigned long mils;
static int releaseDelay;
static int attackDelay; // These are the same for all voice objects. Release and attack
static int waveTa; // delay for the fake envelope and the type of waveform type
voice(){} // Constructer.
void playNote(int midi){
// Whrn called, simultaneously release the note, set the waves to the freq and waveTypes then...
AudioNoInterrupts();
dc->amplitude(0, releaseDelay);
waveA->begin(1, midi2freq(midi, 0), waveTa);
AudioInterrupts();
// Attack! ... I mean, play the note
dc->amplitude(1, attackDelay);
wavePlaying = true;
mils = millis();
return;
}
void stopNote(){
// release note
dc->amplitude(0, releaseDelay);
wavePlaying = false;
return;
}
bool isOn(){
// Check to see if the note is currently playing
return (wavePlaying);
}
unsigned long timer(){
// Return timestamp of when the note was played
return (mils);
}
};
// Defaults for the variables that can be controlled by the encoder
int voice::releaseDelay = 20;
int voice::attackDelay = 20;
int voice::waveTa = WAVEFORM_SAWTOOTH;
// keyboard matrix. Rows 0, 1, 2, 6, 7 & 8 are for triggering the note
// Rows 3, 4, 5, 9, 10 & 11 are for calculating velocity
// (velocity to come at a later point in time.)
// these are the midi numbers that correspond to the keys in the physical matrix
int keymatrix[12][11] = {
{ 0, 41, 47, 53, 71, 59, 65, 77, 83, 89, 95},
{ 0, 39, 45, 51, 69, 57, 63, 75, 81, 87, 93},
{ 0, 37, 43, 49, 67, 55, 61, 73, 79, 85, 91},
{ 0, 41, 47, 53, 71, 59, 65, 77, 83, 89, 95},
{ 0, 39, 45, 51, 69, 57, 63, 75, 81, 87, 93},
{ 0, 37, 43, 49, 67, 55, 61, 73, 79, 85, 91},
{ 0, 38, 44, 50, 68, 56, 62, 74, 80, 86, 92},
{ 0, 40, 46, 52, 70, 58, 64, 76, 82, 88, 94},
{36, 42, 48, 54, 72, 60, 66, 78, 84, 90, 96},
{ 0, 38, 44, 50, 68, 56, 62, 74, 80, 86, 92},
{ 0, 40, 46, 52, 70, 58, 64, 76, 82, 88, 94},
{36, 42, 48, 54, 72, 60, 66, 78, 84, 90, 96}
};
// Variable to keep track if a key is being held already or not
bool keyHeld[12][11] = {false};
int NUM_OF_VOICES = 1; // for simplicity for this t
// Setup MCP chips
Adafruit_MCP23017 mcpRead;
Adafruit_MCP23017 mcpWrite;
// initialize array of voices
voice voices;
// play the note and serial print for debugging
void playTheNote(int i, int j){
if (j == 0 || j == 1 || j == 2 || j == 6 || j == 7 || j == 8){
Serial.print("KeyMatrix[");
Serial.print(j);
Serial.print("][");
Serial.print(i);
Serial.println("]");
Serial.println("Play Midi Note: " + (String)keymatrix[j][i]);
voices.playNote(keymatrix[j][i]);
voices.keyAssigned = keymatrix[j][i];
Serial.print("Memory usage max: ");
Serial.println(AudioMemoryUsageMax());
}
}
void readKeys(){
for(int i = 0; i < 11; i++){
mcpWrite.digitalWrite(i, LOW);
for(int j = 0; j < 12; j++){
if(!mcpRead.digitalRead(j)){
// Check to see if it was a new press
if(!keyHeld[j][i]){
playTheNote(i, j);
keyHeld[j][i] = true;
}
// if the key is already being held, do nothing ... no else statement here
} else {
// that means this key is not being held, stop the note
keyHeld[j][i] = false;
// Find the voice that was assigned to that key and stop it!
for( int k = 0; k < NUM_OF_VOICES; k++){
if(voices.keyAssigned == keymatrix[j][i]){
voices.stopNote();
}
}
}
}
mcpWrite.digitalWrite(i, HIGH);
}
}
void setup(){
Serial.begin(9600); // Chart for line out levels in Vols peak to peak
AudioMemory(32); // 14: 2.98 | 20: 2.14 | 26: 1.53
sgtl5000_1.enable(); // 15: 2.83 | 21: 2.02 | 27: 1.44
sgtl5000_1.volume(0.90); // 16: 2.67 | 22: 1.91 | 28: 1.37
sgtl5000_1.lineOutLevel(29); // 17: 2.53 | 23: 1.80 | 29: 1.29
// 18: 2.39 | 24: 1.71 | 30: 1.22
// 19: 2.26 | 25: 1.62 | 31: 1.16
// assign the software waveTypes and DC waves to their
// appropriate voice aryau
voices.waveA = &waveform1;
voices.dc = &dc1;
mixer1.gain(0, 1);
mcpRead.begin(0); // Connected to Black / Blue "Rows" - These are read
mcpWrite.begin(1); // Connected to Green "Columns" These are put high
// Set the 16 I/O pins on the MCPs to output and input for MCP1 and 2, respectively
for(int i = 0; i < 16; i++){
mcpWrite.pinMode(i, OUTPUT);
mcpWrite.digitalWrite(i, LOW);
mcpRead.pinMode(i, INPUT);
mcpRead.pullUp(i, HIGH);
}
}
void loop() {
readKeys();
}