From 961023189afde29a3395b4bd069af7b300372f1e Mon Sep 17 00:00:00 2001 From: Vincent Sijben Date: Tue, 16 Apr 2024 22:56:04 +0200 Subject: [PATCH] add buffersize setting and changed FA basic example slightly + version bump --- FrequencyAnalyzer.md | 1 + examples/FrequencyAnalyzer/basics/basics.pde | 66 ++++++------------- resources/build.properties | 4 +- .../arduinocontrols/ArduinoControls.java | 22 ++++++- .../AudioFileInputSource.java | 6 +- .../frequencyanalyzer/FrequencyAnalyzer.java | 47 +++++++++---- .../frequencyanalyzer/LineInInputSource.java | 9 +-- .../MicrophoneInputSource.java | 8 ++- 8 files changed, 93 insertions(+), 70 deletions(-) diff --git a/FrequencyAnalyzer.md b/FrequencyAnalyzer.md index ba45f4e..eb20548 100644 --- a/FrequencyAnalyzer.md +++ b/FrequencyAnalyzer.md @@ -65,6 +65,7 @@ You can tweak the behaviour of this library with the following functions (you ca * `.addMinim(minim)` mandatory to add the global minim object to the class. * `.setFile("example.mp3")` to set the file for the audioplayer. * `.setAudioInputMode(AudioInputMode.AUDIO_FILE)` to set the input mode to AudioInputMode.AUDIO_FILE. You can also set it to AudioInputMode.LINE_IN or AudioInputMode.MICROPHONE. Defaults to AudioInputMode.MICROPHONE. +* `.setAudioInputMode(AudioInputMode.AUDIO_FILE, n)` same as previous one, but you can also set the bufferSize. Needs to be a power of 2. A lower amount results in less audio resolution and decreases delay * `.setAudioOutputMode(AudioOutputMode.STEREO)` to set the output mode to AudioOutputMode.STEREO. Defaults to AudioOutputMode.MONO. Use it to get access to both left and right channel analysis. * `.showInfoPanel()` to show the infopanel. * `.setInfoPanelY(n)` to offset the starting y-position of the infopanel by n pixels. Useful for when you have multiple infopanels to get them all lined up. diff --git a/examples/FrequencyAnalyzer/basics/basics.pde b/examples/FrequencyAnalyzer/basics/basics.pde index b9e966e..6dec456 100644 --- a/examples/FrequencyAnalyzer/basics/basics.pde +++ b/examples/FrequencyAnalyzer/basics/basics.pde @@ -16,6 +16,7 @@ void setup() { .addMinim(minim) .setFile("https://github.com/vincentsijben/bpm-timings-for-processing/raw/main/assets/infraction_music_-_ritmo.mp3") .setAudioInputMode(AudioInputMode.AUDIO_FILE) + //.setAudioInputMode(AudioInputMode.AUDIO_FILE, 128) // also set bufferSize, needs to be power of 2. Lower amount means less audio resolution and decreasing delay //.setAudioInputMode(AudioInputMode.LINE_IN) .setAudioOutputMode(AudioOutputMode.MONO) ; @@ -26,13 +27,9 @@ void draw() { background(0); // Clear the screen with a black background noFill(); - // Example visualization: Draw waveforms for left and right channels - //drawWaveform(fa.getLeftChannelBuffer(), 0, height / 4, width, height / 2, color(255, 0, 0)); // Red for left channel - //drawWaveform(fa.getRightChannelBuffer(), 0, 3 * height / 4, width, height / 2, color(0, 0, 255)); // Blue for right channel - - // Example visualization: Draw the frequency spectrum using FFT analysis // Only visible when MONO output (mixed fft) + //println(fa.specSize()); for (int i = 0; i < fa.specSize(); i++) { // Calculate the amplitude and position for each frequency band float amplitude = fa.getBand(i); @@ -44,54 +41,17 @@ void draw() { // Only visible when STEREO output (fftRight and fftLeft) // Visualize the left channel - drawChannelFFT(fa.getFFTLeft(), color(255, 255, 0), 0, height / 2); + //drawChannelFFT(fa.getFFTLeft(), color(255, 255, 0), 0, height / 2); - // Visualize the right channel - drawChannelFFT(fa.getFFTRight(), color(0, 0, 255), height / 2, height); + //// Visualize the right channel + //drawChannelFFT(fa.getFFTRight(), color(0, 0, 255), height / 2, height); fill(0, 200, 0); circle(width/4*1, height/2, fa.getAvgRawRight(0)*30); circle(width/4*2, height/2, fa.getAvgRaw(10)*30); circle(width/4*3, height/2, fa.getAvgRaw(20)*30); - //} - //if (currentInputSource instanceof AudioFileInputSource) { - - // AudioFileInputSource lineInInputSource = (AudioFileInputSource) currentInputSource; - // lineInInputSource.performFFT(); // Perform FFT on both channels - - // // Visualize the left channel - // drawChannelFFT(lineInInputSource.fftLeft, color(255, 0, 0), 0, height / 2); - - // // Visualize the right channel - // drawChannelFFT(lineInInputSource.fftRight, color(0, 0, 255), height / 2, height); - //} - - // if (currentInputSource instanceof LineInInputSource) { - - // LineInInputSource lineInInputSource = (LineInInputSource) currentInputSource; - // lineInInputSource.performFFT(); // Perform FFT on both channels - - // // Visualize the left channel - // drawChannelFFT(lineInInputSource.fftLeft, color(255, 0, 0), 0, height / 2); - - // // Visualize the right channel - // drawChannelFFT(lineInInputSource.fftRight, color(0, 0, 255), height / 2, height); - //} -} - -void drawChannelFFT(FFT fft2, int color2, float startY, float endY) { - stroke(color2); - //println(fft2.specSize()); - for (int i = 0; i < fft2.specSize(); i++) { - // Map the frequency band index to the x-coordinate - float x = map(i, 0, fft2.specSize(), 0, width); - // Map the amplitude to the y-coordinate, scaling by the height of the channel visualization area - - float amplitude = fft2.getBand(i) * 5; // Scale factor for visual effect - float y = map(amplitude, 0, 100, endY, startY); // Invert direction for visual effect - line(x, endY, x, y); - } + } void drawWaveform(float[] buffer, float x, float y, float w, float h, int strokeColor) { @@ -105,3 +65,17 @@ void drawWaveform(float[] buffer, float x, float y, float w, float h, int stroke } endShape(); } + +//void drawChannelFFT(FFT fft2, int color2, float startY, float endY) { +// stroke(color2); +// //println(fft2.specSize()); +// for (int i = 0; i < fft2.specSize(); i++) { +// // Map the frequency band index to the x-coordinate +// float x = map(i, 0, fft2.specSize(), 0, width); +// // Map the amplitude to the y-coordinate, scaling by the height of the channel visualization area + +// float amplitude = fft2.getBand(i) * 5; // Scale factor for visual effect +// float y = map(amplitude, 0, 100, endY, startY); // Invert direction for visual effect +// line(x, endY, x, y); +// } +//} diff --git a/resources/build.properties b/resources/build.properties index 8420e72..52ea3f3 100644 --- a/resources/build.properties +++ b/resources/build.properties @@ -132,12 +132,12 @@ source.repository=https://github.com/vincentsijben/bpm-tmings-for-processing.git # This is used to compare different versions of the same Library, and check if # an update is available. -library.version=16 +library.version=17 # The version as the user will see it. -library.prettyVersion=1.2.2 +library.prettyVersion=1.2.3 # The min and max revision of Processing compatible with your Library. diff --git a/src/bpm/library/arduinocontrols/ArduinoControls.java b/src/bpm/library/arduinocontrols/ArduinoControls.java index e5f43c8..f1cbde0 100644 --- a/src/bpm/library/arduinocontrols/ArduinoControls.java +++ b/src/bpm/library/arduinocontrols/ArduinoControls.java @@ -41,6 +41,7 @@ public ArduinoControls(PApplet parent) { //https://github.com/benfry/processing4/wiki/Library-Basics parent.registerMethod("draw", this); + parent.registerMethod("pre", this); parent.registerMethod("post", this); parent.registerMethod("keyEvent", this); } @@ -286,6 +287,12 @@ public ArduinoControls setInfoPanelKey(char keyboardKey) { } public void draw() { + // make sure everything in the main sketch is wrapped inside pushMatrix and popMatrix, so the infopanel is always shown top left, even in 3D mode + // pushMatrix in registermethod pre() + // popMatrix in registermethod draw() + this.parent.popMatrix(); + this.parent.popStyle(); + this.parent.hint(PConstants.DISABLE_DEPTH_TEST); if (this.infoPanel.show) { //System.out.println(""+this.infoPanel.x + this.infoPanel.y + this.infoPanel.w + this.infoPanel.h); boolean portrait = false; //this.infoPanelLocation[2] < this.infoPanelLocation[3]; @@ -306,6 +313,7 @@ public void draw() { this.parent.image(overlay, this.infoPanel.x, this.infoPanel.y, this.infoPanel.w, this.infoPanel.h); // Draw the overlay onto the main canvas } + this.parent.hint(PConstants.ENABLE_DEPTH_TEST); } @@ -320,6 +328,18 @@ public void draw() { public void post() { // https://github.com/benfry/processing4/wiki/Library-Basics // you cant draw in post() but its perfect for resetting the inputButtonsOnce array: - if (this.parent.frameCount != this.lastFrameCount) for (PushButton button : pushbuttons) button.pressedOnce = false; + } + + public void pre() { + + if (this.parent.frameCount != this.lastFrameCount) for (PushButton button : pushbuttons) button.pressedOnce = false; + + // make sure everything in the main sketch is wrapped inside pushMatrix and popMatrix, so the infopanel is always shown top left, even in 3D mode + // pushMatrix in registermethod pre() + // popMatrix in registermethod draw() + this.parent.pushMatrix(); + this.parent.pushStyle(); } +} + diff --git a/src/bpm/library/frequencyanalyzer/AudioFileInputSource.java b/src/bpm/library/frequencyanalyzer/AudioFileInputSource.java index 46b0846..a82a291 100644 --- a/src/bpm/library/frequencyanalyzer/AudioFileInputSource.java +++ b/src/bpm/library/frequencyanalyzer/AudioFileInputSource.java @@ -8,6 +8,7 @@ class AudioFileInputSource implements AudioInputSource { FFT fftLeft, fftRight, fftMixed; AudioPlayer player; String filePath; + int bufferSize = 1024; // Default setting public AudioOutputMode channelOutput = AudioOutputMode.MONO; // Default setting @@ -15,14 +16,15 @@ public void setAudioOutputMode(AudioOutputMode mode) { this.channelOutput = mode; } - public AudioFileInputSource(Minim minim, String filePath) { + public AudioFileInputSource(Minim minim, int size, String filePath) { this.minim = minim; this.filePath = filePath; + this.bufferSize = size; } @Override public void init() { - this.player = minim.loadFile(filePath, 2048); // Load the file with a buffer size of 2048 + this.player = minim.loadFile(filePath, this.bufferSize); // Load the file with a buffer size of 2048 this.fftLeft = new FFT(this.player.bufferSize(), this.player.sampleRate()); this.fftRight = new FFT(this.player.bufferSize(), this.player.sampleRate()); this.fftMixed = new FFT(this.player.bufferSize(), this.player.sampleRate()); diff --git a/src/bpm/library/frequencyanalyzer/FrequencyAnalyzer.java b/src/bpm/library/frequencyanalyzer/FrequencyAnalyzer.java index be38866..a231598 100644 --- a/src/bpm/library/frequencyanalyzer/FrequencyAnalyzer.java +++ b/src/bpm/library/frequencyanalyzer/FrequencyAnalyzer.java @@ -26,19 +26,22 @@ public class FrequencyAnalyzer { private float durationResetMaxValue; private int startTime; private float maxVal; + private int bufferSize; public FrequencyAnalyzer(PApplet parent) { this.parent = parent; this.infoPanel = new InfoPanel(parent); - + this.enableKeyPress = true; this.keyPressedActionTaken = false; this.durationResetMaxValue = 0.0f; this.startTime = 0; this.maxVal = 0.1f; //avoid NaN when using maxVal in map() in the first frame. + this.bufferSize = 1024; parent.registerMethod("draw", this); + parent.registerMethod("pre", this); parent.registerMethod("post", this); parent.registerMethod("keyEvent", this); parent.registerMethod("dispose", this); @@ -47,8 +50,8 @@ public FrequencyAnalyzer(PApplet parent) { public FrequencyAnalyzer addMinim(Minim minim) { this.minim = minim; this.setAudioInputMode(AudioInputMode.MICROPHONE); - - + + return this; } @@ -79,7 +82,7 @@ public FrequencyAnalyzer setAudioOutputMode(AudioOutputMode mode) { return this; } - public FrequencyAnalyzer setAudioInputMode(AudioInputMode newMode) { + public FrequencyAnalyzer setAudioInputMode(AudioInputMode newMode, int size) { if (newMode == this.currentInputMode) return this; if (currentInputSource != null) { currentInputSource.close(); @@ -89,18 +92,18 @@ public FrequencyAnalyzer setAudioInputMode(AudioInputMode newMode) { // Initialize the new input source based on the selected mode switch (newMode) { case MICROPHONE: - currentInputSource = new MicrophoneInputSource(minim); // Assuming a MicrophoneSource class exists + currentInputSource = new MicrophoneInputSource(minim, size); // Assuming a MicrophoneSource class exists break; case LINE_IN: - currentInputSource = new LineInInputSource(minim); + currentInputSource = new LineInInputSource(minim, size); break; case AUDIO_FILE: - if (this.file == null) System.out.println("no audio file was set"); - + if (this.file == null) System.out.println("no audio file was set"); + //currentInputSource = new AudioFileInputSource(minim, "https://github.com/vincentsijben/bpm-timings-for-processing/raw/main/assets/infraction_music_-_ritmo.mp3"); // Assuming an AudioFileSource class exists - currentInputSource = new AudioFileInputSource(minim, this.file); // Assuming an AudioFileSource class exists + currentInputSource = new AudioFileInputSource(minim, size, this.file); // Assuming an AudioFileSource class exists //"stereotest.mp3" - + break; } @@ -115,6 +118,10 @@ public FrequencyAnalyzer setAudioInputMode(AudioInputMode newMode) { return this; } + public FrequencyAnalyzer setAudioInputMode(AudioInputMode newMode) { + return setAudioInputMode(newMode, 1024); + } + @@ -212,9 +219,8 @@ public FrequencyAnalyzer disableKeyPress() { private void toggleMuteOrMonitoring() { if (currentInputSource.isMonitoring()) currentInputSource.disableMonitoring(); else currentInputSource.enableMonitoring(); - + //todo: add muted toggle? - } public void keyEvent(KeyEvent event) { @@ -288,6 +294,14 @@ public FrequencyAnalyzer setInfoPanelKey(char keyboardKey) { } public void draw() { + // make sure everything in the main sketch is wrapped inside pushMatrix and popMatrix, so the infopanel is always shown top left, even in 3D mode + // pushMatrix in registermethod pre() + // popMatrix in registermethod draw() + this.parent.popMatrix(); + this.parent.popStyle(); + this.parent.hint(PConstants.DISABLE_DEPTH_TEST); + + if (this.infoPanel.show) { PGraphics overlay = this.infoPanel.overlay; overlay.beginDraw(); @@ -337,6 +351,7 @@ public void draw() { overlay.endDraw(); this.parent.image(overlay, this.infoPanel.x, this.infoPanel.y, this.infoPanel.w, this.infoPanel.h); // Draw the overlay onto the main canvas } + this.parent.hint(PConstants.ENABLE_DEPTH_TEST); } @@ -351,6 +366,9 @@ public void draw() { public void post() { // https://github.com/benfry/processing4/wiki/Library-Basics // you cant draw in post() but its perfect for the fft analysis: + } + + public void pre() { if (this.minim != null) { this.performFFT(); @@ -363,6 +381,11 @@ public void post() { // } //} } + // make sure everything in the main sketch is wrapped inside pushMatrix and popMatrix, so the infopanel is always shown top left, even in 3D mode + // pushMatrix in registermethod pre() + // popMatrix in registermethod draw() + this.parent.pushMatrix(); + this.parent.pushStyle(); } public void dispose() { diff --git a/src/bpm/library/frequencyanalyzer/LineInInputSource.java b/src/bpm/library/frequencyanalyzer/LineInInputSource.java index 8239fa4..5ed5d7a 100644 --- a/src/bpm/library/frequencyanalyzer/LineInInputSource.java +++ b/src/bpm/library/frequencyanalyzer/LineInInputSource.java @@ -7,6 +7,7 @@ class LineInInputSource implements AudioInputSource { Minim minim; AudioInput lineIn; FFT fftLeft, fftRight, fftMixed; + int bufferSize = 1024; //Default setting private AudioOutputMode channelOutput = AudioOutputMode.MONO; // Default setting @@ -14,15 +15,15 @@ public void setAudioOutputMode(AudioOutputMode mode) { this.channelOutput = mode; } - public LineInInputSource(Minim minim) { + public LineInInputSource(Minim minim, int size) { this.minim = minim; + this.bufferSize = size; } @Override public void init() { // Assuming a stereo input for line-in - this.lineIn = this.minim.getLineIn(Minim.MONO); - + this.lineIn = this.minim.getLineIn(Minim.MONO, this.bufferSize); this.fftLeft = new FFT(this.lineIn.bufferSize(), this.lineIn.sampleRate()); this.fftRight = new FFT(this.lineIn.bufferSize(), this.lineIn.sampleRate()); this.fftMixed = new FFT(this.lineIn.bufferSize(), this.lineIn.sampleRate()); @@ -34,7 +35,7 @@ public void init() { @Override public void start() { // Line-in starts automatically with getLineIn, but you might want to add logic here if needed - this.lineIn.enableMonitoring(); + //this.lineIn.enableMonitoring(); } @Override diff --git a/src/bpm/library/frequencyanalyzer/MicrophoneInputSource.java b/src/bpm/library/frequencyanalyzer/MicrophoneInputSource.java index 628cb03..a7522a9 100644 --- a/src/bpm/library/frequencyanalyzer/MicrophoneInputSource.java +++ b/src/bpm/library/frequencyanalyzer/MicrophoneInputSource.java @@ -7,6 +7,7 @@ class MicrophoneInputSource implements AudioInputSource { Minim minim; AudioInput mic; FFT fftLeft, fftRight, fftMixed; + int bufferSize = 1024; //Default setting public AudioOutputMode channelOutput = AudioOutputMode.MONO; // Default setting @@ -14,18 +15,19 @@ public void setAudioOutputMode(AudioOutputMode mode) { this.channelOutput = mode; } - public MicrophoneInputSource(Minim minim) { + public MicrophoneInputSource(Minim minim, int size) { this.minim = minim; + this.bufferSize = size; } @Override public void init() { // Initialize the microphone input. Assuming mono input. - this.mic = minim.getLineIn(Minim.MONO); + this.mic = minim.getLineIn(Minim.MONO, this.bufferSize); this.fftLeft = new FFT(this.mic.bufferSize(), this.mic.sampleRate()); this.fftRight = new FFT(this.mic.bufferSize(), this.mic.sampleRate()); this.fftMixed = new FFT(this.mic.bufferSize(), this.mic.sampleRate()); - this.fftLeft.logAverages(22, 3); + this.fftLeft.logAverages(22, 3); this.fftRight.logAverages(22, 3); this.fftMixed.logAverages(22, 3); }