processing / processing-sound

Audio library for Processing built with JSyn
https://processing.org/reference/libraries/sound/
GNU Lesser General Public License v2.1
148 stars 50 forks source link

Implement recording samples from AudioIn (and other sound sources) #55

Open dhowe opened 4 years ago

dhowe commented 4 years ago

I'm trying to build a simple demo where you start recording with a keypress from AudioIn, then stop the recording, see the waveform of the recorded sound, and hear it playing. Seems like it should be trivial, but I don't find the needed methods... How to record a sample then play it? thanks

AudioIn input;
Amplitude loudness;

void setup() {
  size(100, 400);
  noStroke();
  input = new AudioIn(this, 0);
  loudness = new Amplitude(this);
}

void keyPressed() {
  if (keyCode == 32) {     // start record
    loudness.input(input);  
  }
  if (keyCode == 10) {     // stop record

    input.stop();   // How to convert input to AudioSample ?
  }
}

void draw() {
  float f = loudness.analyze() * 5;
  background(125, 255, 125);
  rect(0, height, 100, -height * f);
}
dhowe commented 4 years ago

Any thoughts?? This seems like it should be quite simple

@kevinstadler

kevinstadler commented 4 years ago

This can be achieved using the Waveform analyzer and then copying data from there to an AudioSample. The main issue is that, unless you add your own code to incrementally gather new sample data, it will be necessary to decide the length of the sample (or at least the maximum length) beforehand.

The basic structure would look like this (not tested):

AudioIn input;
Waveform waveform;
AudioSample sample;

// allocate a buffer for up to a 10 second sample
int maxSampleSize = 44100 * 10;

void setup() {
  size(100, 400);
  noStroke();
  input = new AudioIn(this, 0);
  waveform = new Waveform(this, maxSampleSize);
}

void keyPressed() {
  if (keyCode == 32) {     // start record
    waveform.input(input);
  }
  if (keyCode == 10) {     // stop record
    float[] samples = waveform.analyze();
    int startIndexOfRecordedSample = maxSampleSize - waveform.getLastAnalysisOffset();
    // if we recorded for >= 10 seconds this index will be 0, otherwise it will be later into the float[] array
    sample = new AudioSample(this, Arrays.copyOfRange(samples, startIndexOfRecordedSample, maxSampleSize));
  }
}
kevinstadler commented 1 year ago

Just looking into this again.

In the short term, DIY solutions can be made easier by exposing AudioIn's internal JSyn ChannelIn through a public method (documented only in the Javadoc, for advanced users).

In the long run it would be great to have dedicated user friendly methods to record not just from AudioIn, but potentially any sound source. Here's a rough idea for an interface that I cooked up, would be great to hear @dhowe's opinions on this if you're still working with this?

Assuming the user has already created a fixed length AudioSample (or SoundFile), they could record into that buffer using the following new methods:

RecordingSession is a new interface which provides a controller for the recording session. It has the following methods:

In the case of one-off fixed length recordings the user can just fire and forget without keeping the RecordingSession object, since the recording will finish automatically after duration. The nice thing about having an external interface/object for the recordings is that several recordings (even from different audio sources) can be written into the same AudioSample at the same time to create crazy mashups (and even while the AudioSample itself is being played back!

To allow for the creation of recordings of unknown/dynamic length (like described above in this issue), one could add a new class AudioRecording extends AudioSample implements RecordingSession which is returned by the following new AudioIn methods: