1 #include "controllers/controlpickermenu.h"
2 
3 #include "effects/effectchainslot.h"
4 #include "effects/effectparameterslot.h"
5 #include "effects/effectrack.h"
6 #include "effects/effectslot.h"
7 #include "engine/controls/cuecontrol.h"
8 #include "engine/controls/loopingcontrol.h"
9 #include "mixer/playermanager.h"
10 #include "moc_controlpickermenu.cpp"
11 #include "recording/defs_recording.h"
12 #include "vinylcontrol/defs_vinylcontrol.h"
13 
ControlPickerMenu(QWidget * pParent)14 ControlPickerMenu::ControlPickerMenu(QWidget* pParent)
15         : QMenu(pParent) {
16     m_effectMasterOutputStr = tr("Master Output");
17     m_effectHeadphoneOutputStr = tr("Headphone Output");
18     m_deckStr = tr("Deck %1");
19     m_samplerStr = tr("Sampler %1");
20     m_previewdeckStr = tr("Preview Deck %1");
21     m_microphoneStr = tr("Microphone %1");
22     m_auxStr = tr("Auxiliary %1");
23     m_resetStr = tr("Reset to default");
24     m_effectRackStr = tr("Effect Rack %1");
25     m_effectUnitStr = tr("Effect Unit %1");
26     m_effectStr = tr("Slot %1");
27     m_parameterStr = tr("Parameter %1");
28     m_libraryStr = tr("Library");
29 
30     // Mixer Controls
31     QMenu* mixerMenu = addSubmenu(tr("Mixer"));
32     // Crossfader / Orientation
33     QMenu* crossfaderMenu = addSubmenu(tr("Crossfader / Orientation"), mixerMenu);
34     addControl("[Master]",
35             "crossfader",
36             tr("Crossfader"),
37             tr("Master crossfader"),
38             crossfaderMenu,
39             true);
40     addDeckAndSamplerControl("orientation",
41             tr("Orientation"),
42             tr("Mix orientation (e.g. left, right, center)"),
43             crossfaderMenu);
44     addDeckAndSamplerControl("orientation_left",
45             tr("Orient Left"),
46             tr("Set mix orientation to left"),
47             crossfaderMenu);
48     addDeckAndSamplerControl("orientation_center",
49             tr("Orient Center"),
50             tr("Set mix orientation to center"),
51             crossfaderMenu);
52     addDeckAndSamplerControl("orientation_right",
53             tr("Orient Right"),
54             tr("Set mix orientation to right"),
55             crossfaderMenu);
56     // Main Output
57     QMenu* mainOutputMenu = addSubmenu(tr("Main Output"), mixerMenu);
58     addControl("[Master]",
59             "gain",
60             tr("Main Output Gain"),
61             tr("Main Output gain"),
62             mainOutputMenu,
63             true);
64     addControl("[Master]",
65             "balance",
66             tr("Main Output Balance"),
67             tr("Main Output balance"),
68             mainOutputMenu,
69             true);
70     addControl("[Master]",
71             "delay",
72             tr("Main Output Delay"),
73             tr("Main Output delay"),
74             mainOutputMenu,
75             true);
76     // Headphone
77     QMenu* headphoneMenu = addSubmenu(tr("Headphone"), mixerMenu);
78     addControl("[Master]",
79             "headGain",
80             tr("Headphone Gain"),
81             tr("Headphone gain"),
82             headphoneMenu,
83             true);
84     addControl("[Master]",
85             "headMix",
86             tr("Headphone Mix"),
87             tr("Headphone mix (pre/main)"),
88             headphoneMenu,
89             true);
90     addControl("[Master]",
91             "headSplit",
92             tr("Headphone Split Cue"),
93             tr("Toggle headphone split cueing"),
94             headphoneMenu);
95     addControl("[Master]",
96             "headDelay",
97             tr("Headphone Delay"),
98             tr("Headphone delay"),
99             headphoneMenu,
100             true);
101     mixerMenu->addSeparator();
102     // EQs
103     QMenu* eqMenu = addSubmenu(tr("Equalizers"), mixerMenu);
104     constexpr int kNumEqRacks = 1;
105     const int iNumDecks = static_cast<int>(ControlObject::get(ConfigKey("[Master]", "num_decks")));
106     for (int iRackNumber = 0; iRackNumber < kNumEqRacks; ++iRackNumber) {
107         // TODO: Although there is a mode with 4-band EQs, it's not feasible
108         // right now to add support for learning both it and regular 3-band eqs.
109         // Since 3-band is by far the most common, stick with that.
110         const int kMaxEqs = 3;
111         QList<QString> eqNames;
112         eqNames.append(tr("Low EQ"));
113         eqNames.append(tr("Mid EQ"));
114         eqNames.append(tr("High EQ"));
115         for (int deck = 1; deck <= iNumDecks; ++deck) {
116             QMenu* deckMenu = addSubmenu(QString("Deck %1").arg(deck), eqMenu);
117             for (int effect = kMaxEqs - 1; effect >= 0; --effect) {
118                 const QString group = EqualizerRack::formatEffectSlotGroupString(
119                         iRackNumber, 0, QString("[Channel%1]").arg(deck));
120                 QMenu* bandMenu = addSubmenu(eqNames[effect], deckMenu);
121                 QString control = "parameter%1";
122                 addControl(group,
123                         control.arg(effect + 1),
124                         tr("Adjust %1").arg(eqNames[effect]),
125                         tr("Adjust %1").arg(eqNames[effect]),
126                         bandMenu,
127                         true,
128                         tr("Deck %1").arg(deck));
129 
130                 control = "button_parameter%1";
131                 addControl(group,
132                         control.arg(effect + 1),
133                         tr("Kill %1").arg(eqNames[effect]),
134                         tr("Kill %1").arg(eqNames[effect]),
135                         bandMenu,
136                         false,
137                         tr("Deck %1").arg(deck));
138             }
139         }
140     }
141     mixerMenu->addSeparator();
142     // Volume / Pfl controls
143     addDeckAndSamplerControl("volume", tr("Volume"), tr("Volume Fader"), mixerMenu, true);
144     addDeckAndSamplerControl("volume_set_one",
145             tr("Full Volume"),
146             tr("Set to full volume"),
147             mixerMenu);
148     addDeckAndSamplerControl("volume_set_zero",
149             tr("Zero Volume"),
150             tr("Set to zero volume"),
151             mixerMenu);
152     addDeckAndSamplerAndPreviewDeckControl("pregain",
153             tr("Track Gain"),
154             tr("Track Gain knob"),
155             mixerMenu,
156             true);
157     addDeckAndSamplerControl("mute", tr("Mute"), tr("Mute button"), mixerMenu);
158     mixerMenu->addSeparator();
159     addDeckAndSamplerControl("pfl",
160             tr("Headphone Listen"),
161             tr("Headphone listen (pfl) button"),
162             mixerMenu);
163 
164     addSeparator();
165 
166     // Transport
167     QMenu* transportMenu = addSubmenu(tr("Transport"));
168     addDeckAndSamplerAndPreviewDeckControl("play", tr("Play"), tr("Play button"), transportMenu);
169     addDeckAndSamplerAndPreviewDeckControl("back", tr("Fast Rewind"), tr("Fast Rewind button"), transportMenu);
170     addDeckAndSamplerAndPreviewDeckControl("fwd", tr("Fast Forward"), tr("Fast Forward button"), transportMenu);
171     addDeckAndSamplerAndPreviewDeckControl("playposition", tr("Strip Search"),
172                                            tr("Strip-search through track"), transportMenu);
173     addDeckAndSamplerAndPreviewDeckControl("reverse", tr("Play Reverse"), tr("Play Reverse button"), transportMenu);
174     addDeckAndSamplerAndPreviewDeckControl("reverseroll", tr("Reverse Roll (Censor)"),
175                                            tr("Reverse roll (Censor) button"), transportMenu);
176     addDeckAndSamplerAndPreviewDeckControl("start", tr("Jump To Start"), tr("Jumps to start of track"), transportMenu);
177     addDeckAndSamplerAndPreviewDeckControl("start_play", tr("Play From Start"),
178                                            tr("Jump to start of track and play"), transportMenu);
179     addDeckAndSamplerAndPreviewDeckControl("stop", tr("Stop"), tr("Stop button"), transportMenu);
180     addDeckAndSamplerAndPreviewDeckControl("start_stop", tr("Stop And Jump To Start"),
181                                            tr("Stop playback and jump to start of track"), transportMenu);
182     addDeckAndSamplerAndPreviewDeckControl("end", tr("Jump To End"), tr("Jump to end of track"), transportMenu);
183     transportMenu->addSeparator();
184     addDeckAndSamplerAndPreviewDeckControl("eject", tr("Eject"), tr("Eject track"), transportMenu);
185     addDeckAndSamplerControl("repeat", tr("Repeat Mode"), tr("Toggle repeat mode"), transportMenu);
186     addDeckAndSamplerControl("slip_enabled", tr("Slip Mode"), tr("Toggle slip mode"), transportMenu);
187 
188     // BPM / Beatgrid
189     QMenu* bpmMenu = addSubmenu(tr("BPM / Beatgrid"));
190     addDeckAndSamplerControl("bpm", tr("BPM"), tr("BPM"), bpmMenu, true);
191     addDeckAndSamplerControl("bpm_up", tr("BPM +1"), tr("Increase BPM by 1"), bpmMenu);
192     addDeckAndSamplerControl("bpm_down", tr("BPM -1"), tr("Decrease BPM by 1"), bpmMenu);
193     addDeckAndSamplerControl("bpm_up_small", tr("BPM +0.1"), tr("Increase BPM by 0.1"), bpmMenu);
194     addDeckAndSamplerControl("bpm_down_small", tr("BPM -0.1"), tr("Decrease BPM by 0.1"), bpmMenu);
195     addDeckAndSamplerControl("bpm_tap", tr("BPM Tap"), tr("BPM tap button"), bpmMenu);
196     bpmMenu->addSeparator();
197     addDeckAndSamplerControl("beats_adjust_faster", tr("Adjust Beatgrid Faster +.01"), tr("Increase track's average BPM by 0.01"), bpmMenu);
198     addDeckAndSamplerControl("beats_adjust_slower", tr("Adjust Beatgrid Slower -.01"), tr("Decrease track's average BPM by 0.01"), bpmMenu);
199     addDeckAndSamplerControl("beats_translate_earlier", tr("Move Beatgrid Earlier"), tr("Adjust the beatgrid to the left"), bpmMenu);
200     addDeckAndSamplerControl("beats_translate_later", tr("Move Beatgrid Later"), tr("Adjust the beatgrid to the right"), bpmMenu);
201     addDeckControl("beats_translate_curpos", tr("Adjust Beatgrid"),
202                    tr("Align beatgrid to current position"), bpmMenu);
203     addDeckControl("beats_translate_match_alignment", tr("Adjust Beatgrid - Match Alignment"),
204                    tr("Adjust beatgrid to match another playing deck."), bpmMenu);
205     bpmMenu->addSeparator();
206     addDeckAndSamplerControl("quantize", tr("Quantize Mode"), tr("Toggle quantize mode"), bpmMenu);
207 
208     QMenu* syncMenu = addSubmenu(tr("Sync"));
209     addDeckAndSamplerControl("sync_enabled",
210             tr("Sync / Sync Lock"),
211             tr("Tap to sync tempo (and phase with quantize enabled), hold to "
212                "enable permanent sync"),
213             syncMenu);
214     addDeckAndSamplerControl("beatsync",
215             tr("Beat Sync One-Shot"),
216             tr("One-time beat sync tempo (and phase with quantize enabled)"),
217             syncMenu);
218     addDeckAndSamplerControl("beatsync_tempo",
219             tr("Sync Tempo One-Shot"),
220             tr("One-time beat sync (tempo only)"),
221             syncMenu);
222     addDeckAndSamplerControl("beatsync_phase",
223             tr("Sync Phase One-Shot"),
224             tr("One-time beat sync (phase only)"),
225             syncMenu);
226     syncMenu->addSeparator();
227     addControl("[InternalClock]", "sync_master", tr("Internal Sync Master"),
228                tr("Toggle Internal Sync Master"), syncMenu);
229     addControl("[InternalClock]", "bpm", tr("Internal Master BPM"),
230                tr("Internal Master BPM"), syncMenu);
231     addControl("[InternalClock]", "bpm_up", tr("Internal Master BPM +1"),
232                tr("Increase internal master BPM by 1"), syncMenu);
233 
234     addControl("[InternalClock]", "bpm_down", tr("Internal Master BPM -1"),
235                tr("Decrease internal master BPM by 1"), syncMenu);
236 
237     addControl("[InternalClock]", "bpm_up_small", tr("Internal Master BPM +0.1"),
238                tr("Increase internal master BPM by 0.1"), syncMenu);
239     addControl("[InternalClock]", "bpm_down_small", tr("Internal Master BPM -0.1"),
240                tr("Decrease internal master BPM by 0.1"), syncMenu);
241     syncMenu->addSeparator();
242     addDeckAndSamplerControl("sync_master", tr("Sync Master"), tr("Toggle sync master"), syncMenu);
243     addDeckAndSamplerControl("sync_mode", tr("Sync Mode"),
244                              tr("Sync mode 3-state toggle (OFF, FOLLOWER, MASTER)"), syncMenu);
245 
246     // Speed
247     QMenu* speedMenu = addSubmenu(tr("Speed"));
248     addDeckAndSamplerControl("rate", tr("Playback Speed"),
249                              tr("Playback speed control (Vinyl \"Pitch\" slider)"), speedMenu, true);
250     speedMenu->addSeparator();
251     addDeckAndSamplerControl("rate_perm_up", tr("Increase Speed"),
252                              tr("Adjust speed faster (coarse)"), speedMenu);
253     addDeckAndSamplerControl("rate_perm_up_small", tr("Increase Speed (Fine)"),
254                              tr("Adjust speed faster (fine)"), speedMenu);
255     addDeckAndSamplerControl("rate_perm_down", tr("Decrease Speed"),
256                              tr("Adjust speed slower (coarse)"), speedMenu);
257     addDeckAndSamplerControl("rate_perm_down_small", tr("Increase Speed (Fine)"),
258                              tr("Adjust speed slower (fine)"), speedMenu);
259     speedMenu->addSeparator();
260     addDeckAndSamplerControl("rate_temp_up", tr("Temporarily Increase Speed"),
261                              tr("Temporarily increase speed (coarse)"), speedMenu);
262     addDeckAndSamplerControl("rate_temp_up_small", tr("Temporarily Increase Speed (Fine)"),
263                              tr("Temporarily increase speed (fine)"), speedMenu);
264     addDeckAndSamplerControl("rate_temp_down", tr("Temporarily Decrease Speed"),
265                              tr("Temporarily decrease speed (coarse)"), speedMenu);
266     addDeckAndSamplerControl("rate_temp_down_small", tr("Temporarily Decrease Speed (Fine)"),
267                              tr("Temporarily decrease speed (fine)"), speedMenu);
268     // Pitch (Musical Key)
269     QMenu* pitchMenu = addSubmenu(tr("Pitch (Musical Key)"));
270     addDeckAndSamplerControl("pitch",
271             tr("Pitch (Musical key)"),
272             tr("Pitch control (does not affect tempo), center is original "
273                "pitch"),
274             pitchMenu,
275             true);
276     addDeckAndSamplerControl("pitch_up",
277             tr("Increase Pitch"),
278             tr("Increases the pitch by one semitone"),
279             pitchMenu);
280     addDeckAndSamplerControl("pitch_up_small",
281             tr("Increase Pitch (Fine)"),
282             tr("Increases the pitch by 10 cents"),
283             pitchMenu);
284     addDeckAndSamplerControl("pitch_down",
285             tr("Decrease Pitch"),
286             tr("Decreases the pitch by one semitone"),
287             pitchMenu);
288     addDeckAndSamplerControl("pitch_down_small",
289             tr("Decrease Pitch (Fine)"),
290             tr("Decreases the pitch by 10 cents"),
291             pitchMenu);
292     addDeckAndSamplerControl("pitch_adjust",
293             tr("Pitch Adjust"),
294             tr("Adjust pitch from speed slider pitch"),
295             pitchMenu,
296             true);
297     pitchMenu->addSeparator();
298     addDeckAndSamplerControl("sync_key", tr("Match Key"), tr("Match musical key"), pitchMenu);
299     addDeckAndSamplerControl("reset_key", tr("Reset Key"), tr("Resets key to original"), pitchMenu);
300     addDeckAndSamplerControl("keylock", tr("Keylock"), tr("Toggle keylock mode"), pitchMenu);
301 
302     // Vinyl Control
303     QMenu* vinylControlMenu = addSubmenu(tr("Vinyl Control"));
304     addDeckControl("vinylcontrol_enabled", tr("Toggle Vinyl Control"),
305                    tr("Toggle Vinyl Control (ON/OFF)"), vinylControlMenu);
306     addDeckControl("vinylcontrol_mode", tr("Vinyl Control Mode"),
307                    tr("Toggle vinyl-control mode (ABS/REL/CONST)"), vinylControlMenu);
308     addDeckControl("vinylcontrol_cueing", tr("Vinyl Control Cueing Mode"),
309                    tr("Toggle vinyl-control cueing mode (OFF/ONE/HOT)"), vinylControlMenu);
310     addDeckControl("passthrough", tr("Vinyl Control Passthrough"),
311                    tr("Pass through external audio into the internal mixer"), vinylControlMenu);
312     addControl(VINYL_PREF_KEY, "Toggle", tr("Vinyl Control Next Deck"),
313                tr("Single deck mode - Switch vinyl control to next deck"), vinylControlMenu);
314 
315     // Cues
316     QMenu* cueMenu = addSubmenu(tr("Cues"));
317     addDeckControl("cue_default", tr("Cue"), tr("Cue button"), cueMenu);
318     addDeckControl("cue_set", tr("Set Cue"), tr("Set cue point"), cueMenu);
319     addDeckControl("cue_goto", tr("Go-To Cue"), tr("Go to cue point"), cueMenu);
320     addDeckAndSamplerAndPreviewDeckControl("cue_gotoandplay", tr("Go-To Cue And Play"),
321                    tr("Go to cue point and play"), cueMenu);
322     addDeckControl("cue_gotoandstop", tr("Go-To Cue And Stop"),
323                    tr("Go to cue point and stop"), cueMenu);
324     addDeckControl("cue_preview", tr("Preview Cue"),
325                    tr("Preview from cue point"), cueMenu);
326     addDeckControl("cue_cdj", tr("Cue (CDJ Mode)"),
327                    tr("Cue button (CDJ mode)"), cueMenu);
328     addDeckControl("play_stutter", tr("Stutter Cue"),
329                    tr("Stutter cue"), cueMenu);
330     addDeckControl("cue_play", tr("CUP (Cue + Play)"),
331                        tr("Go to cue point and play after release"), cueMenu);
332 
333     // Hotcues
334     QMenu* hotcueMainMenu = addSubmenu(tr("Hotcues"));
335     QString hotcueActivateTitle = tr("Hotcue %1");
336     QString hotcueClearTitle = tr("Clear Hotcue %1");
337     QString hotcueSetTitle = tr("Set Hotcue %1");
338     QString hotcueGotoTitle = tr("Jump To Hotcue %1");
339     QString hotcueGotoAndStopTitle = tr("Jump To Hotcue %1 And Stop");
340     QString hotcueGotoAndPlayTitle = tr("Jump To Hotcue %1 And Play");
341     QString hotcuePreviewTitle = tr("Preview Hotcue %1");
342     QString hotcueActivateDescription = tr("Set, preview from or jump to hotcue %1");
343     QString hotcueClearDescription = tr("Clear hotcue %1");
344     QString hotcueSetDescription = tr("Set hotcue %1");
345     QString hotcueGotoDescription = tr("Jump to hotcue %1");
346     QString hotcueGotoAndStopDescription = tr("Jump to hotcue %1 and stop");
347     QString hotcueGotoAndPlayDescription = tr("Jump to hotcue %1 and play");
348     QString hotcuePreviewDescription = tr("Preview from hotcue %1");
349     addDeckControl("shift_cues_earlier",
350             tr("Shift cue points earlier"),
351             tr("Shift cue points 10 milliseconds earlier"),
352             hotcueMainMenu);
353     addDeckControl("shift_cues_earlier_small",
354             tr("Shift cue points earlier (fine)"),
355             tr("Shift cue points 1 millisecond earlier"),
356             hotcueMainMenu);
357     addDeckControl("shift_cues_later",
358             tr("Shift cue points later"),
359             tr("Shift cue points 10 milliseconds later"),
360             hotcueMainMenu);
361     addDeckControl("shift_cues_later_small",
362             tr("Shift cue points later (fine)"),
363             tr("Shift cue points 1 millisecond later"),
364             hotcueMainMenu);
365     // add menus for hotcues 1-16.
366     // though, keep the menu small put additional hotcues in a separate menu,
367     // but don't create that submenu for less than 4 additional hotcues.
368     int preferredHotcuesVisible = 16;
369     int moreMenuThreshold = 4;
370     QMenu* parentMenu = hotcueMainMenu;
371     QMenu* hotcueMoreMenu = nullptr;
372     bool moreHotcues = NUM_HOT_CUES >= preferredHotcuesVisible + moreMenuThreshold;
373     if (moreHotcues) {
374         // populate menu here, add it below #preferredHotcuesVisible
375         hotcueMoreMenu = new QMenu(
376                 tr("Hotcues %1-%2").arg(preferredHotcuesVisible + 1).arg(NUM_HOT_CUES),
377                 hotcueMainMenu);
378     }
379     for (int i = 1; i <= NUM_HOT_CUES; ++i) {
380         if (moreHotcues && i > preferredHotcuesVisible) {
381             parentMenu = hotcueMoreMenu;
382         }
383         QMenu* hotcueSubMenu = addSubmenu(tr("Hotcue %1").arg(QString::number(i)), parentMenu);
384         addDeckAndSamplerControl(QString("hotcue_%1_activate").arg(i),
385                                  hotcueActivateTitle.arg(QString::number(i)),
386                                  hotcueActivateDescription.arg(QString::number(i)),
387                                  hotcueSubMenu);
388         addDeckAndSamplerControl(QString("hotcue_%1_clear").arg(i),
389                                  hotcueClearTitle.arg(QString::number(i)),
390                                  hotcueClearDescription.arg(QString::number(i)),
391                                  hotcueSubMenu);
392         addDeckAndSamplerControl(QString("hotcue_%1_set").arg(i),
393                                  hotcueSetTitle.arg(QString::number(i)),
394                                  hotcueSetDescription.arg(QString::number(i)),
395                                  hotcueSubMenu);
396         addDeckAndSamplerControl(QString("hotcue_%1_goto").arg(i),
397                                  hotcueGotoTitle.arg(QString::number(i)),
398                                  hotcueGotoDescription.arg(QString::number(i)),
399                                  hotcueSubMenu);
400         addDeckAndSamplerControl(QString("hotcue_%1_gotoandstop").arg(i),
401                                  hotcueGotoAndStopTitle.arg(QString::number(i)),
402                                  hotcueGotoAndStopDescription.arg(QString::number(i)),
403                                  hotcueSubMenu);
404         addDeckAndSamplerControl(QString("hotcue_%1_gotoandplay").arg(i),
405                                  hotcueGotoAndPlayTitle.arg(QString::number(i)),
406                                  hotcueGotoAndPlayDescription.arg(QString::number(i)),
407                                  hotcueSubMenu);
408         addDeckAndSamplerControl(QString("hotcue_%1_activate_preview").arg(i),
409                                  hotcuePreviewTitle.arg(QString::number(i)),
410                                  hotcuePreviewDescription.arg(QString::number(i)),
411                                  hotcueSubMenu);
412     }
413     if (moreHotcues) {
414         hotcueMainMenu->addSeparator();
415         hotcueMainMenu->addMenu(hotcueMoreMenu);
416     }
417 
418     // Intro/outro range markers
419     QMenu* introOutroMenu = addSubmenu(tr("Intro / Outro Markers"));
420     const QStringList markerTitles = {
421             tr("Intro Start Marker"),
422             tr("Intro End Marker"),
423             tr("Outro Start Marker"),
424             tr("Outro End Marker")};
425     const QStringList markerNames = {
426             tr("intro start marker"),
427             tr("intro end marker"),
428             tr("outro start marker"),
429             tr("outro end marker")};
430     const QStringList markerCOs = {
431             "intro_start",
432             "intro_end",
433             "outro_start",
434             "outro_end"};
435 
436     for (int i = 0; i < markerTitles.size(); ++i) {
437         QMenu* tempMenu = addSubmenu(markerTitles[i], introOutroMenu);
438         addDeckAndSamplerAndPreviewDeckControl(
439                 QString("%1_activate").arg(markerCOs[i]),
440                 tr("Activate %1", "[intro/outro marker").arg(markerTitles[i]),
441                 tr("Jump to or set the %1", "[intro/outro marker").arg(markerNames[i]),
442                 tempMenu);
443         addDeckAndSamplerAndPreviewDeckControl(
444                 QString("%1_set").arg(markerCOs[i]),
445                 tr("Set %1", "[intro/outro marker").arg(markerTitles[i]),
446                 tr("Set or jump to the %1", "[intro/outro marker").arg(markerNames[i]),
447                 tempMenu);
448         addDeckAndSamplerAndPreviewDeckControl(
449                 QString("%1_clear").arg(markerCOs[i]),
450                 tr("Clear %1", "[intro/outro marker").arg(markerTitles[i]),
451                 tr("Clear the %1", "[intro/outro marker").arg(markerNames[i]),
452                 tempMenu);
453     }
454 
455     // Loops
456     QMenu* loopMenu = addSubmenu(tr("Looping"));
457     // add beatloop_activate and beatlooproll_activate to both the
458     // Loop and Beat-Loop menus to make sure users can find them.
459     QString beatloopActivateTitle = tr("Loop Selected Beats");
460     QString beatloopActivateDescription = tr("Create a beat loop of selected beat size");
461     QString beatloopRollActivateTitle = tr("Loop Roll Selected Beats");
462     QString beatloopRollActivateDescription = tr("Create a rolling beat loop of selected beat size");
463     QString beatLoopTitle = tr("Loop %1 Beats");
464     QString beatLoopRollTitle = tr("Loop Roll %1 Beats");
465     QString beatLoopDescription = tr("Create %1-beat loop");
466     QString beatLoopRollDescription = tr("Create temporary %1-beat loop roll");
467 
468     QList<double> beatSizes = LoopingControl::getBeatSizes();
469 
470     QMap<double, QString> humanBeatSizes;
471     humanBeatSizes[0.03125] = tr("1/32");
472     humanBeatSizes[0.0625] = tr("1/16");
473     humanBeatSizes[0.125] = tr("1/8");
474     humanBeatSizes[0.25] = tr("1/4");
475     humanBeatSizes[0.5] = tr("1/2");
476     humanBeatSizes[1] = tr("1");
477     humanBeatSizes[2] = tr("2");
478     humanBeatSizes[4] = tr("4");
479     humanBeatSizes[8] = tr("8");
480     humanBeatSizes[16] = tr("16");
481     humanBeatSizes[32] = tr("32");
482     humanBeatSizes[64] = tr("64");
483 
484     // Beatloops
485     addDeckControl("beatloop_activate",
486             beatloopActivateTitle,
487             beatloopActivateDescription,
488             loopMenu);
489     QMenu* loopActivateMenu = addSubmenu(tr("Loop Beats"), loopMenu);
490     foreach (double beats, beatSizes) {
491         QString humanBeats = humanBeatSizes.value(beats, QString::number(beats));
492         addDeckControl(QString("beatloop_%1_toggle").arg(beats),
493                 beatLoopTitle.arg(humanBeats),
494                 beatLoopDescription.arg(humanBeats),
495                 loopActivateMenu);
496     }
497     loopMenu->addSeparator();
498 
499     addDeckControl("beatlooproll_activate",
500             beatloopRollActivateTitle,
501             beatloopRollActivateDescription,
502             loopMenu);
503     QMenu* looprollActivateMenu = addSubmenu(tr("Loop Roll Beats"), loopMenu);
504     foreach (double beats, beatSizes) {
505         QString humanBeats = humanBeatSizes.value(beats, QString::number(beats));
506         addDeckControl(QString("beatlooproll_%1_activate").arg(beats),
507                 beatLoopRollTitle.arg(humanBeats),
508                 beatLoopRollDescription.arg(humanBeats),
509                 looprollActivateMenu);
510     }
511     loopMenu->addSeparator();
512 
513     addDeckControl("loop_in", tr("Loop In"), tr("Loop In button"), loopMenu);
514     addDeckControl("loop_out", tr("Loop Out"), tr("Loop Out button"), loopMenu);
515     addDeckControl("loop_exit", tr("Loop Exit"), tr("Loop Exit button"), loopMenu);
516     addDeckControl("reloop_toggle",
517             tr("Reloop/Exit Loop"),
518             tr("Toggle loop on/off and jump to Loop In point if loop is behind "
519                "play position"),
520             loopMenu);
521     addDeckControl("reloop_andstop",
522             tr("Reloop And Stop"),
523             tr("Enable loop, jump to Loop In point, and stop"),
524             loopMenu);
525     addDeckControl("loop_halve", tr("Loop Halve"), tr("Halve the loop length"), loopMenu);
526     addDeckControl("loop_double", tr("Loop Double"), tr("Double the loop length"), loopMenu);
527 
528     // Beat Jump / Loop Move
529     QMenu* beatJumpMenu = addSubmenu(tr("Beat Jump / Loop Move"));
530     QString beatJumpForwardTitle = tr("Jump / Move Loop Forward %1 Beats");
531     QString beatJumpBackwardTitle = tr("Jump / Move Loop Backward %1 Beats");
532     QString beatJumpForwardDescription = tr("Jump forward by %1 beats, or if a loop is enabled, move the loop forward %1 beats");
533     QString beatJumpBackwardDescription = tr("Jump backward by %1 beats, or if a loop is enabled, move the loop backward %1 beats");
534     addDeckControl("beatjump_forward", tr("Beat Jump / Loop Move Forward Selected Beats"), tr("Jump forward by the selected number of beats, or if a loop is enabled, move the loop forward by the selected number of beats"), beatJumpMenu);
535     addDeckControl("beatjump_backward", tr("Beat Jump / Loop Move Backward Selected Beats"), tr("Jump backward by the selected number of beats, or if a loop is enabled, move the loop backward by the selected number of beats"), beatJumpMenu);
536     beatJumpMenu->addSeparator();
537 
538     QMenu* beatjumpFwdSubmenu = addSubmenu(tr("Beat Jump / Loop Move Forward"), beatJumpMenu);
539     foreach (double beats, beatSizes) {
540         QString humanBeats = humanBeatSizes.value(beats, QString::number(beats));
541         addDeckControl(QString("beatjump_%1_forward").arg(beats),
542                 beatJumpForwardTitle.arg(humanBeats),
543                 beatJumpForwardDescription.arg(humanBeats),
544                 beatjumpFwdSubmenu);
545     }
546 
547     QMenu* beatjumpBwdSubmenu = addSubmenu(tr("Beat Jump / Loop Move Backward"), beatJumpMenu);
548     foreach (double beats, beatSizes) {
549         QString humanBeats = humanBeatSizes.value(beats, QString::number(beats));
550         addDeckControl(QString("beatjump_%1_backward").arg(beats),
551                 beatJumpBackwardTitle.arg(humanBeats),
552                 beatJumpBackwardDescription.arg(humanBeats),
553                 beatjumpBwdSubmenu);
554     }
555 
556     // Loop moving
557     QString loopMoveForwardTitle = tr("Move Loop +%1 Beats");
558     QString loopMoveBackwardTitle = tr("Move Loop -%1 Beats");
559     QString loopMoveForwardDescription = tr("Move loop forward by %1 beats");
560     QString loopMoveBackwardDescription = tr("Move loop backward by %1 beats");
561 
562     QMenu* loopmoveFwdSubmenu = addSubmenu(tr("Loop Move Forward"), beatJumpMenu);
563     foreach (double beats, beatSizes) {
564         QString humanBeats = humanBeatSizes.value(beats, QString::number(beats));
565         addDeckControl(QString("loop_move_%1_forward").arg(beats),
566                 loopMoveForwardTitle.arg(humanBeats),
567                 loopMoveForwardDescription.arg(humanBeats),
568                 loopmoveFwdSubmenu);
569     }
570 
571     QMenu* loopmoveBwdSubmenu = addSubmenu(tr("Loop Move Backward"), beatJumpMenu);
572     foreach (double beats, beatSizes) {
573         QString humanBeats = humanBeatSizes.value(beats, QString::number(beats));
574         addDeckControl(QString("loop_move_%1_backward").arg(beats),
575                 loopMoveBackwardTitle.arg(humanBeats),
576                 loopMoveBackwardDescription.arg(humanBeats),
577                 loopmoveBwdSubmenu);
578     }
579 
580     addSeparator();
581 
582     // Library Controls
583     QMenu* libraryMenu = addSubmenu(tr("Library"));
584     QMenu* navigationMenu = addSubmenu(tr("Navigation"), libraryMenu);
585     addControl("[Library]",
586             "MoveUp",
587             tr("Move up"),
588             tr("Equivalent to pressing the UP key on the keyboard"),
589             navigationMenu,
590             false,
591             m_libraryStr);
592     addControl("[Library]",
593             "MoveDown",
594             tr("Move down"),
595             tr("Equivalent to pressing the DOWN key on the keyboard"),
596             navigationMenu,
597             false,
598             m_libraryStr);
599     addControl("[Library]",
600             "MoveVertical",
601             tr("Move up/down"),
602             tr("Move vertically in either direction using a knob, as if "
603                "pressing UP/DOWN keys"),
604             navigationMenu,
605             false,
606             m_libraryStr);
607     addControl("[Library]",
608             "ScrollUp",
609             tr("Scroll Up"),
610             tr("Equivalent to pressing the PAGE UP key on the keyboard"),
611             navigationMenu,
612             false,
613             m_libraryStr);
614     addControl("[Library]",
615             "ScrollDown",
616             tr("Scroll Down"),
617             tr("Equivalent to pressing the PAGE DOWN key on the keyboard"),
618             navigationMenu,
619             false,
620             m_libraryStr);
621     addControl("[Library]",
622             "ScrollVertical",
623             tr("Scroll up/down"),
624             tr("Scroll vertically in either direction using a knob, as if "
625                "pressing PGUP/PGDOWN keys"),
626             navigationMenu,
627             false,
628             m_libraryStr);
629     addControl("[Library]",
630             "MoveLeft",
631             tr("Move left"),
632             tr("Equivalent to pressing the LEFT key on the keyboard"),
633             navigationMenu,
634             false,
635             m_libraryStr);
636     addControl("[Library]",
637             "MoveRight",
638             tr("Move right"),
639             tr("Equivalent to pressing the RIGHT key on the keyboard"),
640             navigationMenu,
641             false,
642             m_libraryStr);
643     addControl("[Library]",
644             "MoveHorizontal",
645             tr("Move left/right"),
646             tr("Move horizontally in either direction using a knob, as if "
647                "pressing LEFT/RIGHT keys"),
648             navigationMenu,
649             false,
650             m_libraryStr);
651     navigationMenu->addSeparator();
652     addControl("[Library]",
653             "MoveFocusForward",
654             tr("Move focus to right pane"),
655             tr("Equivalent to pressing the TAB key on the keyboard"),
656             navigationMenu,
657             false,
658             m_libraryStr);
659     addControl("[Library]",
660             "MoveFocusBackward",
661             tr("Move focus to left pane"),
662             tr("Equivalent to pressing the SHIFT+TAB key on the keyboard"),
663             navigationMenu,
664             false,
665             m_libraryStr);
666     addControl("[Library]",
667             "MoveFocus",
668             tr("Move focus to right/left pane"),
669             tr("Move focus one pane to right or left using a knob, as if "
670                "pressing TAB/SHIFT+TAB keys"),
671             navigationMenu,
672             false,
673             m_libraryStr);
674     libraryMenu->addSeparator();
675     addControl("[Library]", "GoToItem",
676                tr("Go to the currently selected item"),
677                tr("Choose the currently selected item and advance forward one pane if appropriate"),
678                libraryMenu, false, m_libraryStr);
679     // Load track (these can be loaded into any channel)
680     addDeckAndSamplerControl("LoadSelectedTrack",
681             tr("Load Track"),
682             tr("Load selected track"),
683             libraryMenu);
684     addDeckAndSamplerAndPreviewDeckControl("LoadSelectedTrackAndPlay",
685             tr("Load Track and Play"),
686             tr("Load selected track and play"),
687             libraryMenu);
688     libraryMenu->addSeparator();
689     // Auto DJ
690     addControl("[Library]", "AutoDjAddBottom",
691                tr("Add to Auto DJ Queue (bottom)"),
692                tr("Append the selected track to the Auto DJ Queue"),
693                libraryMenu, false, m_libraryStr);
694     addControl("[Library]", "AutoDjAddTop",
695                tr("Add to Auto DJ Queue (top)"),
696                tr("Prepend selected track to the Auto DJ Queue"),
697                libraryMenu, false, m_libraryStr);
698     addControl("[Library]", "AutoDjAddReplace",
699                tr("Add to Auto DJ Queue (replace)"),
700                tr("Replace Auto DJ Queue with selected tracks"),
701                libraryMenu, false, m_libraryStr);
702     libraryMenu->addSeparator();
703     addControl("[Recording]", "toggle_recording",
704                tr("Record Mix"),
705                tr("Toggle mix recording"),
706                libraryMenu, false, m_libraryStr);
707 
708     // Effect Controls
709     QMenu* effectsMenu = addSubmenu(tr("Effects"));
710 
711     // Quick Effect Rack COs
712     QMenu* quickEffectMenu = addSubmenu(tr("Quick Effects"), effectsMenu);
713     for (int i = 1; i <= iNumDecks; ++i) {
714         addControl(QString("[QuickEffectRack1_[Channel%1]]").arg(i), "super1",
715                    tr("Deck %1 Quick Effect Super Knob").arg(i),
716                    tr("Quick Effect Super Knob (control linked effect parameters)"),
717                    quickEffectMenu, false, tr("Quick Effect"));
718         addControl(QString("[QuickEffectRack1_[Channel%1]_Effect1]").arg(i), "enabled",
719                    tr("Deck %1 Quick Effect Enable Button").arg(i),
720                    tr("Quick Effect Enable Button"),
721                    quickEffectMenu, false, tr("Quick Effect"));
722     }
723 
724     effectsMenu->addSeparator();
725 
726     // When kNumEffectRacks is changed to >1 put effect unit actions into
727     // separate "Effect Rack N" submenus.
728     const int kNumEffectRacks = 1;
729     for (int iRackNumber = 1; iRackNumber <= kNumEffectRacks; ++iRackNumber) {
730         const QString rackGroup = StandardEffectRack::formatGroupString(
731                 iRackNumber - 1);
732         QString descriptionPrefix = m_effectRackStr.arg(iRackNumber);
733 
734         const int numEffectUnits = static_cast<int>(ControlObject::get(
735                 ConfigKey(rackGroup, "num_effectunits")));
736         for (int iEffectUnitNumber = 1; iEffectUnitNumber <= numEffectUnits;
737              ++iEffectUnitNumber) {
738             const QString effectUnitGroup =
739                     StandardEffectRack::formatEffectChainSlotGroupString(
740                         iRackNumber - 1, iEffectUnitNumber - 1);
741 
742             descriptionPrefix = QString("%1").arg(m_effectUnitStr.arg(iEffectUnitNumber));
743 
744             QMenu* effectUnitMenu = addSubmenu(m_effectUnitStr.arg(iEffectUnitNumber),
745                     effectsMenu);
746             addControl(effectUnitGroup, "clear",
747                        tr("Clear Unit"),
748                        tr("Clear effect unit"),
749                        effectUnitMenu, false, descriptionPrefix);
750             addControl(effectUnitGroup, "enabled",
751                        tr("Toggle Unit"),
752                        tr("Enable or disable effect processing"),
753                        effectUnitMenu, false, descriptionPrefix);
754             addControl(effectUnitGroup, "mix",
755                        tr("Dry/Wet"),
756                        tr("Adjust the balance between the original (dry) and processed (wet) signal."),
757                        effectUnitMenu, true, descriptionPrefix);
758             addControl(effectUnitGroup, "super1",
759                        tr("Super Knob"),
760                        tr("Super Knob (control effects' Meta Knobs)"),
761                        effectUnitMenu, true, descriptionPrefix);
762             addControl(effectUnitGroup,
763                     "mix_mode",
764                     tr("Mix Mode Toggle"),
765                     tr("Toggle effect unit between D/W and D+W modes"),
766                     effectUnitMenu,
767                     false,
768                     descriptionPrefix);
769             addControl(effectUnitGroup, "next_chain",
770                        tr("Next Chain"),
771                        tr("Next chain preset"),
772                        effectUnitMenu, false, descriptionPrefix);
773             addControl(effectUnitGroup, "prev_chain",
774                        tr("Previous Chain"),
775                        tr("Previous chain preset"),
776                        effectUnitMenu, false, descriptionPrefix);
777             addControl(effectUnitGroup, "chain_selector",
778                        tr("Next/Previous Chain"),
779                        tr("Next or previous chain preset"),
780                        effectUnitMenu, false, descriptionPrefix);
781             addControl(effectUnitGroup, "show_parameters",
782                        tr("Show Effect Parameters"),
783                        tr("Show Effect Parameters"),
784                        effectUnitMenu, false, descriptionPrefix);
785 
786             QString assignMenuTitle = tr("Effect Unit Assignment");
787             QString assignString = tr("Assign ");
788             QMenu* effectUnitGroups = addSubmenu(assignMenuTitle,
789                     effectUnitMenu);
790 
791             QString groupDescriptionPrefix = QString("%1").arg(
792                     m_effectUnitStr.arg(iEffectUnitNumber));
793 
794             addControl(effectUnitGroup, "group_[Master]_enable",
795                     assignString + m_effectMasterOutputStr, // in ComboBox
796                     assignString + m_effectMasterOutputStr, // description below
797                     effectUnitGroups,
798                     false,
799                     groupDescriptionPrefix);
800             addControl(effectUnitGroup,
801                     "group_[Headphone]_enable",
802                     assignString + m_effectHeadphoneOutputStr,
803                     assignString + m_effectHeadphoneOutputStr,
804                     effectUnitGroups,
805                     false,
806                     groupDescriptionPrefix);
807 
808             for (int iDeckNumber = 1; iDeckNumber <= iNumDecks; ++iDeckNumber) {
809                 // PlayerManager::groupForDeck is 0-indexed.
810                 QString playerGroup = PlayerManager::groupForDeck(iDeckNumber - 1);
811                 // TODO(owen): Fix bad i18n here.
812                 addControl(effectUnitGroup,
813                         QString("group_%1_enable").arg(playerGroup),
814                         assignString + m_deckStr.arg(iDeckNumber),
815                         assignString + m_deckStr.arg(iDeckNumber),
816                         effectUnitGroups,
817                         false,
818                         groupDescriptionPrefix);
819             }
820 
821             const int iNumSamplers = static_cast<int>(ControlObject::get(
822                     ConfigKey("[Master]", "num_samplers")));
823             for (int iSamplerNumber = 1; iSamplerNumber <= iNumSamplers;
824                  ++iSamplerNumber) {
825                 // PlayerManager::groupForSampler is 0-indexed.
826                 QString playerGroup = PlayerManager::groupForSampler(iSamplerNumber - 1);
827                 // TODO(owen): Fix bad i18n here.
828                 addControl(effectUnitGroup,
829                         QString("group_%1_enable").arg(playerGroup),
830                         assignString + m_samplerStr.arg(iSamplerNumber),
831                         assignString + m_samplerStr.arg(iSamplerNumber),
832                         effectUnitGroups,
833                         false,
834                         groupDescriptionPrefix);
835             }
836 
837             const int iNumMicrophones = static_cast<int>(ControlObject::get(
838                     ConfigKey("[Master]", "num_microphones")));
839             for (int iMicrophoneNumber = 1; iMicrophoneNumber <= iNumMicrophones;
840                  ++iMicrophoneNumber) {
841                 QString micGroup = PlayerManager::groupForMicrophone(iMicrophoneNumber - 1);
842                 // TODO(owen): Fix bad i18n here.
843                 addControl(effectUnitGroup,
844                         QString("group_%1_enable").arg(micGroup),
845                         assignString + m_microphoneStr.arg(iMicrophoneNumber),
846                         assignString + m_microphoneStr.arg(iMicrophoneNumber),
847                         effectUnitGroups,
848                         false,
849                         groupDescriptionPrefix);
850             }
851 
852             const int iNumAuxiliaries = static_cast<int>(ControlObject::get(
853                     ConfigKey("[Master]", "num_auxiliaries")));
854             for (int iAuxiliaryNumber = 1; iAuxiliaryNumber <= iNumAuxiliaries;
855                  ++iAuxiliaryNumber) {
856                 QString auxGroup = PlayerManager::groupForAuxiliary(iAuxiliaryNumber - 1);
857                 // TODO(owen): Fix bad i18n here.
858                 addControl(effectUnitGroup,
859                         QString("group_%1_enable").arg(auxGroup),
860                         assignString + m_auxStr.arg(iAuxiliaryNumber),
861                         assignString + m_auxStr.arg(iAuxiliaryNumber),
862                         effectUnitGroups,
863                         false,
864                         groupDescriptionPrefix);
865             }
866 
867             const int numEffectSlots = static_cast<int>(ControlObject::get(
868                     ConfigKey(effectUnitGroup, "num_effectslots")));
869             for (int iEffectSlotNumber = 1; iEffectSlotNumber <= numEffectSlots;
870                      ++iEffectSlotNumber) {
871                 const QString effectSlotGroup =
872                         StandardEffectRack::formatEffectSlotGroupString(
873                             iRackNumber - 1, iEffectUnitNumber - 1,
874                             iEffectSlotNumber - 1);
875 
876                 QMenu* effectSlotMenu = addSubmenu(m_effectStr.arg(iEffectSlotNumber),
877                                                    effectUnitMenu);
878 
879                 QString slotDescriptionPrefix =
880                         QString("%1, %2").arg(descriptionPrefix,
881                                               m_effectStr.arg(iEffectSlotNumber));
882 
883                 addControl(effectSlotGroup, "clear",
884                            tr("Clear"), tr("Clear the current effect"),
885                            effectSlotMenu, false, slotDescriptionPrefix);
886                 addControl(effectSlotGroup, "meta",
887                            tr("Meta Knob"), tr("Effect Meta Knob (control linked effect parameters)"),
888                            effectSlotMenu, false, slotDescriptionPrefix);
889                 addControl(effectSlotGroup, "enabled",
890                            tr("Toggle"), tr("Toggle the current effect"),
891                            effectSlotMenu, false, slotDescriptionPrefix);
892                 addControl(effectSlotGroup, "next_effect",
893                            tr("Next"), tr("Switch to next effect"),
894                            effectSlotMenu, false, slotDescriptionPrefix);
895                 addControl(effectSlotGroup, "prev_effect",
896                            tr("Previous"), tr("Switch to the previous effect"),
897                            effectSlotMenu, false, slotDescriptionPrefix);
898                 addControl(effectSlotGroup, "effect_selector",
899                            tr("Next or Previous"),
900                            tr("Switch to either next or previous effect"),
901                            effectSlotMenu, false, slotDescriptionPrefix);
902 
903                 // Effect parameter knobs
904                 const int numParameterSlots = static_cast<int>(ControlObject::get(
905                         ConfigKey(effectSlotGroup, "num_parameterslots")));
906                 for (int iParameterSlotNumber = 1; iParameterSlotNumber <= numParameterSlots;
907                      ++iParameterSlotNumber) {
908                     // The parameter slot group is the same as the effect slot
909                     // group on a standard effect rack.
910                     const QString parameterSlotGroup =
911                             StandardEffectRack::formatEffectSlotGroupString(
912                                 iRackNumber - 1, iEffectUnitNumber - 1,
913                                 iEffectSlotNumber - 1);
914                     const QString parameterSlotItemPrefix = EffectParameterSlot::formatItemPrefix(
915                             iParameterSlotNumber - 1);
916                     QMenu* parameterSlotMenu = addSubmenu(
917                         m_parameterStr.arg(iParameterSlotNumber),
918                         effectSlotMenu);
919 
920                     QString parameterDescriptionPrefix =
921                             QString("%1, %2").arg(slotDescriptionPrefix,
922                                                   m_parameterStr.arg(iParameterSlotNumber));
923 
924                     // Likely to change soon.
925                     addControl(parameterSlotGroup, parameterSlotItemPrefix,
926                                tr("Parameter Value"),
927                                tr("Parameter Value"),
928                                parameterSlotMenu, true,
929                                parameterDescriptionPrefix);
930                     addControl(parameterSlotGroup, parameterSlotItemPrefix + "_link_type",
931                                tr("Meta Knob Mode"),
932                                tr("Set how linked effect parameters change when turning the Meta Knob."),
933                                parameterSlotMenu, false,
934                                parameterDescriptionPrefix);
935                     addControl(parameterSlotGroup, parameterSlotItemPrefix + "_link_inverse",
936                                tr("Meta Knob Mode Invert"),
937                                tr("Invert how linked effect parameters change when turning the Meta Knob."),
938                                parameterSlotMenu, false,
939                                parameterDescriptionPrefix);
940                 }
941 
942                 // Effect parameter buttons
943                 const int numButtonParameterSlots = static_cast<int>(ControlObject::get(
944                         ConfigKey(effectSlotGroup, "num_button_parameterslots")));
945                 for (int iParameterSlotNumber = 1; iParameterSlotNumber <= numButtonParameterSlots;
946                         ++iParameterSlotNumber) {
947                     // The parameter slot group is the same as the effect slot
948                     // group on a standard effect rack.
949                     const QString parameterSlotGroup =
950                             StandardEffectRack::formatEffectSlotGroupString(
951                                     iRackNumber - 1, iEffectUnitNumber - 1, iEffectSlotNumber - 1);
952                     const QString parameterSlotItemPrefix =
953                             EffectButtonParameterSlot::formatItemPrefix(
954                                     iParameterSlotNumber - 1);
955                     QMenu* parameterSlotMenu = addSubmenu(
956                             m_parameterStr.arg(iParameterSlotNumber),
957                             effectSlotMenu);
958 
959                     QString parameterDescriptionPrefix =
960                             QString("%1, %2").arg(slotDescriptionPrefix,
961                                     m_parameterStr.arg(iParameterSlotNumber));
962 
963                     // Likely to change soon.
964                     addControl(parameterSlotGroup,
965                             parameterSlotItemPrefix,
966                             tr("Button Parameter Value"),
967                             tr("Button Parameter Value"),
968                             parameterSlotMenu,
969                             true,
970                             parameterDescriptionPrefix);
971                 }
972             }
973         }
974         // Clear Effect Rack
975         addControl(rackGroup,
976                 "clear",
977                 tr("Clear Effect Rack"),
978                 tr("Clear effect rack"),
979                 effectsMenu,
980                 false,
981                 descriptionPrefix);
982     }
983 
984     // Microphone Controls
985     QMenu* microphoneMenu = addSubmenu(tr("Microphone / Auxiliary"));
986 
987     addMicrophoneAndAuxControl("talkover",
988                                tr("Microphone On/Off"),
989                                tr("Microphone on/off"), microphoneMenu,
990                                true, false);
991     addControl("[Master]", "duckStrength",
992                tr("Microphone Ducking Strength"),
993                tr("Microphone Ducking Strength"),
994                microphoneMenu, true);
995     addControl("[Master]", "talkoverDucking",
996                tr("Microphone Ducking Mode"),
997                tr("Toggle microphone ducking mode (OFF, AUTO, MANUAL)"),
998                microphoneMenu);
999     addMicrophoneAndAuxControl("passthrough",
1000                                tr("Auxiliary On/Off"),
1001                                tr("Auxiliary on/off"),
1002                                microphoneMenu,
1003                                false, true);
1004     microphoneMenu->addSeparator();
1005     addMicrophoneAndAuxControl("pregain",
1006                                tr("Gain"),
1007                                tr("Gain knob"), microphoneMenu,
1008                                true, true, true);
1009     addMicrophoneAndAuxControl("volume",
1010                                tr("Volume Fader"),
1011                                tr("Volume Fader"), microphoneMenu,
1012                                true, true, true);
1013     addMicrophoneAndAuxControl("volume_set_one",
1014                                tr("Full Volume"),
1015                                tr("Set to full volume"), microphoneMenu,
1016                                true, true);
1017     addMicrophoneAndAuxControl("volume_set_zero",
1018                                tr("Zero Volume"),
1019                                tr("Set to zero volume"), microphoneMenu,
1020                                true, true);
1021     addMicrophoneAndAuxControl("mute",
1022                                tr("Mute"),
1023                                tr("Mute button"), microphoneMenu,
1024                                true, true);
1025     addMicrophoneAndAuxControl("pfl",
1026             tr("Headphone Listen"),
1027             tr("Headphone listen button"),
1028             microphoneMenu,
1029             true,
1030             true);
1031     microphoneMenu->addSeparator();
1032     addMicrophoneAndAuxControl("orientation",
1033                                tr("Orientation"),
1034                                tr("Mix orientation (e.g. left, right, center)"),
1035                                microphoneMenu, true, true);
1036     addMicrophoneAndAuxControl("orientation_left",
1037                                tr("Orient Left"),
1038                                tr("Set mix orientation to left"),
1039                                microphoneMenu,
1040                                true, true);
1041     addMicrophoneAndAuxControl("orientation_center",
1042                                tr("Orient Center"),
1043                                tr("Set mix orientation to center"), microphoneMenu,
1044                                true, true);
1045     addMicrophoneAndAuxControl("orientation_right",
1046                                tr("Orient Right"),
1047                                tr("Set mix orientation to right"), microphoneMenu,
1048                                true, true);
1049 
1050     // AutoDJ Controls
1051     QMenu* autodjMenu = addSubmenu(tr("Auto DJ"));
1052     addControl("[AutoDJ]", "shuffle_playlist",
1053                tr("Auto DJ Shuffle"),
1054                tr("Shuffle the content of the Auto DJ queue"), autodjMenu);
1055     addControl("[AutoDJ]", "skip_next",
1056                tr("Auto DJ Skip Next"),
1057                tr("Skip the next track in the Auto DJ queue"), autodjMenu);
1058     addControl("[AutoDJ]", "fade_now",
1059                tr("Auto DJ Fade To Next"),
1060                tr("Trigger the transition to the next track"), autodjMenu);
1061     addControl("[AutoDJ]", "enabled",
1062                tr("Auto DJ Toggle"),
1063                tr("Toggle Auto DJ On/Off"), autodjMenu);
1064 
1065     // Skin Controls
1066     QMenu* guiMenu = addSubmenu(tr("User Interface"));
1067     addControl("[Samplers]",
1068             "show_samplers",
1069             tr("Samplers Show/Hide"),
1070             tr("Show/hide the sampler section"),
1071             guiMenu);
1072     addControl("[Microphone]",
1073             "show_microphone",
1074             tr("Microphone & Auxiliary Show/Hide"),
1075             tr("Show/hide the microphone & auxiliary section"),
1076             guiMenu);
1077     addControl("[PreviewDeck]",
1078             "show_previewdeck",
1079             tr("Preview Deck Show/Hide"),
1080             tr("Show/hide the preview deck"),
1081             guiMenu);
1082     addControl("[EffectRack1]",
1083             "show",
1084             tr("Effect Rack Show/Hide"),
1085             tr("Show/hide the effect rack"),
1086             guiMenu);
1087     addControl("[Skin]",
1088             "show_4effectunits",
1089             tr("4 Effect Units Show/Hide"),
1090             tr("Switches between showing 2 and 4 effect units"),
1091             guiMenu);
1092     addControl("[Master]",
1093             "show_mixer",
1094             tr("Mixer Show/Hide"),
1095             tr("Show or hide the mixer."),
1096             guiMenu);
1097     addControl("[Library]",
1098             "show_coverart",
1099             tr("Cover Art Show/Hide (Library)"),
1100             tr("Show/hide cover art in the library"),
1101             guiMenu);
1102     addControl("[Master]",
1103             "maximize_library",
1104             tr("Library Maximize/Restore"),
1105             tr("Maximize the track library to take up all the available screen "
1106                "space."),
1107             guiMenu);
1108 
1109     guiMenu->addSeparator();
1110 
1111     addControl("[Skin]",
1112             "show_4decks",
1113             tr("Toggle 4 Decks"),
1114             tr("Switches between showing 2 decks and 4 decks."),
1115             guiMenu);
1116     addControl("[Skin]",
1117             "show_coverart",
1118             tr("Cover Art Show/Hide (Decks)"),
1119             tr("Show/hide cover art in the main decks"),
1120             guiMenu);
1121     addControl(VINYL_PREF_KEY,
1122             "show_vinylcontrol",
1123             tr("Vinyl Control Show/Hide"),
1124             tr("Show/hide the vinyl control section"),
1125             guiMenu);
1126 
1127     QString spinnyTitle = tr("Vinyl Spinner Show/Hide");
1128     QString spinnyDescription = tr("Show/hide spinning vinyl widget");
1129     QMenu* spinnyMenu = addSubmenu(spinnyTitle, guiMenu);
1130     guiMenu->addSeparator();
1131     addControl("[Skin]",
1132             "show_spinnies",
1133             tr("Vinyl Spinners Show/Hide (All Decks)"),
1134             tr("Show/Hide all spinnies"),
1135             spinnyMenu);
1136     // TODO(ronso0) Add hint that this currently only affects the Shade skin
1137     for (int i = 1; i <= iNumDecks; ++i) {
1138         addControl(QString("[Spinny%1]").arg(i),
1139                 "show_spinny",
1140                 QString("%1: %2").arg(m_deckStr.arg(i), spinnyTitle),
1141                 QString("%1: %2").arg(m_deckStr.arg(i), spinnyDescription),
1142                 spinnyMenu);
1143     }
1144 
1145     guiMenu->addSeparator();
1146 
1147     addControl("[Skin]",
1148             "show_waveforms",
1149             tr("Toggle Waveforms"),
1150             tr("Show/hide the scrolling waveforms."),
1151             guiMenu);
1152     addDeckControl("waveform_zoom", tr("Waveform Zoom"), tr("Waveform zoom"), guiMenu);
1153     addDeckControl("waveform_zoom_down", tr("Waveform Zoom In"), tr("Zoom waveform in"), guiMenu);
1154     addDeckControl("waveform_zoom_up", tr("Waveform Zoom Out"), tr("Zoom waveform out"), guiMenu);
1155 
1156     guiMenu->addSeparator();
1157 
1158     // Controls to change a deck's star rating
1159     addDeckAndPreviewDeckControl("stars_up", tr("Star Rating Up"),
1160         tr("Increase the track rating by one star"), guiMenu);
1161     addDeckAndPreviewDeckControl("stars_down", tr("Star Rating Down"),
1162         tr("Decrease the track rating by one star"), guiMenu);
1163 
1164     // Misc. controls
1165     addControl("[Shoutcast]",
1166             "enabled",
1167             tr("Start/Stop Live Broadcasting"),
1168             tr("Stream your mix over the Internet."),
1169             guiMenu);
1170     addControl(RECORDING_PREF_KEY,
1171             "toggle_recording",
1172             tr("Record Mix"),
1173             tr("Start/stop recording your mix."),
1174             guiMenu);
1175 }
1176 
~ControlPickerMenu()1177 ControlPickerMenu::~ControlPickerMenu() {
1178 }
1179 
addSingleControl(const QString & group,const QString & control,const QString & title,const QString & description,QMenu * pMenu,const QString & prefix,const QString & actionTitle)1180 void ControlPickerMenu::addSingleControl(const QString& group,
1181         const QString& control,
1182         const QString& title,
1183         const QString& description,
1184         QMenu* pMenu,
1185         const QString& prefix,
1186         const QString& actionTitle) {
1187     int controlIndex;
1188 
1189     if (prefix.isEmpty()) {
1190         controlIndex = addAvailableControl(ConfigKey(group, control), title, description);
1191     } else {
1192         QString prefixedTitle = QString("%1: %2").arg(prefix, title);
1193         QString prefixedDescription = QString("%1: %2").arg(prefix, description);
1194         controlIndex = addAvailableControl(ConfigKey(group, control), prefixedTitle, prefixedDescription);
1195     }
1196 
1197     auto pAction = make_parented<QAction>(actionTitle.isEmpty() ? title : actionTitle, pMenu);
1198     connect(pAction, &QAction::triggered,
1199             this, [this, controlIndex] { controlChosen(controlIndex); });
1200     pMenu->addAction(pAction);
1201 }
1202 
addControl(const QString & group,const QString & control,const QString & title,const QString & description,QMenu * pMenu,bool addReset,const QString & prefix)1203 void ControlPickerMenu::addControl(const QString& group,
1204         const QString& control,
1205         const QString& title,
1206         const QString& description,
1207         QMenu* pMenu,
1208         bool addReset,
1209         const QString& prefix) {
1210     addSingleControl(group, control, title, description, pMenu, prefix);
1211 
1212     if (addReset) {
1213         QString resetTitle = QString("%1 (%2)").arg(title, m_resetStr);
1214         QString resetDescription = QString("%1 (%2)").arg(description, m_resetStr);
1215         QString resetControl = QString("%1_set_default").arg(control);
1216 
1217         addSingleControl(group, resetControl, resetTitle, resetDescription, pMenu, prefix);
1218     }
1219 }
1220 
addPlayerControl(const QString & control,const QString & controlTitle,const QString & controlDescription,QMenu * pMenu,bool deckControls,bool samplerControls,bool previewdeckControls,bool addReset)1221 void ControlPickerMenu::addPlayerControl(const QString& control,
1222         const QString& controlTitle,
1223         const QString& controlDescription,
1224         QMenu* pMenu,
1225         bool deckControls,
1226         bool samplerControls,
1227         bool previewdeckControls,
1228         bool addReset) {
1229     const int iNumSamplers = static_cast<int>(
1230             ControlObject::get(ConfigKey("[Master]", "num_samplers")));
1231     const int iNumDecks = static_cast<int>(ControlObject::get(ConfigKey("[Master]", "num_decks")));
1232     const int iNumPreviewDecks = static_cast<int>(
1233             ControlObject::get(ConfigKey("[Master]", "num_preview_decks")));
1234 
1235     parented_ptr<QMenu> controlMenu = make_parented<QMenu>(controlTitle, pMenu);
1236     pMenu->addMenu(controlMenu);
1237 
1238     parented_ptr<QMenu> resetControlMenu = nullptr;
1239     QString resetControl = QString("%1_set_default").arg(control);
1240     if (addReset) {
1241         QString resetMenuTitle = QString("%1 (%2)").arg(controlTitle, m_resetStr);
1242         resetControlMenu = make_parented<QMenu>(resetMenuTitle, pMenu);
1243         pMenu->addMenu(resetControlMenu);
1244     }
1245 
1246     for (int i = 1; deckControls && i <= iNumDecks; ++i) {
1247         // PlayerManager::groupForDeck is 0-indexed.
1248         QString prefix = m_deckStr.arg(i);
1249         QString group = PlayerManager::groupForDeck(i - 1);
1250         addSingleControl(group, control, controlTitle, controlDescription,
1251                          controlMenu, prefix, prefix);
1252 
1253         if (resetControlMenu) {
1254             QString resetTitle = QString("%1 (%2)").arg(controlTitle, m_resetStr);
1255             QString resetDescription = QString("%1 (%2)").arg(controlDescription, m_resetStr);
1256             addSingleControl(group, resetControl, resetTitle, resetDescription,
1257                     resetControlMenu, prefix, prefix);
1258         }
1259     }
1260 
1261     for (int i = 1; previewdeckControls && i <= iNumPreviewDecks; ++i) {
1262         // PlayerManager::groupForPreviewDeck is 0-indexed.
1263         QString prefix;
1264         if (iNumPreviewDecks == 1) {
1265             prefix = m_previewdeckStr.arg("");
1266         } else {
1267             prefix = m_previewdeckStr.arg(i);
1268         }
1269         QString group = PlayerManager::groupForPreviewDeck(i - 1);
1270         addSingleControl(group, control, controlTitle, controlDescription,
1271                          controlMenu, prefix, prefix);
1272 
1273         if (resetControlMenu) {
1274             QString resetTitle = QString("%1 (%2)").arg(controlTitle, m_resetStr);
1275             QString resetDescription = QString("%1 (%2)").arg(controlDescription, m_resetStr);
1276             addSingleControl(group, resetControl, resetTitle, resetDescription,
1277                     resetControlMenu, prefix, prefix);
1278         }
1279     }
1280 
1281     if (samplerControls) {
1282         QMenu* samplerControlMenu = new QMenu(tr("Samplers"), controlMenu);
1283         controlMenu->addMenu(samplerControlMenu);
1284         QMenu* samplerResetControlMenu = nullptr;
1285         if (resetControlMenu) {
1286             samplerResetControlMenu = new QMenu(tr("Samplers"), resetControlMenu);
1287             resetControlMenu->addMenu(samplerResetControlMenu);
1288         }
1289         for (int i = 1; i <= iNumSamplers; ++i) {
1290             // PlayerManager::groupForSampler is 0-indexed.
1291             QString prefix = m_samplerStr.arg(i);
1292             QString group = PlayerManager::groupForSampler(i - 1);
1293             addSingleControl(group,
1294                     control,
1295                     controlTitle,
1296                     controlDescription,
1297                     samplerControlMenu,
1298                     prefix,
1299                     prefix);
1300 
1301             if (resetControlMenu) {
1302                 QString resetTitle = QString("%1 (%2)").arg(controlTitle, m_resetStr);
1303                 QString resetDescription = QString("%1 (%2)").arg(controlDescription, m_resetStr);
1304                 addSingleControl(group,
1305                         resetControl,
1306                         resetTitle,
1307                         resetDescription,
1308                         samplerResetControlMenu,
1309                         prefix,
1310                         prefix);
1311             }
1312         }
1313     }
1314 }
1315 
addMicrophoneAndAuxControl(const QString & control,const QString & controlTitle,const QString & controlDescription,QMenu * pMenu,bool microphoneControls,bool auxControls,bool addReset)1316 void ControlPickerMenu::addMicrophoneAndAuxControl(const QString& control,
1317         const QString& controlTitle,
1318         const QString& controlDescription,
1319         QMenu* pMenu,
1320         bool microphoneControls,
1321         bool auxControls,
1322         bool addReset) {
1323     parented_ptr<QMenu> controlMenu = make_parented<QMenu>(controlTitle, pMenu);
1324     pMenu->addMenu(controlMenu);
1325 
1326     parented_ptr<QMenu> resetControlMenu = nullptr;
1327     QString resetControl = QString("%1_set_default").arg(control);
1328     if (addReset) {
1329         QString resetHelpText = QString("%1 (%2)").arg(controlTitle, m_resetStr);
1330         resetControlMenu = make_parented<QMenu>(resetHelpText, pMenu);
1331         pMenu->addMenu(resetControlMenu);
1332     }
1333 
1334     if (microphoneControls) {
1335         const int kNumMicrophones = static_cast<int>(
1336                 ControlObject::get(ConfigKey("[Master]", "num_microphones")));
1337         for (int i = 1; i <= kNumMicrophones; ++i) {
1338             QString prefix = m_microphoneStr.arg(i);
1339             QString group = PlayerManager::groupForMicrophone(i - 1);
1340             addSingleControl(group, control, controlTitle, controlDescription,
1341                              controlMenu, prefix, prefix);
1342 
1343             if (resetControlMenu) {
1344                 QString resetTitle = QString("%1 (%2)").arg(controlTitle, m_resetStr);
1345                 QString resetDescription = QString("%1 (%2)").arg(controlDescription, m_resetStr);
1346                 addSingleControl(group, resetControl, resetTitle, resetDescription,
1347                         resetControlMenu, prefix, prefix);
1348             }
1349         }
1350     }
1351 
1352     const int kNumAuxiliaries = static_cast<int>(
1353             ControlObject::get(ConfigKey("[Master]", "num_auxiliaries")));
1354     if (auxControls) {
1355         for (int i = 1; i <= kNumAuxiliaries; ++i) {
1356             QString prefix = m_auxStr.arg(i);
1357             QString group = PlayerManager::groupForAuxiliary(i - 1);
1358             addSingleControl(group, control, controlTitle, controlDescription,
1359                              controlMenu, prefix, prefix);
1360 
1361             if (resetControlMenu) {
1362                 QString resetTitle = QString("%1 (%2)").arg(controlTitle, m_resetStr);
1363                 QString resetDescription = QString("%1 (%2)").arg(controlDescription, m_resetStr);
1364                 addSingleControl(group, resetControl, resetTitle, resetDescription,
1365                         resetControlMenu, prefix, prefix);
1366             }
1367         }
1368     }
1369 }
1370 
addDeckAndSamplerControl(const QString & control,const QString & title,const QString & controlDescription,QMenu * pMenu,bool addReset)1371 void ControlPickerMenu::addDeckAndSamplerControl(const QString& control,
1372         const QString& title,
1373         const QString& controlDescription,
1374         QMenu* pMenu,
1375         bool addReset) {
1376     addPlayerControl(control, title, controlDescription, pMenu, true, true, false, addReset);
1377 }
1378 
addDeckAndPreviewDeckControl(const QString & control,const QString & title,const QString & controlDescription,QMenu * pMenu,bool addReset)1379 void ControlPickerMenu::addDeckAndPreviewDeckControl(const QString& control,
1380         const QString& title,
1381         const QString& controlDescription,
1382         QMenu* pMenu,
1383         bool addReset) {
1384     addPlayerControl(control, title, controlDescription, pMenu, true, false, true, addReset);
1385 }
1386 
addDeckAndSamplerAndPreviewDeckControl(const QString & control,const QString & title,const QString & controlDescription,QMenu * pMenu,bool addReset)1387 void ControlPickerMenu::addDeckAndSamplerAndPreviewDeckControl(const QString& control,
1388         const QString& title,
1389         const QString& controlDescription,
1390         QMenu* pMenu,
1391         bool addReset) {
1392     addPlayerControl(control, title, controlDescription, pMenu, true, true, true, addReset);
1393 }
1394 
addDeckControl(const QString & control,const QString & title,const QString & controlDescription,QMenu * pMenu,bool addReset)1395 void ControlPickerMenu::addDeckControl(const QString& control,
1396         const QString& title,
1397         const QString& controlDescription,
1398         QMenu* pMenu,
1399         bool addReset) {
1400     addPlayerControl(control, title, controlDescription, pMenu, true, false, false, addReset);
1401 }
1402 
addSamplerControl(const QString & control,const QString & title,const QString & controlDescription,QMenu * pMenu,bool addReset)1403 void ControlPickerMenu::addSamplerControl(const QString& control,
1404         const QString& title,
1405         const QString& controlDescription,
1406         QMenu* pMenu,
1407         bool addReset) {
1408     addPlayerControl(control, title, controlDescription, pMenu, false, true, false, addReset);
1409 }
1410 
addPreviewDeckControl(const QString & control,const QString & title,const QString & controlDescription,QMenu * pMenu,bool addReset)1411 void ControlPickerMenu::addPreviewDeckControl(const QString& control,
1412         const QString& title,
1413         const QString& controlDescription,
1414         QMenu* pMenu,
1415         bool addReset) {
1416     addPlayerControl(control, title, controlDescription, pMenu, false, false, true, addReset);
1417 }
1418 
addSubmenu(QString title,QMenu * pParent)1419 QMenu* ControlPickerMenu::addSubmenu(QString title, QMenu* pParent) {
1420     if (pParent == nullptr) {
1421         pParent = this;
1422     }
1423     auto subMenu = make_parented<QMenu>(title, pParent);
1424     pParent->addMenu(subMenu);
1425     return subMenu;
1426 }
1427 
controlChosen(int controlIndex)1428 void ControlPickerMenu::controlChosen(int controlIndex) {
1429     if (controlIndex < 0 || controlIndex >= m_controlsAvailable.size()) {
1430         return;
1431     }
1432     emit controlPicked(m_controlsAvailable[controlIndex]);
1433 }
1434 
addAvailableControl(const ConfigKey & key,const QString & title,const QString & description)1435 int ControlPickerMenu::addAvailableControl(const ConfigKey& key,
1436         const QString& title,
1437         const QString& description) {
1438     m_controlsAvailable.append(key);
1439     m_descriptionsByKey.insert(key, description);
1440     m_titlesByKey.insert(key, title);
1441     // return the index of the control which will be connected to the index
1442     // of the respective action in the menu
1443     return m_controlsAvailable.size() - 1;
1444 }
1445 
controlExists(const ConfigKey & key) const1446 bool ControlPickerMenu::controlExists(const ConfigKey& key) const {
1447     return m_titlesByKey.contains(key);
1448 }
1449 
descriptionForConfigKey(const ConfigKey & key) const1450 QString ControlPickerMenu::descriptionForConfigKey(const ConfigKey& key) const {
1451     return m_descriptionsByKey.value(key, QString());
1452 }
1453 
controlTitleForConfigKey(const ConfigKey & key) const1454 QString ControlPickerMenu::controlTitleForConfigKey(const ConfigKey& key) const {
1455     return m_titlesByKey.value(key, QString());
1456 }
1457