33// W_Spectrogram.pde //
44// //
55// //
6- // Created by: Richard Waltman, September 2019 //
6+ // Created by: Richard Waltman, September 2019 //
7+ // Refactored by: Richard Waltman, April 2025 //
78// //
89// ////////////////////////////////////////////////////
910
10- class W_Spectrogram extends Widget {
11- // to see all core variables/methods of the Widget class, refer to Widget.pde
11+ class W_Spectrogram extends WidgetWithSettings {
1212 public ExGChannelSelect spectChanSelectTop;
1313 public ExGChannelSelect spectChanSelectBot;
1414 private boolean chanSelectWasOpen = false ;
15- List<controlP5. Controller > cp5ElementsToCheck = new ArrayList< controlP5 . Controller > () ;
15+ List<controlP5. Controller > cp5ElementsToCheck;
1616
1717 int xPos = 0 ;
1818 int hueLimit = 160 ;
@@ -43,22 +43,10 @@ class W_Spectrogram extends Widget {
4343 float [] topFFTAvg;
4444 float [] botFFTAvg;
4545
46- private SpectrogramMaxFrequency maxFrequency = SpectrogramMaxFrequency . MAX_60 ;
47- private SpectrogramWindowSize windowSize = SpectrogramWindowSize . ONE_MINUTE ;
48- private FFTLogLin logLin = FFTLogLin . LIN ;
49-
5046 W_Spectrogram () {
5147 super ();
5248 widgetTitle = " Spectrogram" ;
5349
54- // Add channel select dropdown to this widget
55- spectChanSelectTop = new DualExGChannelSelect (ourApplet, x, y, w, navH, true );
56- spectChanSelectBot = new DualExGChannelSelect (ourApplet, x, y + navH, w, navH, false );
57- activateDefaultChannels();
58-
59- cp5ElementsToCheck. addAll(spectChanSelectTop. getCp5ElementsForOverlapCheck());
60- cp5ElementsToCheck. addAll(spectChanSelectBot. getCp5ElementsForOverlapCheck());
61-
6250 xPos = w - 1 ; // draw on the right, and shift pixels to the left
6351 prevW = w;
6452 prevH = h;
@@ -70,20 +58,62 @@ class W_Spectrogram extends Widget {
7058 // Fetch/calculate the time strings for the horizontal axis ticks
7159 horizontalAxisLabelStrings = fetchTimeStrings();
7260
73- List<String > maxFrequencyList = EnumHelper . getEnumStrings(SpectrogramMaxFrequency . class);
74- List<String > windowSizeList = EnumHelper . getEnumStrings(SpectrogramWindowSize . class);
75- List<String > logLinList = EnumHelper . getEnumStrings(FFTLogLin . class);
76-
77- addDropdown(" spectrogramMaxFrequencyDropdown" , " Max Hz" , maxFrequencyList, maxFrequency. getIndex());
78- addDropdown(" spectrogramWindowDropdown" , " Window" , windowSizeList, windowSize. getIndex());
79- addDropdown(" spectrogramLogLinDropdown" , " Log/Lin" , logLinList, logLin. getIndex());
80-
8161 // Resize the height of the data image using default
62+ SpectrogramMaxFrequency maxFrequency = widgetSettings. get(SpectrogramMaxFrequency . class);
8263 dataImageH = maxFrequency. getAxisLabels()[0 ] * 2 ;
8364 // Create image using correct dimensions! Fixes bug where image size and labels do not align on session start.
8465 dataImg = createImage (dataImageW, dataImageH, RGB );
8566 }
8667
68+ @Override
69+ protected void initWidgetSettings () {
70+ super . initWidgetSettings();
71+ widgetSettings. set(SpectrogramMaxFrequency . class, SpectrogramMaxFrequency . MAX_60 )
72+ .set(SpectrogramWindowSize . class, SpectrogramWindowSize . ONE_MINUTE )
73+ .set(FFTLogLin . class, FFTLogLin . LIN );
74+
75+ initDropdown(SpectrogramMaxFrequency . class, " spectrogramMaxFrequencyDropdown" , " Max Hz" );
76+ initDropdown(SpectrogramWindowSize . class, " spectrogramWindowDropdown" , " Window" );
77+ initDropdown(FFTLogLin . class, " spectrogramLogLinDropdown" , " Log/Lin" );
78+
79+ spectChanSelectTop = new DualExGChannelSelect (ourApplet, x, y, w, navH, true );
80+ spectChanSelectBot = new DualExGChannelSelect (ourApplet, x, y + navH, w, navH, false );
81+ activateDefaultChannels();
82+
83+ // Save both channel selections with unique identifiers
84+ saveNamedChannels(" top" , spectChanSelectTop. getActiveChannels());
85+ saveNamedChannels(" bottom" , spectChanSelectBot. getActiveChannels());
86+
87+ cp5ElementsToCheck = new ArrayList<controlP5. Controller > ();
88+ cp5ElementsToCheck. addAll(spectChanSelectTop. getCp5ElementsForOverlapCheck());
89+ cp5ElementsToCheck. addAll(spectChanSelectBot. getCp5ElementsForOverlapCheck());
90+ }
91+
92+ @Override
93+ protected void applySettings () {
94+ updateDropdownLabel(SpectrogramMaxFrequency . class, " spectrogramMaxFrequencyDropdown" );
95+ updateDropdownLabel(SpectrogramWindowSize . class, " spectrogramWindowDropdown" );
96+ updateDropdownLabel(FFTLogLin . class, " spectrogramLogLinDropdown" );
97+ applyMaxFrequency();
98+ applyWindowSize();
99+
100+ // Apply saved channel selections if available
101+ if (hasNamedChannels(" top" )) {
102+ applyNamedChannels(" top" , spectChanSelectTop);
103+ }
104+
105+ if (hasNamedChannels(" bottom" )) {
106+ applyNamedChannels(" bottom" , spectChanSelectBot);
107+ }
108+ }
109+
110+ @Override
111+ protected void updateChannelSettings () {
112+ // Save current channel selections before saving settings
113+ saveNamedChannels(" top" , spectChanSelectTop. getActiveChannels());
114+ saveNamedChannels(" bottom" , spectChanSelectBot. getActiveChannels());
115+ }
116+
87117 void update (){
88118 super . update();
89119
@@ -148,8 +178,11 @@ class W_Spectrogram extends Widget {
148178 // draw the spectrogram if the widget is open, and update pixels if board is streaming data
149179 if (currentBoard. isStreaming()) {
150180 pushStyle ();
181+
151182 dataImg. loadPixels();
152183
184+ FFTLogLin logLin = widgetSettings. get(FFTLogLin . class);
185+
153186 // Shift all pixels to the left! (every scrollspeed ms)
154187 if (millis () - lastShift > scrollSpeed) {
155188 for (int r = 0 ; r < dataImg. height ; r++ ) {
@@ -265,6 +298,7 @@ class W_Spectrogram extends Widget {
265298 fill (255 );
266299 strokeWeight (2 );
267300 textSize (11 );
301+ SpectrogramWindowSize windowSize = widgetSettings. get(SpectrogramWindowSize . class);
268302 int horizontalAxisDivCount = windowSize. getAxisLabels(). length;
269303 for (int i = 0 ; i < horizontalAxisDivCount; i++ ) {
270304 float offset = scaledW * dataImageW * (float (i) / horizontalAxisDivCount);
@@ -297,6 +331,7 @@ class W_Spectrogram extends Widget {
297331 fill (255 );
298332 textSize (12 );
299333 strokeWeight (2 );
334+ SpectrogramMaxFrequency maxFrequency = widgetSettings. get(SpectrogramMaxFrequency . class);
300335 int verticalAxisDivCount = maxFrequency. getAxisLabels(). length - 1 ;
301336 for (int i = 0 ; i < verticalAxisDivCount; i++ ) {
302337 float offset = scaledH * dataImageH * (float (i) / verticalAxisDivCount);
@@ -329,6 +364,7 @@ class W_Spectrogram extends Widget {
329364 return ;
330365 }
331366 }
367+ FFTLogLin logLin = widgetSettings. get(FFTLogLin . class);
332368 pushStyle ();
333369 // draw color scale reference to the right of the spectrogram
334370 for (int i = 0 ; i < colorScaleHeight; i++ ) {
@@ -405,6 +441,7 @@ class W_Spectrogram extends Widget {
405441 time = LocalDateTime . ofInstant(Instant . ofEpochMilli(getCurrentTimeStamp()),
406442 TimeZone . getDefault(). toZoneId());
407443 }
444+ SpectrogramWindowSize windowSize = widgetSettings. get(SpectrogramWindowSize . class);
408445 for (int i = 0 ; i < windowSize. getAxisLabels(). length; i++ ) {
409446 long l = (long )(windowSize. getAxisLabels()[i] * 60f );
410447 LocalDateTime t = time. minus(l, ChronoUnit . SECONDS );
@@ -434,24 +471,33 @@ class W_Spectrogram extends Widget {
434471 }
435472
436473 public void setLogLin (int n ) {
437- logLin = logLin . values()[n] ;
474+ widgetSettings . setByIndex( FFTLogLin . class, n) ;
438475 }
439476
440477 public void setMaxFrequency (int n ) {
441- maxFrequency = maxFrequency. values()[n];
478+ widgetSettings. setByIndex(SpectrogramMaxFrequency . class, n);
479+ applyMaxFrequency();
480+ }
481+
482+ public void setWindowSize (int n ) {
483+ widgetSettings. setByIndex(SpectrogramWindowSize . class, n);
484+ applyWindowSize();
485+ }
486+
487+ private void applyMaxFrequency () {
488+ SpectrogramMaxFrequency maxFrequency = widgetSettings. get(SpectrogramMaxFrequency . class);
442489 // Resize the height of the data image
443490 dataImageH = maxFrequency. getAxisLabels()[0 ] * 2 ;
444491 // Overwrite the existing image
445492 dataImg = createImage (dataImageW, dataImageH, RGB );
446493 }
447494
448- public void setWindowSize ( int n ) {
449- windowSize = windowSize . values()[n] ;
495+ private void applyWindowSize ( ) {
496+ SpectrogramWindowSize windowSize = widgetSettings . get( SpectrogramWindowSize . class) ;
450497 setScrollSpeed(windowSize. getScrollSpeed());
451498 horizontalAxisLabelStrings. clear();
452499 horizontalAxisLabelStrings = fetchTimeStrings();
453500 dataImg = createImage (dataImageW, dataImageH, RGB );
454-
455501 }
456502};
457503
@@ -466,69 +512,3 @@ public void spectrogramWindowDropdown(int n) {
466512public void spectrogramLogLinDropdown (int n ) {
467513 ((W_Spectrogram ) widgetManager. getWidget(" W_Spectrogram" )). setLogLin(n);
468514}
469-
470-
471- // Save data from the Active channel checkBoxes - Top
472- // JSONArray saveActiveChanSpectTop = new JSONArray();
473- /*
474- //FIX ME
475- int numActiveSpectChanTop = w_spectrogram.spectChanSelectTop.getActiveChannels().size();
476- for (int i = 0; i < numActiveSpectChanTop; i++) {
477- int activeChannel = w_spectrogram.spectChanSelectTop.getActiveChannels().get(i);
478- saveActiveChanSpectTop.setInt(i, activeChannel);
479- }
480- saveSpectrogramSettings.setJSONArray("activeChannelsTop", saveActiveChanSpectTop);
481- //Save data from the Active channel checkBoxes - Bottom
482- JSONArray saveActiveChanSpectBot = new JSONArray();
483- int numActiveSpectChanBot = w_spectrogram.spectChanSelectBot.getActiveChannels().size();
484- for (int i = 0; i < numActiveSpectChanBot; i++) {
485- int activeChannel = w_spectrogram.spectChanSelectBot.getActiveChannels().get(i);
486- saveActiveChanSpectBot.setInt(i, activeChannel);
487- }
488- */
489- // saveSpectrogramSettings.setJSONArray("activeChannelsBot", saveActiveChanSpectBot);
490- // Save Spectrogram_Max Freq Setting. The max frq variable is updated every time the user selects a dropdown in the spectrogram widget
491- // FIX ME
492- /*
493- saveSpectrogramSettings.setInt("Spectrogram_Max Freq", spectMaxFrqSave);
494- saveSpectrogramSettings.setInt("Spectrogram_Sample Rate", spectSampleRateSave);
495- saveSpectrogramSettings.setInt("Spectrogram_LogLin", spectLogLinSave);
496- */
497-
498- // Get Band Power widget settings
499- // FIX ME
500- /*
501- loadBPActiveChans.clear();
502- JSONObject loadBPSettings = loadSettingsJSONData.getJSONObject(kJSONKeyBandPower);
503- JSONArray loadBPChan = loadBPSettings.getJSONArray("activeChannels");
504- for (int i = 0; i < loadBPChan.size(); i++) {
505- loadBPActiveChans.add(loadBPChan.getInt(i));
506- }
507- loadBPAutoClean = loadBPSettings.getInt("bpAutoClean");
508- loadBPAutoCleanThreshold = loadBPSettings.getInt("bpAutoCleanThreshold");
509- loadBPAutoCleanTimer = loadBPSettings.getInt("bpAutoCleanTimer");
510- //println("Settings: band power active chans loaded = " + loadBPActiveChans );
511- */
512-
513- /*
514- try {
515- //Get Spectrogram widget settings
516- loadSpectActiveChanTop.clear();
517- loadSpectActiveChanBot.clear();
518- JSONObject loadSpectSettings = loadSettingsJSONData.getJSONObject(kJSONKeySpectrogram);
519- JSONArray loadSpectChanTop = loadSpectSettings.getJSONArray("activeChannelsTop");
520- for (int i = 0; i < loadSpectChanTop.size(); i++) {
521- loadSpectActiveChanTop.add(loadSpectChanTop.getInt(i));
522- }
523- JSONArray loadSpectChanBot = loadSpectSettings.getJSONArray("activeChannelsBot");
524- for (int i = 0; i < loadSpectChanBot.size(); i++) {
525- loadSpectActiveChanBot.add(loadSpectChanBot.getInt(i));
526- }
527- spectMaxFrqLoad = loadSpectSettings.getInt("Spectrogram_Max Freq");
528- spectSampleRateLoad = loadSpectSettings.getInt("Spectrogram_Sample Rate");
529- spectLogLinLoad = loadSpectSettings.getInt("Spectrogram_LogLin");
530- //println(loadSpectActiveChanTop, loadSpectActiveChanBot);
531- } catch (Exception e) {
532- e.printStackTrace();
533- }
534- */
0 commit comments