~tytel/helm/development

1066 by mtytel
Updated license year.
1
/* Copyright 2013-2017 Matt Tytel
704 by Matthew
Removed ^M line endings created by JUCE.
2
 *
3
 * helm is free software: you can redistribute it and/or modify
4
 * it under the terms of the GNU General Public License as published by
5
 * the Free Software Foundation, either version 3 of the License, or
6
 * (at your option) any later version.
7
 *
8
 * helm is distributed in the hope that it will be useful,
9
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
 * GNU General Public License for more details.
12
 *
13
 * You should have received a copy of the GNU General Public License
14
 * along with helm.  If not, see <http://www.gnu.org/licenses/>.
15
 */
16
17
#include "synth_slider.h"
1128 by mtytel
Better popup look and feel.
18
19
#include "default_look_and_feel.h"
774 by mtytel
Using english strings in tooltip instead of internal.
20
#include "full_interface.h"
21
#include "helm_common.h"
704 by Matthew
Removed ^M line endings created by JUCE.
22
#include "synth_gui_interface.h"
23
#include "text_look_and_feel.h"
24
1207 by mtytel
Popup displays for knobs and added bpm slider back in.
25
#define DEFAULT_POPUP_BUFFER 10
26
704 by Matthew
Removed ^M line endings created by JUCE.
27
namespace {
28
  enum MenuIds {
29
    kCancel = 0,
30
    kArmMidiLearn,
31
    kClearMidiLearn,
32
    kDefaultValue,
33
    kClearModulations,
34
    kModulationList
35
  };
1259 by mtytel
Removed a bunch of modal dialogs in exchange for async ones.
36
37
  static void sliderPopupCallback(int result, SynthSlider* slider) {
38
    if (slider != nullptr && result != kCancel)
39
      slider->handlePopupResult(result);
40
  }
704 by Matthew
Removed ^M line endings created by JUCE.
41
} // namespace
42
1182 by Matt Tytel
Fixed initialization type warnings.
43
const float SynthSlider::rotary_angle = 0.8f * static_cast<float>(mopo::PI);
1125 by mtytel
Fixed up linear slider modulations.
44
const float SynthSlider::linear_rail_width = 2.0f;
1124 by mtytel
Working version of the new openGL rotary modulation meters.
45
783 by mtytel
Adding mixer section. Doesn't load old patches yet.
46
SynthSlider::SynthSlider(String name) : Slider(name), bipolar_(false), flip_coloring_(false),
1151 by mtytel
Added filter blend, better UI, button hovering tooltip.
47
                                        active_(true), snap_to_value_(false), snap_value_(0.0),
704 by Matthew
Removed ^M line endings created by JUCE.
48
                                        string_lookup_(nullptr), parent_(nullptr) {
1207 by mtytel
Popup displays for knobs and added bpm slider back in.
49
  popup_placement_ = BubbleComponent::below;
50
  popup_buffer_ = DEFAULT_POPUP_BUFFER;
51
704 by Matthew
Removed ^M line endings created by JUCE.
52
  if (!mopo::Parameters::isParameter(name.toStdString()))
53
    return;
54
1124 by mtytel
Working version of the new openGL rotary modulation meters.
55
  setRotaryParameters(2.0f * mopo::PI - rotary_angle, 2.0f * mopo::PI + rotary_angle, true);
1203 by Matt Tytel
Better slider display values. secs instead of Hz.
56
  details_ = mopo::Parameters::getDetails(name.toStdString());
57
  if (details_.steps)
58
    setRange(details_.min, details_.max, (details_.max - details_.min) / (details_.steps - 1));
704 by Matthew
Removed ^M line endings created by JUCE.
59
  else
1203 by Matt Tytel
Better slider display values. secs instead of Hz.
60
    setRange(details_.min, details_.max);
61
62
  setDoubleClickReturnValue(true, details_.default_value);
704 by Matthew
Removed ^M line endings created by JUCE.
63
  setTextBoxStyle(Slider::NoTextBox, true, 0, 0);
64
65
  setBufferedToImage(true);
66
  setColour(Slider::backgroundColourId, Colour(0xff303030));
67
  setColour(Slider::textBoxOutlineColourId, Colour(0x00000000));
68
}
69
1207 by mtytel
Popup displays for knobs and added bpm slider back in.
70
void SynthSlider::resized() {
71
  if (parent_ == nullptr)
72
    parent_ = findParentComponentOfClass<FullInterface>();
73
74
  setPopupDisplayEnabled(true, parent_);
75
  Slider::resized();
76
}
77
704 by Matthew
Removed ^M line endings created by JUCE.
78
void SynthSlider::mouseDown(const MouseEvent& e) {
79
  SynthGuiInterface* parent = findParentComponentOfClass<SynthGuiInterface>();
979 by mtytel
Standalone and Plugin version now share a lot more code.
80
  if (parent == nullptr)
81
    return;
82
  SynthBase* synth = parent->getSynth();
704 by Matthew
Removed ^M line endings created by JUCE.
83
84
  if (e.mods.isPopupMenu()) {
85
    PopupMenu m;
1128 by mtytel
Better popup look and feel.
86
    m.setLookAndFeel(DefaultLookAndFeel::instance());
704 by Matthew
Removed ^M line endings created by JUCE.
87
88
    if (isDoubleClickReturnEnabled())
89
      m.addItem(kDefaultValue, "Set to Default Value");
90
91
    std::vector<mopo::ModulationConnection*> connections;
979 by mtytel
Standalone and Plugin version now share a lot more code.
92
    m.addItem(kArmMidiLearn, "Learn MIDI Assignment");
93
    if (parent->getSynth()->isMidiMapped(getName().toStdString()))
94
      m.addItem(kClearMidiLearn, "Clear MIDI Assignment");
95
96
    connections = parent->getSynth()->getDestinationConnections(getName().toStdString());
97
98
    String disconnect("Disconnect from ");
99
    for (int i = 0; i < connections.size(); ++i)
100
      m.addItem(kModulationList + i, disconnect + connections[i]->source);
101
102
    if (connections.size() > 1)
103
      m.addItem(kClearModulations, "Disconnect all modulations");
704 by Matthew
Removed ^M line endings created by JUCE.
104
1259 by mtytel
Removed a bunch of modal dialogs in exchange for async ones.
105
    m.showMenuAsync(PopupMenu::Options(),
106
                    ModalCallbackFunction::forComponent(sliderPopupCallback, this));
704 by Matthew
Removed ^M line endings created by JUCE.
107
  }
108
  else {
109
    Slider::mouseDown(e);
110
111
    if (parent)
979 by mtytel
Standalone and Plugin version now share a lot more code.
112
      synth->beginChangeGesture(getName().toStdString());
704 by Matthew
Removed ^M line endings created by JUCE.
113
    if (isRotary()) {
114
      click_position_ = e.getScreenPosition().toFloat();
115
      setMouseCursor(MouseCursor::NoCursor);
116
    }
117
  }
118
}
119
120
void SynthSlider::mouseUp(const MouseEvent& e) {
1242 by mtytel
Fixed up setting values loop and modulation popups.
121
  if (!e.mods.isPopupMenu()) {
1344 by mtytel
Added midi learn and DAW settings to buttons.
122
    Slider::mouseUp(e);
123
982 by mtytel
More audio thread improvements.
124
    SynthGuiInterface* parent = findParentComponentOfClass<SynthGuiInterface>();
125
    if (parent)
126
      parent->getSynth()->endChangeGesture(getName().toStdString());
127
1242 by mtytel
Fixed up setting values loop and modulation popups.
128
    if (isRotary()) {
129
      setMouseCursor(MouseCursor::ParentCursor);
130
      Desktop::getInstance().getMainMouseSource().setScreenPosition(click_position_);
131
    }
704 by Matthew
Removed ^M line endings created by JUCE.
132
  }
133
}
134
135
void SynthSlider::mouseEnter(const MouseEvent &e) {
136
  Slider::mouseEnter(e);
137
  notifyTooltip();
1007 by mtytel
Don't lock when adding/removing/changing modulations.
138
  for (SynthSlider::SliderListener* listener : slider_listeners_)
971 by mtytel
Made modulation buttons highlight when hovered over destination
139
    listener->hoverStarted(getName().toStdString());
140
}
141
142
void SynthSlider::mouseExit(const MouseEvent &e) {
143
  Slider::mouseExit(e);
1007 by mtytel
Don't lock when adding/removing/changing modulations.
144
  for (SynthSlider::SliderListener* listener : slider_listeners_)
971 by mtytel
Made modulation buttons highlight when hovered over destination
145
    listener->hoverEnded(getName().toStdString());
704 by Matthew
Removed ^M line endings created by JUCE.
146
}
147
148
void SynthSlider::valueChanged() {
149
  Slider::valueChanged();
150
  notifyTooltip();
1242 by mtytel
Fixed up setting values loop and modulation popups.
151
  notifyGuis();
1207 by mtytel
Popup displays for knobs and added bpm slider back in.
152
153
  if (popup_placement_ == BubbleComponent::below && popup_buffer_) {
154
    Component* popup = getCurrentPopupDisplay();
155
    if (popup) {
156
      Rectangle<int> bounds = popup->getBounds();
157
      Rectangle<int> local_bounds = getLocalArea(popup, popup->getLocalBounds());
158
159
      int y_diff = getHeight() + popup_buffer_ - local_bounds.getY();
160
      bounds.setY(bounds.getY() + y_diff);
161
      popup->setBounds(bounds);
162
    }
163
  }
704 by Matthew
Removed ^M line endings created by JUCE.
164
}
165
166
String SynthSlider::getTextFromValue(double value) {
167
  if (string_lookup_) {
826 by Matthew
Using faster clamp. Modifying distortion.
168
    int lookup = mopo::utils::iclamp(value, 0, getMaximum());
704 by Matthew
Removed ^M line endings created by JUCE.
169
    return string_lookup_[lookup];
170
  }
171
172
  float display_value = value;
1203 by Matt Tytel
Better slider display values. secs instead of Hz.
173
  switch (details_.display_skew) {
704 by Matthew
Removed ^M line endings created by JUCE.
174
    case mopo::ValueDetails::kQuadratic:
175
      display_value = powf(display_value, 2.0f);
176
      break;
177
    case mopo::ValueDetails::kExponential:
178
      display_value = powf(2.0f, display_value);
179
      break;
1142 by mtytel
Much better peak meter.
180
    case mopo::ValueDetails::kSquareRoot:
181
      display_value = sqrt(display_value);
182
      break;
704 by Matthew
Removed ^M line endings created by JUCE.
183
    default:
184
      break;
185
  }
1203 by Matt Tytel
Better slider display values. secs instead of Hz.
186
  display_value += details_.post_offset;
187
  if (details_.display_invert)
188
    display_value = 1.0 / display_value;
189
  display_value *= details_.display_multiply;
704 by Matthew
Removed ^M line endings created by JUCE.
190
1203 by Matt Tytel
Better slider display values. secs instead of Hz.
191
  return formatValue(display_value);
704 by Matthew
Removed ^M line endings created by JUCE.
192
}
193
1151 by mtytel
Added filter blend, better UI, button hovering tooltip.
194
double SynthSlider::snapValue(double attempted_value, DragMode drag_mode) {
1062 by mtytel
Added init synth function. Added zero snapping for lfo and steps.
195
  const double percent = 0.05;
1151 by mtytel
Added filter blend, better UI, button hovering tooltip.
196
  if (!snap_to_value_ || drag_mode != DragMode::absoluteDrag)
197
    return attempted_value;
1062 by mtytel
Added init synth function. Added zero snapping for lfo and steps.
198
199
  double range = getMaximum() - getMinimum();
200
  double radius = percent * range;
1151 by mtytel
Added filter blend, better UI, button hovering tooltip.
201
  if (attempted_value - snap_value_ <= radius && attempted_value - snap_value_ >= -radius)
202
    return snap_value_;
203
  return attempted_value;
1062 by mtytel
Added init synth function. Added zero snapping for lfo and steps.
204
}
205
704 by Matthew
Removed ^M line endings created by JUCE.
206
void SynthSlider::drawShadow(Graphics &g) {
207
  if (&getLookAndFeel() == TextLookAndFeel::instance())
208
    drawRectangularShadow(g);
209
  else if (isRotary())
210
    drawRotaryShadow(g);
1125 by mtytel
Fixed up linear slider modulations.
211
  else {
212
    g.setColour(Colour(0xff222222));
213
    g.fillRect(getBounds());
214
  }
704 by Matthew
Removed ^M line endings created by JUCE.
215
}
216
217
void SynthSlider::drawRotaryShadow(Graphics &g) {
1123 by mtytel
Prepping for openGL modulation meters.
218
  static const DropShadow shadow(Colour(0xee000000), 3, Point<int>(0, 0));
219
  static const float stroke_percent = 0.12f;
704 by Matthew
Removed ^M line endings created by JUCE.
220
221
  g.saveState();
222
  g.setOrigin(getX(), getY());
223
224
  float full_radius = std::min(getWidth() / 2.0f, getHeight() / 2.0f);
1123 by mtytel
Prepping for openGL modulation meters.
225
  float stroke_width = 2.0f * full_radius * stroke_percent;
704 by Matthew
Removed ^M line endings created by JUCE.
226
  Path shadow_path;
1123 by mtytel
Prepping for openGL modulation meters.
227
  float outer_radius = full_radius - stroke_width;
704 by Matthew
Removed ^M line endings created by JUCE.
228
  shadow_path.addCentredArc(full_radius, full_radius,
1123 by mtytel
Prepping for openGL modulation meters.
229
                            0.89f * full_radius, 0.87f * full_radius,
1124 by mtytel
Working version of the new openGL rotary modulation meters.
230
                            0, -rotary_angle, rotary_angle, true);
704 by Matthew
Removed ^M line endings created by JUCE.
231
  shadow.drawForPath(g, shadow_path);
1123 by mtytel
Prepping for openGL modulation meters.
232
233
  Path rail_outer;
234
  rail_outer.addCentredArc(full_radius, full_radius, outer_radius, outer_radius,
1124 by mtytel
Working version of the new openGL rotary modulation meters.
235
                           0.0f, -rotary_angle, rotary_angle, true);
1123 by mtytel
Prepping for openGL modulation meters.
236
237
  g.setColour(Colour(0xff333333));
238
239
  PathStrokeType outer_stroke =
240
      PathStrokeType(stroke_width, PathStrokeType::beveled, PathStrokeType::butt);
241
  g.strokePath(rail_outer, outer_stroke);
242
704 by Matthew
Removed ^M line endings created by JUCE.
243
  g.restoreState();
244
}
245
246
void SynthSlider::drawRectangularShadow(Graphics &g) {
247
  static const DropShadow shadow(Colour(0xbb000000), 2, Point<int>(0, 0));
248
249
  g.saveState();
250
  g.setOrigin(getX(), getY());
251
  shadow.drawForRectangle(g, getLocalBounds());
1199 by mtytel
Adding text slider modulation meters in.
252
  g.setColour(Colour(0xff333333));
253
  g.fillRect(getLocalBounds());
1125 by mtytel
Fixed up linear slider modulations.
254
704 by Matthew
Removed ^M line endings created by JUCE.
255
  g.restoreState();
256
}
257
971 by mtytel
Made modulation buttons highlight when hovered over destination
258
void SynthSlider::flipColoring(bool flip_coloring) {
259
  flip_coloring_ = flip_coloring;
260
  repaint();
261
}
262
263
void SynthSlider::setBipolar(bool bipolar) {
264
  bipolar_ = bipolar;
265
  repaint();
266
}
267
268
void SynthSlider::setActive(bool active) {
269
  active_ = active;
270
  repaint();
271
}
272
1007 by mtytel
Don't lock when adding/removing/changing modulations.
273
void SynthSlider::addSliderListener(SynthSlider::SliderListener* listener) {
274
  slider_listeners_.push_back(listener);
971 by mtytel
Made modulation buttons highlight when hovered over destination
275
}
276
1203 by Matt Tytel
Better slider display values. secs instead of Hz.
277
String SynthSlider::formatValue(float value) {
278
  static const int number_length = 5;
1248 by mtytel
Minor printing change.
279
  static const int max_decimals = 3;
1203 by Matt Tytel
Better slider display values. secs instead of Hz.
280
281
  if (details_.steps)
282
    return String(value) + " " + details_.display_units;
283
284
  String format = String(value, max_decimals);
285
  format = format.substring(0, number_length);
286
  int spaces = number_length - format.length();
287
  
288
  for (int i = 0; i < spaces; ++i)
289
    format = " " + format;
290
  
291
  return format + " " + details_.display_units;
292
}
293
1242 by mtytel
Fixed up setting values loop and modulation popups.
294
void SynthSlider::notifyGuis() {
295
  for (SynthSlider::SliderListener* listener : slider_listeners_)
296
    listener->guiChanged(this);
297
}
298
1259 by mtytel
Removed a bunch of modal dialogs in exchange for async ones.
299
void SynthSlider::handlePopupResult(int result) {
300
  SynthGuiInterface* parent = findParentComponentOfClass<SynthGuiInterface>();
301
  if (parent == nullptr)
302
    return;
303
304
  SynthBase* synth = parent->getSynth();
305
  std::vector<mopo::ModulationConnection*> connections =
306
      parent->getSynth()->getDestinationConnections(getName().toStdString());
307
308
  if (result == kArmMidiLearn)
1358 by mtytel
Fixed Midi mapping to stepped value
309
    synth->armMidiLearn(getName().toStdString());
1259 by mtytel
Removed a bunch of modal dialogs in exchange for async ones.
310
  else if (result == kClearMidiLearn)
311
    synth->clearMidiLearn(getName().toStdString());
312
  else if (result == kDefaultValue)
313
    setValue(getDoubleClickReturnValue());
314
  else if (result == kClearModulations) {
315
    for (mopo::ModulationConnection* connection : connections) {
316
      std::string source = connection->source;
317
      synth->disconnectModulation(connection);
318
    }
319
    for (SynthSlider::SliderListener* listener : slider_listeners_)
320
      listener->modulationsChanged(getName().toStdString());
321
  }
322
  else if (result >= kModulationList) {
323
    int connection_index = result - kModulationList;
324
    std::string source = connections[connection_index]->source;
325
    synth->disconnectModulation(connections[connection_index]);
326
327
    for (SynthSlider::SliderListener* listener : slider_listeners_)
328
      listener->modulationsChanged(getName().toStdString());
329
  }
330
}
331
704 by Matthew
Removed ^M line endings created by JUCE.
332
void SynthSlider::notifyTooltip() {
1151 by mtytel
Added filter blend, better UI, button hovering tooltip.
333
  if (parent_ == nullptr)
704 by Matthew
Removed ^M line endings created by JUCE.
334
    parent_ = findParentComponentOfClass<FullInterface>();
774 by mtytel
Using english strings in tooltip instead of internal.
335
  if (parent_) {
336
    std::string name = getName().toStdString();
337
    if (mopo::Parameters::isParameter(name))
338
      name = mopo::Parameters::getDetails(name).display_name;
339
340
    parent_->setToolTipText(name, getTextFromValue(getValue()));
341
  }
704 by Matthew
Removed ^M line endings created by JUCE.
342
}