Allolib  1.0
C++ Components For Interactive Multimedia
al_ParameterGUI.hpp
1 #ifndef AL_PARAMETERGUI_H
2 #define AL_PARAMETERGUI_H
3 
4 /* Allolib --
5  Multimedia / virtual environment application class library
6 
7  Copyright (C) 2009. AlloSphere Research Group, Media Arts & Technology,
8  UCSB. Copyright (C) 2012-2018. The Regents of the University of California.
9  All rights reserved.
10 
11  Redistribution and use in source and binary forms, with or without
12  modification, are permitted provided that the following conditions are met:
13 
14  Redistributions of source code must retain the above copyright
15  notice, this list of conditions and the following disclaimer.
16 
17  Redistributions in binary form must reproduce the above
18  copyright notice, this list of conditions and the following disclaimer in the
19  documentation and/or other materials provided with the distribution.
20 
21  Neither the name of the University of California nor the names of its
22  contributors may be used to endorse or promote products derived from this
23  software without specific prior written permission.
24 
25  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
26  IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
28  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
29  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
30  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
31  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
32  OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
33  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
34  OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
35  ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 
37  File description:
38  Expose parameters on the network
39  File author(s):
40  AndrĂ©s Cabrera mantaraya36@gmail.com
41 */
42 
43 #include <climits>
44 
45 #include "al/io/al_AudioIO.hpp"
46 #include "al/io/al_ControlNav.hpp"
47 #include "al/io/al_Imgui.hpp"
48 #include "al/scene/al_DynamicScene.hpp"
49 #include "al/scene/al_SynthRecorder.hpp"
50 #include "al/scene/al_SynthSequencer.hpp"
51 #include "al/ui/al_Parameter.hpp"
52 #include "al/ui/al_ParameterBundle.hpp"
53 #include "al/ui/al_ParameterMIDI.hpp"
54 #include "al/ui/al_PresetHandler.hpp"
55 #include "al/ui/al_PresetMIDI.hpp"
56 #include "al/ui/al_PresetSequencer.hpp"
57 #include "al/ui/al_SequenceRecorder.hpp"
58 
59 namespace al {
60 
61 class BundleGUIManager;
62 
65 class ParameterGUI {
66 public:
67  // static void init() { imguiInit(); }
68  // static void cleanup() { imguiShutdown(); }
69 
70  static inline void draw(ParameterMeta *param) { drawParameterMeta(param); }
71 
72  // These functions require no state other than the parameter itself
73  static void drawVectorParameters(std::vector<ParameterMeta *> params,
74  std::string suffix = "");
75  static void drawParameterMeta(ParameterMeta *param, std::string suffix = "");
76  static void drawParameter(Parameter *param, std::string suffix = "");
77  static void drawParameterString(ParameterString *param,
78  std::string suffix = "");
79  static void drawParameterInt(ParameterInt *param, std::string suffix);
80  static void drawParameterBool(ParameterBool *param, std::string suffix = "");
81  static void drawParameterPose(ParameterPose *param, std::string suffix = "");
82  static void drawParameterColor(ParameterColor *param,
83  std::string suffix = "");
84  static void drawMenu(ParameterMenu *param, std::string suffix = "");
85  static void drawChoice(ParameterChoice *param, std::string suffix = "");
86  static void drawVec3(ParameterVec3 *param, std::string suffix = "");
87  static void drawVec4(ParameterVec4 *param, std::string suffix = "");
88  static void drawTrigger(Trigger *param, std::string suffix = "");
89 
90  static void drawSynthController(PolySynth *param, std::string suffix = "");
91 
92  // Display for al types
93  static void drawNav(Nav *mNav, std::string suffix = "");
94  static void drawDynamicScene(DynamicScene *scene, std::string suffix = "");
95 
96  // These functions are for use in bundles to only display one from a group of
97  // parameters
98  static void drawParameterMeta(std::vector<ParameterMeta *> params,
99  std::string suffix, int index = 0);
100  static void drawParameter(std::vector<Parameter *> params, std::string suffix,
101  int index = 0);
102  static void drawParameterString(std::vector<ParameterString *> params,
103  std::string suffix, int index = 0);
104  static void drawParameterInt(std::vector<ParameterInt *> params,
105  std::string suffix, int index = 0);
106  static void drawParameterBool(std::vector<ParameterBool *> params,
107  std::string suffix, int index = 0);
108  static void drawParameterPose(std::vector<ParameterPose *> params,
109  std::string suffix, int index = 0);
110  static void drawParameterColor(std::vector<ParameterColor *> params,
111  std::string suffix, int index = 0);
112  static void drawMenu(std::vector<ParameterMenu *> params, std::string suffix,
113  int index = 0);
114  static void drawChoice(std::vector<ParameterChoice *> params,
115  std::string suffix, int index = 0);
116  static void drawVec3(std::vector<ParameterVec3 *> params, std::string suffix,
117  int index = 0);
118  static void drawVec4(std::vector<ParameterVec4 *> params, std::string suffix,
119  int index = 0);
120  static void drawTrigger(std::vector<Trigger *> params, std::string suffix,
121  int index = 0);
122 
123  // These functions require additional state that is passed as reference
124 
126  std::string currentBank;
127  int currentBankIndex = 0;
128  std::vector<std::string> mapList;
129  int presetHandlerBank = 0;
130  bool newMap = false;
131  std::string enteredText;
132  std::string newMapText;
133  bool storeButtonState{false};
134  };
135 
136  static PresetHandlerState &drawPresetHandler(PresetHandler *presetHandler,
137  int presetColumns = 10,
138  int presetRows = 4);
139  static void drawPresetSequencer(PresetSequencer *presetSequencer,
140  int &currentPresetSequencerItem);
141  static void drawSequenceRecorder(SequenceRecorder *sequenceRecorder);
142  static void drawSynthSequencer(SynthSequencer *synthSequencer);
143  static void drawSynthRecorder(SynthRecorder *synthRecorder);
144 
145  static void drawAudioIO(AudioIO &io) { drawAudioIO(&io); }
146  static void drawAudioIO(AudioIO *io);
147  static void drawParameterMIDI(ParameterMIDI &midi) {
148  drawParameterMIDI(&midi);
149  }
150  static void drawParameterMIDI(ParameterMIDI *midi);
151  static void drawPresetMIDI(PresetMIDI *presetMidi);
152 
153  static void drawBundleGroup(std::vector<ParameterBundle *> bundles,
154  std::string suffix, int &currentBundle,
155  bool &bundleGlobal);
156  static void drawBundle(ParameterBundle *bundle);
157  static void drawBundleManager(BundleGUIManager *manager);
158 
159  static bool usingInput() { return isImguiUsingInput(); }
160  static bool usingKeyboard() { return isImguiUsingKeyboard(); }
161 
162  // Convenience function for use in ImGui::Combo
163  static auto vector_getter(void *vec, int idx, const char **out_text) {
164  auto &vector = *static_cast<std::vector<std::string> *>(vec);
165  if (idx < 0 || idx >= static_cast<int>(vector.size())) {
166  return false;
167  }
168  *out_text = vector.at(idx).c_str();
169  return true;
170  }
171 
172  static void beginPanel(std::string name, float x = -1, float y = -1,
173  float width = -1, float height = -1,
174  ImGuiWindowFlags window_flags = 0) {
175  if (x >= 0 || y >= 0) {
176  ImGui::SetNextWindowPos(ImVec2(x, y));
177  // window_flags |= ImGuiWindowFlags_NoMove; // if (no_move)
178  }
179  if (width >= 0 || height >= 0) {
180  ImGui::SetNextWindowSize(ImVec2(width, height));
181  // window_flags |= ImGuiWindowFlags_NoResize; // if (no_resize)
182  }
183  // ImGuiWindowFlags window_flags = 0;
184  // window_flags |= ImGuiWindowFlags_NoTitleBar; // if (no_titlebar)
185  // window_flags |= ImGuiWindowFlags_NoScrollbar; // if (no_scrollbar)
186  // window_flags |= ImGuiWindowFlags_MenuBar; // if (!no_menu)
187  // window_flags |= ImGuiWindowFlags_NoCollapse; // if (no_collapse)
188  // window_flags |= ImGuiWindowFlags_NoNav; // if (no_nav)
189 
190  ImGui::Begin(name.c_str(), nullptr, window_flags);
191  }
192 
193  static void endPanel() { ImGui::End(); }
194 };
195 
199 public:
200  void drawBundleGUI() {
201  std::unique_lock<std::mutex> lk(mBundleLock);
202  std::string suffix = "##_bundle_" + mName;
203  ParameterGUI::drawBundleGroup(mBundles, suffix, mCurrentBundle,
204  mBundleGlobal);
205  }
206 
207  BundleGUIManager &registerParameterBundle(ParameterBundle &bundle) {
208  if (mName.size() == 0 || bundle.name() == mName) {
209  std::unique_lock<std::mutex> lk(mBundleLock);
210  if (mName.size() == 0) {
211  mName = bundle.name();
212  }
213  mBundles.push_back(&bundle);
214  } else {
215  std::cout << "Warning: bundle name mismatch. Bundle '" << bundle.name()
216  << "' ingnored." << std::endl;
217  }
218  return *this;
219  }
222  return registerParameterBundle(newBundle);
223  }
224 
227  return registerParameterBundle(*newBundle);
228  }
229 
230  std::string name() { return mName; }
231 
232  int &currentBundle() { return mCurrentBundle; }
233  bool &bundleGlobal() { return mBundleGlobal; }
234  std::vector<ParameterBundle *> bundles() { return mBundles; }
235 
236 private:
237  std::mutex mBundleLock;
238  std::vector<ParameterBundle *> mBundles;
239  std::string mName;
240  int mCurrentBundle{0};
241  bool mBundleGlobal{false};
242 };
243 
246 template <class VoiceType> class SynthGUIManager {
247 public:
248  SynthGUIManager(std::string name = "") {
249  mControlVoice.init();
250  for (auto *param : mControlVoice.triggerParameters()) {
251  mPresetHandler << *param;
252  }
253  // mSynth = &synth;
254 
255  mName = name;
256  if (mName == "") {
257  mName = demangle(typeid(mControlVoice).name());
258  }
259  mPresetHandler.setRootPath(mName + "-data");
260  mPresetSequencer << mPresetHandler;
261  mPresetSequenceRecorder << mPresetHandler;
262 
263  mSequencer.setDirectory(mName + "-data");
264  mRecorder.setDirectory(mName + "-data");
265  // mSequencer << *mSynth;
266  mRecorder << mSequencer.synth();
267 
268  // template<class VoiceType>
269  mSequencer.synth().template registerSynthClass<VoiceType>(
270  demangle(typeid(VoiceType).name()));
271  mSequencer.synth().template allocatePolyphony<VoiceType>(16);
272  }
273 
274  std::string name() { return mName; }
275 
276  void drawSynthWidgets() {
277  drawFields();
278  drawAllNotesOffButton();
279  drawPresets();
280  ImGui::Separator();
281  ImGui::Columns(2, nullptr, true);
282  if (ImGui::Selectable("Polyphonic", mCurrentTab == 1)) {
283  mCurrentTab = 1;
284  triggerOff();
285  }
286  ImGui::NextColumn();
287  if (ImGui::Selectable("Static", mCurrentTab == 2)) {
288  mCurrentTab = 2;
289  synthSequencer().stopSequence();
290  synth().allNotesOff();
291  // while (synth().getActiveVoices()) {} // Spin until all
292  // voices have been removed triggerOn();
293  }
294 
295  ImGui::Columns(1);
296  if (mCurrentTab == 1) {
297  drawAllNotesOffButton();
298  drawSynthSequencer();
299  drawSynthRecorder();
300  } else {
301  drawTriggerButton();
302  drawPresetSequencer();
303  drawPresetSequencerRecorder();
304  }
305  }
306 
307  void setCurrentTab(int tab) {
308  if (tab >= 1 && tab <= 2) {
309  mCurrentTab = tab;
310  } else {
311  std::cerr << "ERROR: Can't set tab for SynthGUIManager:" << tab
312  << std::endl;
313  }
314  }
315 
320  ParameterGUI::beginPanel(demangle(typeid(mControlVoice).name()).c_str());
321  drawSynthWidgets();
322  ParameterGUI::endPanel();
323  }
324 
325  void render(AudioIOData &io) { synthSequencer().render(io); }
326 
327  void render(Graphics &g) { synthSequencer().render(g); }
328 
329  void configureVoiceFromGui(VoiceType *voice) {
330  for (size_t i = 0; i < mControlVoice.triggerParameters().size(); i++) {
331  voice->triggerParameters()[i]->set(mControlVoice.triggerParameters()[i]);
332  }
333  }
334 
335  void drawFields() {
336  for (auto *param : mControlVoice.triggerParameters()) {
337  ParameterGUI::drawParameterMeta(param);
338  }
339  }
340 
341  void drawPresets(int columns = 12, int rows = 4) {
342  ParameterGUI::drawPresetHandler(&mPresetHandler, columns, rows);
343  }
344 
345  void drawSynthSequencer() { ParameterGUI::drawSynthSequencer(&mSequencer); }
346 
347  void drawSynthRecorder() { ParameterGUI::drawSynthRecorder(&mRecorder); }
348 
349  void drawPresetSequencer() {
350  static int currentItem{0};
351  ParameterGUI::drawPresetSequencer(&mPresetSequencer, currentItem);
352  }
353 
354  void drawPresetSequencerRecorder() {
355  ParameterGUI::drawSequenceRecorder(&mPresetSequenceRecorder);
356  }
357 
358  void createBundle(uint8_t bundleSize) {
359  for (uint8_t i = 0; i < bundleSize; i++) {
360  auto voice = mSequencer.synth().template getVoice<VoiceType>();
361  auto bundle = std::make_shared<ParameterBundle>(
362  demangle(typeid(mControlVoice).name()));
363  for (auto *param : voice->triggerParameters()) {
364  *bundle << param;
365  }
366  mBundles.push_back(bundle);
367  mBundleGui << *bundle;
368  mSequencer.synth().triggerOn(voice);
369  }
370  }
371 
372  void drawBundle() {
373  if (mBundles.size() > 0) {
374  mBundleGui.drawBundleGUI();
375  }
376  }
377 
378  void drawTriggerButton() {
379  std::string buttonName =
380  mCurrentTriggerState ? "Turn off##paramGUI" : "Trigger##paramGUI";
381  if (ImGui::Button(buttonName.c_str(),
382  ImVec2(ImGui::GetWindowWidth(), 40))) {
383  if (!mCurrentTriggerState) {
384  triggerOn();
385  } else {
386  triggerOff();
387  }
388  }
389  }
390 
391  void drawAllNotesOffButton() {
392  std::string buttonName = "All notes off##paramGUI";
393  if (ImGui::Button(buttonName.c_str(), ImVec2(ImGui::GetWindowWidth(), 0))) {
394  synth().allNotesOff();
395  }
396  }
397 
403  void triggerOn(int id = INT_MIN) {
404  if (id == INT_MIN) {
405  if (!mCurrentTriggerState) {
406  mTriggerVoiceId =
407  mSequencer.synth().triggerOn(&mControlVoice, 0, INT_MIN);
408  mCurrentTriggerState = true;
409  }
410  } else {
411  VoiceType *voice = synth().template getVoice<VoiceType>();
412  configureVoiceFromGui(voice);
413  synth().triggerOn(voice, 0, id);
414  }
415  }
416 
417  void triggerOff(int id = INT_MIN) {
418  if (id == INT_MIN) {
419  if (mCurrentTriggerState) {
420  mControlVoice.free();
421  while (mControlVoice.id() != -1) {
422  // Wait a bit
423  std::this_thread::sleep_for(std::chrono::milliseconds(10));
424  }
425  mSequencer.synth().popFreeVoice(&mControlVoice);
426  mCurrentTriggerState = false;
427  }
428  } else {
429  synth().triggerOff(id);
430  }
431  }
432 
433  bool triggerButtonState() { return mCurrentTriggerState; }
434 
435  VoiceType *voice() { return &mControlVoice; }
436 
437  void recallPreset(int index) { mPresetHandler.recallPreset(index); }
438 
439  PresetSequencer &presetSequencer() { return mPresetSequencer; }
440  PresetHandler &presetHandler() { return mPresetHandler; }
441 
442  PolySynth &synth() { return mSequencer.synth(); }
443  SynthSequencer &synthSequencer() { return mSequencer; }
444  SynthRecorder &synthRecorder() { return mRecorder; }
445 
446 private:
447  std::string mName;
448  VoiceType mControlVoice;
449 
450  PresetHandler mPresetHandler;
451  PresetSequencer mPresetSequencer;
452  SequenceRecorder mPresetSequenceRecorder;
453 
454  // PolySynth *mSynth;
455  SynthSequencer mSequencer{TimeMasterMode::TIME_MASTER_AUDIO};
456  SynthRecorder mRecorder;
457 
458  std::vector<std::shared_ptr<ParameterBundle>> mBundles;
459  BundleGUIManager mBundleGui;
460 
461  bool mCurrentTriggerState{false};
462  int mTriggerVoiceId{INT_MIN};
463 
464  int mCurrentTab = 1;
465 };
466 
467 } // namespace al
468 
469 #endif // AL_PARAMETERGUI_H
BundleGUIManager & operator<<(ParameterBundle *newBundle)
Register parameter using the streaming operator.
BundleGUIManager & operator<<(ParameterBundle &newBundle)
Register parameter using the streaming operator.
The DynamicScene class.
std::string name() const
get the name for this bundle
A parameter representing selected items from a list.
The Parameter class.
The ParameterMIDI class connects Parameter objects to MIDI messages.
The ParameterMeta class defines the base interface for Parameter metadata.
A PolySynth manages polyphony and rendering of SynthVoice instances.
virtual void allNotesOff()
Turn off all notes immediately (without calling triggerOff() )
void triggerOff(int id)
trigger release of voice with id
int triggerOn(SynthVoice *voice, int offsetFrames=0, int id=-1, void *userData=nullptr)
trigger Puts voice in active voice lit and calls triggerOn() for it
bool popFreeVoice(SynthVoice *voice)
Remove voice from the free voice pool.
The PresetHandler class handles sorting and recalling of presets.
void recallPreset(std::string name)
Recall a preset by name.
The PresetSequencer class allows triggering presets from a PresetHandler over time.
The SequenceRecorder class records preset changes in a ".sequence" file.
void triggerOn(int id=INT_MIN)
Trigger a free voice. If no id provided the internal voice is triggered.
void drawSynthControlPanel()
Draws a panel with all the synth controls.
The SynthRecorder class records the events arriving at a PolySynth.
Event Sequencer triggering audio visual "notes".
void render(AudioIOData &io)
Insert this function within the audio callback.
Definition: al_App.hpp:23