Skip to content

Instantly share code, notes, and snippets.

@benfarahmand
Last active July 5, 2021 21:11
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save benfarahmand/a38936d994d5f7360546bf186b76dacc to your computer and use it in GitHub Desktop.
Save benfarahmand/a38936d994d5f7360546bf186b76dacc to your computer and use it in GitHub Desktop.
This Gist will automatically find the start and end of words within an audio file (containing only words) and then will allow you to play just those words by clicking on the segment of the audio file containing the word
import ddf.minim.*; //
import ddf.minim.ugens.*;
// we must import this package to create an AudioFormat object
import javax.sound.sampled.*;
import java.io.*;
import java.util.*;
Minim minim;
AudioSample sound;
AudioFormat format;
FloatList sampleAverage;
FloatList startClips;
FloatList endClips;
int samples = 500;//16384/2;
float max = 0;
float threshold = 50;
float silenceThreshold = 25;
int rangeOfSampleForThresdhold = 5;
float[] leftSamples, rightSamples, samplesVal, movingAvg;
ArrayList<AudioSample> clips;
ArrayList<float[]> clipSamples;
String[] filenames;
int currentFile=0;//modify this number to pick up where you left off, otherwise, should =0
int fontSize = 21;
int span = 7; //the span of the moving average
void setup() {
size(1000, 300);
background(255);
minim = new Minim(this);
String path = sketchPath();
filenames = listFileNames(path+"/data/");
//remove non-mp3 files
int i = 0 ;
while ( i < filenames.length ) {
if (!filenames[i].contains("mp3")) filenames=removeTheElement(filenames, i);
else i++;
}
doAnalysis(filenames[currentFile]);
textAlign(LEFT, TOP);
textSize(fontSize);
noLoop();
}
void draw() {
background(255);
stroke(150);
fill(190);
strokeWeight(0.5);
for ( int i=0; i< sampleAverage.size(); i++) {
float x = map(i, 0, sampleAverage.size(), 0, width);
float y = map(sampleAverage.get(i), 0, max, 0, height);
line(x, height-y, x, height);
}
strokeWeight(0.7);
stroke(20);
line(0, height-threshold, width, height-threshold);
line(0, height-silenceThreshold, width, height-silenceThreshold);
strokeWeight(1);
stroke(0, 200, 0);
for (int i = 0; i < startClips.size(); i++) {
line(startClips.get(i), height, startClips.get(i), 0);
}
stroke(200, 0, 0);
for (int i = 0; i < endClips.size(); i++) {
line(endClips.get(i), height, endClips.get(i), 0);
}
noStroke();
fill(0);
for (int i = 0; i < startClips.size(); i++) {
if (mouseX > startClips.get(i) && mouseX < endClips.get(i)) {
rect(startClips.get(i), height*9/10, endClips.get(i)-startClips.get(i), height/10);
}
}
stroke(0);
strokeWeight(2);
for (int i = 1; i < movingAvg.length; i++) {
float x1 = map(i-1, 0, movingAvg.length, 0, width);
float y1 = map(movingAvg[i-1], 0, max, 0, height);
float x2 = map(i, 0, movingAvg.length, 0, width);
float y2 = map(movingAvg[i], 0, max, 0, height);
line(x1, height-y1, x2, height-y2);
}
noStroke();
fill(255);
rect(0, 0, textWidth(filenames[currentFile]), fontSize*1.2);
fill(0);
text(filenames[currentFile], 0, 0);
}
float[] getClipSamples(float[] samples, int i, FloatList start, FloatList end) {
//how to get the count of samples
//get the sample start and end mapped to the closest array location
float sampleStartClose = map(start.get(i), 0, width, 0, samples.length);
float sampleEndClose = map(end.get(i), 0, width, 0, samples.length);
//found the float to int
int sampleStart = round(sampleStartClose);
int sampleEnd = round(sampleEndClose);
float[] clip = new float[sampleEnd-sampleStart];
for (int j = 0; j < sampleEnd-sampleStart; j++) {
clip[j] = samples[j+sampleStart];
}
return clip;
}
void doAnalysis(String fileToLoad) {
sound = minim.loadSample(fileToLoad, 2048);
format = sound.getFormat();
leftSamples = sound.getChannel(AudioSample.LEFT);
rightSamples = sound.getChannel(AudioSample.RIGHT);
samplesVal = new float[rightSamples.length];
for (int i=0; i <rightSamples.length; i++) {
samplesVal[i] = leftSamples[i]+ rightSamples[i];
}
sampleAverage = new FloatList();
startClips = new FloatList();
endClips = new FloatList();
int average=0;
for (int i = 0; i< samplesVal.length; i+=1) {
average += abs(samplesVal[i])*1000 ; // sample are low value so *1000
if ( i % samples == 0 && i!=0) {
sampleAverage.append( average / samples);
if (( average / samples)>max) max=( average / samples);
average = 0;
}
}
//for ( int i=0; i< sampleAverage.size(); i++) {
// float x = map(i, 0, sampleAverage.size(), 0, width);
// float y = map(sampleAverage.get(i), 0, max, 0, height);
//}
movingAvg = calculateTheMovingAverage(sampleAverage);
FloatList movingA = new FloatList();
for(int i = 0 ; i < movingAvg.length ; i++){
movingA.append(movingAvg[i]);
}
for(int i = 0 ; i < movingA.size() ; i++){
float y = map(movingA.get(i), 0, max, 0, height);
findStartAndEndOfEachWord(movingA,y,i);
}
//need to remove duplicates from both start and end clips
for (int i = 0; i < startClips.size(); i++) {
float value = startClips.get(i);
int j = i+1;
while (j<startClips.size()) {
if (value == startClips.get(j)) startClips.remove(j);
else j++;
}
}
for (int i = 0; i < endClips.size(); i++) {
float value = endClips.get(i);
int j = i+1;
while (j<endClips.size()) {
if (value == endClips.get(j)) endClips.remove(j);
else j++;
}
}
clips = new ArrayList<AudioSample>();
clipSamples = new ArrayList<float[]>();
//find the start and end points from the samples file and
//then create a new samples variable, store it in there,
//then save the new sample variable
for (int i = 0; i < startClips.size() && i < endClips.size(); i ++) {
if (startClips.get(i)<endClips.get(i)) {
float[] clip = getClipSamples(samplesVal, i, startClips, endClips);
AudioSample clipSample = minim.createSample(clip, format);
clips.add(clipSample);
clipSamples.add(clip);
}
}
}
void findStartAndEndOfEachWord(FloatList list, float y, int i) {
if (y>threshold) {
startClips.append(findTheStart(list, i));
endClips.append(findTheEnd(list, i));
}
}
float findTheStart(FloatList mySamples, int i) {
float startTime = 0.0;
float average = 0.0; //take the average for the next and previous n# samples
int rangeOfSamples = rangeOfSampleForThresdhold;
for (int j = i; j > 0 && j < mySamples.size(); j--) {
average = 0.0;
for (int k = j; k > j-rangeOfSamples && k>0; k--) {
average+=map(sampleAverage.get(k), 0, max, 0, height);
}
for (int k = j; k < j+rangeOfSamples && k<mySamples.size(); k++) {
average+=map(sampleAverage.get(k), 0, max, 0, height);
}
average=average/(2*rangeOfSamples);
if (average < silenceThreshold) {
startTime = map(j, 0, sampleAverage.size(), 0, width);
break;
}
}
return startTime;
}
float findTheEnd(FloatList mySamples, int i) {
float endTime = 0.0;
float average = 0.0; //take the average for the next and previous n# samples
int rangeOfSamples = rangeOfSampleForThresdhold;
for (int j = i; j > 0 && j < mySamples.size(); j++) {
average = 0.0;
for (int k = j; k > j-rangeOfSamples && k>0; k--) {
average+=map(sampleAverage.get(k), 0, max, 0, height);
}
for (int k = j; k < j+rangeOfSamples && k<mySamples.size(); k++) {
average+=map(sampleAverage.get(k), 0, max, 0, height);
}
average=average/(2*rangeOfSamples);
if (average < silenceThreshold) {
endTime = map(j, 0, sampleAverage.size(), 0, width);
break;
}
}
return endTime;
}
float[] calculateTheMovingAverage(FloatList mySamples){
float[] movingAverage = new float[mySamples.size()];
for(int i = span ; i < mySamples.size()-span ; i++){
int avg = 0;
for(int j = i-span/2 ; j < i+span/2 ; j++){
avg += mySamples.get(j);
}
avg = avg/span;
movingAverage[i]=(avg);
}
return movingAverage;
}
void findTheMaximums(FloatList mySamples){
for(int i = 0 ; i < mySamples.size() ; i++){
}
}
void findTheMinimums(FloatList mySamples){
for(int i = 0 ; i < mySamples.size() ; i++){
}
}
// This function returns all the files in a directory as an array of Strings
String[] listFileNames(String dir) {
File file = new File(dir);
if (file.isDirectory()) {
String names[] = file.list();
return names;
} else {
// If it's not a directory
return null;
}
}
String[] removeTheElement(String[] arr,
int index)
{
// If the array is empty
// or the index is not in array range
// return the original array
if (arr == null
|| index < 0
|| index >= arr.length) {
return arr;
}
// Create another array of size one less
String[] anotherArray = new String[arr.length - 1];
// Copy the elements except the index
// from original array to the other array
for (int i = 0, k = 0; i < arr.length; i++) {
// if the index is
// the removal element index
if (i == index) {
continue;
}
// if the index is not
// the removal element index
anotherArray[k++] = arr[i];
}
// return the resultant array
return anotherArray;
}
void mousePressed() {
for (int i = 0; i < startClips.size(); i++) {
if (mouseX > startClips.get(i) && mouseX < endClips.get(i)) {
AudioSample clip = clips.get(i);
clip.trigger();
}
}
}
void keyPressed() {
if (key=='s') {
//solution for saving the clips to files: https://forum.processing.org/two/discussion/4339/how-to-save-a-wav-file-using-audiosystem-and-audioinputstream-of-javasound
//noLoop();
//now try to save the first audio clip
println("Starting Save of "+filenames[currentFile]);
String folderName = splitTokens(filenames[currentFile],".")[0];
File theDir = new File("/Users/ben/Documents/Processing/sound_file_p3/data/"+folderName+"/");
if (!theDir.exists()){
theDir.mkdirs();
}
String[] listOfFiles = new String[clips.size()];
for (int i = 0; i < clips.size(); i++) {
listOfFiles[i] = saveTheClipToFile(clipSamples.get(i), i, folderName);
print(round((i*100.0 / clips.size())) + "% ");
}
println("");
//create a list of file names in the folder
saveStrings(theDir+"/filenames.txt", listOfFiles);
println("Done Saving");
}
//load next file
if (key =='n') {
currentFile++;
if(currentFile==filenames.length) currentFile=filenames.length-1;
doAnalysis(filenames[currentFile]);
}
//load previous file
if (key =='p') {
currentFile--;
if(currentFile<0) currentFile=0;
doAnalysis(filenames[currentFile]);
}
}
String saveTheClipToFile(float[] a, int n, String folderName) {
byte[] pcm_data = new byte[2*a.length];
for (int i = 0; i < a.length; i++) {
// Convert float32 to 16 bit integer, values between -32768 and 32767 (since -1 < floatArray[i] < 1)
int aux = floor(32767 * a[i]);
// 16 bits per iteration, so I need to generate and fill 2 separate bytes per iterarion
// Convert first 8 bits (+ significant) of the 16 bit int to a byte
pcm_data[i*2] = byte(aux);
// Convert last 8 bits (- significant) of the 16 bit int to a byte
pcm_data[(i*2)+1] = (byte)((int) aux >> 8);
}
AudioFormat frmt = new AudioFormat(44100, 16, 1, true, false);
AudioInputStream ais = new AudioInputStream(
new ByteArrayInputStream(pcm_data), frmt,
pcm_data.length);// / format.getFrameSize()
try {
AudioSystem.write(ais, AudioFileFormat.Type.WAVE, new
File("/Users/ben/Documents/Processing/sound_file_p3/data/"+folderName+"/word"+n+".wav")
);
return "word"+n+".wav";
}
catch(Exception e) {
e.printStackTrace();
return "none";
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment