Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 10 of 10

Thread: Help needed for the Granular effect

  1. #1
    Junior Member
    Join Date
    Feb 2019
    Posts
    7

    Help needed for the Granular effect

    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.

    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));
    }
    Attached Files Attached Files
    Last edited by Elekris; 02-10-2019 at 11:15 PM.

  2. #2
    Junior Member
    Join Date
    Feb 2019
    Posts
    7
    Also here's how the uncommented AudioConnections should look like:

    Click image for larger version. 

Name:	Download.png 
Views:	36 
Size:	31.0 KB 
ID:	15874

  3. #3
    Junior Member
    Join Date
    Feb 2019
    Posts
    7
    So now I also tested out the Granular effect example with a few songs that I played from a micro SD card and a potentiometer. The setSpeed() method has absolutely no effect. Does anyone have a clue?

  4. #4
    Senior Member
    Join Date
    Feb 2017
    Posts
    299
    The Granular effect is only a pass-through unless you've entered Freeze or PitchShift mode. Until then, the setSpeed() method (seemingly) does nothing. Did you activate one of these two modes?

  5. #5
    Junior Member
    Join Date
    Feb 2019
    Posts
    7
    Quote Originally Posted by gfvalvo View Post
    The Granular effect is only a pass-through unless you've entered Freeze or PitchShift mode. Until then, the setSpeed() method (seemingly) does nothing. Did you activate one of these two modes?
    Yes, I found that out by accident. Nevertheless thank you very much for your reply. I think I'll just write beginPitchShift(0) into my setup() or smth.

    Another minor question that I have: The volume() method of the AudioControlSGTL5000 object can only be used a single time, right? At least I can't use it more often.

  6. #6
    Senior Member
    Join Date
    Feb 2017
    Posts
    299
    Quote Originally Posted by Elekris View Post
    The volume() method of the AudioControlSGTL5000 object can only be used a single time, right?
    Took a quick look at the code, I see no evidence of that.

  7. #7
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    20,171
    Quote Originally Posted by Elekris View Post
    Another minor question that I have: The volume() method of the AudioControlSGTL5000 object can only be used a single time, right? At least I can't use it more often.
    What gave you this impression that the volume can't be changed?

    It can indeed be changed as often as you like, at least as fast as the relatively slow I2C protocol allows. If you look at the audio library tutorial, you'll see in part 1-5 (pages 6-7 in the PDF) there's an example where a knob is repeatedly read with analogRead() and the position used to adjust the volume.

    If seeing (and hearing) is believing, you can see me & Alysia demo that volume changing code in the video. Skip forward to 6:14 in the video for just the volume change while the audio library is playing a WAV file from the SD card.


  8. #8
    Junior Member
    Join Date
    Feb 2019
    Posts
    7
    It suddenly worked when I booted it up this morning. \_(ツ)_/

    Also I'm having issues with my breadboard (MB-102). I can't use a lot of pins because they just won't connect to it properly. Any tips?

  9. #9
    Junior Member
    Join Date
    Feb 2019
    Posts
    7
    @PaulStoffregen

    Thank you for your reply. I got that impression because it simply didn't work at the time I was writing my reply. Even the Serial Monitor showed the right value, which I passed to the volume() method.
    On the other hand, I've already used the volume() method multiple times in a sketch successfully, so I guess my response was premature.
    Having said this, I would take it with a grain of salt, since I'm still a beginner and probably not aware of a few things.

  10. #10
    Junior Member
    Join Date
    Feb 2019
    Posts
    7
    Ok (almost) everything works now. I resolved the "issue" with the breadboard.
    The only problem that is left is the samples glitching out whenever they overlap each other. I assume that is because of the Audioprocessor handling multiple granular effects at the same time?
    Last edited by Elekris; 02-12-2019 at 11:55 PM.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •