ASPiK SDK
Loading...
Searching...
No Matches
customviews.h
Go to the documentation of this file.
1// -----------------------------------------------------------------------------
2// ASPiK Custom Views File: customviews.h
3//
14// -----------------------------------------------------------------------------
15#pragma once
16#include "vstgui/vstgui.h"
17#include "vstgui/vstgui_uidescription.h" // for IController
18
19#include "../PluginKernel/pluginstructures.h"
20// #pragma warning(disable: 4244)
21
22namespace VSTGUI {
23
24// --- with an update cycle of ~50mSec, we need at least 2205 samples; this should be more than enough
25const int DATA_QUEUE_LEN = 4096;
26
27// --- custom message; add more here...
28const unsigned int MESSAGE_SET_STRINGLIST = 0;
29const unsigned int MESSAGE_SET_STRING = 1;
30
31
51class WaveView : public CControl, public ICustomView
52{
53public:
54 WaveView(const CRect& size, IControlListener* listener, int32_t tag);
55 ~WaveView();
56
58 virtual void updateView() override;
59
61 virtual void pushDataValue(double data) override;
62
66 void addWaveDataPoint(float fSample);
67
70 void clearBuffer();
71
75 void showXAxis(bool _paintXAxis) { paintXAxis = _paintXAxis; }
76
80 void draw(CDrawContext* pContext) override;
81
82 // --- for CControl pure abstract functions
83 CLASS_METHODS(WaveView, CControl)
84
85protected:
86 // --- turn on/off zerodB line
87 bool paintXAxis = true;
88
89 // --- circular buffer and index values
90 double* circularBuffer = nullptr;
91 int writeIndex = 0;
92 int readIndex = 0;
95
96private:
97 // --- lock-free queue for incoming data, sized to DATA_QUEUE_LEN in length
99
100};
101
102#ifdef HAVE_FFTW
103// --- FFTW (REQUIRED)
104#include "fftw3.h"
105
119enum class spectrumViewWindowType {kRectWindow, kHannWindow, kBlackmanHarrisWindow};
120
121// --- change this for higher accuracy; needs to be power of 2
122const int FFT_LEN = 512;
123
124// --- SpectrumView
125/*
126*/
150class SpectrumView : public CControl, public ICustomView
151{
152public:
153 SpectrumView(const CRect& size, IControlListener* listener, int32_t tag);
154 ~SpectrumView();
155
157 virtual void updateView() override;
158
160 virtual void pushDataValue(double data) override;
161
163 void showFilledFFT(bool _filledFFT) { filledFFT = _filledFFT; }
164
168 void setWindow(spectrumViewWindowType _window);
169
171 void draw(CDrawContext* pContext) override;
172
173 // --- for CControl pure abstract functions
174 CLASS_METHODS(SpectrumView, CControl)
175
176protected:
177 // --- for windowing; this doesn't need to be saved in current
178 // implementation but you may need it for homework/upgrading the object
179 spectrumViewWindowType window = spectrumViewWindowType::kRectWindow;
180
181 // --- setup FFTW
182 fftw_complex* data = nullptr;
183 fftw_complex* fft_result = nullptr;
184 fftw_complex* ifft_result = nullptr;
185 fftw_plan plan_forward;
186 fftw_plan plan_backward;
187
188 // --- for FFT data input
189 int fftInputCounter = 0;
190
195 bool addFFTInputData(double inputSample);
196
197 // --- a double buffer pair of magnitude arrays
198 double fftMagnitudeArray_A[FFT_LEN] = {0.0};
199 double fftMagnitudeArray_B[FFT_LEN] = {0.0};
200
201 // --- buffer for the assigned window
202 double fftWindow[FFT_LEN] = {1.0};
203
204 // --- pointer to mag buffer that drawing thread uses; note that
205 // this pointer is never shared with any other function
206 double* currentFFTMagBuffer = nullptr;
207
208 // --- NOTE: move these to another file for use by other objects!!
209 // --- helper for FFT magnitude
210 inline double getMagnitude(double re, double im)
211 {
212 return sqrt((re*re)+(im*im));
213 }
214
220 inline double normalizeBufferGetFMax(double* buffer, unsigned int bufferSize, int* ptrMaxIndex)
221 {
222 double max = 0;
223 double maxRetValue = 0;
224 *ptrMaxIndex = 0;
225
226 for(int j=0; j<bufferSize; j++)
227 {
228 if((fabs(buffer[j])) > max)
229 {
230 max = fabs(buffer[j]);
231 *ptrMaxIndex = j;
232 }
233 }
234
235 if(max > 0)
236 {
237 for(int j=0; j<bufferSize; j++)
238 {
239 buffer[j] = buffer[j]/max;
240 if(j == *ptrMaxIndex)
241 maxRetValue = buffer[j];
242 }
243 }
244
245 return maxRetValue;
246 }
247
253 inline double interpArrayValue(double* array, int arraySize, double fractionalIndex)
254 {
255 // --- extract [index_x] values
256 int x1 = (int)fractionalIndex;
257 int x2 = x1 + 1;
258
259 // --- check invalid conditions
260 if(x1 >= arraySize)
261 return 0.0;
262 if(x2 >= arraySize)
263 return array[x1];
264 if(x2 - x1 == 0) // 0 slope: should not ever happen
265 return array[x1];
266
267 // --- calculate decimal position of x
268 double dx = (fractionalIndex - x1)/(x2 - x1);
269
270 // --- use weighted sum method of interpolating
271 return dx*array[x2] + (1-dx)*array[x1];
272 }
273
274protected:
275 // --- filled/unfilled FFT
276 bool filledFFT = true;
277
278private:
279 // --- lock-free queue for incoming data, sized to FFT_LEN in length
281
282 // --- a pair of lock-free queues to store empty and full magnitude buffers
283 // these are setup as double buffers but you can easily extend them
284 // to quad (4) and octal (8) if you want
285 moodycamel::ReaderWriterQueue<double*,2>* fftMagBuffersReady = nullptr;
286 moodycamel::ReaderWriterQueue<double*,2>* fftMagBuffersEmpty = nullptr;
287};
288#endif // defined FFTW
289
290
291// --- custom view example
292const unsigned int MESSAGE_SHOW_CONTROL = 0;
293const unsigned int MESSAGE_HIDE_CONTROL = 1;
294const unsigned int MESSAGE_SET_CONTROL_ALPHA = 2;
295const unsigned int MESSAGE_QUERY_CONTROL = 3;
296
297// --- example of a custom view message; here we control the visual appearance of a control
310{
312 CustomViewMessage(const CustomViewMessage& initMessage)
313 {
314 message = initMessage.message;
315 visible = initMessage.visible;
316 showAlternateGraphic = initMessage.showAlternateGraphic;
317 controlAlpha = initMessage.controlAlpha;
318 queryString = initMessage.queryString;
319 replyString = initMessage.replyString;
320 messageData = initMessage.messageData;
321 }
322
323 CustomViewMessage& operator =(const CustomViewMessage& viewMessage)
324 {
325 message = viewMessage.message;
326 visible = viewMessage.visible;
327 showAlternateGraphic = viewMessage.showAlternateGraphic;
328 controlAlpha = viewMessage.controlAlpha;
329 queryString = viewMessage.queryString;
330 replyString = viewMessage.replyString;
331 messageData = viewMessage.messageData;
332 return *this;
333 }
334
335 // --- show/hide flag
336 unsigned int message = MESSAGE_HIDE_CONTROL;
337
338 bool visible = true;
339 bool showAlternateGraphic = false;
340 double controlAlpha = 1.0; // transparency: 0 = invisible (100% transparent) and 1 = solidly visible (0% transparent)
341 std::string queryString;
342 std::string replyString;
343 void* messageData = nullptr;
344};
345
358class CustomKnobView : public CAnimKnob, public ICustomView
359{
360public:
361 CustomKnobView(const CRect& size, IControlListener* listener, int32_t tag, int32_t subPixmaps,
362 CCoord heightOfOneImage, CBitmap* background, const CPoint &offset,
363 bool bSwitchKnob = false);
364
366 virtual void updateView() override;
367
369 virtual void sendMessage(void* data) override;
370
371protected:
372 virtual ~CustomKnobView(void);
373
374private:
375 // --- lock-free queue for incoming data, sized to 32 in length
377};
378
379
414class KnobLinkController : public IController
415{
416public:
420 KnobLinkController(IController* _parentController)
421 {
422 // --- save the parent listener
423 parentController = _parentController;
424
425 // --- INITIALIZE LINK STATE
426 linkControls = false;
427 }
429 {
430 linkedKnobs.clear();
431 }
432
437 bool isLinkedControl(CControl* control)
438 {
439 return std::find(linkedKnobs.begin(), linkedKnobs.end(), control) != linkedKnobs.end();
440 }
441
449 virtual CView* verifyView(CView* view, const UIAttributes& attributes, const IUIDescription* description) override
450 {
451 CAnimKnob* knob = dynamic_cast<CAnimKnob*>(view);
452 CTextButton* button = dynamic_cast<CTextButton*>(view);
453
454 // --- save button, push back knob onto list
455 if (button)
456 {
457 linkControl = button;
458 if (button->getValueNormalized() != 0)
459 linkControls = true;
460 else
461 linkControls = false;
462 }
463 else if (knob)
464 linkedKnobs.push_back(knob);
465
466 return view;
467 }
468
473 virtual void valueChanged(CControl* control) override
474 {
475 // --- set the link flag
476 if (control == linkControl)
477 {
478 if (control->getValueNormalized() != 0)
479 linkControls = true;
480 else
481 linkControls = false;
482
483 return parentController->valueChanged(control);
484 }
485
486 // --- check flag
487 if (!linkControls)
488 return parentController->valueChanged(control);
489
490 // --- we are linking
491 //
492 // --- make sure this is not a rogue control
493 if (isLinkedControl(control))
494 {
495 // --- iterate list
496 for (std::vector<CAnimKnob*>::iterator it = linkedKnobs.begin(); it != linkedKnobs.end(); ++it)
497 {
498 // --- set the control value for all knobs except the one generating this message
499 CControl* ctrl = *it;
500
501 if (ctrl && control != ctrl)
502 {
503 // --- set the control visually
504 ctrl->setValueNormalized(control->getValueNormalized());
505
506 // --- do the value change at parent level, to set on plugin
507 parentController->valueChanged(ctrl);
508
509 ctrl->invalid();
510 }
511 }
512 }
513 // --- do the value change at parent level, to set on plugin
514 parentController->valueChanged(control);
515 }
516
522 virtual CView* createView(const UIAttributes& attributes, const IUIDescription* description) override { return parentController->createView(attributes, description); }
523
527 virtual void controlBeginEdit(CControl* pControl)override { parentController->controlBeginEdit(pControl); }
528
532 virtual void controlEndEdit(CControl* pControl)override { parentController->controlEndEdit(pControl); }
533
538 virtual void controlTagWillChange(CControl* pControl) override
539 {
540 pControl->setListener(parentController);
541 parentController->controlTagWillChange(pControl);
542 pControl->setListener(this);
543 }
544
549 virtual void controlTagDidChange(CControl* pControl) override
550 {
551 pControl->setListener(parentController);
552 parentController->controlTagDidChange(pControl);
553 pControl->setListener(this);
554 }
555
556protected:
557 // --- the parent controller; we can issue IController commands to it!
558 IController* parentController = nullptr;
559
560 // --- a CTextButton is the switcher (linkControl)
561 CTextButton* linkControl = nullptr;
562
563 // --- when linked, all of these controls move when one of them moves,
564 // regardless of their control tags
565 typedef std::vector<CAnimKnob*> KnobList;
566 KnobList linkedKnobs;
567
568 // --- flag for linking knobs
569 bool linkControls = false;
570};
571
572
584{
586
587 // --- members
588 unsigned int message = MESSAGE_SET_STRING;
589
590 std::string labelString;
591 std::vector<std::string> stringList;
592 // uint32_t stringCount = 0;
593 uint32_t controlStringCount = 0;
594};
595
607{
609 // --- for external thunk clients, RackAFX or standalone ASPiK
610 uint32_t message = MESSAGE_SET_STRING;
611
612 const char* labelString;
613 char** stringList;
614 uint32_t stringCount = 0;
615};
616
627class CustomTextLabel : public CTextLabel, public ICustomView
628{
629public:
630 CustomTextLabel(const CRect& size, CHoriTxtAlign horizTA = CHoriTxtAlign::kCenterText, UTF8StringPtr txt = 0, CBitmap* background = 0, const int32_t style = 0)
631 : CTextLabel(size, txt, background, style)
632 {
633 textAlignment = horizTA;
634 }
635
637 virtual void updateView() override {
638 // --- force redraw
639 invalid();
640 }
641
642 void draw(CDrawContext* pContext) override
643 {
644 setHoriAlign(textAlignment);
645 CTextLabel::draw(pContext);
646 }
647
649 virtual void sendMessage(void* data) override
650 {
652
653 // --- set the text in the label
654 if (viewMessage->message == MESSAGE_SET_STRING)
655 {
656 // --- change the label
657 this->setText(viewMessage->labelString.c_str());
658 }
659 }
660
661protected:
663 virtual ~CustomTextLabel(void) { }
664 CHoriTxtAlign textAlignment = CHoriTxtAlign::kCenterText;
665};
666
667
678class CustomOptionMenu : public COptionMenu, public ICustomView
679{
680public:
681 CustomOptionMenu(const CRect& size, IControlListener* listener, int32_t tag, CBitmap* background = nullptr, CBitmap* bgWhenClick = nullptr, const int32_t style = 0)
682 : COptionMenu(size, listener, tag, background, bgWhenClick, style)
683 {
684 this->setNbItemsPerColumn(8);
685 }
686
688 virtual void updateView() override {
689 // --- force redraw
690 invalid();
691 }
692
693
695 virtual void valueChanged() override
696 {
697 CMenuItem* item = getEntry((int32_t)getValue());
698 if (!item) return;
699 if (item->getTitle() == "-----")
700 {
701 setValue((float)lastActualEntryIndex);
702 setDirty();
703 }
704
705 COptionMenu::valueChanged();
706 }
707
709 virtual void sendMessage(void* data) override
710 {
712
713 // --- set the text in the label
714 if (viewMessage->message == MESSAGE_SET_STRINGLIST)
715 {
716 // --- clear items
717 this->removeAllEntry();
718 for (uint32_t i = 0; i < viewMessage->controlStringCount; i++)
719 {
720 if (i < viewMessage->stringList.size())
721 {
722 COptionMenu::addEntry(viewMessage->stringList[i].c_str(), i);
723 if(viewMessage->stringList[i].compare("-----") != 0)
724 lastActualEntryIndex = i;// --- save for later to avoid a non-setting
725 }
726 else
727 COptionMenu::addEntry("-----", i);
728 }
729 // --- should never happen
730 if (lastActualEntryIndex < 0) lastActualEntryIndex = 0;
731 }
732 }
733
734protected:
736 virtual ~CustomOptionMenu(void) { }
737 int32_t lastActualEntryIndex = 0;
738};
739
740}
Custom View interface to allow plugin core to create safe communication channels with GUI custom view...
Definition: pluginstructures.h:1462
This object demonstrates how to subclass an existing VSTGUI4 control to setup a communcation channel ...
Definition: customviews.h:359
virtual void updateView() override
Definition: customviews.cpp:471
virtual void sendMessage(void *data) override
Definition: customviews.cpp:453
Custom object for dynamic menus that can change their contents programmatically - NOTE: the number of...
Definition: customviews.h:679
virtual void updateView() override
Definition: customviews.h:688
virtual void valueChanged() override
Definition: customviews.h:695
virtual void sendMessage(void *data) override
Definition: customviews.h:709
virtual ~CustomOptionMenu(void)
Definition: customviews.h:736
Custom object for dynamic lables that can change their text programmatically. .
Definition: customviews.h:628
virtual void sendMessage(void *data) override
Definition: customviews.h:649
virtual void updateView() override
Definition: customviews.h:637
virtual ~CustomTextLabel(void)
Definition: customviews.h:663
This object displays an audio histogram waveform view. .
Definition: customviews.h:52
void draw(CDrawContext *pContext) override
Definition: customviews.cpp:104
CRect currentRect
the rect to draw into
Definition: customviews.h:94
int circularBufferLength
circular buffer length
Definition: customviews.h:93
int readIndex
circular buffer read location
Definition: customviews.h:92
int writeIndex
circular buffer write location
Definition: customviews.h:91
virtual void pushDataValue(double data) override
Definition: customviews.cpp:54
virtual void updateView() override
Definition: customviews.cpp:62
void showXAxis(bool _paintXAxis)
Definition: customviews.h:75
void addWaveDataPoint(float fSample)
Definition: customviews.cpp:87
bool paintXAxis
flag for painting X Axis
Definition: customviews.h:87
double * circularBuffer
circular buffer to store peak values
Definition: customviews.h:90
void clearBuffer()
Definition: customviews.cpp:96
Definition: readerwriterqueue.h:60
double getMagnitude(double re, double im)
calculates magnitude of a complex numb er
Definition: fxobjects.h:1003
Custom structure for passing messages and data to and from the plugin core object....
Definition: customviews.h:310
Custom structure for dynamic option menus in RackAFX only - NOTE: unused in ASPiK and not needed....
Definition: customviews.h:607
Custom structure for dynamic option menus. .
Definition: customviews.h:584