Synth not capturing all key presses

Status
Not open for further replies.

mattbott

Member
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 :)

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();
}
 
Maybe I2C isn't fast enough?

Is there a way for me to change the clock speed of I2C? Will that mess with the Audio board at all?
 
Reading each pin separately with digitalRead() has a huge amount of overhead. You should use readGPIOAB() to read them all at once.
 
I replaced the read logic with readGPIOAB(), took some time to figure out bitwise comparisons and wooo!!! It works great now!!

Thanks a lot! =D
 
Status
Not open for further replies.
Back
Top