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