Hi -
I’d like to render a small audio file (.wav) into an SVG represention of its amplitude.
Is this possible? Is Processing the best tool for the job or is there something else that would be better for this purpose?
Thanks!
Do you have code to turn a .wav file into an image? Once you’ve got the image then use this reference to convert to SVG: https://processing.org/reference/libraries/svg/index.html
I don’t have code that can do that. I’m unclear how one would do it in Processing.
There are resources (tutorials, references, examples, etc.) here:
To get you started:
Examples that come with Processing Development Environment:
:)
- You can get started here: https://processing.org/reference/libraries/sound/Amplitude.html .
- Add your sound file with code that looks like this:
sample = new SoundFile(this, "xxxxx.wav");
sample.loop();
// Create an Input stream which is routed into the Amplitude analyzer
amp = new Amplitude(this);
amp.input(sample);
- In draw() plot the amplitude*200 with vertical lines.
- Save image of screen.
- Convert image to svg.
note that loading SVG can get slow if you have many lines (which is not a problem on Processing canvas). You should start with a short sound sample.
Hi there -
Thank you for the ideas - but I’m not sure this is exactly what I’m after. I think the “Sound” library is mainly for creating a real-time representation of sound, as it is playing - and I’m hoping to render a short audio file. I also think the vertical lines might not be an accurate representation of the waveform. I’m hoping to convert a waveform directly into a vector file.
First you need to extract data from the wav file.
I came across this:
And got this working with Processing:
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.AudioInputStream;
void setup()
{
File file = new File(sketchPath() + "/data/" + "loonwail.wav");
AudioInputStream ais = null;
try {
ais = AudioSystem.getAudioInputStream(file);
int frameLength = (int) ais.getFrameLength();
int frameSize = (int) ais.getFormat().getFrameSize();
byte[] eightBitByteArray = new byte[frameLength * frameSize];
int result = ais.read(eightBitByteArray);
int channels = ais.getFormat().getChannels();
int[][] samples = new int[channels][frameLength];
int sampleIndex = 0;
for (int t = 0; t < eightBitByteArray.length; ) {
for (int channel = 0; channel < channels; channel++) {
int low = (int) eightBitByteArray[t];
t++;
int high = (int) eightBitByteArray[t];
t++;
int sample = getSixteenBitSample(high, low);
println(hex(sample));
samples[channel][sampleIndex] = sample;
}
sampleIndex++;
}
}
catch (Exception exp) {
exp.printStackTrace();
}
finally {
try {
ais.close();
}
catch (Exception e) {
}
}
}
int getSixteenBitSample(int high, int low)
{
return (high << 8) + (low & 0x00ff);
}
The above may not be a solution but was a first step for me exploring this.
Have fun!
:)
There are online converters that you can use to visualize the waveform of an audio file. You might want to use them instead of using the Sound library.
The output from the amplitude demo is a series of y values that you can use to draw a curve if that’s what you’d rather have.
Where’s the output? This is what I see on a mac:
Hello @bdon,
That code was a dead end but a worthwhile exploration for me and will keep exploring. Not any time soon though.
A solution to extract meaningful WAV file data is still needed.
I have written code to convert data to an SVG (used some sample data from sine waves) using the Processing library and the SVG vectors will represent the data.
The Processing SVG library only saves an SVG from the last frame so I stored data in an ArrayList and generated the shape and an SVG in a single frame; one vector for each data point.
I will revisit this topic when I have more time.
:)
Hello @bdon,
This reference was very helpful:
I modified it and was able to plot data:
//https://sangorrin.blogspot.com/2014/03/reading-wav-file-in-java.html
import java.io.*;
int x, y, xlast, ylast;
void setup()
{
size(1000, 200);
int bytes, cursor, unsigned;
try {
FileInputStream s = new FileInputStream(sketchPath() + "/data/" + "loonwail.wav");
BufferedInputStream b = new BufferedInputStream(s);
byte[] data = new byte[width];
b.skip(44);
cursor = 0;
//while ((bytes = b.read(data)) > 0)
while ((bytes = b.read(data)) > 0 && cursor <width)
{
// do something
for (int i=0; i<bytes; i++)
{
xlast = x;
ylast = y;
unsigned = data[i] & 0xFF; // Java..
System.out.println(cursor + " " + unsigned);
cursor++;
strokeWeight(2);
x = 5*i;
y = 5*(unsigned-128)+height/2;
//point(5*i, y);
line(x, y, xlast, ylast);
}
}
b.read(data);
b.close();
}
catch(Exception e)
{
e.printStackTrace();
}
}
void draw()
{
}
Output:
Add a few lines of code from here and you can generate an SVG:
:)
On my mac I have no output from this demo with a direct copy/paste; the only thing I changed is the filename to another file with the .wav extension (inside a ‘data’ folder).
Addendum:
The code does run with an '.mp3’ file if I increase the window size and reverse the sign for the y value, ie change from: y = 5*(unsigned-128)+height/2;
to: y = -5*(unsigned-128);
Observation:
unsigned is always zero with println(). Maybe real value is a float that’s always less than 1 and it gets rounded to zero?
Output(macos):
Is your posted output upside down?
And the exploring continues… turns out it was not a dead end!
It works with a 16-bit WAV file (code to generate SVG can be added):
// Code adapted by glv from:
// https://stackoverflow.com/questions/27517265/graphing-wav-file-in-java
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.AudioInputStream;
void setup()
{
size(700, 400);
background(32);
// Download it once and then comment these lines!
//byte[] data = loadBytes("https://github.com/processing/processing-sound/raw/main/examples/Soundfile/StereoSample/data/stereo441hzoutofphase.wav");
//saveBytes(sketchPath() + "/data/" + "stereo441hzoutofphase.wav", data);
File file = new File(sketchPath() + "/data/" + "stereo441hzoutofphase.wav");
AudioInputStream ais = null;
try {
ais = AudioSystem.getAudioInputStream(file);
int frameLength = (int) ais.getFrameLength();
int frameSize = (int) ais.getFormat().getFrameSize();
println(frameLength);
println(frameSize);
println(frameLength * frameSize);
byte[] eightBitByteArray = new byte[frameLength * frameSize];
int result = ais.read(eightBitByteArray);
println(result);
int channels = ais.getFormat().getChannels();
//int[][] samples = new int[channels][frameLength];
float max=0, min=0;
float y0, y1;
strokeWeight(2);
stroke(128);
line(0, height/4, width, height/4);
line(0, 3*height/4, width, 3*height/4);
//int sampleIndex = 0;
//for (int t = 0; t < eightBitByteArray.length; )
for (int t = 0; t < 1324; )
{
for (int channel = 0; channel < channels; channel++)
{
int low = (int) eightBitByteArray[t];
t++;
int high = (int) eightBitByteArray[t];
t++;
int sample = getSixteenBitSample(high, low);
int vol = 50;
strokeWeight(2);
if (channel == 0)
{
y0 = sample/4095.0*vol + height/4;
stroke(255, 0, 0);
point (t/2, y0);
}
else if (channel == 1)
{
y1 = sample/4095.0*vol + 3*height/4;
stroke(255, 255, 0);
point (t/2, y1);
}
if (sample > max) max = sample;
if (sample < min) min = sample;
// It is 16-bit signed data!
// Only for debug:
//println(channel, sample);
//samples[channel][sampleIndex] = sample;
}
//sampleIndex++;
}
println(min, max);
}
catch (Exception exp)
{
exp.printStackTrace();
}
finally
{
try
{
ais.close();
}
catch (Exception e)
{
}
}
}
int getSixteenBitSample(int high, int low)
{
return (high << 8) + (low & 0x00ff);
}
:)