Skip to content

Commit 0264fde

Browse files
committed
v0.6
1. add visibility control button 2. add FFT size dropdown menu 3. add scroll speed menu 4. add enhanced STFT spectrogram with time-frequency reassignment
1 parent b86283d commit 0264fde

File tree

5 files changed

+576
-162
lines changed

5 files changed

+576
-162
lines changed

CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
cmake_minimum_required(VERSION 3.22)
44

5-
project(PerceptoMap VERSION 0.5)
5+
project(PerceptoMap VERSION 0.6)
66

77

88
### Dependency versions ###

Source/PluginEditor.cpp

Lines changed: 121 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,22 @@ SpectrogramAudioProcessorEditor::SpectrogramAudioProcessorEditor(SpectrogramAudi
3030
// Set sample rate
3131
spectrogram.setSampleRate(audioProcessor.getSampleRate());
3232

33+
// Fixed display of fold/expand button (always visible)
34+
addAndMakeVisible(toggleUiButton);
35+
toggleUiButton.setTooltip("Show/Hide control panels");
36+
toggleUiButton.setAlwaysOnTop(true);
37+
toggleUiButton.onClick = [this]()
38+
{
39+
controlsVisible = !controlsVisible;
40+
updateControlsVisibility();
41+
// switch arrow
42+
toggleUiButton.setButtonText(controlsVisible ? HideMenuText : ShowMenuText);
43+
resized();
44+
repaint();
45+
};
46+
// init visibility
47+
updateControlsVisibility();
48+
3349
// Add and configure freeze button
3450
addAndMakeVisible(freezeButton);
3551
freezeButton.setTooltip("Freeze or resume spectrogram scrolling");
@@ -40,6 +56,41 @@ SpectrogramAudioProcessorEditor::SpectrogramAudioProcessorEditor(SpectrogramAudi
4056
spectrogram.setFrozen(isFrozen);
4157
};
4258

59+
// Add and configure FFT size dropdown
60+
addAndMakeVisible(fftSizeBox);
61+
fftSizeBox.setTooltip("Select FFT window size. Larger = better frequency, worse time resolution.");
62+
fftSizeBox.addItem("512", 9); // 2^9
63+
fftSizeBox.addItem("1024", 10); // 2^10
64+
fftSizeBox.addItem("2048", 11); // 2^11
65+
fftSizeBox.addItem("4096", 12); // 2^12
66+
fftSizeBox.addItem("8192", 13); // 2^13
67+
68+
fftSizeBox.setSelectedId(11); // default: 2048
69+
fftSizeBox.onChange = [this]()
70+
{
71+
const int newOrder = fftSizeBox.getSelectedId();
72+
spectrogram.setFFTOrder(newOrder);
73+
updateLegendImage();
74+
repaint();
75+
};
76+
77+
// Add and configure scroll speed dropdown
78+
addAndMakeVisible(scrollSpeedBox);
79+
scrollSpeedBox.setTooltip(
80+
"Scroll speed (controls overlap)"
81+
);
82+
scrollSpeedBox.addItem("x1", 1); // overlap = 1
83+
scrollSpeedBox.addItem("x2", 2); // overlap = 2
84+
scrollSpeedBox.addItem("x4", 4); // overlap = 4
85+
scrollSpeedBox.addItem("x8", 8); // overlap = 8
86+
87+
scrollSpeedBox.setSelectedId(2); // default: overlap = 2
88+
scrollSpeedBox.onChange = [this]()
89+
{
90+
const int ov = scrollSpeedBox.getSelectedId();
91+
spectrogram.setOverlap(ov);
92+
};
93+
4394
// Add and configure colour scheme combo box
4495
addAndMakeVisible(colourSchemeBox);
4596
colourSchemeBox.setTooltip("Select the color map used for the spectrogram display.");
@@ -65,12 +116,14 @@ SpectrogramAudioProcessorEditor::SpectrogramAudioProcessorEditor(SpectrogramAudi
65116
spectrogramModeBox.setTooltip(
66117
"Select the type of spectrogram to display.\n"
67118
"- Linear: Standard STFT spectrogram with linear or log frequency axis.\n"
119+
"- Linear+: Enhanced STFT spectrogram after time-frequency reassignment with linear or log frequency axis.\n"
68120
"- Mel: Mel-scaled spectrogram that spaces frequencies according to nonlinear human pitch perception.\n"
69121
"- MFCC: Mel-frequency cepstral coefficient, representing timbral texture. Typically used in audio classification and speech recognition.\n"
70122
"- Spectral Centroid: STFT spectrogram with added curves showing where the energy is centered and how widely it is spread across frequencies.\n"
71123
"- Chroma: Chromagram showing the energy distribution across the 12 pitch classes (C to B), regardless of octave. Useful for analyzing harmonic content and key."
72124
);
73125
spectrogramModeBox.addItem("Linear", static_cast<int>(SpectrogramComponent::SpectrogramMode::Linear));
126+
spectrogramModeBox.addItem("Linear+", static_cast<int>(SpectrogramComponent::SpectrogramMode::LinearPlus));
74127
spectrogramModeBox.addItem("Mel", static_cast<int>(SpectrogramComponent::SpectrogramMode::Mel));
75128
spectrogramModeBox.addItem("MFCC", static_cast<int>(SpectrogramComponent::SpectrogramMode::MFCC));
76129
spectrogramModeBox.addItem("Spectral Centroid", static_cast<int>(SpectrogramComponent::SpectrogramMode::LinearWithCentroid));
@@ -159,30 +212,34 @@ void SpectrogramAudioProcessorEditor::paint(juce::Graphics& g)
159212

160213
// Legend aligned to topBar (same vertical height), placed at top-right corner
161214
const int topBarHeight = 30;
162-
const int margin = 40;
215+
const int baseMargin = 40;
216+
const int rightReserve = baseMargin + toggleW + gap;
163217

164-
const int legendX = getWidth() - legendImage.getWidth() - margin;
165-
const int legendY = (topBarHeight - legendImage.getHeight()) / 2; // vertical center inside topBar
218+
if (controlsVisible)
219+
{
220+
const int legendX = getWidth() - legendImage.getWidth() - rightReserve;
221+
const int legendY = (topBarHeight - legendImage.getHeight()) / 2; // vertical center inside topBar
166222

167-
// Draw legend color bar
168-
g.drawImage(legendImage, legendX, legendY, legendImage.getWidth(), legendImage.getHeight(),
169-
0, 0, legendImage.getWidth(), legendImage.getHeight());
223+
// Draw legend color bar
224+
g.drawImage(legendImage, legendX, legendY, legendImage.getWidth(), legendImage.getHeight(),
225+
0, 0, legendImage.getWidth(), legendImage.getHeight());
170226

171-
// dB labels
172-
g.setColour(juce::Colours::white);
173-
g.setFont(12.0f);
227+
// dB labels
228+
g.setColour(juce::Colours::white);
229+
g.setFont(12.0f);
174230

175-
if (spectrogram.getCurrentMode() == SpectrogramComponent::SpectrogramMode::MFCC ||
176-
spectrogram.getCurrentMode() == SpectrogramComponent::SpectrogramMode::Chroma)
177-
{
178-
// normalized legend label for MFCC and Chromagram [0, 1]
179-
g.drawText("0.0", legendX - 50, legendY, 45, legendImage.getHeight(), juce::Justification::right);
180-
g.drawText("1.0", legendX + legendImage.getWidth() + 5, legendY, 40, legendImage.getHeight(), juce::Justification::left);
181-
}
182-
else
183-
{
184-
g.drawText(juce::String((int)spectrogram.getFloorDb()) + " dB", legendX - 50, legendY, 45, legendImage.getHeight(), juce::Justification::right);
185-
g.drawText("0 dB", legendX + legendImage.getWidth() + 5, legendY, 40, legendImage.getHeight(), juce::Justification::left);
231+
if (spectrogram.getCurrentMode() == SpectrogramComponent::SpectrogramMode::MFCC ||
232+
spectrogram.getCurrentMode() == SpectrogramComponent::SpectrogramMode::Chroma)
233+
{
234+
// normalized legend label for MFCC and Chromagram [0, 1]
235+
g.drawText("0.0", legendX - 50, legendY, 45, legendImage.getHeight(), juce::Justification::right);
236+
g.drawText("1.0", legendX + legendImage.getWidth() + 5, legendY, 40, legendImage.getHeight(), juce::Justification::left);
237+
}
238+
else
239+
{
240+
g.drawText(juce::String((int)spectrogram.getFloorDb()) + " dB", legendX - 50, legendY, 45, legendImage.getHeight(), juce::Justification::right);
241+
g.drawText("0 dB", legendX + legendImage.getWidth() + 5, legendY, 40, legendImage.getHeight(), juce::Justification::left);
242+
}
186243
}
187244
}
188245

@@ -203,25 +260,55 @@ void SpectrogramAudioProcessorEditor::updateLegendImage()
203260
}
204261
}
205262

263+
void SpectrogramAudioProcessorEditor::updateControlsVisibility()
264+
{
265+
fftSizeBox.setVisible(controlsVisible);
266+
scrollSpeedBox.setVisible(controlsVisible);
267+
colourSchemeBox.setVisible(controlsVisible);
268+
spectrogramModeBox.setVisible(controlsVisible);
269+
logScaleBox.setVisible(controlsVisible);
270+
floorDbSlider.setVisible(controlsVisible);
271+
normFactorSlider.setVisible(controlsVisible);
272+
freezeButton.setVisible(controlsVisible);
273+
}
274+
206275
void SpectrogramAudioProcessorEditor::resized()
207276
{
208277
auto area = getLocalBounds();
209-
// top row: freeze button & legend bar
210-
auto topRow = area.removeFromTop(30);
211-
freezeButton.setBounds(topRow.removeFromLeft(100).reduced(5));
278+
279+
// fixed small button for menu visibility
280+
const int baseMargin = 6;
281+
toggleUiButton.setBounds(getWidth() - baseMargin - toggleW, baseMargin / 2, toggleW, toggleW);
282+
283+
const int rowH = controlsVisible ? kRowHeight : 0;
284+
285+
// top row: freeze button & FFT settings & legend bar
286+
auto topRow = area.removeFromTop(rowH);
287+
if (controlsVisible)
288+
{
289+
// freeze button
290+
freezeButton.setBounds(topRow.removeFromLeft(100).reduced(5));
291+
// FFT size dropdown
292+
fftSizeBox.setBounds(topRow.removeFromLeft(100).reduced(5));
293+
// scroll speed (overlap)
294+
scrollSpeedBox.setBounds(topRow.removeFromLeft(100).reduced(5));
295+
}
212296

213297
// second row: dropdown menu etc.
214-
auto secondRow = area.removeFromTop(30);
215-
// colour scheme
216-
colourSchemeBox.setBounds(secondRow.removeFromLeft(110).reduced(5));
217-
// spectrogram mode
218-
spectrogramModeBox.setBounds(secondRow.removeFromLeft(110).reduced(5));
219-
// y axis type: log or linear (for linear STFT spectrogram)
220-
logScaleBox.setBounds(secondRow.removeFromLeft(110).reduced(5));
221-
// slider floor value colour scheme
222-
floorDbSlider.setBounds(secondRow.removeFromLeft(200).reduced(5));
223-
// slider norm factor
224-
normFactorSlider.setBounds(secondRow.removeFromLeft(200).reduced(5));
298+
auto secondRow = area.removeFromTop(rowH);
299+
if (controlsVisible)
300+
{
301+
// colour scheme
302+
colourSchemeBox.setBounds(secondRow.removeFromLeft(100).reduced(5));
303+
// spectrogram mode
304+
spectrogramModeBox.setBounds(secondRow.removeFromLeft(100).reduced(5));
305+
// y axis type: log or linear (for linear STFT spectrogram)
306+
logScaleBox.setBounds(secondRow.removeFromLeft(100).reduced(5));
307+
// slider floor value colour scheme
308+
floorDbSlider.setBounds(secondRow.removeFromLeft(200).reduced(5));
309+
// slider norm factor
310+
normFactorSlider.setBounds(secondRow.removeFromLeft(200).reduced(5));
311+
}
225312

226313
// rest: spectrogram
227314
spectrogram.setBounds(area);

Source/PluginEditor.h

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,11 @@ class SpectrogramAudioProcessorEditor : public juce::AudioProcessorEditor
3232
void resized() override;
3333

3434
// Optional accessor if processor needs to push audio data to this
35-
SpectrogramComponent& getSpectrogramComponent() { return spectrogram; }
35+
SpectrogramComponent& getSpectrogramComponent() {return spectrogram;}
36+
37+
// display/hide menu button text
38+
static constexpr const char* ShowMenuText = "+";
39+
static constexpr const char* HideMenuText = "-";
3640

3741
private:
3842
juce::TooltipWindow tooltipWindow;
@@ -45,10 +49,23 @@ class SpectrogramAudioProcessorEditor : public juce::AudioProcessorEditor
4549
juce::Image legendImage;
4650
void updateLegendImage();
4751

52+
// display/hide menu button
53+
juce::TextButton toggleUiButton{HideMenuText};
54+
bool controlsVisible = true;
55+
static constexpr int kRowHeight = 30; // Height of each row
56+
const int toggleW = 24;
57+
const int gap = 8;
58+
void updateControlsVisibility();
59+
4860
// freeze button
49-
juce::TextButton freezeButton{ "Freeze" };
61+
juce::TextButton freezeButton{"Freeze"};
5062
bool isFrozen = false;
5163

64+
// dropdown menu for FFT size
65+
juce::ComboBox fftSizeBox;
66+
// dropdown menu for scroll speed
67+
juce::ComboBox scrollSpeedBox;
68+
5269
// dropdown menu for spectrogram color scheme
5370
juce::ComboBox colourSchemeBox;
5471
// dropdown menu for log scale

0 commit comments

Comments
 (0)