Skip to content

Commit

Permalink
WrapUp the Features of the Tuning Editor (#5241)
Browse files Browse the repository at this point in the history
* WrapUp the Features of the Tuning Editor

- Export to HTML moved to the SCL/KBM page from the tuning menu.
- Tuning Library button on SCL/KBM page
- Drag n Drop onto the Tuning Thingy
- Kill the generators tab
- Show tuning tones in radial view
  • Loading branch information
baconpaul authored Oct 13, 2021
1 parent f6bf027 commit 744b6ec
Show file tree
Hide file tree
Showing 7 changed files with 179 additions and 16 deletions.
4 changes: 0 additions & 4 deletions src/surge-xt/gui/SurgeGUIEditor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2838,10 +2838,6 @@ juce::PopupMenu SurgeGUIEditor::makeTuningMenu(const juce::Point<int> &where, bo

tuningSubMenu.addSeparator();

tuningSubMenu.addItem(Surge::GUI::toOSCaseForMenu("Show Current Tuning Information..."),
(!this->synth->storage.isStandardTuning), false,
[this]() { showHTML(this->tuningToHtml()); });

tuningSubMenu.addItem(Surge::GUI::toOSCaseForMenu("Factory Tuning Library..."), [this]() {
auto path = this->synth->storage.datapath / "tuning_library";
Surge::GUI::openFileOrFolder(path);
Expand Down
2 changes: 1 addition & 1 deletion src/surge-xt/gui/SurgeGUIEditor.h
Original file line number Diff line number Diff line change
Expand Up @@ -603,11 +603,11 @@ class SurgeGUIEditor : public Surge::GUI::IComponentTagValue::Listener,

public:
bool useDevMenu = false;
SurgeSynthEditor *juceEditor{nullptr};

private:
float blinktimer = 0;
bool blinkstate = false;
SurgeSynthEditor *juceEditor{nullptr};
int firstIdleCountdown = 0;

juce::PopupMenu makeSmoothMenu(const juce::Point<int> &where,
Expand Down
1 change: 1 addition & 0 deletions src/surge-xt/gui/SurgeGUIEditorOverlays.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,7 @@ std::unique_ptr<Surge::Overlays::OverlayComponent> SurgeGUIEditor::createOverlay

auto pt = std::make_unique<Surge::Overlays::TuningOverlay>();
pt->setStorage(&(this->synth->storage));
pt->setEditor(this);
pt->setSkin(currentSkin, bitmapStore);
pt->setTuning(synth->storage.currentTuning);
pt->setEnclosingParentPosition(juce::Rectangle<int>(px, py, w, h));
Expand Down
1 change: 1 addition & 0 deletions src/surge-xt/gui/overlays/OverlayComponent.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ struct OverlayComponent : juce::Component
bool canTearOut{false};
void setCanTearOut(bool b) { canTearOut = b; }
bool getCanTearOut() { return canTearOut; }
virtual void onTearOutChanged(bool isTornOut) {}

std::pair<bool, Surge::Storage::DefaultKey> canMoveAround{false, Surge::Storage::nKeys};
void setCanMoveAround(std::pair<bool, Surge::Storage::DefaultKey> b) { canMoveAround = b; }
Expand Down
6 changes: 6 additions & 0 deletions src/surge-xt/gui/overlays/OverlayWrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ void OverlayWrapper::doTearOut(const juce::Point<int> &showAt)
if (auto oc = dynamic_cast<Surge::Overlays::OverlayComponent *>(primaryChild.get()))
{
t = oc->getEnclosingParentTitle();
oc->onTearOutChanged(true);
}
auto dw = std::make_unique<TearOutWindow>(t, juce::DocumentWindow::closeButton |
juce::DocumentWindow::minimiseButton);
Expand Down Expand Up @@ -219,6 +220,11 @@ void OverlayWrapper::doTearIn()
setBounds(locationBeforeTearOut);
parentBeforeTearOut->addAndMakeVisible(*this);
parentBeforeTearOut = nullptr;

if (auto oc = dynamic_cast<Surge::Overlays::OverlayComponent *>(primaryChild.get()))
{
oc->onTearOutChanged(false);
}
}

void OverlayWrapper::mouseDown(const juce::MouseEvent &e)
Expand Down
167 changes: 157 additions & 10 deletions src/surge-xt/gui/overlays/TuningOverlays.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
#include "SurgeStorage.h"
#include "UserDefaults.h"
#include "SurgeGUIUtils.h"
#include "SurgeGUIEditor.h"
#include "SurgeSynthEditor.h" // bit gross but so I can do DnD
#include "fmt/core.h"
#include <chrono>

Expand Down Expand Up @@ -226,25 +228,67 @@ class TuningTableListBoxModel : public juce::TableListBoxModel,

class RadialScaleGraph;

class RadialScaleGraph : public juce::Component
class RadialScaleGraph : public juce::Component, juce::TextEditor::Listener
{
public:
RadialScaleGraph()
{
toneList = std::make_unique<juce::Viewport>();
toneInterior = std::make_unique<juce::Component>();
toneList->setViewedComponent(toneInterior.get(), false);
addAndMakeVisible(*toneList);

setTuning(Tunings::Tuning(Tunings::evenTemperament12NoteScale(), Tunings::tuneA69To(440)));
}

void setTuning(const Tunings::Tuning &t)
{
int priorLen = tuning.scale.count;
tuning = t;
scale = t.scale;

if (toneEditors.empty() || priorLen != scale.count || toneEditors.size() != scale.count + 1)
{
toneInterior->removeAllChildren();
auto w = usedForSidebar - 15;
auto m = 2;
auto h = 24;
toneInterior->setSize(w, (scale.count + 1) * (h + m));
toneEditors.clear();
for (int i = 0; i < scale.count + 1; ++i)
{
auto te = std::make_unique<juce::TextEditor>("tone");
te->setText(std::to_string(i), juce::NotificationType::dontSendNotification);
te->setBounds(m, i * (h + m) + m, w - 2 * m, h);
te->setEnabled(i != 0);
te->addListener(this);
toneInterior->addAndMakeVisible(*te);
toneEditors.push_back(std::move(te));
}
}

toneEditors[0]->setText("-", juce::NotificationType::dontSendNotification);
for (int i = 0; i < scale.count; ++i)
{
auto td = fmt::format("{:.5f}", scale.tones[i].cents);
if (scale.tones[i].type == Tunings::Tone::kToneRatio)
{
td = fmt::format("{:d}/{:d}", scale.tones[i].ratio_n, scale.tones[i].ratio_d);
}
toneEditors[i + 1]->setText(td, juce::NotificationType::dontSendNotification);
}

notesOn.clear();
notesOn.resize(scale.count);
for (int i = 0; i < scale.count; ++i)
notesOn[i] = false;
setNotesOn(bitset);
}

private:
void textEditorReturnKeyPressed(TextEditor &editor) override;

public:
virtual void paint(juce::Graphics &g) override;
Tunings::Tuning tuning;
Tunings::Scale scale;
Expand All @@ -254,6 +298,15 @@ class RadialScaleGraph : public juce::Component

juce::AffineTransform screenTransform, screenTransformInverted;
std::function<void(int index, double)> onToneChanged = [](int, double) {};
std::function<void(int index, const std::string &s)> onToneStringChanged =
[](int, const std::string &) {};
static constexpr int usedForSidebar = 140;

std::unique_ptr<juce::Viewport> toneList;
std::unique_ptr<juce::Component> toneInterior;
std::vector<std::unique_ptr<juce::TextEditor>> toneEditors;

void resized() override { toneList->setBounds(0, 0, usedForSidebar, getHeight()); }

std::vector<bool> notesOn;
std::bitset<128> bitset{0};
Expand All @@ -270,6 +323,24 @@ class RadialScaleGraph : public juce::Component
notesOn[tuning.scalePositionForMidiNote(i)] = true;
}
}

for (int i = 0; i < scale.count + 1; ++i)
{
auto ni = i % (scale.count);
if (notesOn[ni])
{
toneEditors[i]->setColour(juce::TextEditor::ColourIds::backgroundColourId,
juce::Colour(0xFFaaaa50));
}
else
{
toneEditors[i]->setColour(juce::TextEditor::ColourIds::backgroundColourId,
juce::Colours::black);
}
toneEditors[i]->repaint();
}
toneInterior->repaint();
toneList->repaint();
repaint();
}
virtual void mouseMove(const juce::MouseEvent &e) override;
Expand All @@ -287,7 +358,7 @@ void RadialScaleGraph::paint(Graphics &g)
notesOn[i] = 0;
}
g.fillAll(getLookAndFeel().findColour(juce::ResizableWindow::backgroundColourId));
int w = getWidth();
int w = getWidth() - usedForSidebar;
int h = getHeight();
float r = std::min(w, h) / 2.1;
float xo = (w - 2 * r) / 2.0;
Expand All @@ -296,10 +367,12 @@ void RadialScaleGraph::paint(Graphics &g)

g.saveState();

screenTransform = AffineTransform::scale(1.0 / (1.0 + outerRadiusExtension * 1.1))
screenTransform = AffineTransform()
.scaled(1.0 / (1.0 + outerRadiusExtension * 1.1))
.scaled(r, -r)
.translated(r, r)
.translated(xo, yo);
.translated(xo, yo)
.translated(usedForSidebar, 0);
screenTransformInverted = screenTransform.inverted();

g.addTransform(screenTransform);
Expand Down Expand Up @@ -462,6 +535,7 @@ struct IntervalMatrix : public juce::Component
};

distanceButton = std::make_unique<juce::ToggleButton>("Show Distance from Even");
distanceButton->setToggleState(false, juce::NotificationType::dontSendNotification);
distanceButton->onClick = [this]() {
intervalPainter->mode = IntervalPainter::DIST;
intervalPainter->repaint();
Expand Down Expand Up @@ -490,7 +564,7 @@ struct IntervalMatrix : public juce::Component
{
INTERV,
DIST
} mode;
} mode{INTERV};
IntervalPainter(IntervalMatrix *m) : matrix(m) {}

static constexpr int cellH{14}, cellW{35};
Expand Down Expand Up @@ -783,12 +857,23 @@ void RadialScaleGraph::mouseDrag(const juce::MouseEvent &e)
}
}

void RadialScaleGraph::textEditorReturnKeyPressed(TextEditor &editor)
{
for (int i = 1; i <= scale.count; ++i)
{
if (&editor == toneEditors[i].get())
{
onToneStringChanged(i - 1, editor.getText().toStdString());
}
}
}

struct SCLKBMDisplay : public juce::Component,
Surge::GUI::SkinConsumingComponent,
juce::TextEditor::Listener,
juce::Button::Listener
{
SCLKBMDisplay()
SCLKBMDisplay(TuningOverlay *o) : overlay(o)
{
scl = std::make_unique<juce::TextEditor>();
scl->setFont(Surge::GUI::getFontManager()->getFiraMonoAtSize(10));
Expand All @@ -808,6 +893,14 @@ struct SCLKBMDisplay : public juce::Component,
apply->addListener(this);
apply->setEnabled(false);
addAndMakeVisible(*apply);

exportButton = std::make_unique<juce::TextButton>("Export HTML", "Export HTML");
exportButton->addListener(this);
addAndMakeVisible(*exportButton);

libButton = std::make_unique<juce::TextButton>("Tuning Library", "Tuning Library");
libButton->addListener(this);
addAndMakeVisible(*libButton);
}
void setTuning(const Tunings::Tuning &t)
{
Expand All @@ -824,18 +917,36 @@ struct SCLKBMDisplay : public juce::Component,
scl->setBounds(2, 2, w / 2 - 4, h - 4);
kbm->setBounds(w / 2 + 2, 2, w / 2 - 4, h - 4);
apply->setBounds(w - 102, h + 2, 100, 18);
exportButton->setBounds(w - 204, h + 2, 100, 18);
libButton->setBounds(w - 306, h + 2, 100, 18);
}

void textEditorTextChanged(TextEditor &editor) override { apply->setEnabled(true); }
void buttonClicked(Button *button) override
{
onTextChanged(scl->getText().toStdString(), kbm->getText().toStdString());
if (button == apply.get())
{
onTextChanged(scl->getText().toStdString(), kbm->getText().toStdString());
}
if (button == exportButton.get())
{
if (overlay && overlay->editor)
{
overlay->editor->showHTML(overlay->editor->tuningToHtml());
}
}
if (button == libButton.get())
{
auto path = overlay->storage->datapath / "tuning_library";
Surge::GUI::openFileOrFolder(path);
}
}
std::function<void(const std::string &scl, const std::string &kbl)> onTextChanged =
[](auto a, auto b) {};

std::unique_ptr<juce::TextEditor> scl, kbm;
std::unique_ptr<juce::Button> apply;
std::unique_ptr<juce::Button> apply, exportButton, libButton;
TuningOverlay *overlay{nullptr};

JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(SCLKBMDisplay);
};
Expand All @@ -858,20 +969,22 @@ TuningOverlay::TuningOverlay()
tabArea =
std::make_unique<juce::TabbedComponent>(juce::TabbedButtonBar::Orientation::TabsAtBottom);

sclKbmDisplay = std::make_unique<SCLKBMDisplay>();
sclKbmDisplay = std::make_unique<SCLKBMDisplay>(this);
sclKbmDisplay->onTextChanged = [this](const std::string &s, const std::string &k) {
this->onNewSCLKBM(s, k);
};

radialScaleGraph = std::make_unique<RadialScaleGraph>();
radialScaleGraph->onToneChanged = [this](int note, double d) { this->onToneChanged(note, d); };
radialScaleGraph->onToneStringChanged = [this](int note, const std::string &d) {
this->onToneStringChanged(note, d);
};

intervalMatrix = std::make_unique<IntervalMatrix>(this);

tabArea->addTab("SCL/KBM", juce::Colours::black, sclKbmDisplay.get(), false);
tabArea->addTab("Radial", juce::Colours::black, radialScaleGraph.get(), false);
tabArea->addTab("Interval", juce::Colours::black, intervalMatrix.get(), false);
tabArea->addTab("Generators", juce::Colours::black, new juce::Label("S"), true);
addAndMakeVisible(*tabArea);
}

Expand Down Expand Up @@ -906,6 +1019,23 @@ void TuningOverlay::onToneChanged(int tone, double newCentsValue)
}
}

void TuningOverlay::onToneStringChanged(int tone, const std::string &newStringValue)
{
if (storage)
{
try
{
auto parsed = Tunings::toneFromString(newStringValue);
storage->currentScale.tones[tone] = parsed;
recalculateScaleText();
}
catch (Tunings::TuningError &e)
{
storage->reportError(e.what(), "Tuning Tone Conversion");
}
}
}

void TuningOverlay::onNewSCLKBM(const std::string &scl, const std::string &kbm)
{
if (!storage)
Expand Down Expand Up @@ -978,5 +1108,22 @@ void TuningOverlay::onSkinChanged()
tuningKeyboardTable->repaint();
}

void TuningOverlay::onTearOutChanged(bool isTornOut) { doDnD = isTornOut; }
bool TuningOverlay::isInterestedInFileDrag(const StringArray &files)
{
if (!doDnD)
return false;
if (editor)
return editor->juceEditor->isInterestedInFileDrag(files);
return false;
}
void TuningOverlay::filesDropped(const StringArray &files, int x, int y)
{
if (!doDnD)
return;
if (editor)
editor->juceEditor->filesDropped(files, x, y);
}

} // namespace Overlays
} // namespace Surge
Loading

0 comments on commit 744b6ec

Please sign in to comment.