Skip to content

Waveform #22

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Mar 15, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions examples/Analysis/AudioWaveform/AudioWaveform.pde
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/**
* This sketch shows how to use the Waveform class to analyze a stream
* of sound. Change the number of samples to get a longer/shorter part of the waveform.
*/

import processing.sound.*;

// Declare the sound source and Waveform analyzer variables
SoundFile sample;
Waveform waveform;

// Define how many samples of the Waveform you want to use (must be a power of two)
int samples = 128;

public void setup() {
size(640, 360);
background(255);

// Load and play a soundfile and loop it.
sample = new SoundFile(this, "beat.aiff");
sample.loop();

// Create the Waveform analyzer and connect the playing soundfile to it.
waveform = new Waveform(this, samples);
waveform.input(sample);
}

public void draw() {
// Set background color, noFill and stroke style
background(0);
stroke(255);
strokeWeight(2);
noFill();

// Perform the analysis
waveform.analyze();

beginShape();
for(int i = 0; i < samples; i++){
// Draw current data of the waveform
// Each sample in the data array is between -1 and +1
vertex(
map(i, 0, samples, 0, width),
map(waveform.data[i], -1, 1, 0, height)
);
}
endShape();
}
Binary file added examples/Analysis/AudioWaveform/data/beat.aiff
Binary file not shown.
32 changes: 32 additions & 0 deletions src/processing/sound/JSynWaveform.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package processing.sound;

import java.util.Arrays;

import com.jsyn.data.FloatSample;
import com.jsyn.unitgen.FixedRateMonoWriter;

/**
* This class copies all input to an audio buffer of the given size and returns it.
*
* @author icalvin102
*/
class JSynWaveform extends FixedRateMonoWriter {

private FloatSample buffer;

protected JSynWaveform(int bufferSize) {
super();
this.buffer = new FloatSample(bufferSize);

// write any connected input into the output buffer ad infinitum
this.dataQueue.queueLoop(this.buffer);
}

protected void calculateWaveform(float[] target) {
// get position currently being written to
int pos = (int) this.dataQueue.getFrameCount() % this.buffer.getNumFrames();
for (int i = 0; i < this.buffer.getNumFrames(); i++) {
target[i] = (float)(2*this.buffer.readDouble((pos + i) % this.buffer.getNumFrames()));
}
}
}
105 changes: 105 additions & 0 deletions src/processing/sound/Waveform.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package processing.sound;

import com.jsyn.ports.UnitOutputPort;

import processing.core.PApplet;

/**
* This is a Waveform analyzer. It returns the waveform of the
* of an audio stream the moment it is queried with the analyze()
* method.
*
* @author icalvin102
*
* @webref sound
**/
public class Waveform extends Analyzer {

public float[] data;

private JSynWaveform waveform;

public PApplet app;

public Waveform(PApplet parent) {
this(parent, 512);
}

/**
* @param parent
* typically use "this"
* @param samples
* number of waveform samples as an integer (default 512).
* This parameter needs to be a power of 2 (e.g. 16, 32, 64, 128,
* ...).
* @webref sound
*/
public Waveform(PApplet parent, int samples) {
super(parent);
app = parent;
if (samples < 0 || Integer.bitCount(samples) != 1) {
// TODO throw RuntimeException?
Engine.printError("number of waveform frames needs to be a power of 2");
} else {
// FFT buffer size is twice the number of frequency bands
this.waveform = new JSynWaveform(samples);
this.data = new float[samples];
}
}

protected void removeInput() {
this.waveform.input.disconnectAll();
this.input = null;
}

protected void setInput(UnitOutputPort input) {
// superclass makes sure that input unit is actually playing, just connect it
Engine.getEngine().add(this.waveform);
this.waveform.input.connect(input);
this.waveform.start();
}

/**
* Gets the content of the current audiobuffer from the input source, writes it
* into this Waveform's `data` array, and returns it.
*
* @return the current audiobuffer of the input source. The array has as
* many elements as this Waveform analyzer's number of samples
*/
public float[] analyze() {
return this.analyze(this.data);
}

/**
* Gets the content of the current audiobuffer from the input source.
*
* @param value
* an array with as many elements as this Waveform analyzer's number of
* samples
* @return the current audiobuffer of the input source. The array has as
* many elements as this Waveform analyzer's number of samples
* @webref sound
**/
public float[] analyze(float[] value) {
if (this.input == null) {
Engine.printWarning("this Waveform has no sound source connected to it, nothing to analyze");
}
this.waveform.calculateWaveform(value);
return value;
}

// Below are just duplicated methods from superclasses which are required
// for the online reference to build the corresponding pages.

/**
* Define the audio input for the analyzer.
*
* @param input
* the input sound source. Can be an oscillator, noise generator,
* SoundFile or AudioIn.
* @webref sound
**/
public void input(SoundObject input) {
super.input(input);
}
}