Browse Source

Initial commit

master
Phr00t 4 years ago committed by GitHub
parent
commit
8820fab8af
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      lib/export.txt
  2. BIN
      lib/jl1.0.1.jar
  3. BIN
      lib/jopt-simple-5.0.4.jar
  4. BIN
      lib/jsminim.jar
  5. BIN
      lib/jsoup-1.11.2.jar
  6. BIN
      lib/mp3spi1.9.5.jar
  7. BIN
      lib/tritonus_aos.jar
  8. BIN
      lib/tritonus_share.jar
  9. BIN
      lib/trove-3.0.3.jar
  10. 388
      src/autostepper/AutoStepper.java
  11. 49
      src/autostepper/GoogleImageSearch.java
  12. 133
      src/autostepper/SMGenerator.java
  13. 269
      src/autostepper/StepGenerator.java
  14. 47
      src/ddf/minim/analysis/BartlettHannWindow.java
  15. 47
      src/ddf/minim/analysis/BartlettWindow.java
  16. 678
      src/ddf/minim/analysis/BeatDetect.java
  17. 65
      src/ddf/minim/analysis/BlackmanWindow.java
  18. 46
      src/ddf/minim/analysis/CosineWindow.java
  19. 151
      src/ddf/minim/analysis/DFT.java
  20. 404
      src/ddf/minim/analysis/FFT.java
  21. 940
      src/ddf/minim/analysis/FourierTransform.java
  22. 67
      src/ddf/minim/analysis/GaussWindow.java
  23. 48
      src/ddf/minim/analysis/HammingWindow.java
  24. 46
      src/ddf/minim/analysis/HannWindow.java
  25. 48
      src/ddf/minim/analysis/LanczosWindow.java
  26. 48
      src/ddf/minim/analysis/RectangularWindow.java
  27. 48
      src/ddf/minim/analysis/TriangularWindow.java
  28. 122
      src/ddf/minim/analysis/WindowFunction.java
  29. 76
      src/ddf/minim/analysis/package.html
  30. 81
      src/ddf/minim/effects/BandPass.java
  31. 406
      src/ddf/minim/effects/ChebFilter.java
  32. 142
      src/ddf/minim/effects/Convolver.java
  33. 49
      src/ddf/minim/effects/HighPassSP.java
  34. 228
      src/ddf/minim/effects/IIRFilter.java
  35. 64
      src/ddf/minim/effects/LowPassFS.java
  36. 51
      src/ddf/minim/effects/LowPassSP.java
  37. 76
      src/ddf/minim/effects/NotchFilter.java
  38. 110
      src/ddf/minim/effects/WavetableEnvelope.java
  39. 51
      src/ddf/minim/javasound/BasicMetaData.java
  40. 1181
      src/ddf/minim/javasound/FloatSampleBuffer.java
  41. 880
      src/ddf/minim/javasound/FloatSampleTools.java
  42. 182
      src/ddf/minim/javasound/JSAudioInput.java
  43. 176
      src/ddf/minim/javasound/JSAudioOutput.java
  44. 360
      src/ddf/minim/javasound/JSAudioRecording.java
  45. 161
      src/ddf/minim/javasound/JSAudioRecordingClip.java
  46. 61
      src/ddf/minim/javasound/JSAudioSample.java
  47. 716
      src/ddf/minim/javasound/JSBaseAudioRecordingStream.java
  48. 224
      src/ddf/minim/javasound/JSBufferedSampleRecorder.java
  49. 40
      src/ddf/minim/javasound/JSMPEGAudioRecordingStream.java
  50. 881
      src/ddf/minim/javasound/JSMinim.java
  51. 34
      src/ddf/minim/javasound/JSPCMAudioRecordingStream.java
  52. 186
      src/ddf/minim/javasound/JSStreamingSampleRecorder.java
  53. 111
      src/ddf/minim/javasound/MP3MetaData.java
  54. 895
      src/ddf/minim/javasound/MpegAudioFileReader.java
  55. 124
      src/ddf/minim/javasound/MpegAudioFileReaderWorkaround.java
  56. 139
      src/ddf/minim/javasound/SampleSignal.java
  57. 442
      src/ddf/minim/signals/Oscillator.java
  58. 171
      src/ddf/minim/signals/PinkNoise.java
  59. 82
      src/ddf/minim/signals/PulseWave.java
  60. 50
      src/ddf/minim/signals/SawWave.java
  61. 51
      src/ddf/minim/signals/SineWave.java
  62. 51
      src/ddf/minim/signals/SquareWave.java
  63. 51
      src/ddf/minim/signals/TriangleWave.java
  64. 120
      src/ddf/minim/signals/WhiteNoise.java
  65. 72
      src/ddf/minim/spi/AudioOut.java
  66. 121
      src/ddf/minim/spi/AudioRecording.java
  67. 119
      src/ddf/minim/spi/AudioRecordingStream.java
  68. 52
      src/ddf/minim/spi/AudioResource.java
  69. 54
      src/ddf/minim/spi/AudioStream.java
  70. 160
      src/ddf/minim/spi/MinimServiceProvider.java
  71. 69
      src/ddf/minim/spi/SampleRecorder.java
  72. 389
      src/ddf/minim/ugens/ADSR.java
  73. 36
      src/ddf/minim/ugens/Abs.java
  74. 91
      src/ddf/minim/ugens/Balance.java
  75. 140
      src/ddf/minim/ugens/BitCrush.java
  76. 165
      src/ddf/minim/ugens/Bypass.java
  77. 64
      src/ddf/minim/ugens/Constant.java
  78. 310
      src/ddf/minim/ugens/Damp.java
  79. 97
      src/ddf/minim/ugens/DefaultInstrument.java
  80. 245
      src/ddf/minim/ugens/Delay.java
  81. 126
      src/ddf/minim/ugens/EnvelopeFollower.java
  82. 390
      src/ddf/minim/ugens/FilePlayer.java
  83. 243
      src/ddf/minim/ugens/Flanger.java
  84. 266
      src/ddf/minim/ugens/Frequency.java
  85. 96
      src/ddf/minim/ugens/Gain.java
  86. 335
      src/ddf/minim/ugens/GranulateRandom.java
  87. 253
      src/ddf/minim/ugens/GranulateSteady.java
  88. 51
      src/ddf/minim/ugens/Instrument.java
  89. 147
      src/org/json/simple/ItemList.java
  90. 381
      src/org/json/simple/JSONArray.java
  91. 12
      src/org/json/simple/JSONAware.java
  92. 132
      src/org/json/simple/JSONObject.java
  93. 15
      src/org/json/simple/JSONStreamAware.java
  94. 316
      src/org/json/simple/JSONValue.java
  95. 23
      src/org/json/simple/parser/ContainerFactory.java
  96. 110
      src/org/json/simple/parser/ContentHandler.java
  97. 533
      src/org/json/simple/parser/JSONParser.java
  98. 90
      src/org/json/simple/parser/ParseException.java
  99. 688
      src/org/json/simple/parser/Yylex.java
  100. 58
      src/org/json/simple/parser/Yytoken.java

1
lib/export.txt

@ -0,0 +1 @@ @@ -0,0 +1 @@
name = Minim Audio

BIN
lib/jl1.0.1.jar

Binary file not shown.

BIN
lib/jopt-simple-5.0.4.jar

Binary file not shown.

BIN
lib/jsminim.jar

Binary file not shown.

BIN
lib/jsoup-1.11.2.jar

Binary file not shown.

BIN
lib/mp3spi1.9.5.jar

Binary file not shown.

BIN
lib/tritonus_aos.jar

Binary file not shown.

BIN
lib/tritonus_share.jar

Binary file not shown.

BIN
lib/trove-3.0.3.jar

Binary file not shown.

388
src/autostepper/AutoStepper.java

@ -0,0 +1,388 @@ @@ -0,0 +1,388 @@
/*
- might need better timing for input?
*/
package autostepper;
import ddf.minim.AudioPlayer;
import ddf.minim.Minim;
import ddf.minim.MultiChannelBuffer;
import ddf.minim.analysis.BeatDetect;
import ddf.minim.analysis.FFT;
import ddf.minim.spi.AudioRecordingStream;
import gnu.trove.list.array.TFloatArrayList;
import java.io.*;
import java.util.ArrayList;
import java.util.Scanner;
/**
*
* @author Phr00t
*/
public class AutoStepper {
public static boolean DEBUG_STEPS = false;
public static float MAX_BPM = 170f, MIN_BPM = 70f, BPM_SENSITIVITY = 0.05f, STARTSYNC = 0.0f;
public static boolean USETAPPER = false;
public static Minim minim;
public static AutoStepper myAS = new AutoStepper();
public static final int KICKS = 0, ENERGY = 1, SNARE = 2, HAT = 3;
// collected song data
private final TFloatArrayList[] manyTimes = new TFloatArrayList[4];
private final TFloatArrayList[] fewTimes = new TFloatArrayList[4];
public String sketchPath( String fileName ) {
return fileName;
}
public InputStream createInput( String fileName ) {
try {
return new FileInputStream(new File(fileName));
} catch(Exception e) {
return null;
}
}
public static String getArg(String[] args, String argname, String def) {
try {
for(String s : args) {
s = s.replace("\"", "");
if( s.startsWith(argname) ) {
return s.substring(s.indexOf("=") + 1).toLowerCase();
}
}
} catch(Exception e) { }
return def;
}
public static boolean hasArg(String[] args, String argname) {
for(String s : args) {
if( s.toLowerCase().equals(argname) ) return true;
}
return false;
}
public static void main(String[] args) {
minim = new Minim(myAS);
String outputDir, input;
float duration;
System.out.println("Starting AutoStepper by Phr00t's Software, v1.0 (See www.phr00t.com for more goodies!)");
if( hasArg(args, "help") || hasArg(args, "h") || hasArg(args, "?") || hasArg(args, "-help") || hasArg(args, "-?") || hasArg(args, "-h") ) {
System.out.println("Argument usage (all fields are optional):\n"
+ "input=<file or dir> output=<songs dir> duration=<seconds to process> synctime=<offset start time in seconds> tap=<true/false>");
return;
}
MAX_BPM = Float.parseFloat(getArg(args, "maxbpm", "170f"));
outputDir = getArg(args, "output", ".");
if( outputDir.endsWith("/") == false ) outputDir += "/";
input = getArg(args, "input", ".");
duration = Float.parseFloat(getArg(args, "duration", "90"));
STARTSYNC = Float.parseFloat(getArg(args, "synctime", "0.0"));
BPM_SENSITIVITY = Float.parseFloat(getArg(args, "bpmsensitivity", "0.05"));
USETAPPER = getArg(args, "tap", "false").equals("true");
File inputFile = new File(input);
if( inputFile.isFile() ) {
myAS.analyzeUsingAudioRecordingStream(inputFile, duration, outputDir);
} else if( inputFile.isDirectory() ) {
System.out.println("Processing directory: " + inputFile.getAbsolutePath());
File[] allfiles = inputFile.listFiles();
for(File f : allfiles) {
String extCheck = f.getName().toLowerCase();
if( f.isFile() &&
(extCheck.endsWith(".mp3") || extCheck.endsWith(".wav") || extCheck.endsWith(".flac")) ) {
myAS.analyzeUsingAudioRecordingStream(f, duration, outputDir);
} else {
System.out.println("Skipping unsupported file: " + f.getName());
}
}
} else {
System.out.println("Couldn't find any input files.");
}
}
TFloatArrayList calculateDifferences(TFloatArrayList arr, float timeThreshold) {
TFloatArrayList diff = new TFloatArrayList();
int currentlyAt = 0;
while(currentlyAt < arr.size() - 1) {
float mytime = arr.getQuick(currentlyAt);
int oldcurrentlyat = currentlyAt;
for(int i=currentlyAt+1;i<arr.size();i++) {
float diffcheck = arr.getQuick(i) - mytime;
if( diffcheck >= timeThreshold ) {
diff.add(diffcheck);
currentlyAt = i;
break;
}
}
if( oldcurrentlyat == currentlyAt ) break;
}
return diff;
}
float getDifferenceAverage(TFloatArrayList arr) {
float avg = 0f;
for(int i=0;i<arr.size()-1;i++) {
avg += Math.abs(arr.getQuick(i+1) - arr.getQuick(i));
}
if( arr.size() <= 1 ) return 0f;
return avg / arr.size()-1;
}
float getMostCommon(TFloatArrayList arr, float threshold, boolean closestToInteger) {
ArrayList<TFloatArrayList> values = new ArrayList<>();
for(int i=0;i<arr.size();i++) {
float val = arr.get(i);
// check for this value in our current lists
boolean notFound = true;
for(int j=0;j<values.size();j++) {
TFloatArrayList tal = values.get(j);
for(int k=0;k<tal.size();k++) {
float listValue = tal.get(k);
if( Math.abs(listValue - val) < threshold ) {
notFound = false;
tal.add(val);
break;
}
}
if( notFound == false ) break;
}
// if it wasn't found, start a new list
if( notFound ) {
TFloatArrayList newList = new TFloatArrayList();
newList.add(val);
values.add(newList);
}
}
// get the longest list
int longest = 0;
TFloatArrayList longestList = null;
for(int i=0;i<values.size();i++) {
TFloatArrayList check = values.get(i);
if( check.size() > longest ||
check.size() == longest && getDifferenceAverage(check) < getDifferenceAverage(longestList) ) {
longest = check.size();
longestList = check;
}
}
if( longestList == null ) return -1f;
if( longestList.size() == 1 && values.size() > 1 ) {
// one value only, no average needed.. but what to pick?
// just pick the smallest one... or integer, if we want that instead
if( closestToInteger ) {
float closestIntDiff = 1f;
float result = arr.getQuick(0);
for(int i=0;i<arr.size();i++) {
float diff = Math.abs(Math.round(arr.getQuick(i)) - arr.getQuick(i));
if( diff < closestIntDiff ) {
closestIntDiff = diff;
result = arr.getQuick(i);
}
}
return result;
} else {
float smallest = 99999f;
for(int i=0;i<arr.size();i++) {
if( arr.getQuick(i) < smallest ) smallest = arr.getQuick(i);
}
return smallest;
}
}
// calculate average
float avg = 0f;
for(int i=0;i<longestList.size();i++) {
avg += longestList.get(i);
}
return avg / longestList.size();
}
public float getBestOffset(float timePerBeat, TFloatArrayList times, float groupBy) {
TFloatArrayList offsets = new TFloatArrayList();
for(int i=0;i<times.size();i++) {
offsets.add(times.getQuick(i) % timePerBeat);
}
return getMostCommon(offsets, groupBy, false);
}
public void AddCommonBPMs(TFloatArrayList common, TFloatArrayList times, float doubleSpeed, float timePerSample) {
float commonBPM = 60f / getMostCommon(calculateDifferences(times, doubleSpeed), timePerSample, true);
if( commonBPM > MAX_BPM ) {
common.add(commonBPM * 0.5f);
} else if( commonBPM < MIN_BPM / 2f ) {
common.add(commonBPM * 4f);
} else if( commonBPM < MIN_BPM ) {
common.add(commonBPM * 2f);
} else common.add(commonBPM);
}
public static float tappedOffset;
public int getTappedBPM(String filename) {
AudioPlayer ap = minim.loadFile(filename, 2048);
System.out.println("\n********************************************************************\n\nPress [ENTER] to start song, then press [ENTER] to tap to the beat.\nIt will complete after 30 entries.\nDon't worry about hitting the first beat, just start anytime.\n\n********************************************************************");
TFloatArrayList positions = new TFloatArrayList();
Scanner in = new Scanner(System.in);
try {
in.nextLine();
} catch(Exception e) { }
long milli = System.nanoTime();
ap.play();
milli = (System.nanoTime() + milli) / 2;
try {
for(int i=0;i<30;i++) {
while(System.in.available()==0) { }
long now = System.nanoTime();
while(System.in.available()>0) { System.in.read(); }
double time = ((double)(now - milli) / 1000000000.0);
positions.add((float)time);
System.out.println("#" + positions.size() + "/30: " + time + "s");
}
} catch(Exception e) { }
ap.close();
//TFloatArrayList diffs = calculateDifferences(positions, 60f / (MAX_BPM * 2f));
//float mostCommon = getMostCommon(diffs, 0.025f);
float avg = ((positions.getQuick(positions.size()-1) - positions.getQuick(0)) / (positions.size() - 1));
int BPM = (int)Math.floor(60f / avg);
float timePerBeat = 60f / BPM;
tappedOffset = -getBestOffset(timePerBeat, positions, 0.1f) + timePerBeat * 0.5f;
return BPM;
}
// wayhome: 0.432s, 0.879s, 1.332s, 1.787s, 2.24s, 2.7s.. = 132.74 BPM
// angel: 4.856s, 5.28s... = 139.6 BPM
void analyzeUsingAudioRecordingStream(File filename, float seconds, String outputDir) {
int fftSize = 512;
System.out.println("\n[--- Processing " + seconds + "s of "+ filename.getName() + " ---]");
AudioRecordingStream stream = minim.loadFileStream(filename.getAbsolutePath(), fftSize, false);
// tell it to "play" so we can read from it.
stream.play();
// create the fft we'll use for analysis
BeatDetect manybd = new BeatDetect(BeatDetect.FREQ_ENERGY, fftSize, stream.getFormat().getSampleRate());
BeatDetect fewbd = new BeatDetect(BeatDetect.FREQ_ENERGY, fftSize, stream.getFormat().getSampleRate());
BeatDetect manybde = new BeatDetect(BeatDetect.SOUND_ENERGY, fftSize, stream.getFormat().getSampleRate());
BeatDetect fewbde = new BeatDetect(BeatDetect.SOUND_ENERGY, fftSize, stream.getFormat().getSampleRate());
manybd.setSensitivity(BPM_SENSITIVITY);
manybde.setSensitivity(BPM_SENSITIVITY);
fewbd.setSensitivity(60f/MAX_BPM);
fewbde.setSensitivity(60f/MAX_BPM);
FFT fft = new FFT( fftSize, stream.getFormat().getSampleRate() );
// create the buffer we use for reading from the stream
MultiChannelBuffer buffer = new MultiChannelBuffer(fftSize, stream.getFormat().getChannels());
// figure out how many samples are in the stream so we can allocate the correct number of spectra
float songTime = stream.getMillisecondLength() / 1000f;
int totalSamples = (int)( songTime * stream.getFormat().getSampleRate() );
float timePerSample = fftSize / stream.getFormat().getSampleRate();
// now we'll analyze the samples in chunks
int totalChunks = (totalSamples / fftSize) + 1;
System.out.println("Performing Beat Detection...");
for(int i=0;i<fewTimes.length;i++) {
if( fewTimes[i] == null ) fewTimes[i] = new TFloatArrayList();
if( manyTimes[i] == null ) manyTimes[i] = new TFloatArrayList();
fewTimes[i].clear();
manyTimes[i].clear();
}
TFloatArrayList MidFFTAmount = new TFloatArrayList(), MidFFTMaxes = new TFloatArrayList();
float largestAvg = 0f, largestMax = 0f;
int lowFreq = fft.freqToIndex(300f);
int highFreq = fft.freqToIndex(3000f);
for(int chunkIdx = 0; chunkIdx < totalChunks; ++chunkIdx) {
stream.read( buffer );
float[] data = buffer.getChannel(0);
float time = chunkIdx * timePerSample;
// now analyze the left channel
manybd.detect(data, time);
manybde.detect(data, time);
fewbd.detect(data, time);
fewbde.detect(data, time);
fft.forward(data);
// fft processing
float avg = fft.calcAvg(300f, 3000f);
float max = 0f;
for(int b=lowFreq;b<=highFreq;b++) {
float bandamp = fft.getBand(b);
if( bandamp > max ) max = bandamp;
}
if( max > largestMax ) largestMax = max;
if( avg > largestAvg ) largestAvg = avg;
MidFFTAmount.add(avg);
MidFFTMaxes.add(max);
// store basic percussion times
if(manybd.isKick()) manyTimes[KICKS].add(time);
if(manybd.isHat()) manyTimes[HAT].add(time);
if(manybd.isSnare()) manyTimes[SNARE].add(time);
if(manybde.isOnset()) manyTimes[ENERGY].add(time);
if(fewbd.isKick()) fewTimes[KICKS].add(time);
if(fewbd.isHat()) fewTimes[HAT].add(time);
if(fewbd.isSnare()) fewTimes[SNARE].add(time);
if(fewbde.isOnset()) fewTimes[ENERGY].add(time);
}
System.out.println("Loudest midrange average to normalize to 1: " + largestAvg);
System.out.println("Loudest midrange maximum to normalize to 1: " + largestMax);
float scaleBy = 1f / largestAvg;
float scaleMaxBy = 1f / largestMax;
for(int i=0;i<MidFFTAmount.size();i++) {
MidFFTAmount.replace(i, MidFFTAmount.get(i) * scaleBy);
MidFFTMaxes.replace(i, MidFFTMaxes.get(i) * scaleMaxBy);
}
// calculate differences between percussive elements,
// then find the most common differences among all
// use this to calculate BPM
TFloatArrayList common = new TFloatArrayList();
float doubleSpeed = 60f / (MAX_BPM * 2f);
for(int i=0;i<fewTimes.length;i++) {
AddCommonBPMs(common, fewTimes[i], doubleSpeed, timePerSample * 1.5f);
AddCommonBPMs(common, manyTimes[i], doubleSpeed, timePerSample * 1.5f);
}
float BPM, startTime, timePerBeat;
if( USETAPPER ) {
BPM = getTappedBPM(filename.getAbsolutePath());
timePerBeat = 60f / BPM;
startTime = tappedOffset;
} else {
if( common.isEmpty() ) {
System.out.println("[--- FAILED: COULDN'T CALCULATE BPM ---]");
return;
}
BPM = Math.round(getMostCommon(common, 0.5f, true));
timePerBeat = 60f / BPM;
TFloatArrayList startTimes = new TFloatArrayList();
for(int i=0;i<fewTimes.length;i++) {
startTimes.add(getBestOffset(timePerBeat, fewTimes[i], 0.01f));
startTimes.add(getBestOffset(timePerBeat, manyTimes[i], 0.01f));
}
// give extra weight to fewKicks
float kickStartTime = getBestOffset(timePerBeat, fewTimes[KICKS], 0.01f);
startTimes.add(kickStartTime);
startTimes.add(kickStartTime);
startTime = -getMostCommon(startTimes, 0.02f, false);
}
System.out.println("Time per beat: " + timePerBeat + ", BPM: " + BPM);
// angel = -0.2955827 works perfectly (bpm 140)
// bassjam = -0.1231 works perfectly... (bpm 130)
// cash cash now works with kickStartTime weight, -0.1656 (bpm 114)
// lasttime = -0.0592 works perfectly... (bpm 125)
// wayhome = -0.4279 works perfectly... (bpm 132)
System.out.println("Start Time (without " + STARTSYNC + "s offset yet): " + startTime);
// start making the SM
BufferedWriter smfile = SMGenerator.GenerateSM(BPM, startTime, filename, outputDir);
SMGenerator.AddNotes(smfile, SMGenerator.Beginner, StepGenerator.GenerateNotes(1, 3, manyTimes, fewTimes, MidFFTAmount, MidFFTMaxes, timePerSample, timePerBeat, startTime, seconds, false));
SMGenerator.AddNotes(smfile, SMGenerator.Easy, StepGenerator.GenerateNotes(1, 2, manyTimes, fewTimes, MidFFTAmount, MidFFTMaxes, timePerSample, timePerBeat, startTime, seconds, false));
SMGenerator.AddNotes(smfile, SMGenerator.Medium, StepGenerator.GenerateNotes(2, 4, manyTimes, fewTimes, MidFFTAmount, MidFFTMaxes, timePerSample, timePerBeat, startTime, seconds, false));
SMGenerator.AddNotes(smfile, SMGenerator.Hard, StepGenerator.GenerateNotes(2, 2, manyTimes, fewTimes, MidFFTAmount, MidFFTMaxes, timePerSample, timePerBeat, startTime, seconds, false));
SMGenerator.AddNotes(smfile, SMGenerator.Challenge, StepGenerator.GenerateNotes(2, 1, manyTimes, fewTimes, MidFFTAmount, MidFFTMaxes, timePerSample, timePerBeat, startTime, seconds, true));
SMGenerator.Complete(smfile);
System.out.println("[--------- SUCCESS ----------]");
}
}

49
src/autostepper/GoogleImageSearch.java

@ -0,0 +1,49 @@ @@ -0,0 +1,49 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package autostepper;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
public class GoogleImageSearch {
public static void FindAndSaveImage(String question, String destination) {
String ua = "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:59.0) Gecko/20100101 Firefox/59.0";
String finRes = "";
try {
String googleUrl = "https://www.google.com/search?as_st=y&tbm=isch&as_q=" + question.replace(",", "+").replace(" ", "+") + "&as_epq=&as_oq=&as_eq=&cr=&as_sitesearch=&safe=images&tbs=isz:lt,islt:vga,iar:w";
Document doc1 = Jsoup.connect(googleUrl).userAgent(ua).timeout(6 * 1000).get();
Element media = doc1.select("[data-src]").first();
String finUrl = media.attr("abs:data-src");
saveImage(finUrl.replace("&quot", ""), destination);
} catch (Exception e) {
System.out.println(e);
}
}
public static void saveImage(String imageUrl, String destinationFile) throws IOException {
URL url = new URL(imageUrl);
InputStream is = url.openStream();
OutputStream os = new FileOutputStream(destinationFile);
byte[] b = new byte[2048];
int length;
while ((length = is.read(b)) != -1) {
os.write(b, 0, length);
}
is.close();
os.close();
}
}

133
src/autostepper/SMGenerator.java

@ -0,0 +1,133 @@ @@ -0,0 +1,133 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package autostepper;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/**
*
* @author Phr00t
*/
public class SMGenerator {
private static String Header =
"#TITLE:$TITLE;\n" +
"#SUBTITLE:;\n" +
"#ARTIST:AutoStepper by phr00t.com;\n" +
"#TITLETRANSLIT:;\n" +
"#SUBTITLETRANSLIT:;\n" +
"#ARTISTTRANSLIT:;\n" +
"#GENRE:;\n" +
"#CREDIT:AutoStepper by phr00t.com;\n" +
"#BANNER:$BGIMAGE;\n" +
"#BACKGROUND:$BGIMAGE;\n" +
"#LYRICSPATH:;\n" +
"#CDTITLE:;\n" +
"#MUSIC:$MUSICFILE;\n" +
"#OFFSET:$STARTTIME;\n" +
"#SAMPLESTART:30.0;\n" +
"#SAMPLELENGTH:30.0;\n" +
"#SELECTABLE:YES;\n" +
"#BPMS:0.000000=$BPM;\n" +
"#STOPS:;\n" +
"#KEYSOUNDS:;\n" +
"#ATTACKS:;";
public static String Challenge =
"Challenge:\n" +
" 10:";
public static String Hard =
"Hard:\n" +
" 8:";
public static String Medium =
"Medium:\n" +
" 6:";
public static String Easy =
"Easy:\n" +
" 4:";
public static String Beginner =
"Beginner:\n" +
" 2:";
private static String NoteFramework =
"//---------------dance-single - ----------------\n" +
"#NOTES:\n" +
" dance-single:\n" +
" :\n" +
" $DIFFICULTY\n" +
" 0.733800,0.772920,0.048611,0.850698,0.060764,634.000000,628.000000,6.000000,105.000000,8.000000,0.000000,0.733800,0.772920,0.048611,0.850698,0.060764,634.000000,628.000000,6.000000,105.000000,8.000000,0.000000:\n" +
"$NOTES\n" +
";\n\n";
private static void copyFileUsingStream(File source, File dest) throws IOException {
InputStream is = null;
OutputStream os = null;
try {
is = new FileInputStream(source);
os = new FileOutputStream(dest);
byte[] buffer = new byte[1024];
int length;
while ((length = is.read(buffer)) > 0) {
os.write(buffer, 0, length);
}
} finally {
is.close();
os.close();
}
}
public static void AddNotes(BufferedWriter smfile, String difficulty, String notes) {
try {
smfile.write(NoteFramework.replace("$DIFFICULTY", difficulty).replace("$NOTES", notes));
} catch(Exception e) { }
}
public static void Complete(BufferedWriter smfile) {
try {
smfile.close();
} catch(Exception e) { }
}
public static BufferedWriter GenerateSM(float BPM, float startTime, File songfile, String outputdir) {
String filename = songfile.getName();
String songname = filename.replace(".mp3", " ").replace(".wav", " ").replace(".com", " ").replace(".flac", " ");
String shortName = songname.length() > 30 ? songname.substring(0, 30) : songname;
File dir = new File(outputdir, filename + "_dir/");
dir.mkdirs();
File smfile = new File(dir, filename + ".sm");
// get image for sm
File imgFile = new File(dir, filename + "_img.png");
String imgFileName = "";
if( imgFile.exists() == false ) {
System.out.println("Attempting to get image for background & banner...");
GoogleImageSearch.FindAndSaveImage(songname, imgFile.getAbsolutePath());
}
if( imgFile.exists() ) {
System.out.println("Got an image file!");
imgFileName = imgFile.getName();
} else System.out.println("No image file to use :(");
try {
smfile.delete();
copyFileUsingStream(songfile, new File(dir, filename));
BufferedWriter writer = new BufferedWriter(new FileWriter(smfile));
writer.write(Header.replace("$TITLE", shortName).replace("$BGIMAGE", imgFileName).replace("$MUSICFILE", filename)
.replace("$STARTTIME", Float.toString(startTime + AutoStepper.STARTSYNC)).replace("$BPM", Float.toString(BPM)));
return writer;
} catch(Exception e) {}
return null;
}
}

269
src/autostepper/StepGenerator.java

@ -0,0 +1,269 @@ @@ -0,0 +1,269 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package autostepper;
import gnu.trove.list.array.TFloatArrayList;
import java.util.Random;
/**
*
* @author Phr00t
*/
public class StepGenerator {
static private int MAX_HOLD_BEAT_COUNT = 4;
static private char EMPTY = '0', STEP = '1', HOLD = '2', STOP = '3', MINE = 'M';
static Random rand = new Random();
static private int getHoldCount() {
int ret = 0;
if( holding[0] > 0f ) ret++;
if( holding[1] > 0f ) ret++;
if( holding[2] > 0f ) ret++;
if( holding[3] > 0f ) ret++;
return ret;
}
static private int getRandomHold() {
int hc = getHoldCount();
if(hc == 0) return -1;
int pickHold = rand.nextInt(hc);
for(int i=0;i<4;i++) {
if( holding[i] > 0f ) {
if( pickHold == 0 ) return i;
pickHold--;
}
}
return -1;
}
// make a note line, with lots of checks, balances & filtering
static float[] holding = new float[4];
static float lastJumpTime;
static String lastLine = "0000";
static float lastKickTime = 0f;
static int commaSeperator, commaSeperatorReset, mineCount;
private static char[] getHoldStops(int currentHoldCount, float time, int holds) {
char[] holdstops = new char[4];
holdstops[0] = '0';
holdstops[1] = '0';
holdstops[2] = '0';
holdstops[3] = '0';
if( currentHoldCount > 0 ) {
while( holds < 0 ) {
int index = getRandomHold();
if( index == -1 ) {
holds = 0;
currentHoldCount = 0;
} else {
holding[index] = 0f;
holdstops[index] = STOP;
holds++; currentHoldCount--;
}
}
// if we still have holds, subtract counter until 0
for(int i=0;i<4;i++) {
if( holding[i] > 0f ) {
holding[i] -= 1f;
if( holding[i] <= 0f ) {
holding[i] = 0f;
holdstops[i] = STOP;
currentHoldCount--;
}
}
}
}
return holdstops;
}
private static String getNoteLine(float time, int steps, int holds, boolean mines) {
if( steps == 0 ) {
lastLine = String.valueOf(getHoldStops(getHoldCount(), time, holds));
commaSeperator--;
if( commaSeperator <= 0 ) {
commaSeperator = commaSeperatorReset;
return lastLine + "\n,\n";
}
return lastLine + "\n";
}
if( steps > 1 && time - lastJumpTime < (mines ? 2f : 4f) ) steps = 1; // don't spam jumps
if( steps >= 2 ) {
// no hands
steps = 2;
lastJumpTime = time;
}
// can't hold or step more than 2
int currentHoldCount = getHoldCount();
if( holds + currentHoldCount > 2 ) holds = 2 - currentHoldCount;
if( steps + currentHoldCount > 2 ) steps = 2 - currentHoldCount;
// are we stopping holds?
char[] noteLine = getHoldStops(currentHoldCount, time, holds);
// ok, make the steps
String completeLine;
char[] orig = new char[4];
orig[0] = noteLine[0];
orig[1] = noteLine[1];
orig[2] = noteLine[2];
orig[3] = noteLine[3];
float[] willhold = new float[4];
do {
int stepcount = steps, holdcount = holds;
noteLine[0] = orig[0];
noteLine[1] = orig[1];
noteLine[2] = orig[2];
noteLine[3] = orig[3];
willhold[0] = 0f;
willhold[1] = 0f;
willhold[2] = 0f;
willhold[3] = 0f;
while(stepcount > 0) {
int stepindex = rand.nextInt(4);
if( noteLine[stepindex] != EMPTY || holding[stepindex] > 0f ) continue;
if( holdcount > 0 ) {
noteLine[stepindex] = HOLD;
willhold[stepindex] = MAX_HOLD_BEAT_COUNT;
holdcount--; stepcount--;
} else {
noteLine[stepindex] = STEP;
stepcount--;
}
}
// put in a mine?
if( mines ) {
mineCount--;
if( mineCount <= 0 ) {
mineCount = rand.nextInt(8);
if( rand.nextInt(8) == 0 && noteLine[0] == EMPTY && holding[0] <= 0f ) noteLine[0] = MINE;
if( rand.nextInt(8) == 0 && noteLine[1] == EMPTY && holding[1] <= 0f ) noteLine[1] = MINE;
if( rand.nextInt(8) == 0 && noteLine[2] == EMPTY && holding[2] <= 0f ) noteLine[2] = MINE;
if( rand.nextInt(8) == 0 && noteLine[3] == EMPTY && holding[3] <= 0f ) noteLine[3] = MINE;
}
}
completeLine = String.valueOf(noteLine);
} while( completeLine.equals(lastLine) && completeLine.equals("0000") == false );
if( willhold[0] > holding[0] ) holding[0] = willhold[0];
if( willhold[1] > holding[1] ) holding[1] = willhold[1];
if( willhold[2] > holding[2] ) holding[2] = willhold[2];
if( willhold[3] > holding[3] ) holding[3] = willhold[3];
lastLine = completeLine;
commaSeperator--;
if( commaSeperator <= 0 ) {
completeLine += "\n,\n";
commaSeperator = commaSeperatorReset;
} else completeLine += "\n";
return completeLine;
}
private static boolean isNearATime(float time, TFloatArrayList timelist, float threshold) {
for(int i=0;i<timelist.size();i++) {
float checktime = timelist.get(i);
if( Math.abs(checktime - time) <= threshold ) return true;
if( checktime > time + threshold ) return false;
}
return false;
}
private static float getFFT(float time, TFloatArrayList FFTAmounts, float timePerFFT) {
int index = Math.round(time / timePerFFT);
if( index < 0 || index >= FFTAmounts.size()) return 0f;
return FFTAmounts.getQuick(index);
}
private static boolean sustainedFFT(float startTime, float len, float granularity, float timePerFFT, TFloatArrayList FFTMaxes, TFloatArrayList FFTAvg, float aboveAvg, float dampenAverage) {
int endIndex = Math.round((startTime + len) / timePerFFT);
if( endIndex >= FFTMaxes.size() ) return false;
int wiggleRoom = Math.round(0.1f * len / timePerFFT);
int startIndex = Math.round(startTime / timePerFFT);
int pastGranu = Math.round((startTime + granularity) / timePerFFT);
boolean startThresholdReached = false;
for(int i=startIndex;i<=endIndex;i++) {
float amt = FFTMaxes.getQuick(i);
float avg = FFTAvg.getQuick(i) * dampenAverage;
if( i < pastGranu ) {
startThresholdReached |= amt >= avg + aboveAvg;
} else {
if( startThresholdReached == false ) return false;
if( amt < avg ) {
wiggleRoom--;
if( wiggleRoom <= 0 ) return false;
}
}
}
return true;
}
public static String GenerateNotes(int stepGranularity, int skipChance,
TFloatArrayList[] manyTimes,
TFloatArrayList[] fewTimes,
TFloatArrayList FFTAverages, TFloatArrayList FFTMaxes, float timePerFFT,
float timePerBeat, float timeOffset, float totalTime,
boolean allowMines) {
// reset variables
lastLine = "0000";
lastJumpTime = -10f;
holding[0] = 0f;
holding[1] = 0f;
holding[2] = 0f;
holding[3] = 0f;
lastKickTime = 0f;
String AllNotes = "";
commaSeperatorReset = 4 * stepGranularity;
commaSeperator = commaSeperatorReset;
float lastSkippedTime = -10f;
int totalStepsMade = 0, timeIndex = 0;
boolean skippedLast = false;
float timeGranularity = timePerBeat / stepGranularity;
for(float t = timeOffset; t <= totalTime; t += timeGranularity) {
int steps = 0, holds = 0;
if( t > 0f ) {
float fftavg = getFFT(t, FFTAverages, timePerFFT);
float fftmax = getFFT(t, FFTMaxes, timePerFFT);
boolean sustained = sustainedFFT(t, 0.666f, timeGranularity, timePerFFT, FFTMaxes, FFTAverages, 0.2f, 0.6f);
boolean nearKick = isNearATime(t, fewTimes[AutoStepper.KICKS], timePerBeat / stepGranularity);
boolean nearSnare = isNearATime(t, fewTimes[AutoStepper.SNARE], timePerBeat / stepGranularity);
boolean nearEnergy = isNearATime(t, fewTimes[AutoStepper.ENERGY], timePerBeat / stepGranularity);
steps = sustained || nearKick || nearSnare || nearEnergy ? 1 : 0;
if( sustained ) {
holds = 1 + (nearEnergy ? 1 : 0);
} else if( fftmax < 0.4f ) {
holds = fftmax < 0.2f ? -2 : -1;
}
if( nearKick && nearSnare &&
steps > 0 && lastLine.contains("1") == false && lastLine.contains("2") == false ) {
// only jump in high areas, on solid beats (not half beats)
steps = 2;
}
// wait, are we skipping new steps?
// if we just got done from a jump, don't have a half beat
// if we are holding something, don't do half-beat steps
if( timeIndex % 2 == 1 &&
(skipChance > 1 && timeIndex % 2 == 1 && rand.nextInt(skipChance) > 0 || getHoldCount() > 0) ||
t - lastJumpTime < timePerBeat ) {
steps = 0;
if( holds > 0 ) holds = 0;
}
}
if( AutoStepper.DEBUG_STEPS ) {
AllNotes += getNoteLine(t, timeIndex % 2 == 0 ? 1 : 0, -2, allowMines);
} else AllNotes += getNoteLine(t, steps, holds, allowMines);
totalStepsMade += steps;
timeIndex++;
}
// fill out the last empties
while( commaSeperator > 0 ) {
AllNotes += "3333\n";
commaSeperator--;
}
int _stepCount = AllNotes.length() - AllNotes.replace("1", "").length();
int _holdCount = AllNotes.length() - AllNotes.replace("2", "").length();
int _mineCount = AllNotes.length() - AllNotes.replace("M", "").length();
System.out.println("Steps: " + _stepCount + ", Holds: " + _holdCount + ", Mines: " + _mineCount);
return AllNotes;
}
}

47
src/ddf/minim/analysis/BartlettHannWindow.java

@ -0,0 +1,47 @@ @@ -0,0 +1,47 @@
/*
* Copyright (c) 2007 - 2008 by Damien Di Fede <ddf@compartmental.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library General Public License as published
* by the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package ddf.minim.analysis;
/**
* A Bartlett-Hann window function.
*
* @author Damien Di Fede
* @author Corban Brook
* @see <a href="http://en.wikipedia.org/wiki/Window_function#Bartlett.E2.80.93Hann_window">The Bartlett-Hann Window</a>
*
* @invisible
*/
public class BartlettHannWindow extends WindowFunction
{
/** Constructs a Bartlett-Hann window. */
public BartlettHannWindow()
{
}
protected float value(int length, int index)
{
return (float) (0.62 - 0.48 * Math.abs(index / (length - 1) - 0.5) - 0.38 * Math.cos(TWO_PI * index / (length - 1)));
}
public String toString()
{
return "Bartlett-Hann Window";
}
}

47
src/ddf/minim/analysis/BartlettWindow.java

@ -0,0 +1,47 @@ @@ -0,0 +1,47 @@
/*
* Copyright (c) 2007 - 2008 by Damien Di Fede <ddf@compartmental.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library General Public License as published
* by the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package ddf.minim.analysis;
/**
* A Bartlett window function.
*
* @author Damien Di Fede
* @author Corban Brook
* @see <a href="http://en.wikipedia.org/wiki/Window_function#Bartlett_window_.28zero_valued_end-points.29">The Bartlett Window</a>
*
* @invisible
*/
public class BartlettWindow extends WindowFunction
{
/** Constructs a Bartlett window. */
public BartlettWindow()
{
}
protected float value(int length, int index)
{
return 2f / (length - 1) * ((length - 1) / 2f - Math.abs(index - (length - 1) / 2f));
}
public String toString()
{
return "Bartlett Window";
}
}

678
src/ddf/minim/analysis/BeatDetect.java

@ -0,0 +1,678 @@ @@ -0,0 +1,678 @@
/*
* Copyright (c) 2007 - 2008 by Damien Di Fede <ddf@compartmental.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library General Public License as published
* by the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package ddf.minim.analysis;
import ddf.minim.AudioBuffer;
import ddf.minim.Minim;
/**
* The BeatDetect class allows you to analyze an audio stream for beats (rhythmic onsets).
* <a href="http://www.gamedev.net/reference/programming/features/beatdetection">Beat
* Detection Algorithms</a> by Frederic Patin describes beats in the following
* way: <blockquote> The human listening system determines the rhythm of music
* by detecting a pseudo periodical succession of beats. The signal which is
* intercepted by the ear contains a certain energy, this energy is converted
* into an electrical signal which the brain interprets. Obviously, The more
* energy the sound transports, the louder the sound will seem. But a sound will
* be heard as a <em>beat</em> only if his energy is largely superior to the
* sound's energy history, that is to say if the brain detects a
* <em>brutal variation in sound energy</em>. Therefore if the ear intercepts
* a monotonous sound with sometimes big energy peaks it will detect beats,
* however, if you play a continuous loud sound you will not perceive any beats.
* Thus, the beats are big variations of sound energy. </blockquote> In fact,
* the two algorithms in this class are based on two algorithms described in
* that paper.
* <p>
* To use this class, inside of <code>draw()</code> you must first call
* <code>detect()</code>, passing the <code>AudioBuffer</code> you want to
* analyze. You may then use the <code>isXXX</code> functions to find out what
* beats have occurred in that frame. For example, you might use
* <code>isKick()</code> to cause a circle to pulse.
* <p>
* BeatDetect has two modes: sound energy tracking and frequency energy
* tracking. In sound energy mode, the level of the buffer, as returned by
* <code>level()</code>, is used as the instant energy in each frame. Beats,
* then, are spikes in this value, relative to the previous one second of sound.
* In frequency energy mode, the same process is used but instead of tracking
* the level of the buffer, an FFT is used to obtain a spectrum, which is then
* divided into average bands using <code>logAverages()</code>, and each of
* these bands is tracked individually. The result is that it is possible to
* track sounds that occur in different parts of the frequency spectrum
* independently (like the kick drum and snare drum).
* <p>
* In sound energy mode you use <code>isOnset()</code> to query the algorithm
* and in frequency energy mode you use <code>isOnset(int i)</code>,
* <code>isKick()</code>, <code>isSnare()</code>, and
* <code>isRange()</code> to query particular frequnecy bands or ranges of
* frequency bands. It should be noted that <code>isKick()</code>,
* <code>isSnare()</code>, and <code>isHat()</code> merely call
* <code>isRange()</code> with values determined by testing the algorithm
* against music with a heavy beat and they may not be appropriate for all kinds
* of music. If you find they are performing poorly with your music, you should
* use <code>isRange()</code> directly to locate the bands that provide the
* most meaningful information for you.
*
* @author Damien Di Fede
*
* @example Analysis/SoundEnergyBeatDetection
*/
public class BeatDetect
{
/** Constant used to request frequency energy tracking mode.
*
* @example Analysis/FrequencyEnergyBeatDetection
*/
public static final int FREQ_ENERGY = 0;
/** Constant used to request sound energy tracking mode.
*
* @example Analysis/SoundEnergyBeatDetection
*/
public static final int SOUND_ENERGY = 1;
private int algorithm;
private int sampleRate;
private int timeSize;
private int valCnt;
private float[] valGraph;
private double sensitivity;
// for circular buffer support
private int insertAt;
// vars for sEnergy
private boolean isOnset;
private float[] eBuffer;
private float[] dBuffer;
private double lastTrueTime;
// vars for fEnergy
private boolean[] fIsOnset;
private FFT spect;
private float[][] feBuffer;
private float[][] fdBuffer;
private double[] fTimer;
private float[] varGraph;
private int varCnt;
/**
* Create a BeatDetect object that is in SOUND_ENERGY mode.
* <code>timeSize</code> and <code>sampleRate</code> will be set to 1024
* and 44100, respectively, so that it is possible to switch into FREQ_ENERGY
* mode with meaningful values.
*
*/
public BeatDetect(int algo, int timeSize, float sampleRate) {
this.sampleRate = (int)sampleRate;
this.timeSize = timeSize;
if( algo == SOUND_ENERGY ) {
initSEResources();
algorithm = SOUND_ENERGY;
} else if( algo == FREQ_ENERGY ) {
initFEResources();
algorithm = FREQ_ENERGY;
}
initGraphs();
sensitivity = 0.3;
}
/**
* Set the object to use the requested algorithm. If an invalid value is
* passed, the function will report and error and default to
* BeatDetect.SOUND_ENERGY
*
* @param algo
* int: either BeatDetect.SOUND_ENERGY or BeatDetect.FREQ_ENERGY
*
* @related BeatDetect
*/
public void detectMode(int algo)
{
if (algo < 0 || algo > 1)
{
Minim.error("Unrecognized detect mode, defaulting to SOUND_ENERGY.");
algo = SOUND_ENERGY;
}
if (algo == SOUND_ENERGY)
{
if (algorithm == FREQ_ENERGY)
{
releaseFEResources();
initSEResources();
initGraphs();
algorithm = algo;
}
}
else
{
if (algorithm == SOUND_ENERGY)
{
releaseSEResources();
initFEResources();
initGraphs();
algorithm = FREQ_ENERGY;
}
}
}
private void initGraphs()
{
valCnt = varCnt = 0;
valGraph = new float[512];
varGraph = new float[512];
}
private void initSEResources()
{
isOnset = false;
eBuffer = new float[sampleRate / timeSize];
dBuffer = new float[sampleRate / timeSize];
lastTrueTime = 0; //System.currentTimeMillis();
insertAt = 0;
}
private void initFEResources()
{
spect = new FFT(timeSize, sampleRate);
spect.logAverages(60, 3);
int numAvg = spect.avgSize();
fIsOnset = new boolean[numAvg];
feBuffer = new float[numAvg][sampleRate / timeSize];
fdBuffer = new float[numAvg][sampleRate / timeSize];
fTimer = new double[numAvg];
double start = 0.0;
for (int i = 0; i < fTimer.length; i++)
{
fTimer[i] = start;
}
insertAt = 0;
}
private void releaseSEResources()
{
isOnset = false;
eBuffer = null;
dBuffer = null;
lastTrueTime = 0;
}
private void releaseFEResources()
{
spect = null;
fIsOnset = null;
feBuffer = null;
fdBuffer = null;
fTimer = null;
}
/**
* Analyze the samples in <code>buffer</code>.
* This is a cumulative process, so you must call this function every frame.
*
* @param buffer
* AudioBuffer: the buffer to analyze.
*
* @example Analysis/SoundEnergyBeatDetection
*
* @related BeatDetect
*/
public void detect(AudioBuffer buffer, double time)
{
detect( buffer.toArray(), time );
}
public void detect(AudioBuffer buffer) {
detect(buffer.toArray(), (float)System.currentTimeMillis() / 1000.0);
}
/**
* Analyze the samples in <code>buffer</code>. This is a cumulative
* process, so you must call this function every frame.
*
* @param buffer
* float[]: the buffer to analyze
*
* @related BeatDetect
*/
public void detect(float[] buffer, double time) {
switch (algorithm)
{
case SOUND_ENERGY:
sEnergy(buffer, time);
break;
case FREQ_ENERGY:
fEnergy(buffer, time);
break;
}
}
public void detect(float[] buffer) {
detect(buffer, (double)System.currentTimeMillis() / 1000.0);
}
/**
* In frequency energy mode this returns the number of frequency bands
* currently being used. In sound energy mode this always returns 0.
*
* @return int: the length of the FFT's averages array
*
* @related BeatDetect
*/
public int detectSize()
{
if ( algorithm == FREQ_ENERGY )
{
return spect.avgSize();
}
return 0;
}
@Deprecated
public int dectectSize()
{
return detectSize();
}
/**
* Returns the center frequency of the i<sup>th</sup> frequency band.
* In sound energy mode this always returns 0.
*
* @param i
* int: which detect band you want the center frequency of.
*
* @return float: the center frequency of the i<sup>th</sup> frequency band
*
* @related BeatDetect
*/
public float getDetectCenterFrequency(int i)
{
if ( algorithm == FREQ_ENERGY )
{
return spect.getAverageCenterFrequency(i);
}
return 0;
}
/**
* Sets the sensitivity of the algorithm. After a beat has been detected, the
* algorithm will wait for <code>millis</code> milliseconds before allowing
* another beat to be reported. You can use this to dampen the algorithm if
* it is giving too many false-positives. The default value is 10, which is
* essentially no damping. If you try to set the sensitivity to a negative
* value, an error will be reported and it will be set to 10 instead.
*
* @param millis
* int: the sensitivity in milliseconds
*
* @example Analysis/FrequencyEnergyBeatDetection
*
* @related BeatDetect
*/
public void setSensitivity(double sec)
{
if (sec < 0)
{
Minim.error("BeatDetect: sensitivity cannot be less than zero. Defaulting to 10.");
sensitivity = 0.3;
}
else
{
sensitivity = sec;
}
}
/**
* In sound energy mode this returns true when a beat has been detected. In
* frequency energy mode this always returns false.
*
* @return boolean: true if a beat has been detected.
*
* @example Analysis/SoundEnergyBeatDetection
*
* @related BeatDetect
*/
public boolean isOnset()
{
return isOnset;
}
/**
* In frequency energy mode this returns true when a beat has been detect in
* the <code>i<sup>th</sup></code> frequency band. In sound energy mode
* this always returns false.
*
* @param i
* int: the frequency band to query
* @return boolean: true if a beat has been detected in the requested band
*
* @example Analysis/SoundEnergyBeatDetection
*
* @related BeatDetect
*/
public boolean isOnset(int i)
{
if (algorithm == SOUND_ENERGY)
{
return false;
}
return fIsOnset[i];
}
/**
* In frequency energy mode this returns true if a beat corresponding to the
* frequency range of a kick drum has been detected. This has been tuned to
* work well with dance / techno music and may not perform well with other
* styles of music