Audio Scrubbing via Arduino

edited June 2015 in Arduino

Hello. Based on the example file -minim/Scrubbing, I want to control the play, scrub forward and backward with inputs from Arduino. So far, I am able to use a momentary switch to play the audio (when pressed, the audio plays and when released it stops). But I am not able to control scrub forward or backward. As you see, I am trying to use analogRead to control the rewind. Processing is recognising the serial data from Arduino board. I am using StandardFirmata.

(If I can get the Arduino inputs to work, I will not need to have the graphic buttons.)

Any assistance is much appreciated. Thank you very much.

The example has two tabs - Scrubbing and Button. Below I copy the two tabs. First Scrubbing tab, which I have tried to alter. I have not changed the coding of the second tab, Button.

/* First Scrubbing tab */

    /**
     * This is a relatively simple file player that lets you scrub forward and backward in an audio file.<br />
     * It should be noted that it's not *exactly* scrubbing because the playback speed is not changed,
     * it's simply that the position in the song is changed by very small increments when fast-forwarding or rewinding.
     * But the end result is convincing enough.
     * <p>
     * The positioning code is inside of the Play, Rewind, and Forward classes, which are in button.pde.
     * <p>
     * For more information about Minim and additional features, 
     * visit http://code.compartmental.net/minim/
     */

    import processing.serial.*;  //new
    import cc.arduino.*;  //new
    import ddf.minim.*;

    Minim minim;
    AudioPlayer song;
    Play play;
    Rewind rewind;
    Forward ffwd;

    Arduino arduino;  //new


    void setup()
    {
      size(512, 200, P3D);
      minim = new Minim(this);
      // load a file from the data folder, use a sample buffer of 1024 samples
      song = minim.loadFile("fair1939.wav", 512);
      // buttons for control
      play = new Play(width/2 - 50, 130, 20, 10);
      rewind = new Rewind(width/2, 130, 20, 10);
      ffwd = new Forward(width/2 + 50, 130, 20, 10);

      arduino = new Arduino(this, Arduino.list()[5], 57600);
      arduino.pinMode(2, Arduino.INPUT);
      arduino.pinMode(7, Arduino.INPUT);
    }

    void draw()
    {
      background(0);
      // draw the wave form
      // this wav is MONO, so we only need the left channel, 
      // though we could have used the right channel and gotten the same values
      stroke(255);
      for (int i = 0; i < song.bufferSize () - 1; i++)
      {
        line(i, 50 - song.left.get(i)*50, i+1, 50 - song.left.get(i+1)*10);
      }
      // draw the position in the song
      // the position is in milliseconds,
      // to get a meaningful graphic, we need to map the value to the range [0, width]
      float x = map(song.position(), 0, song.length(), 0, width);
      stroke(255, 0, 0);
      line(x, 50 - 20, x, 50 + 20);
      // do the controls
      play.update();
      play.draw();
      rewind.update();
      rewind.draw();
      ffwd.update(); 
      ffwd.draw();

      if (arduino.digitalRead(2) == Arduino.HIGH) {

        song.play();
      } else {
        song.pause();
      }

      if (arduino.analogRead(0)>400) {
        //rewind.mousePressed();
        print("hello");
      }
    }

    void mousePressed()
    {
      play.mousePressed();
      rewind.mousePressed();
      ffwd.mousePressed();
    }

    void mouseReleased()
    {
      play.mouseReleased();
      rewind.mouseReleased();
      ffwd.mouseReleased();
    }

/* Second Button tab (has not been altered from the example)*/

    abstract class Button
    {
      int x, y, hw, hh;

      Button(int x, int y, int hw, int hh)
      {
        this.x = x;
        this.y = y;
        this.hw = hw;
        this.hh = hh;
      }

      boolean pressed()
      {
        return mouseX > x - hw && mouseX < x + hw && mouseY > y - hh && mouseY < y + hh;
      }

      abstract void mousePressed();
      abstract void mouseReleased();
      abstract void update();
      abstract void draw();
    }

    class Play extends Button
    {
      boolean play;
      boolean invert;

      Play(int x, int y, int hw, int hh) 
      { 
        super(x, y, hw, hh); 
        play = true;
      }

      // code to handle playing and pausing the file
      void mousePressed()
      {
        if ( pressed() )
        {
          invert = true;
          if ( song.isPlaying() ) 
          {
            song.pause();
            play = true;
          }
          else
          {
            song.loop();
            play = false;
          }
        }
      }

      void mouseReleased()
      {
        invert = false;
      }

      // play is a boolean value used to determine what to draw on the button
      void update()
      {
        if ( song.isPlaying() ) play = false;
        else play = true;
      }

      void draw()
      {
        if ( invert )
        {
          fill(255);
          stroke(0);
        }
        else
        {
          noFill();
          stroke(255);
        }
        rect(x - hw, y - hh, hw*2, hh*2);
        if ( invert )
        {
          fill(0);
          stroke(255);
        }
        else
        {
          fill(255);
          noStroke();
        }
        if ( play )
        {
          triangle(x - hw/3, y - hh/2, x - hw/3, y + hh/2, x + hw/2, y);
        }
        else
        {
          rect(x - hw/3, y - hh/2, hw/4, hh);
          rect(x + hw/8, y - hh/2, hw/4, hh);
        }
      }
    }

    class Rewind extends Button
    {
      boolean invert;
      boolean pressed;

      Rewind(int x, int y, int hw, int hh)
      {
        super(x, y, hw, hh);
        invert = false;
      }

      // code used to scrub backward in the file
      void update()
      {
        // if the rewind button is currently being pressed
        if (pressed)
        {
          // get the current song position
          int pos = song.position();
          // if it greater than 200 milliseconds
          if ( pos > 200 )
          {
            // rewind the song by 200 milliseconds
            song.skip(-200);
          }
          else
          {
            // if the song hasn't played more than 100 milliseconds
            // just rewind to the beginning
            song.rewind();
          }
        }
      }

      void mousePressed()
      {
        pressed = pressed();
        if ( pressed ) 
        {
          invert = true;
          // if the song isn't currently playing, rewind it to the beginning
          if ( !song.isPlaying() ) song.rewind();      
        }
      }

      void mouseReleased()
      {
        pressed = false;
        invert = false;
      }

      void draw()
      {
        if ( invert )
        {
          fill(255);
          stroke(0);
        }
        else
        {
          noFill();
          stroke(255);
        }
        rect(x - hw, y - hh, hw*2, hh*2);
        if ( invert )
        {
          fill(0);
          stroke(255);
        }
        else
        {
          fill(255);
          noStroke();
        }
        triangle(x - hw/2, y, x, y - hh/2, x, y + hh/2);
        triangle(x, y, x + hw/2, y - hh/2, x + hw/2, y + hh/2);    
      }  
    }

    class Forward extends Button
    {
      boolean invert;
      boolean pressed;

      Forward(int x, int y, int hw, int hh)
      {
        super(x, y, hw, hh);
        invert = false;
      }

      void update()
      {
        // if the forward button is currently being pressed
        if (pressed)
        {
          // get the current position of the song
          int pos = song.position();
          // if the song's position is more than 40 milliseconds from the end of the song
          if ( pos < song.length() - 40 )
          {
            // forward the song by 40 milliseconds
            song.skip(40);
          }
          else
          {
            // otherwise, cue the song at the end of the song
            song.cue( song.length() );
          }
          // start the song playing
          song.play();
        }
      }

      void mousePressed()
      {
        pressed = pressed();
        if ( pressed ) 
        {
          invert = true;      
        }
      }

      void mouseReleased()
      {
        pressed = false;
        invert = false;
      }

      void draw()
      {
        if ( invert )
        {
          fill(255);
          stroke(0);
        }
        else
        {
          noFill();
          stroke(255);
        }
        rect(x - hw, y - hh, hw*2, hh*2);
        if ( invert )
        {
          fill(0);
          stroke(255);
        }
        else
        {
          fill(255);
          noStroke();
        }
        triangle(x, y, x - hw/2, y - hh/2, x - hw/2, y + hh/2);
        triangle(x, y - hh/2, x, y + hh/2, x + hw/2, y);    
      }  
    }

Answers

  • You'll get a better response if you format your code. Here's how:

    http://forum.processing.org/two/discussion/8045/how-to-format-code-and-text

  • Thank you very much for the suggestion. I wasn't aware of the feature - first time posting. I reformatted the code!

  • edited June 2015

    Hi, the problem is that when Arduino (2) is low, continuously sends "0" (song.pause).. Try this, Arduino (2) one push start, one push stop ..

      if (arduino.digitalRead(2) == Arduino.HIGH && state==0) {  
        state = 1;
      } 
      if (arduino.digitalRead(2) == Arduino.LOW && state==1) {
       // song.pause();
        state = 2;
      }
      if (arduino.digitalRead(2) == Arduino.HIGH && state==2) {   
        state = 3;
      } 
      if (arduino.digitalRead(2) == Arduino.LOW && state==3) {
        song.pause();
        state =0;
      }
      if (state==2) {
        song.play();
        if (pos > 18000) {
          song.rewind();
        }
      } 
    ////////////////rewind
      if (arduino.digitalRead(3) == Arduino.HIGH ) { 
        song.skip(-200);
      }
     //////////////forward
      if (arduino.digitalRead(4) == Arduino.HIGH ) { 
        song.skip(40);
        song.play();
      }
    
  • Hello. Thank you for your suggestion.

    I replace the following part of the first tab with yours, but did not work. I did declare both state and pos as int in void draw() because it gave me error. Also, am I suppose to keep the second tab, Button? I have a feeling I don't need to...?

    Please let me know if I followed your direction.

      if (arduino.digitalRead(2) == Arduino.HIGH) {
        song.play();
      } else {
        song.pause();
      }
    
      if (arduino.analogRead(0)>400) {
        //rewind.mousePressed();
        print("hello");
      }
    
  • sorry..., pos need to start over from the beginning the song

      declare   
     int state = 0;
     int pos = 0;
     //and put in the draw()
     pos = song.position();
    

    the problem is, else song.pause. try..

     if (arduino.digitalRead(2) == Arduino.HIGH && state==0) {  
        state = 1;
      } 
      if (arduino.digitalRead(2) == Arduino.LOW && state==1) {
       // song.pause();
        state = 2;
      }
      if (arduino.digitalRead(2) == Arduino.HIGH && state==2) {   
        state = 3;
      } 
      if (arduino.digitalRead(2) == Arduino.LOW && state==3) {
        song.pause();
        state =0;
      }
      if (state==2) {
        song.play();
        if (pos > 18000) {
          song.rewind();
        }
      }
     if (arduino.analogRead(0)>400) {
        song.skip(-200);
      }
    
  • edited June 2015

    perhaps I understand .. :D

     if (arduino.digitalRead(3) == Arduino.HIGH && state==0) {  
        state = 1;
      } 
      if (arduino.digitalRead(3) == Arduino.LOW && state==1) {
        song.pause();
        state = 0;
      }
       if (state==1) {
        song.play(); 
      }
    if (arduino.analogRead(0)>400) {
       song.skip(-200);
     } 
    
  • Thank you so much, camperos!!!

    I took your suggestions and here is what I have at the moment. Please see the sketch below.

    When a momentary button[digital 2] is pressed, it starts playing the audio file. While this file is play, I am able to scrub forward with Analog 0[A0] and backward with Analog 1[A1]! This is very close to what I am looking for!!

    What is neat is that without playing the audio file, I can fast forward or rewind by pressing either A0 or A1. And when I press Digital 2, the audio begins to play at that point.

    Something I need to figure out is that I am not able to pause the file with Digital 2 pin.

    I would like to understand what the int pos; and pos = song.position(); were supposed to do. Currently, they are commented out.

    /*** This is a relatively simple file player that lets you scrub forward and backward in an audio file.<br />
     * It should be noted that it's not *exactly* scrubbing because the playback speed is not changed,
     * it's simply that the position in the song is changed by very small increments when fast-forwarding or rewinding.
     * But the end result is convincing enough.
     * <p>
     * The positioning code is inside of the Play, Rewind, and Forward classes, which are in button.pde.
     * <p>
     * For more information about Minim and additional features, 
     * visit <a href="http://code.compartmental.net/minim/" target="_blank" rel="nofollow">http://code.compartmental.net/minim/</a>;
     */
    
    import processing.serial.*;  //new
    import cc.arduino.*;  //new
    import ddf.minim.*;
    
    Minim minim;
    AudioPlayer song;
    Arduino arduino;  //new
    
    void setup()
    {
      size(512, 200, P3D);
    
      minim = new Minim(this);
      // load a file from the data folder, use a sample buffer of 1024 samples
      song = minim.loadFile("fair1939.wav", 512);
      // buttons for control
      //play = new Play(width/2 - 50, 130, 20, 10);
      //rewind = new Rewind(width/2, 130, 20, 10);
      //ffwd = new Forward(width/2 + 50, 130, 20, 10);
    
      arduino = new Arduino(this, Arduino.list()[5], 57600);
      arduino.pinMode(2, Arduino.INPUT);
    }
    
    void draw()
    {
      int state = 0;
      //int pos = 0;
      //pos = song.position();
    
      background(0);
      // draw the wave form
      // this wav is MONO, so we only need the left channel, 
      // though we could have used the right channel and gotten the same values
      stroke(255);
      for (int i = 0; i < song.bufferSize () - 1; i++)
      {
        line(i, 50 - song.left.get(i)*50, i+1, 50 - song.left.get(i+1)*10);
      }
      // draw the position in the song
      // the position is in milliseconds,
      // to get a meaningful graphic, we need to map the value to the range [0, width]
      float x = map(song.position(), 0, song.length(), 0, width);
      stroke(255, 0, 0);
      line(x, 50 - 20, x, 50 + 20);
    
    
      if (arduino.digitalRead(2) == Arduino.HIGH && state==0) {  
        state = 1;
      } 
      if (arduino.digitalRead(2) == Arduino.LOW && state==1) {
        song.pause();
        state = 0;
      }
      if (state==1) {
        song.play();
      }
    
      if (arduino.analogRead(1)>400) {
        song.skip(40);
      }
      if (arduino.analogRead(0)>400) {
        song.skip(-200);
      }
    }
    
  • song.pause, if state ==0

     if (arduino.digitalRead(2) == Arduino.HIGH && state==0) {  
        state = 1;
      } 
      if (arduino.digitalRead(2) == Arduino.LOW && state==1) {
         state = 0;
      }
      if (state==1) {
        song.play();
      }
       if (state==0) {
        song.pause();
      }
    
  • or

      if (arduino.digitalRead(2) == Arduino.HIGH && state==0) {  
        state = 1;
      } 
      if (arduino.digitalRead(2) == Arduino.LOW && state==1) {
         state = 0;
      }
      if (state==1) {
        song.play();
      }
       else if (state==0) {
        song.pause();
      }
       if (arduino.analogRead(1)>400) {
       song.play();
       song.skip(40);
      }
      if (arduino.analogRead(0)>400) {
        song.play();
        song.skip(-200);
      }
    
  • Hi, Cameros. Thank you very much for your extensive help. I will be following up this thread once I make a bit more progress. I am trying to loop the audio infinitely. somehow song.loop is not working...

Sign In or Register to comment.