How to record and playback without saving to file.

edited January 2017 in Library Questions

Hi, everyone!

Beginners' question!

In Minim, is there an example on how to record microphone input to memory, (turn it into an AudioSample maybe?) then patch it later, (and repeatedly, in a loop) to output without saving to disk (i.e. without doing what the RecordAndPlayBack example does?

Many many many thanks! :-)

Answers

  • The procedure is rather complex, to say the least, but it is possible.

  • Hey! Thanks Lord_of_the_Galaxy!

    I'm brave and eager to learn ;-)

    I suppose it involves concatenating copies of the internal buffers of AudioInput to fill the internal buffers of an AudioSample??

    If you could point me to an example which is reasonably similar I might be able to work it out on my own :-)

    I've only come accross this thread (https://processing.org/discourse/beta/num_1200857589.html) which is a bit outdated, but maybe I can work from it?

    Thanks again!

  • edited January 2017

    Did you try that code? It probably will work.
    If you're worried about the delay, there's no solution using Minim only. You'll need to bypass the OS or something like that - and I don't know any solution.

  • edited January 2017

    Hi!

    It almost worked but it didn't really.

    I ended up coding my own. Basically I created a MemoryRecorder class which inherits from AudioListener and creates an AudioSample.

    This is it:

      class MemoryRecorder implements AudioListener
      {
           private AudioSample sample;
           FloatList left;
           FloatList right;
           private Minim min;
           boolean updated = false;
           boolean start = false;
    
        MemoryRecorder(Minim _min, int length)
        {
         min = _min;
         left = new FloatList(length);
         right = new FloatList(length);
        }
    
        void start()
        {
           start = true;
        }
    
        synchronized void samples(float[] samp)
        {
        //  println("mono");
          if (start) { 
            left.append(samp);
            right.append(samp);
          }
        }
    
        synchronized void samples(float[] sampL, float[] sampR)
        {
        //  println("stereo");
          if (start) {
            left.append(sampL);
            right.append(sampR);
          }
        }
    
        AudioSample getSample()
        {
          if (!updated) 
          {
            // when we create a sample we need to provide an AudioFormat so 
            // the sound will be played back correctly.
            AudioFormat format = new AudioFormat( 44100f, // sample rate
                                              16,    // sample size in bits
                                              2,     // channels
                                              true,  // signed
                                              true   // bigEndian
                                            );
    
            sample = min.createSample(left.array(), 
                                  right.array(),
                                  format,
                                  1024);
            updated = true;
          }
          return sample;
        }
      }
    

    You can add this class as listener to your AudioInput. Then you can call .getSample() to get an AudioSample which you can then .trigger() whenever you need to.

  • edited January 2017

    I don't get why you'd need FloatList when the only things you do w/ it is to receive a whole float[] array w/ append(), then spit it out again w/ array(). 8-}

    If the objective is simply forcing an array cloning, just invoke clone() over the array's reference: *-:)

    float[] left, right;
    
    synchronized void samples(float[] sampL, float[] sampR) {
      if (start) {
        left  = sampL.clone();
        right = sampR.clone();
      }
    }
    
  • edited January 2017

    Hi GoToLoop!

    Thanks for the comment.

    I use append() because I want to create AudioSamples that are much longer than one buffer length. In other words, I'd call array() after many many invocations of samples().

    Cheers!

  • edited January 2017 Answer ✓

    Oh, so you're concat() all the passed arrays, my bad! b-(
    Definitely a good use for FloatList::append(). ;;)
    Even though Processing provides concat() as an array util function:
    https://Processing.org/reference/concat_.html

    Just for completeness & curiosity's sake, here's how that would turn out for your subclass: :>

    import ddf.minim.AudioListener;
    import processing.core.PApplet;
    
    class MemoryRecorder implements AudioListener {
      float[] left = {}, right = {};
      boolean start;
    
      @Override
      synchronized public void samples(float[] sampM) {
        if (start) {
          left  = PApplet.concat(left, sampM);
          right = PApplet.concat(right, sampM);
        }
      }
    
      @Override
      synchronized public void samples(float[] sampL, float[] sampR) {
        if (start) {
          left  = PApplet.concat(left, sampL);
          right = PApplet.concat(right, sampR);
        }
      }
    }
    
  • Thanks GoToLoop! I didn't know that! ^:)^

  • Good job both of you, it seems like it would work (I can't test now).

Sign In or Register to comment.