HOWTO: Manipulate an AudioSample in Minim 2.0

Kyle says, “Like what if I want to load up a sound and remove certain portions that fit some criteria or rearrange it (i.e., non-real-time processing)?”

What you want to do is currently possible to a certain degree using AudioSample. It now has a method called getChannel(int), which comes from the BufferedAudio interface. This method returns in a float array the actual samples being used by the object when you trigger it. You can then manipulate those values and hear the change when you trigger it.

Example (view online)

import ddf.minim.*;
 
Minim minim;
AudioSample jingle;
 
void setup()
{
  size(512, 200, P3D);
 
  minim = new Minim(this);
 
  jingle = minim.loadSample("jingle.mp3", 2048);
  // get the left channel of the audio as a float array
  // getChannel is defined in the interface BuffereAudio, 
  // which also defines two constants to use as an argument
  // BufferedAudio.LEFT and BufferedAudio.RIGHT
  float[] leftChannel = jingle.getChannel(BufferedAudio.LEFT);
  // now we are just going to reverse the left channel
  float[] reversed = reverse(leftChannel);
  arraycopy(reversed, 0, leftChannel, 0, leftChannel.length);
}
 
void draw()
{
  background(0);
  stroke(255);
  for(int i = 0; i < jingle.bufferSize() - 1; i++)
  {
    line(i, 50 - jingle.left.get(i)*50, i+1, 50 - jingle.left.get(i+1)*50);
    line(i, 150 - jingle.right.get(i)*50, i+1, 150 - jingle.right.get(i+1)*50);
  }
}
 
void keyPressed()
{
  jingle.trigger();
}
 
void stop()
{
  // always close Minim audio classes when you finish with them
  jingle.close();
  minim.stop();
 
  super.stop();
}
  1. Kyle’s avatar

    Excellent — this is very helpful. In my case, this should be perfect; but if I wanted to increase the length of the float[] during processing would there be any way to get it all back into the AudioSample? Obviously the arraycopy() would crash.

    Also, I was concerned about length constraints, but I think you should run into memory problems first. Java arrays can be 2^27 elements long, or about 50 minutes at 44.1kHz.

  2. ddf’s avatar

    Yes, the one limitation here is that you can’t make the array longer. You can effectively make it shorter by having a bunch of zeros at the end of the existing array, but that will be problematic if you are trying to loop it. I will probably add the ability to set new sample arrays for existing AudioSamples, there’s no particular reason not to.

  3. Kyle’s avatar

    I just noticed that if I want to save this audio to disk, I need to fake out Minim into thinking I’m doing it in real time. The best solution I have right now involves: extending AudioSample with a class that has a method dump(), which will just call samples() with all the BufferedAudio samples on all the AudioListeners attached to it. Is there a better way?

    It’d be really great if AudioSample had a constructor that just allowed you to pass a float[] to it, and then a method that allowed you to save the AudioSample.

  4. Kyle’s avatar

    The above idea doesn’t work since splitter in AudioSource is private rather than protected. So I made this really, really terrible hack. I added this to AudioSource:

    public void nrtDump(float[] inputSamples) {
    for(int i = 0; i < inputSamples.length; i += bufferSize()) {
    float[] cur = new float[bufferSize()];
    int remaining = inputSamples.length – i;
    int curLength = Math.min(remaining, bufferSize());
    System.arraycopy(inputSamples, i, cur, 0, curLength);
    splitter.samples(cur);
    }
    }

    And then in my main code I wrote this:

    audio = minim.loadSample(“audiofile.wav”, 512);
    float[] leftChannel = audio.getChannel(BufferedAudio.LEFT);
    float[] reversed = reverse(leftChannel);
    AudioRecorder recorder = minim.createRecorder(audio, “data/output.wav”, true);
    recorder.beginRecord();
    audio.nrtDump(reversed);
    recorder.endRecord();
    recorder.save();

    With that said, something like: minim.createSample(float[] samples); (returning an AudioSample) and minim.saveSample(AudioSample sample); would be amazing.

  5. Kyle’s avatar

    I implemented the above, adding:

    minim.createSample(float[] samples, int sampleRate);
    minim.createSample(float[] samples, int bufferSize, int sampleRate);
    minim.saveSample(AudioSample sample, String filename);

    Source is here: http://www.rpi.edu/~mcdonk/random/minim-2.02.zip

  6. scape’s avatar

    i’m looking to extend what kyle did into the original source, without recompiling it; is it possible to do so? I need a way to push an array of float samples to a channel for playback, and it looks like it’s on a todo list from ddf, but I’m not sure.