ASPiK SDK
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 
22 namespace VSTGUI {
23 
24 // --- with an update cycle of ~50mSec, we need at least 2205 samples; this should be more than enough
25 const int DATA_QUEUE_LEN = 4096;
26 
27 // --- custom message; add more here...
28 const unsigned int MESSAGE_SET_STRINGLIST = 0;
29 const unsigned int MESSAGE_SET_STRING = 1;
30 
31 
51 class WaveView : public CControl, public ICustomView
52 {
53 public:
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 
85 protected:
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;
94  CRect currentRect;
95 
96 private:
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 
119 enum class spectrumViewWindowType {kRectWindow, kHannWindow, kBlackmanHarrisWindow};
120 
121 // --- change this for higher accuracy; needs to be power of 2
122 const int FFT_LEN = 512;
123 
124 // --- SpectrumView
125 /*
126 */
150 class SpectrumView : public CControl, public ICustomView
151 {
152 public:
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 
176 protected:
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 
274 protected:
275  // --- filled/unfilled FFT
276  bool filledFFT = true;
277 
278 private:
279  // --- lock-free queue for incoming data, sized to FFT_LEN in length
280  moodycamel::ReaderWriterQueue<double,FFT_LEN>* dataQueue = nullptr;
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
292 const unsigned int MESSAGE_SHOW_CONTROL = 0;
293 const unsigned int MESSAGE_HIDE_CONTROL = 1;
294 const unsigned int MESSAGE_SET_CONTROL_ALPHA = 2;
295 const unsigned int MESSAGE_QUERY_CONTROL = 3;
296 
297 // --- example of a custom view message; here we control the visual appearance of a control
310 {
311  CustomViewMessage() {}
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 
358 class CustomKnobView : public CAnimKnob, public ICustomView
359 {
360 public:
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 
371 protected:
372  virtual ~CustomKnobView(void);
373 
374 private:
375  // --- lock-free queue for incoming data, sized to 32 in length
377 };
378 
379 
414 class KnobLinkController : public IController
415 {
416 public:
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 
556 protected:
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 
627 class CustomTextLabel : public CTextLabel, public ICustomView
628 {
629 public:
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  {
651  TextDisplayViewMessage* viewMessage = (TextDisplayViewMessage*)data;
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 
661 protected:
663  virtual ~CustomTextLabel(void) { }
664  CHoriTxtAlign textAlignment = CHoriTxtAlign::kCenterText;
665 };
666 
667 
678 class CustomOptionMenu : public COptionMenu, public ICustomView
679 {
680 public:
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  {
711  TextDisplayViewMessage* viewMessage = (TextDisplayViewMessage*)data;
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 
734 protected:
736  virtual ~CustomOptionMenu(void) { }
737  int32_t lastActualEntryIndex = 0;
738 };
739 
740 }
This object displays an audio histogram waveform view. .
Definition: customviews.h:51
bool paintXAxis
flag for painting X Axis
Definition: customviews.h:87
int circularBufferLength
circular buffer length
Definition: customviews.h:93
Custom object for dynamic menus that can change their contents programmatically - NOTE: the number of...
Definition: customviews.h:678
virtual void updateView() override
Definition: customviews.cpp:62
virtual void updateView() override
Definition: customviews.h:688
Custom object for dynamic lables that can change their text programmatically. .
Definition: customviews.h:627
Custom structure for dynamic option menus in RackAFX only - NOTE: unused in ASPiK and not needed...
Definition: customviews.h:606
void draw(CDrawContext *pContext) override
Definition: customviews.cpp:104
CRect currentRect
the rect to draw into
Definition: customviews.h:94
CustomKnobView(const CRect &size, IControlListener *listener, int32_t tag, int32_t subPixmaps, CCoord heightOfOneImage, CBitmap *background, const CPoint &offset, bool bSwitchKnob=false)
CustomKnobView constructor.
Definition: customviews.cpp:440
void clearBuffer()
Definition: customviews.cpp:96
virtual void pushDataValue(double data) override
Definition: customviews.cpp:54
Custom View interface to allow plugin core to create safe communication channels with GUI custom view...
Definition: pluginstructures.h:1461
int writeIndex
circular buffer write location
Definition: customviews.h:91
virtual ~CustomOptionMenu(void)
Definition: customviews.h:736
This object demonstrates how to subclass an existing VSTGUI4 control to setup a communcation channel ...
Definition: customviews.h:358
Definition: customcontrols.cpp:20
virtual void sendMessage(void *data) override
Definition: customviews.h:709
virtual void sendMessage(void *data) override
Definition: customviews.h:649
void showXAxis(bool _paintXAxis)
Definition: customviews.h:75
double * circularBuffer
circular buffer to store peak values
Definition: customviews.h:90
double getMagnitude(double re, double im)
calculates magnitude of a complex numb er
Definition: fxobjects.h:1003
virtual void sendMessage(void *data) override
Definition: customviews.cpp:453
virtual void valueChanged() override
Definition: customviews.h:695
WaveView(const CRect &size, IControlListener *listener, int32_t tag)
WaveView constructor.
Definition: customviews.cpp:25
virtual ~CustomTextLabel(void)
Definition: customviews.h:663
int readIndex
circular buffer read location
Definition: customviews.h:92
Custom structure for dynamic option menus. .
Definition: customviews.h:583
void addWaveDataPoint(float fSample)
Definition: customviews.cpp:87
virtual void updateView() override
Definition: customviews.cpp:471
virtual void updateView() override
Definition: customviews.h:637
Custom structure for passing messages and data to and from the plugin core object. See the Custom View tutorial project for more informaiton.
Definition: customviews.h:309