diff --git a/editor/CMakeLists.txt b/editor/CMakeLists.txt
index 65cdb6d9..bd9a38a8 100644
--- a/editor/CMakeLists.txt
+++ b/editor/CMakeLists.txt
@@ -3,8 +3,23 @@ include("cmake/Vstgui.cmake")
set(EDITOR_RESOURCES
logo.png
+ logo_text.png
+ logo_text@2x.png
+ icon_white.png
+ icon_white@2x.png
+ knob48.png
+ knob48@2x.png
+ Fonts/fluentui-system-regular-20.ttf
PARENT_SCOPE)
+function(copy_editor_resources SOURCE_DIR DESTINATION_DIR)
+ foreach(res ${EDITOR_RESOURCES})
+ get_filename_component(_dir "${res}" DIRECTORY)
+ file(MAKE_DIRECTORY "${DESTINATION_DIR}/${_dir}")
+ file(COPY "${SOURCE_DIR}/${res}" DESTINATION "${DESTINATION_DIR}/${_dir}")
+ endforeach()
+endfunction()
+
# editor
add_library(sfizz_editor STATIC EXCLUDE_FROM_ALL
src/editor/EditIds.h
@@ -14,8 +29,53 @@ add_library(sfizz_editor STATIC EXCLUDE_FROM_ALL
src/editor/EditorController.h
src/editor/GUIComponents.h
src/editor/GUIComponents.cpp
+ src/editor/NativeHelpers.h
+ src/editor/NativeHelpers.cpp
+ src/editor/layout/main.hpp
src/editor/utility/vstgui_after.h
src/editor/utility/vstgui_before.h)
target_include_directories(sfizz_editor PUBLIC "src")
target_link_libraries(sfizz_editor PRIVATE sfizz-vstgui)
target_link_libraries(sfizz_editor PUBLIC absl::strings)
+if(APPLE)
+ find_library(APPLE_APPKIT_LIBRARY "AppKit")
+ find_library(APPLE_CORESERVICES_LIBRARY "CoreServices")
+ find_library(APPLE_FOUNDATION_LIBRARY "Foundation")
+ target_sources(sfizz_editor PRIVATE
+ src/editor/NativeHelpers.mm)
+ target_link_libraries(sfizz_editor PRIVATE
+ "${APPLE_APPKIT_LIBRARY}"
+ "${APPLE_CORESERVICES_LIBRARY}"
+ "${APPLE_FOUNDATION_LIBRARY}")
+ target_compile_options(sfizz_editor PRIVATE "-fobjc-arc")
+endif()
+
+# dependencies
+if(WIN32)
+ #
+elseif(APPLE)
+ #
+else()
+ find_package(PkgConfig REQUIRED)
+ pkg_check_modules(sfizz-gio "gio-2.0" REQUIRED)
+ target_include_directories(sfizz_editor PRIVATE ${sfizz-gio_INCLUDE_DIRS})
+ target_link_libraries(sfizz_editor PRIVATE ${sfizz-gio_LIBRARIES})
+endif()
+target_include_directories(sfizz_editor PRIVATE "../src/external") # ghc::filesystem
+
+# layout tool
+if(NOT CMAKE_CROSSCOMPILING)
+ add_executable(layout-maker
+ "tools/layout-maker/sources/layout.h"
+ "tools/layout-maker/sources/reader.cpp"
+ "tools/layout-maker/sources/reader.h"
+ "tools/layout-maker/sources/main.cpp")
+ target_link_libraries(layout-maker PRIVATE absl::strings)
+
+ add_custom_command(
+ OUTPUT "${CMAKE_CURRENT_SOURCE_DIR}/src/editor/layout/main.hpp"
+ COMMAND "${CMAKE_CURRENT_BINARY_DIR}/layout-maker"
+ "${CMAKE_CURRENT_SOURCE_DIR}/layout/main.fl"
+ > "${CMAKE_CURRENT_SOURCE_DIR}/src/editor/layout/main.hpp"
+ DEPENDS layout-maker "${CMAKE_CURRENT_SOURCE_DIR}/layout/main.fl")
+endif()
diff --git a/editor/cmake/Vstgui.cmake b/editor/cmake/Vstgui.cmake
index 2cac1f17..25f185bb 100644
--- a/editor/cmake/Vstgui.cmake
+++ b/editor/cmake/Vstgui.cmake
@@ -211,6 +211,8 @@ endif()
if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
# higher C++ requirement on Windows
set_property(TARGET sfizz-vstgui PROPERTY CXX_STANDARD 14)
+ # Windows 10 RS2 DDI for custom fonts
+ target_compile_definitions(sfizz-vstgui PRIVATE "NTDDI_VERSION=0x0A000003")
endif()
if (CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
diff --git a/editor/layout/main.fl b/editor/layout/main.fl
new file mode 100644
index 00000000..1d27e987
--- /dev/null
+++ b/editor/layout/main.fl
@@ -0,0 +1,344 @@
+# data file for the Fltk User Interface Designer (fluid)
+version 1.0305
+header_name {.h}
+code_name {.cxx}
+widget_class mainView {open
+ xywh {410 523 800 475} type Double
+ class LogicalGroup visible
+} {
+ Fl_Group {} {
+ comment {theme=darkTheme} open
+ xywh {0 0 800 110}
+ class LogicalGroup
+ } {
+ Fl_Group {} {open
+ xywh {5 4 100 101} box ROUNDED_BOX align 0
+ class RoundedGroup
+ } {
+ Fl_Box {} {
+ comment {tag=kTagFirstChangePanel+kPanelGeneral}
+ image {../resources/icon_white.png} xywh {7 6 96 96}
+ class SfizzMainButton
+ }
+ }
+ Fl_Group {} {open
+ xywh {110 5 380 100} box ROUNDED_BOX
+ class RoundedGroup
+ } {
+ Fl_Box {} {
+ label {File:}
+ xywh {125 15 40 25} labelsize 16
+ class Label
+ }
+ Fl_Box {} {
+ label {KS:}
+ xywh {125 45 40 25} labelsize 16
+ class Label
+ }
+ Fl_Box {} {
+ label {Separator 1}
+ xywh {120 40 355 5} box BORDER_BOX labeltype NO_LABEL
+ class HLine
+ }
+ Fl_Box {} {
+ label {Separator 2}
+ xywh {120 70 355 5} box BORDER_BOX labeltype NO_LABEL
+ class HLine
+ }
+ Fl_Box sfzFileLabel_ {
+ label {DefaultInstrument.sfz}
+ xywh {190 15 230 25} labelsize 20
+ class Label
+ }
+ Fl_Box {} {
+ label {Key switch}
+ xywh {190 45 230 25} labelsize 20
+ class Label
+ }
+ Fl_Box {} {
+ label {Voices:}
+ xywh {120 75 60 25} labelsize 12 align 24
+ class Label
+ }
+ Fl_Button {} {
+ comment {tag=kTagLoadSfzFile}
+ xywh {425 15 25 25} labelsize 24
+ class LoadFileButton
+ }
+ Fl_Button {} {
+ comment {tag=kTagEditSfzFile}
+ xywh {450 15 25 25} labelsize 24
+ class EditFileButton
+ }
+ Fl_Box infoVoicesLabel_ {
+ xywh {185 75 50 25} labelsize 12 align 16
+ class Label
+ }
+ Fl_Box {} {
+ label {Max:}
+ xywh {240 75 60 25} labelsize 12 align 24
+ class Label
+ }
+ Fl_Box numVoicesLabel_ {
+ xywh {305 75 50 25} labelsize 12 align 16
+ class Label
+ }
+ Fl_Box {} {
+ label {Memory:}
+ xywh {360 75 60 25} labelsize 12 align 24
+ class Label
+ }
+ Fl_Box memoryLabel_ {
+ xywh {425 75 50 25} labelsize 12 align 16
+ class Label
+ }
+ }
+ Fl_Group {} {open
+ xywh {495 5 100 100} box ROUNDED_BOX
+ class RoundedGroup
+ } {
+ Fl_Light_Button {} {
+ label SETUP
+ comment {tag=kTagFirstChangePanel+kPanelSettings}
+ xywh {510 42 70 25}
+ class LightButton
+ }
+ Fl_Light_Button {} {
+ label CC
+ comment {tag=kTagFirstChangePanel+kPanelControls}
+ xywh {510 15 70 25}
+ class LightButton
+ }
+ Fl_Light_Button {} {
+ label INFO
+ comment {tag=kTagFirstChangePanel+kPanelInfo}
+ xywh {510 69 70 25}
+ class LightButton
+ }
+ }
+ Fl_Group {} {open
+ xywh {600 5 195 100} box ROUNDED_BOX
+ class RoundedGroup
+ } {
+ Fl_Dial {} {
+ xywh {615 20 48 48} value 0.5 hide
+ class Knob48
+ }
+ Fl_Box {} {
+ label Center
+ xywh {610 70 60 25} labelsize 12 hide
+ class ValueLabel
+ }
+ Fl_Dial volumeSlider_ {
+ comment {tag=kTagSetVolume}
+ xywh {680 20 48 48} value 0.5
+ class Knob48
+ }
+ Fl_Box volumeLabel_ {
+ label {0.0 dB}
+ xywh {675 70 60 25} labelsize 12
+ class ValueLabel
+ }
+ Fl_Box {} {
+ xywh {745 20 35 70} box BORDER_BOX
+ class VMeter
+ }
+ }
+ }
+ Fl_Group {subPanels_[kPanelGeneral]} {
+ xywh {5 110 790 285} hide
+ class LogicalGroup
+ } {
+ Fl_Group {} {open
+ xywh {5 110 120 285} box ROUNDED_BOX
+ class RoundedGroup
+ } {
+ Fl_Box {} {
+ label {Curves:}
+ xywh {15 120 60 25} labelsize 12 align 20
+ class Label
+ }
+ Fl_Box {} {
+ label {Masters:}
+ xywh {15 145 60 25} labelsize 12 align 20
+ class Label
+ }
+ Fl_Box {} {
+ label {Groups:}
+ xywh {15 170 60 25} labelsize 12 align 20
+ class Label
+ }
+ Fl_Box {} {
+ label {Regions:}
+ xywh {15 195 60 25} labelsize 12 align 20
+ class Label
+ }
+ Fl_Box {} {
+ label {Samples:}
+ xywh {15 220 60 25} labelsize 12 align 20
+ class Label
+ }
+ Fl_Box infoCurvesLabel_ {
+ label 0
+ xywh {75 120 40 25} labelsize 12 align 16
+ class Label
+ }
+ Fl_Box infoMastersLabel_ {
+ label 0
+ xywh {75 145 40 25} labelsize 12 align 16
+ class Label
+ }
+ Fl_Box infoGroupsLabel_ {
+ label 0
+ xywh {75 170 40 25} labelsize 12 align 16
+ class Label
+ }
+ Fl_Box infoRegionsLabel_ {
+ label 0
+ xywh {75 195 40 25} labelsize 12 align 16
+ class Label
+ }
+ Fl_Box infoSamplesLabel_ {
+ label 0
+ xywh {75 220 40 25} labelsize 12 align 16
+ class Label
+ }
+ }
+ Fl_Group {} {open
+ xywh {130 110 665 280}
+ class LogicalGroup
+ } {
+ Fl_Box {} {
+ image {../resources/logo_text.png} xywh {260 125 400 250}
+ class SfizzLargePicture
+ }
+ }
+ }
+ Fl_Group {subPanels_[kPanelControls]} {
+ xywh {5 110 790 285} hide
+ class LogicalGroup
+ } {
+ Fl_Group {} {open
+ xywh {5 110 790 285} box ROUNDED_BOX
+ class RoundedGroup
+ } {
+ Fl_Box {} {
+ label {Controls not available}
+ xywh {5 110 790 285} labelsize 40
+ class Label
+ }
+ }
+ }
+ Fl_Group {subPanels_[kPanelSettings]} {open
+ xywh {5 110 790 285}
+ class LogicalGroup
+ } {
+ Fl_Group {} {
+ label Engine open selected
+ xywh {260 125 280 110} box ROUNDED_BOX labelsize 12 align 17
+ class TitleGroup
+ } {
+ Fl_Spinner numVoicesSlider_ {
+ comment {tag=kTagSetNumVoices}
+ xywh {285 185 60 25} labelsize 12 textsize 12
+ class ValueMenu
+ }
+ Fl_Box {} {
+ label Polyphony
+ xywh {275 145 80 25} labelsize 12
+ class ValueLabel
+ }
+ Fl_Spinner oversamplingSlider_ {
+ comment {tag=kTagSetOversampling}
+ xywh {370 185 60 25} labelsize 12 textsize 12
+ class ValueMenu
+ }
+ Fl_Box {} {
+ label Oversampling
+ xywh {360 145 80 25} labelsize 12
+ class ValueLabel
+ }
+ Fl_Box {} {
+ label {Preload size}
+ xywh {445 145 80 25} labelsize 12
+ class ValueLabel
+ }
+ Fl_Spinner preloadSizeSlider_ {
+ comment {tag=kTagSetPreloadSize}
+ xywh {455 185 60 25} labelsize 12 textsize 12
+ class ValueMenu
+ }
+ }
+ Fl_Group {} {
+ label Tuning open
+ xywh {205 260 390 120} box ROUNDED_BOX labelsize 12 align 17
+ class TitleGroup
+ } {
+ Fl_Box {} {
+ label {Root key}
+ xywh {330 280 80 25} labelsize 12
+ class ValueLabel
+ }
+ Fl_Spinner tuningFrequencySlider_ {
+ comment {tag=kTagSetTuningFrequency}
+ xywh {425 320 60 25} labelsize 12 textsize 12
+ class ValueMenu
+ }
+ Fl_Box {} {
+ label Frequency
+ xywh {415 280 80 25} labelsize 12
+ class ValueLabel
+ }
+ Fl_Dial stretchedTuningSlider_ {
+ comment {tag=kTagSetStretchedTuning}
+ xywh {515 305 48 48} value 0.5
+ class Knob48
+ }
+ Fl_Box {} {
+ label Stretch
+ xywh {500 280 80 25} labelsize 12
+ class ValueLabel
+ }
+ Fl_Box {} {
+ label {Scala file}
+ xywh {225 280 100 25} labelsize 12
+ class ValueLabel
+ }
+ Fl_Button scalaFileButton_ {
+ label DefaultScale
+ comment {tag=kTagLoadScalaFile}
+ xywh {225 320 100 25} labelsize 12
+ class ValueButton
+ }
+ Fl_Spinner scalaRootKeySlider_ {
+ comment {tag=kTagSetScalaRootKey}
+ xywh {340 320 35 25} labelsize 12 textsize 12
+ class ValueMenu
+ }
+ Fl_Spinner scalaRootOctaveSlider_ {
+ comment {tag=kTagSetScalaRootKey}
+ xywh {375 320 30 25} labelsize 12 textsize 12
+ class ValueMenu
+ }
+ }
+ }
+ Fl_Group {subPanels_[kPanelInfo]} {
+ xywh {5 110 790 285} hide
+ class LogicalGroup
+ } {
+ Fl_Group {} {open
+ xywh {5 110 790 285} box ROUNDED_BOX
+ class RoundedGroup
+ } {
+ Fl_Box {} {
+ label {Informative text goes here}
+ xywh {5 110 790 285} labelsize 40
+ class Label
+ }
+ }
+ }
+ Fl_Box {} {
+ xywh {5 400 790 70}
+ class Piano
+ }
+}
diff --git a/editor/resources/Fonts/fluentui-system-regular-20.ttf b/editor/resources/Fonts/fluentui-system-regular-20.ttf
new file mode 100644
index 00000000..4e838582
Binary files /dev/null and b/editor/resources/Fonts/fluentui-system-regular-20.ttf differ
diff --git a/editor/resources/icon_white.png b/editor/resources/icon_white.png
new file mode 100644
index 00000000..d63b6f3d
Binary files /dev/null and b/editor/resources/icon_white.png differ
diff --git a/editor/resources/icon_white.svg b/editor/resources/icon_white.svg
new file mode 100644
index 00000000..4021b787
--- /dev/null
+++ b/editor/resources/icon_white.svg
@@ -0,0 +1,253 @@
+
+
diff --git a/editor/resources/icon_white@2x.png b/editor/resources/icon_white@2x.png
new file mode 100644
index 00000000..e5bc80a2
Binary files /dev/null and b/editor/resources/icon_white@2x.png differ
diff --git a/editor/resources/knob.knob b/editor/resources/knob.knob
new file mode 100644
index 00000000..093bd66c
Binary files /dev/null and b/editor/resources/knob.knob differ
diff --git a/editor/resources/knob48.png b/editor/resources/knob48.png
new file mode 100644
index 00000000..aa5c8eb3
Binary files /dev/null and b/editor/resources/knob48.png differ
diff --git a/editor/resources/knob48@2x.png b/editor/resources/knob48@2x.png
new file mode 100644
index 00000000..2f8d49e6
Binary files /dev/null and b/editor/resources/knob48@2x.png differ
diff --git a/editor/resources/logo_text.png b/editor/resources/logo_text.png
new file mode 100644
index 00000000..d683e44e
Binary files /dev/null and b/editor/resources/logo_text.png differ
diff --git a/editor/resources/logo_text.svg b/editor/resources/logo_text.svg
new file mode 100644
index 00000000..a62481c8
--- /dev/null
+++ b/editor/resources/logo_text.svg
@@ -0,0 +1,252 @@
+
+
diff --git a/editor/resources/logo_text@2x.png b/editor/resources/logo_text@2x.png
new file mode 100644
index 00000000..c3e28c63
Binary files /dev/null and b/editor/resources/logo_text@2x.png differ
diff --git a/editor/src/editor/Editor.cpp b/editor/src/editor/Editor.cpp
index d50aa5b4..8e3089a8 100644
--- a/editor/src/editor/Editor.cpp
+++ b/editor/src/editor/Editor.cpp
@@ -8,7 +8,10 @@
#include "EditorController.h"
#include "EditIds.h"
#include "GUIComponents.h"
+#include "NativeHelpers.h"
#include
+#include
+#include
#include
#include
@@ -18,19 +21,20 @@
using namespace VSTGUI;
-const int Editor::viewWidth { 482 };
-const int Editor::viewHeight { 225 };
+const int Editor::viewWidth { 800 };
+const int Editor::viewHeight { 475 };
struct Editor::Impl : EditorController::Receiver, IControlListener {
EditorController* ctrl_ = nullptr;
CFrame* frame_ = nullptr;
- SharedPointer view_;
+ SharedPointer mainView_;
+
+ std::string currentSfzFile_;
enum {
kPanelGeneral,
- // kPanelControls,
+ kPanelControls,
kPanelSettings,
- kPanelTuning,
kPanelInfo,
kNumPanels,
};
@@ -40,6 +44,7 @@ struct Editor::Impl : EditorController::Receiver, IControlListener {
enum {
kTagLoadSfzFile,
+ kTagEditSfzFile,
kTagSetVolume,
kTagSetNumVoices,
kTagSetOversampling,
@@ -54,19 +59,21 @@ struct Editor::Impl : EditorController::Receiver, IControlListener {
CTextLabel* sfzFileLabel_ = nullptr;
CTextLabel* scalaFileLabel_ = nullptr;
- CSliderBase *volumeSlider_ = nullptr;
+ CTextButton* scalaFileButton_ = nullptr;
+ CControl *volumeSlider_ = nullptr;
CTextLabel* volumeLabel_ = nullptr;
- CSliderBase *numVoicesSlider_ = nullptr;
+ SValueMenu *numVoicesSlider_ = nullptr;
CTextLabel* numVoicesLabel_ = nullptr;
- CSliderBase *oversamplingSlider_ = nullptr;
+ SValueMenu *oversamplingSlider_ = nullptr;
CTextLabel* oversamplingLabel_ = nullptr;
- CSliderBase *preloadSizeSlider_ = nullptr;
+ SValueMenu *preloadSizeSlider_ = nullptr;
CTextLabel* preloadSizeLabel_ = nullptr;
- CSliderBase *scalaRootKeySlider_ = nullptr;
+ SValueMenu *scalaRootKeySlider_ = nullptr;
+ SValueMenu *scalaRootOctaveSlider_ = nullptr;
CTextLabel* scalaRootKeyLabel_ = nullptr;
- CSliderBase *tuningFrequencySlider_ = nullptr;
+ SValueMenu *tuningFrequencySlider_ = nullptr;
CTextLabel* tuningFrequencyLabel_ = nullptr;
- CSliderBase *stretchedTuningSlider_ = nullptr;
+ CControl *stretchedTuningSlider_ = nullptr;
CTextLabel* stretchedTuningLabel_ = nullptr;
CTextLabel* infoCurvesLabel_ = nullptr;
@@ -76,6 +83,8 @@ struct Editor::Impl : EditorController::Receiver, IControlListener {
CTextLabel* infoSamplesLabel_ = nullptr;
CTextLabel* infoVoicesLabel_ = nullptr;
+ CTextLabel* memoryLabel_ = nullptr;
+
void uiReceiveValue(EditId id, const EditValue& v) override;
void createFrameContents();
@@ -90,11 +99,16 @@ struct Editor::Impl : EditorController::Receiver, IControlListener {
}
void chooseSfzFile();
+ void changeSfzFile(const std::string& filePath);
void chooseScalaFile();
+ void changeScalaFile(const std::string& filePath);
+
+ static absl::string_view simplifiedFileName(absl::string_view path, absl::string_view removedSuffix, absl::string_view ifEmpty);
void updateSfzFileLabel(const std::string& filePath);
void updateScalaFileLabel(const std::string& filePath);
- static void updateLabelWithFileName(CTextLabel* label, const std::string& filePath);
+ static void updateLabelWithFileName(CTextLabel* label, const std::string& filePath, absl::string_view removedSuffix);
+ static void updateButtonWithFileName(CTextButton* button, const std::string& filePath, absl::string_view removedSuffix);
void updateVolumeLabel(float volume);
void updateNumVoicesLabel(int numVoices);
void updateOversamplingLabel(int oversamplingLog2);
@@ -142,7 +156,7 @@ void Editor::open(CFrame& frame)
Impl& impl = *impl_;
impl.frame_ = &frame;
- frame.addView(impl.view_.get());
+ frame.addView(impl.mainView_.get());
}
void Editor::close()
@@ -150,7 +164,7 @@ void Editor::close()
Impl& impl = *impl_;
if (impl.frame_) {
- impl.frame_->removeView(impl.view_.get(), false);
+ impl.frame_->removeView(impl.mainView_.get(), false);
impl.frame_ = nullptr;
}
}
@@ -161,22 +175,27 @@ void Editor::Impl::uiReceiveValue(EditId id, const EditValue& v)
case EditId::SfzFile:
{
const std::string& value = v.to_string();
+ currentSfzFile_ = value;
updateSfzFileLabel(value);
}
break;
case EditId::Volume:
{
const float value = v.to_float();
- if (volumeSlider_)
+ if (volumeSlider_) {
volumeSlider_->setValue(value);
+ volumeSlider_->setDirty();
+ }
updateVolumeLabel(value);
}
break;
case EditId::Polyphony:
{
const int value = static_cast(v.to_float());
- if (numVoicesSlider_)
+ if (numVoicesSlider_) {
numVoicesSlider_->setValue(value);
+ numVoicesSlider_->setDirty();
+ }
updateNumVoicesLabel(value);
}
break;
@@ -188,16 +207,20 @@ void Editor::Impl::uiReceiveValue(EditId id, const EditValue& v)
for (int f = value; f > 1; f /= 2)
++log2Value;
- if (oversamplingSlider_)
+ if (oversamplingSlider_) {
oversamplingSlider_->setValue(log2Value);
+ oversamplingSlider_->setDirty();
+ }
updateOversamplingLabel(log2Value);
}
break;
case EditId::PreloadSize:
{
const int value = static_cast(v.to_float());
- if (preloadSizeSlider_)
+ if (preloadSizeSlider_) {
preloadSizeSlider_->setValue(value);
+ preloadSizeSlider_->setDirty();
+ }
updatePreloadSizeLabel(value);
}
break;
@@ -209,25 +232,35 @@ void Editor::Impl::uiReceiveValue(EditId id, const EditValue& v)
break;
case EditId::ScalaRootKey:
{
- const int value = static_cast(v.to_float());
- if (scalaRootKeySlider_)
- scalaRootKeySlider_->setValue(value);
+ const int value = std::max(0, static_cast(v.to_float()));
+ if (scalaRootKeySlider_) {
+ scalaRootKeySlider_->setValue(value % 12);
+ scalaRootKeySlider_->setDirty();
+ }
+ if (scalaRootOctaveSlider_) {
+ scalaRootOctaveSlider_->setValue(value / 12);
+ scalaRootOctaveSlider_->setDirty();
+ }
updateScalaRootKeyLabel(value);
}
break;
case EditId::TuningFrequency:
{
const float value = v.to_float();
- if (tuningFrequencySlider_)
+ if (tuningFrequencySlider_) {
tuningFrequencySlider_->setValue(value);
+ tuningFrequencySlider_->setDirty();
+ }
updateTuningFrequencyLabel(value);
}
break;
case EditId::StretchTuning:
{
const float value = v.to_float();
- if (stretchedTuningSlider_)
+ if (stretchedTuningSlider_) {
stretchedTuningSlider_->setValue(value);
+ stretchedTuningSlider_->setDirty();
+ }
updateStretchedTuningLabel(value);
}
break;
@@ -284,405 +317,319 @@ void Editor::Impl::uiReceiveValue(EditId id, const EditValue& v)
void Editor::Impl::createFrameContents()
{
- const CRect bounds { 0.0, 0.0, static_cast(viewWidth), static_cast(viewHeight) };
- CViewContainer* view = new CViewContainer(bounds);
- view_ = owned(view);
+ CViewContainer* mainView;
- view->setBackgroundColor(CColor(0xff, 0xff, 0xff));
+ SharedPointer iconWhite = owned(new CBitmap("icon_white.png"));
+ SharedPointer knob48 = owned(new CBitmap("knob48.png"));
+ SharedPointer logoText = owned(new CBitmap("logo_text.png"));
- SharedPointer logo = owned(new CBitmap("logo.png"));
+ {
+ const CColor frameBackground = { 0xd3, 0xd7, 0xcf };
- CRect bottomRow = bounds;
- bottomRow.top = bottomRow.bottom - 30;
+ struct Theme {
+ CColor boxBackground;
+ CColor text;
+ CColor titleBoxText;
+ CColor titleBoxBackground;
+ CColor icon;
+ CColor valueText;
+ CColor valueBackground;
+ };
- CRect topRow = bounds;
- topRow.bottom = topRow.top + 30;
+ Theme lightTheme;
+ lightTheme.boxBackground = { 0xba, 0xbd, 0xb6 };
+ lightTheme.text = { 0x00, 0x00, 0x00 };
+ lightTheme.titleBoxText = { 0xff, 0xff, 0xff };
+ lightTheme.titleBoxBackground = { 0x2e, 0x34, 0x36 };
+ lightTheme.icon = lightTheme.text;
+ lightTheme.valueText = { 0xff, 0xff, 0xff };
+ lightTheme.valueBackground = { 0x2e, 0x34, 0x36 };
+ Theme darkTheme;
+ darkTheme.boxBackground = { 0x2e, 0x34, 0x36 };
+ darkTheme.text = { 0xff, 0xff, 0xff };
+ darkTheme.titleBoxText = { 0x00, 0x00, 0x00 };
+ darkTheme.titleBoxBackground = { 0xba, 0xbd, 0xb6 };
+ darkTheme.icon = darkTheme.text;
+ darkTheme.valueText = { 0x2e, 0x34, 0x36 };
+ darkTheme.valueBackground = { 0xff, 0xff, 0xff };
+ Theme& defaultTheme = lightTheme;
+ Theme* theme = &defaultTheme;
+ auto enterTheme = [&theme](Theme& t) { theme = &t; };
+
+ typedef CViewContainer LogicalGroup;
+ typedef SBoxContainer RoundedGroup;
+ typedef STitleContainer TitleGroup;
+ typedef CKickButton SfizzMainButton;
+ typedef CTextLabel Label;
+ typedef CViewContainer HLine;
+ typedef CTextButton LightButton;
+ typedef CAnimKnob Knob48;
+ typedef CTextLabel ValueLabel;
+ typedef CViewContainer VMeter;
+ typedef CView SfizzLargePicture;
+ typedef SValueMenu ValueMenu;
+#if 0
+ typedef CTextButton Button;
+#endif
+ typedef CTextButton ValueButton;
+ typedef CTextButton LoadFileButton;
+ typedef CTextButton EditFileButton;
+ typedef SPiano Piano;
+
+ auto createLogicalGroup = [](const CRect& bounds, int, const char*, CHoriTxtAlign, int) {
+ CViewContainer* container = new CViewContainer(bounds);
+ container->setBackgroundColor(CColor(0x00, 0x00, 0x00, 0x00));
+ return container;
+ };
+ auto createRoundedGroup = [&theme](const CRect& bounds, int, const char*, CHoriTxtAlign, int) {
+ auto* box = new SBoxContainer(bounds);
+ box->setCornerRadius(10.0);
+ box->setBackgroundColor(theme->boxBackground);
+ return box;
+ };
+ auto createTitleGroup = [&theme](const CRect& bounds, int, const char* label, CHoriTxtAlign, int fontsize) {
+ auto* box = new STitleContainer(bounds, label);
+ box->setCornerRadius(10.0);
+ box->setBackgroundColor(theme->boxBackground);
+ box->setTitleFontColor(theme->titleBoxText);
+ box->setTitleBackgroundColor(theme->titleBoxBackground);
+ auto font = owned(new CFontDesc(*box->getTitleFont()));
+ font->setSize(fontsize);
+ box->setTitleFont(font);
+ return box;
+ };
+ auto createSfizzMainButton = [this, &iconWhite](const CRect& bounds, int tag, const char*, CHoriTxtAlign, int) {
+ return new CKickButton(bounds, this, tag, iconWhite);
+ };
+ auto createLabel = [&theme](const CRect& bounds, int, const char* label, CHoriTxtAlign align, int fontsize) {
+ CTextLabel* lbl = new CTextLabel(bounds, label);
+ lbl->setFrameColor(CColor(0x00, 0x00, 0x00, 0x00));
+ lbl->setBackColor(CColor(0x00, 0x00, 0x00, 0x00));
+ lbl->setFontColor(theme->text);
+ lbl->setHoriAlign(align);
+ auto font = owned(new CFontDesc(*lbl->getFont()));
+ font->setSize(fontsize);
+ lbl->setFont(font);
+ return lbl;
+ };
+ auto createHLine = [](const CRect& bounds, int, const char*, CHoriTxtAlign, int) {
+ int y = static_cast(0.5 * (bounds.top + bounds.bottom));
+ CRect lineBounds(bounds.left, y, bounds.right, y + 1);
+ CViewContainer* hline = new CViewContainer(lineBounds);
+ hline->setBackgroundColor(CColor(0xff, 0xff, 0xff, 0xff));
+ return hline;
+ };
+ auto createLightButton = [this](const CRect& bounds, int tag, const char* label, CHoriTxtAlign align, int) {
+ CTextButton* button = new CTextButton(bounds, this, tag, label);
+ button->setTextAlignment(align);
+ return button;
+ };
+ auto createKnob48 = [this, &knob48](const CRect& bounds, int tag, const char*, CHoriTxtAlign, int) {
+ return new CAnimKnob(bounds, this, tag, 31, 48, knob48);
+ };
+ auto createValueLabel = [&theme](const CRect& bounds, int, const char* label, CHoriTxtAlign align, int fontsize) {
+ CTextLabel* lbl = new CTextLabel(bounds, label);
+ lbl->setFrameColor(CColor(0x00, 0x00, 0x00, 0x00));
+ lbl->setBackColor(CColor(0x00, 0x00, 0x00, 0x00));
+ lbl->setFontColor(theme->text);
+ lbl->setHoriAlign(align);
+ auto font = owned(new CFontDesc(*lbl->getFont()));
+ font->setSize(fontsize);
+ lbl->setFont(font);
+ return lbl;
+ };
+ auto createVMeter = [](const CRect& bounds, int, const char*, CHoriTxtAlign, int) {
+ // TODO the volume meter...
+ CViewContainer* container = new CViewContainer(bounds);
+ container->setBackgroundColor(CColor(0x00, 0x00, 0x00, 0x00));
+ return container;
+ };
+ auto createSfizzLargePicture = [&logoText](const CRect& bounds, int, const char*, CHoriTxtAlign, int) {
+ CView* picture = new CView(bounds);
+ picture->setBackground(logoText);
+ return picture;
+ };
+#if 0
+ auto createButton = [this](const CRect& bounds, int tag, const char* label, CHoriTxtAlign align, int fontsize) {
+ CTextButton* button = new CTextButton(bounds, this, tag, label);
+ auto font = owned(new CFontDesc(*button->getFont()));
+ font->setSize(fontsize);
+ button->setFont(font);
+ button->setTextAlignment(align);
+ return button;
+ };
+#endif
+ auto createValueButton = [this, &theme](const CRect& bounds, int tag, const char* label, CHoriTxtAlign align, int fontsize) {
+ CTextButton* button = new CTextButton(bounds, this, tag, label);
+ auto font = owned(new CFontDesc(*button->getFont()));
+ font->setSize(fontsize);
+ button->setFont(font);
+ button->setTextAlignment(align);
+ button->setTextColor(theme->valueText);
+ button->setFrameColor(CColor(0x00, 0x00, 0x00, 0x00));
+ SharedPointer gradient = owned(CGradient::create(0.0, 1.0, theme->valueBackground, theme->valueBackground));
+ button->setGradient(gradient);
+ button->setGradientHighlighted(gradient);
+ return button;
+ };
+ auto createValueMenu = [this, &theme](const CRect& bounds, int tag, const char*, CHoriTxtAlign align, int fontsize) {
+ SValueMenu* vm = new SValueMenu(bounds, this, tag);
+ vm->setHoriAlign(align);
+ auto font = owned(new CFontDesc(*vm->getFont()));
+ font->setSize(fontsize);
+ vm->setFont(font);
+ vm->setFontColor(theme->valueText);
+ vm->setBackColor(theme->valueBackground);
+ vm->setFrameColor(CColor(0x00, 0x00, 0x00, 0x00));
+ vm->setStyle(CParamDisplay::kRoundRectStyle);
+ vm->setRoundRectRadius(5.0);
+ return vm;
+ };
+ auto createGlyphButton = [this, &theme](UTF8StringPtr glyph, const CRect& bounds, int tag, int fontsize) {
+ CTextButton* btn = new CTextButton(bounds, this, tag, glyph);
+ btn->setFont(new CFontDesc("Fluent System Regular W20", fontsize));
+ btn->setTextColor(theme->icon);
+ btn->setFrameColor(CColor(0x00, 0x00, 0x00, 0x00));
+ btn->setGradient(nullptr);
+ btn->setGradientHighlighted(nullptr);
+ return btn;
+ };
+ auto createLoadFileButton = [&createGlyphButton](const CRect& bounds, int tag, const char*, CHoriTxtAlign, int fontsize) {
+ return createGlyphButton("\ue142", bounds, tag, fontsize);
+ };
+ auto createEditFileButton = [&createGlyphButton](const CRect& bounds, int tag, const char*, CHoriTxtAlign, int fontsize) {
+ return createGlyphButton("\ue148", bounds, tag, fontsize);
+ };
+ auto createPiano = [](const CRect& bounds, int, const char*, CHoriTxtAlign, int) {
+ SPiano* piano = new SPiano(bounds);
+ return piano;
+ };
+
+ #include "layout/main.hpp"
+
+ mainView->setBackgroundColor(frameBackground);
+
+ mainView_ = owned(mainView);
+ }
+
+ ///
+ SharedPointer fileDropTarget = owned(new SFileDropTarget);
+
+ fileDropTarget->setFileDropFunction([this](const std::string& file) {
+ changeSfzFile(file);
+ });
+
+ mainView_->setDropTarget(fileDropTarget);
+
+ ///
+ adjustMinMaxToEditRange(volumeSlider_, EditId::Volume);
+ adjustMinMaxToEditRange(numVoicesSlider_, EditId::Polyphony);
+ adjustMinMaxToEditRange(oversamplingSlider_, EditId::Oversampling);
+ adjustMinMaxToEditRange(preloadSizeSlider_, EditId::PreloadSize);
+ if (scalaRootKeySlider_) {
+ scalaRootKeySlider_->setMin(0.0);
+ scalaRootKeySlider_->setMax(11.0);
+ scalaRootKeySlider_->setDefaultValue(
+ static_cast(EditRange::get(EditId::ScalaRootKey).def) % 12);
+ }
+ if (scalaRootOctaveSlider_) {
+ scalaRootOctaveSlider_->setMin(0.0);
+ scalaRootOctaveSlider_->setMax(10.0);
+ scalaRootOctaveSlider_->setDefaultValue(
+ static_cast(EditRange::get(EditId::ScalaRootKey).def) / 12);
+ }
+ adjustMinMaxToEditRange(tuningFrequencySlider_, EditId::TuningFrequency);
+ adjustMinMaxToEditRange(stretchedTuningSlider_, EditId::StretchTuning);
+
+ for (int value : {1, 2, 4, 8, 16, 32, 64, 96, 128, 160, 192, 224, 256})
+ numVoicesSlider_->addEntry(std::to_string(value), value);
+ numVoicesSlider_->setValueToStringFunction2(
+ [](float value, std::string& result, CParamDisplay*) -> bool
+ {
+ result = std::to_string(static_cast(value));
+ return true;
+ });
+
+ for (int log2value = 0; log2value <= 3; ++log2value) {
+ int value = 1 << log2value;
+ oversamplingSlider_->addEntry(std::to_string(value) + "x", log2value);
+ }
+ oversamplingSlider_->setValueToStringFunction2(
+ [](float value, std::string& result, CParamDisplay*) -> bool
+ {
+ result = std::to_string(1 << static_cast(value)) + "x";
+ return true;
+ });
+
+ for (int log2value = 10; log2value <= 16; ++log2value) {
+ int value = 1 << log2value;
+ char text[256];
+ sprintf(text, "%d kB", value / 1024);
+ text[sizeof(text) - 1] = '\0';
+ preloadSizeSlider_->addEntry(text, value);
+ }
+ preloadSizeSlider_->setValueToStringFunction2(
+ [](float value, std::string& result, CParamDisplay*) -> bool
+ {
+ result = std::to_string(static_cast(std::round(value * (1.0 / 1024)))) + " kB";
+ return true;
+ });
+
+ static const std::pair tuningFrequencies[] = {
+ {380.0f, "English pitchpipe 380 (1720)"},
+ {409.0f, "Handel fork 409 (1780)"},
+ {415.0f, "Baroque 415"},
+ {422.5f, "Handel fork 422.5 (1740)"},
+ {423.2f, "Dresden opera 423.2 (1815)"},
+ {435.0f, "French Law 435 (1859)"},
+ {439.0f, "British Phil 439 (1896)"},
+ {440.0f, "International 440"},
+ {442.0f, "European 442"},
+ {445.0f, "Germany, China 445"},
+ {451.0f, "La Scala in Milan 451 (18th)"},
+ };
+
+ for (std::pair value : tuningFrequencies)
+ tuningFrequencySlider_->addEntry(value.second, value.first);
+ tuningFrequencySlider_->setValueToStringFunction(
+ [](float value, char result[256], CParamDisplay*) -> bool
+ {
+ sprintf(result, "%.1f Hz", value);
+ return true;
+ });
+
+ static const char* notesInOctave[12] = {
+ "C", "C#", "D", "D#", "E",
+ "F", "F#", "G", "G#", "A", "A#", "B",
+ };
+ for (int note = 0; note < 12; ++note)
+ scalaRootKeySlider_->addEntry(notesInOctave[note], note);
+ for (int octave = 0; octave <= 10; ++octave)
+ scalaRootOctaveSlider_->addEntry(std::to_string(octave - 1), octave);
+ scalaRootKeySlider_->setValueToStringFunction2(
+ [](float value, std::string& result, CParamDisplay*) -> bool
+ {
+ result = notesInOctave[std::max(0, static_cast(value)) % 12];
+ return true;
+ });
+ scalaRootOctaveSlider_->setValueToStringFunction2(
+ [](float value, std::string& result, CParamDisplay*) -> bool
+ {
+ result = std::to_string(static_cast(value) - 1);
+ return true;
+ });
+
+ ///
CViewContainer* panel;
activePanel_ = 0;
- CRect topLeftLabelBox = topRow;
- topLeftLabelBox.right -= 20 * kNumPanels;
-
- // general panel
- {
- panel = new CViewContainer(bounds);
- view->addView(panel);
- panel->setTransparency(true);
-
- CKickButton* sfizzButton = new CKickButton(bounds, this, kTagLoadSfzFile, logo);
- panel->addView(sfizzButton);
-
- CTextLabel* topLeftLabel = new CTextLabel(topLeftLabelBox, "No file loaded");
- topLeftLabel->setFontColor(CColor(0x00, 0x00, 0x00));
- topLeftLabel->setBackColor(CColor(0x00, 0x00, 0x00, 0x00));
- panel->addView(topLeftLabel);
- sfzFileLabel_ = topLeftLabel;
-
- subPanels_[kPanelGeneral] = panel;
- }
-
- // settings panel
- {
- panel = new CViewContainer(bounds);
- view->addView(panel);
- panel->setTransparency(true);
-
- CTextLabel* topLeftLabel = new CTextLabel(topLeftLabelBox, "Settings");
- topLeftLabel->setFontColor(CColor(0x00, 0x00, 0x00));
- topLeftLabel->setBackColor(CColor(0x00, 0x00, 0x00, 0x00));
- panel->addView(topLeftLabel);
-
- CRect row = topRow;
- row.top += 45.0;
- row.bottom += 45.0;
- row.left += 20.0;
- row.right -= 20.0;
-
- static const CCoord interRow = 35.0;
- static const CCoord interColumn = 20.0;
- static const int numColumns = 3;
-
- auto nthColumn = [&row](int colIndex) -> CRect {
- CRect div = row;
- CCoord columnWidth = (div.right - div.left + interColumn) / numColumns - interColumn;
- div.left = div.left + colIndex * (columnWidth + interColumn);
- div.right = div.left + columnWidth;
- return div;
- };
-
- CTextLabel* label;
- SimpleSlider* slider;
-
- label = new CTextLabel(nthColumn(0), "Volume");
- label->setFontColor(CColor(0x00, 0x00, 0x00));
- label->setFrameColor(CColor(0x00, 0x00, 0x00, 0x00));
- label->setBackColor(CColor(0x00, 0x00, 0x00, 0x00));
- label->setHoriAlign(kLeftText);
- panel->addView(label);
- slider = new SimpleSlider(nthColumn(1), this, kTagSetVolume);
- panel->addView(slider);
- adjustMinMaxToEditRange(slider, EditId::Volume);
- volumeSlider_ = slider;
- label = new CTextLabel(nthColumn(2), "");
- volumeLabel_ = label;
- panel->addView(label);
-
- row.top += interRow;
- row.bottom += interRow;
-
- label = new CTextLabel(nthColumn(0), "Polyphony");
- label->setFontColor(CColor(0x00, 0x00, 0x00));
- label->setFrameColor(CColor(0x00, 0x00, 0x00, 0x00));
- label->setBackColor(CColor(0x00, 0x00, 0x00, 0x00));
- label->setHoriAlign(kLeftText);
- panel->addView(label);
- slider = new SimpleSlider(nthColumn(1), this, kTagSetNumVoices);
- panel->addView(slider);
- adjustMinMaxToEditRange(slider, EditId::Polyphony);
- numVoicesSlider_ = slider;
- label = new CTextLabel(nthColumn(2), "");
- numVoicesLabel_ = label;
- panel->addView(label);
-
- row.top += interRow;
- row.bottom += interRow;
-
- label = new CTextLabel(nthColumn(0), "Oversampling");
- label->setFontColor(CColor(0x00, 0x00, 0x00));
- label->setFrameColor(CColor(0x00, 0x00, 0x00, 0x00));
- label->setBackColor(CColor(0x00, 0x00, 0x00, 0x00));
- label->setHoriAlign(kLeftText);
- panel->addView(label);
- slider = new SimpleSlider(nthColumn(1), this, kTagSetOversampling);
- panel->addView(slider);
- adjustMinMaxToEditRange(slider, EditId::Oversampling);
- oversamplingSlider_ = slider;
- label = new CTextLabel(nthColumn(2), "");
- oversamplingLabel_ = label;
- panel->addView(label);
-
- row.top += interRow;
- row.bottom += interRow;
-
- label = new CTextLabel(nthColumn(0), "Preload size");
- label->setFontColor(CColor(0x00, 0x00, 0x00));
- label->setFrameColor(CColor(0x00, 0x00, 0x00, 0x00));
- label->setBackColor(CColor(0x00, 0x00, 0x00, 0x00));
- label->setHoriAlign(kLeftText);
- panel->addView(label);
- slider = new SimpleSlider(nthColumn(1), this, kTagSetPreloadSize);
- panel->addView(slider);
- adjustMinMaxToEditRange(slider, EditId::PreloadSize);
- preloadSizeSlider_ = slider;
- label = new CTextLabel(nthColumn(2), "");
- preloadSizeLabel_ = label;
- panel->addView(label);
-
- subPanels_[kPanelSettings] = panel;
- }
-
- // tuning panel
- {
- panel = new CViewContainer(bounds);
- view->addView(panel);
- panel->setTransparency(true);
-
- CTextLabel* topLeftLabel = new CTextLabel(topLeftLabelBox, "Tuning");
- topLeftLabel->setFontColor(CColor(0x00, 0x00, 0x00));
- topLeftLabel->setBackColor(CColor(0x00, 0x00, 0x00, 0x00));
- panel->addView(topLeftLabel);
-
- CRect row = topRow;
- row.top += 45.0;
- row.bottom += 45.0;
- row.left += 20.0;
- row.right -= 20.0;
-
- static const CCoord interRow = 35.0;
- static const CCoord interColumn = 20.0;
- static const int numColumns = 3;
-
- auto nthColumn = [&row](int colIndex) -> CRect {
- CRect div = row;
- CCoord columnWidth = (div.right - div.left + interColumn) / numColumns - interColumn;
- div.left = div.left + colIndex * (columnWidth + interColumn);
- div.right = div.left + columnWidth;
- return div;
- };
-
- CTextLabel* label;
- SimpleSlider* slider;
- CTextButton* textbutton;
-
- label = new CTextLabel(nthColumn(0), "Scala file");
- label->setFontColor(CColor(0x00, 0x00, 0x00));
- label->setFrameColor(CColor(0x00, 0x00, 0x00, 0x00));
- label->setBackColor(CColor(0x00, 0x00, 0x00, 0x00));
- label->setHoriAlign(kLeftText);
- panel->addView(label);
- textbutton = new CTextButton(nthColumn(1), this, kTagLoadScalaFile, "Choose");
- panel->addView(textbutton);
- label = new CTextLabel(nthColumn(2), "");
- scalaFileLabel_ = label;
- panel->addView(label);
-
- row.top += interRow;
- row.bottom += interRow;
-
- label = new CTextLabel(nthColumn(0), "Scala root key");
- label->setFontColor(CColor(0x00, 0x00, 0x00));
- label->setFrameColor(CColor(0x00, 0x00, 0x00, 0x00));
- label->setBackColor(CColor(0x00, 0x00, 0x00, 0x00));
- label->setHoriAlign(kLeftText);
- panel->addView(label);
- slider = new SimpleSlider(nthColumn(1), this, kTagSetScalaRootKey);
- panel->addView(slider);
- adjustMinMaxToEditRange(slider, EditId::ScalaRootKey);
- scalaRootKeySlider_ = slider;
- label = new CTextLabel(nthColumn(2), "");
- scalaRootKeyLabel_ = label;
- panel->addView(label);
-
- row.top += interRow;
- row.bottom += interRow;
-
- label = new CTextLabel(nthColumn(0), "Tuning frequency");
- label->setFontColor(CColor(0x00, 0x00, 0x00));
- label->setFrameColor(CColor(0x00, 0x00, 0x00, 0x00));
- label->setBackColor(CColor(0x00, 0x00, 0x00, 0x00));
- label->setHoriAlign(kLeftText);
- panel->addView(label);
- slider = new SimpleSlider(nthColumn(1), this, kTagSetTuningFrequency);
- panel->addView(slider);
- adjustMinMaxToEditRange(slider, EditId::TuningFrequency);
- tuningFrequencySlider_ = slider;
- label = new CTextLabel(nthColumn(2), "");
- tuningFrequencyLabel_ = label;
- panel->addView(label);
-
- row.top += interRow;
- row.bottom += interRow;
-
- label = new CTextLabel(nthColumn(0), "Stretched tuning");
- label->setFontColor(CColor(0x00, 0x00, 0x00));
- label->setFrameColor(CColor(0x00, 0x00, 0x00, 0x00));
- label->setBackColor(CColor(0x00, 0x00, 0x00, 0x00));
- label->setHoriAlign(kLeftText);
- panel->addView(label);
- slider = new SimpleSlider(nthColumn(1), this, kTagSetStretchedTuning);
- panel->addView(slider);
- adjustMinMaxToEditRange(slider, EditId::StretchTuning);
- stretchedTuningSlider_ = slider;
- label = new CTextLabel(nthColumn(2), "");
- stretchedTuningLabel_ = label;
- panel->addView(label);
-
- subPanels_[kPanelTuning] = panel;
- }
-
- // info panel
- {
- panel = new CViewContainer(bounds);
- view->addView(panel);
- panel->setTransparency(true);
-
- CTextLabel* topLeftLabel = new CTextLabel(topLeftLabelBox, "Information");
- topLeftLabel->setFontColor(CColor(0x00, 0x00, 0x00));
- topLeftLabel->setBackColor(CColor(0x00, 0x00, 0x00, 0x00));
- panel->addView(topLeftLabel);
-
- CRect row = topRow;
- row.top += 45.0;
- row.bottom += 45.0;
- row.left += 20.0;
- row.right -= 20.0;
-
- static const CCoord interRow = 20.0;
- static const CCoord interColumn = 20.0;
- static const int numColumns = 3;
-
- auto nthColumn = [&row](int colIndex) -> CRect {
- CRect div = row;
- CCoord columnWidth = (div.right - div.left + interColumn) / numColumns - interColumn;
- div.left = div.left + colIndex * (columnWidth + interColumn);
- div.right = div.left + columnWidth;
- return div;
- };
-
- CTextLabel* label;
-
- label = new CTextLabel(nthColumn(0), "Curves");
- label->setFontColor(CColor(0x00, 0x00, 0x00));
- label->setFrameColor(CColor(0x00, 0x00, 0x00, 0x00));
- label->setBackColor(CColor(0x00, 0x00, 0x00, 0x00));
- label->setHoriAlign(kLeftText);
- panel->addView(label);
- label = new CTextLabel(nthColumn(1), "");
- label->setFontColor(CColor(0x00, 0x00, 0x00));
- label->setFrameColor(CColor(0x00, 0x00, 0x00, 0x00));
- label->setBackColor(CColor(0x00, 0x00, 0x00, 0x00));
- label->setHoriAlign(kLeftText);
- infoCurvesLabel_ = label;
- panel->addView(label);
-
- row.top += interRow;
- row.bottom += interRow;
-
- label = new CTextLabel(nthColumn(0), "Masters");
- label->setFontColor(CColor(0x00, 0x00, 0x00));
- label->setFrameColor(CColor(0x00, 0x00, 0x00, 0x00));
- label->setBackColor(CColor(0x00, 0x00, 0x00, 0x00));
- label->setHoriAlign(kLeftText);
- panel->addView(label);
- label = new CTextLabel(nthColumn(1), "");
- label->setFontColor(CColor(0x00, 0x00, 0x00));
- label->setFrameColor(CColor(0x00, 0x00, 0x00, 0x00));
- label->setBackColor(CColor(0x00, 0x00, 0x00, 0x00));
- label->setHoriAlign(kLeftText);
- infoMastersLabel_ = label;
- panel->addView(label);
-
- row.top += interRow;
- row.bottom += interRow;
-
- label = new CTextLabel(nthColumn(0), "Groups");
- label->setFontColor(CColor(0x00, 0x00, 0x00));
- label->setFrameColor(CColor(0x00, 0x00, 0x00, 0x00));
- label->setBackColor(CColor(0x00, 0x00, 0x00, 0x00));
- label->setHoriAlign(kLeftText);
- panel->addView(label);
- label = new CTextLabel(nthColumn(1), "");
- label->setFontColor(CColor(0x00, 0x00, 0x00));
- label->setFrameColor(CColor(0x00, 0x00, 0x00, 0x00));
- label->setBackColor(CColor(0x00, 0x00, 0x00, 0x00));
- label->setHoriAlign(kLeftText);
- infoGroupsLabel_ = label;
- panel->addView(label);
-
- row.top += interRow;
- row.bottom += interRow;
-
- label = new CTextLabel(nthColumn(0), "Regions");
- label->setFontColor(CColor(0x00, 0x00, 0x00));
- label->setFrameColor(CColor(0x00, 0x00, 0x00, 0x00));
- label->setBackColor(CColor(0x00, 0x00, 0x00, 0x00));
- label->setHoriAlign(kLeftText);
- panel->addView(label);
- label = new CTextLabel(nthColumn(1), "");
- label->setFontColor(CColor(0x00, 0x00, 0x00));
- label->setFrameColor(CColor(0x00, 0x00, 0x00, 0x00));
- label->setBackColor(CColor(0x00, 0x00, 0x00, 0x00));
- label->setHoriAlign(kLeftText);
- infoRegionsLabel_ = label;
- panel->addView(label);
-
- row.top += interRow;
- row.bottom += interRow;
-
- label = new CTextLabel(nthColumn(0), "Samples");
- label->setFontColor(CColor(0x00, 0x00, 0x00));
- label->setFrameColor(CColor(0x00, 0x00, 0x00, 0x00));
- label->setBackColor(CColor(0x00, 0x00, 0x00, 0x00));
- label->setHoriAlign(kLeftText);
- panel->addView(label);
- label = new CTextLabel(nthColumn(1), "");
- label->setFontColor(CColor(0x00, 0x00, 0x00));
- label->setFrameColor(CColor(0x00, 0x00, 0x00, 0x00));
- label->setBackColor(CColor(0x00, 0x00, 0x00, 0x00));
- label->setHoriAlign(kLeftText);
- infoSamplesLabel_ = label;
- panel->addView(label);
-
- row.top += interRow;
- row.bottom += interRow;
-
- label = new CTextLabel(nthColumn(0), "Voices");
- label->setFontColor(CColor(0x00, 0x00, 0x00));
- label->setFrameColor(CColor(0x00, 0x00, 0x00, 0x00));
- label->setBackColor(CColor(0x00, 0x00, 0x00, 0x00));
- label->setHoriAlign(kLeftText);
- panel->addView(label);
- label = new CTextLabel(nthColumn(1), "");
- label->setFontColor(CColor(0x00, 0x00, 0x00));
- label->setFrameColor(CColor(0x00, 0x00, 0x00, 0x00));
- label->setBackColor(CColor(0x00, 0x00, 0x00, 0x00));
- label->setHoriAlign(kLeftText);
- infoVoicesLabel_ = label;
- panel->addView(label);
-
- subPanels_[kPanelInfo] = panel;
- }
-
// all panels
for (unsigned currentPanel = 0; currentPanel < kNumPanels; ++currentPanel) {
panel = subPanels_[currentPanel];
- CTextLabel* descLabel = new CTextLabel(
- bottomRow, "Paul Ferrand and the SFZ Tools work group");
- descLabel->setFontColor(CColor(0x00, 0x00, 0x00));
- descLabel->setBackColor(CColor(0x00, 0x00, 0x00, 0x00));
- panel->addView(descLabel);
-
- for (unsigned i = 0; i < kNumPanels; ++i) {
- CRect btnRect = topRow;
- btnRect.left = topRow.right - (kNumPanels - i) * 50;
- btnRect.right = btnRect.left + 50;
-
- const char *text;
- switch (i) {
- case kPanelGeneral: text = "File"; break;
- case kPanelSettings: text = "Setup"; break;
- case kPanelTuning: text = "Tuning"; break;
- case kPanelInfo: text = "Info"; break;
- default: text = "?"; break;
- }
-
- CTextButton* changePanelButton = new CTextButton(btnRect, this, kTagFirstChangePanel + i, text);
- panel->addView(changePanelButton);
-
- changePanelButton->setRoundRadius(0.0);
- }
+ if (!panel)
+ continue;
panel->setVisible(currentPanel == activePanel_);
}
@@ -697,14 +644,18 @@ void Editor::Impl::chooseSfzFile()
if (fs->runModal()) {
UTF8StringPtr file = fs->getSelectedFile(0);
- if (file) {
- std::string str(file);
- ctrl_->uiSendValue(EditId::SfzFile, str);
- updateSfzFileLabel(str);
- }
+ if (file)
+ changeSfzFile(file);
}
}
+void Editor::Impl::changeSfzFile(const std::string& filePath)
+{
+ ctrl_->uiSendValue(EditId::SfzFile, filePath);
+ currentSfzFile_ = filePath;
+ updateSfzFileLabel(filePath);
+}
+
void Editor::Impl::chooseScalaFile()
{
SharedPointer fs = owned(CNewFileSelector::create(frame_));
@@ -714,42 +665,64 @@ void Editor::Impl::chooseScalaFile()
if (fs->runModal()) {
UTF8StringPtr file = fs->getSelectedFile(0);
- if (file) {
- std::string str(file);
- ctrl_->uiSendValue(EditId::ScalaFile, str);
- updateScalaFileLabel(str);
- }
+ if (file)
+ changeScalaFile(file);
}
}
+void Editor::Impl::changeScalaFile(const std::string& filePath)
+{
+ ctrl_->uiSendValue(EditId::ScalaFile, filePath);
+ updateScalaFileLabel(filePath);
+}
+
+absl::string_view Editor::Impl::simplifiedFileName(absl::string_view path, absl::string_view removedSuffix, absl::string_view ifEmpty)
+{
+ if (path.empty())
+ return ifEmpty;
+
+#if defined (_WIN32)
+ size_t pos = path.find_last_of("/\\");
+#else
+ size_t pos = path.rfind('/');
+#endif
+ path = (pos != path.npos) ? path.substr(pos + 1) : path;
+
+ if (!removedSuffix.empty() && absl::EndsWithIgnoreCase(path, removedSuffix))
+ path.remove_suffix(removedSuffix.size());
+
+ return path;
+}
+
void Editor::Impl::updateSfzFileLabel(const std::string& filePath)
{
- updateLabelWithFileName(sfzFileLabel_, filePath);
+ updateLabelWithFileName(sfzFileLabel_, filePath, ".sfz");
}
void Editor::Impl::updateScalaFileLabel(const std::string& filePath)
{
- updateLabelWithFileName(scalaFileLabel_, filePath);
+ updateLabelWithFileName(scalaFileLabel_, filePath, ".scl");
+ updateButtonWithFileName(scalaFileButton_, filePath, ".scl");
}
-void Editor::Impl::updateLabelWithFileName(CTextLabel* label, const std::string& filePath)
+void Editor::Impl::updateLabelWithFileName(CTextLabel* label, const std::string& filePath, absl::string_view removedSuffix)
{
if (!label)
return;
- std::string fileName;
- if (filePath.empty())
- fileName = "";
- else {
-#if defined (_WIN32)
- size_t pos = filePath.find_last_of("/\\");
-#else
- size_t pos = filePath.rfind('/');
-#endif
- fileName = (pos != filePath.npos) ?
- filePath.substr(pos + 1) : filePath;
- }
+ std::string fileName = std::string(simplifiedFileName(filePath, removedSuffix, ""));
label->setText(fileName.c_str());
+ label->setDirty();
+}
+
+void Editor::Impl::updateButtonWithFileName(CTextButton* button, const std::string& filePath, absl::string_view removedSuffix)
+{
+ if (!button)
+ return;
+
+ std::string fileName = std::string(simplifiedFileName(filePath, removedSuffix, ""));
+ button->setTitle(fileName.c_str());
+ button->setDirty();
}
void Editor::Impl::updateVolumeLabel(float volume)
@@ -762,6 +735,7 @@ void Editor::Impl::updateVolumeLabel(float volume)
sprintf(text, "%.1f dB", volume);
text[sizeof(text) - 1] = '\0';
label->setText(text);
+ label->setDirty();
}
void Editor::Impl::updateNumVoicesLabel(int numVoices)
@@ -774,6 +748,7 @@ void Editor::Impl::updateNumVoicesLabel(int numVoices)
sprintf(text, "%d", numVoices);
text[sizeof(text) - 1] = '\0';
label->setText(text);
+ label->setDirty();
}
void Editor::Impl::updateOversamplingLabel(int oversamplingLog2)
@@ -786,6 +761,7 @@ void Editor::Impl::updateOversamplingLabel(int oversamplingLog2)
sprintf(text, "%dx", 1 << oversamplingLog2);
text[sizeof(text) - 1] = '\0';
label->setText(text);
+ label->setDirty();
}
void Editor::Impl::updatePreloadSizeLabel(int preloadSize)
@@ -795,9 +771,10 @@ void Editor::Impl::updatePreloadSizeLabel(int preloadSize)
return;
char text[64];
- sprintf(text, "%.1f kB", preloadSize * (1.0 / 1024));
+ sprintf(text, "%d kB", static_cast(std::round(preloadSize * (1.0 / 1024))));
text[sizeof(text) - 1] = '\0';
label->setText(text);
+ label->setDirty();
}
void Editor::Impl::updateScalaRootKeyLabel(int rootKey)
@@ -826,6 +803,7 @@ void Editor::Impl::updateScalaRootKeyLabel(int rootKey)
};
label->setText(noteName(rootKey));
+ label->setDirty();
}
void Editor::Impl::updateTuningFrequencyLabel(float tuningFrequency)
@@ -838,6 +816,7 @@ void Editor::Impl::updateTuningFrequencyLabel(float tuningFrequency)
sprintf(text, "%.1f", tuningFrequency);
text[sizeof(text) - 1] = '\0';
label->setText(text);
+ label->setDirty();
}
void Editor::Impl::updateStretchedTuningLabel(float stretchedTuning)
@@ -850,6 +829,7 @@ void Editor::Impl::updateStretchedTuningLabel(float stretchedTuning)
sprintf(text, "%.3f", stretchedTuning);
text[sizeof(text) - 1] = '\0';
label->setText(text);
+ label->setDirty();
}
void Editor::Impl::setActivePanel(unsigned panelId)
@@ -857,9 +837,11 @@ void Editor::Impl::setActivePanel(unsigned panelId)
panelId = std::max(0, std::min(kNumPanels - 1, static_cast(panelId)));
if (activePanel_ != panelId) {
- subPanels_[activePanel_]->setVisible(false);
+ if (subPanels_[activePanel_])
+ subPanels_[activePanel_]->setVisible(false);
+ if (subPanels_[panelId])
+ subPanels_[panelId]->setVisible(true);
activePanel_ = panelId;
- subPanels_[panelId]->setVisible(true);
}
}
@@ -893,6 +875,14 @@ void Editor::Impl::valueChanged(CControl* ctl)
Call::later([this]() { chooseSfzFile(); });
break;
+ case kTagEditSfzFile:
+ if (value != 1)
+ break;
+
+ if (!currentSfzFile_.empty())
+ openFileInExternalEditor(currentSfzFile_.c_str());
+ break;
+
case kTagLoadScalaFile:
if (value != 1)
break;
@@ -921,8 +911,15 @@ void Editor::Impl::valueChanged(CControl* ctl)
break;
case kTagSetScalaRootKey:
- ctrl.uiSendValue(EditId::ScalaRootKey, value);
- updateScalaRootKeyLabel(static_cast(value));
+ {
+ if (scalaRootKeySlider_ && scalaRootOctaveSlider_) {
+ int key = static_cast(scalaRootKeySlider_->getValue());
+ int octave = static_cast(scalaRootOctaveSlider_->getValue());
+ int midiKey = key + 12 * octave;
+ ctrl.uiSendValue(EditId::ScalaRootKey, midiKey);
+ updateScalaRootKeyLabel(midiKey);
+ }
+ }
break;
case kTagSetTuningFrequency:
diff --git a/editor/src/editor/GUIComponents.cpp b/editor/src/editor/GUIComponents.cpp
index a85bf64b..c4e6f958 100644
--- a/editor/src/editor/GUIComponents.cpp
+++ b/editor/src/editor/GUIComponents.cpp
@@ -5,34 +5,422 @@
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
#include "GUIComponents.h"
+#include
#include "utility/vstgui_before.h"
#include "vstgui/lib/cdrawcontext.h"
+#include "vstgui/lib/cgraphicspath.h"
+#include "vstgui/lib/cframe.h"
#include "utility/vstgui_after.h"
-SimpleSlider::SimpleSlider(const CRect& bounds, IControlListener* listener, int32_t tag)
- : CSliderBase(bounds, listener, tag)
+///
+SBoxContainer::SBoxContainer(const CRect& size)
+ : CViewContainer(size)
{
- setStyle(kHorizontal|kLeft);
-
- CPoint offsetHandle(2.0, 2.0);
- setOffsetHandle(offsetHandle);
-
- CCoord handleSize = 20.0;
- setHandleSizePrivate(handleSize, bounds.bottom - bounds.top - 2 * offsetHandle.y);
- setHandleRangePrivate(bounds.right - bounds.left - handleSize - 2 * offsetHandle.x);
+ CViewContainer::setBackgroundColor(CColor(0, 0, 0, 0));
}
-void SimpleSlider::draw(CDrawContext* dc)
+void SBoxContainer::setCornerRadius(CCoord radius)
+{
+ cornerRadius_ = radius;
+ setDirty();
+}
+
+void SBoxContainer::setBackgroundColor(const CColor& color)
+{
+ backgroundColor_ = color;
+ setDirty();
+}
+
+CColor SBoxContainer::getBackgroundColor() const
+{
+ return backgroundColor_;
+}
+
+void SBoxContainer::drawRect(CDrawContext* dc, const CRect& updateRect)
{
CRect bounds = getViewSize();
- CRect handle = calculateHandleRect(getValueNormalized());
dc->setDrawMode(kAntiAliasing);
- dc->setFrameColor(_frame);
- dc->drawRect(bounds, kDrawStroked);
+ SharedPointer path = owned(dc->createGraphicsPath());
+ path->addRoundRect(bounds, cornerRadius_);
- dc->setFillColor(_fill);
- dc->drawRect(handle, kDrawFilled);
+ dc->setFillColor(backgroundColor_);
+ dc->drawGraphicsPath(path.get(), CDrawContext::kPathFilled);
+
+ CViewContainer::drawRect(dc, updateRect);
+}
+
+///
+STitleContainer::STitleContainer(const CRect& size, UTF8StringPtr text)
+ : SBoxContainer(size), text_(text ? text : ""), titleFont_(kNormalFont)
+{
+}
+
+void STitleContainer::setTitleFont(CFontRef font)
+{
+ titleFont_ = font;
+ setDirty();
+}
+
+void STitleContainer::setTitleFontColor(CColor color)
+{
+ titleFontColor_ = color;
+ setDirty();
+}
+
+void STitleContainer::setTitleBackgroundColor(CColor color)
+{
+ titleBackgroundColor_ = color;
+ setDirty();
+}
+
+void STitleContainer::drawRect(CDrawContext* dc, const CRect& updateRect)
+{
+ SBoxContainer::drawRect(dc, updateRect);
+
+ CRect bounds = getViewSize();
+ CCoord cornerRadius = cornerRadius_;
+
+ dc->setDrawMode(kAntiAliasing);
+
+ CCoord fontHeight = titleFont_->getSize();
+ CCoord titleHeight = fontHeight + 8.0;
+
+ CRect titleBounds = bounds;
+ titleBounds.bottom = titleBounds.top + titleHeight;
+
+ SharedPointer path = owned(dc->createGraphicsPath());
+ path->beginSubpath(titleBounds.getBottomRight());
+ path->addLine(titleBounds.getBottomLeft());
+ path->addArc(CRect(titleBounds.left, titleBounds.top, titleBounds.left + 2.0 * cornerRadius, titleBounds.top + 2.0 * cornerRadius), 180., 270., true);
+ path->addArc(CRect(titleBounds.right - 2.0 * cornerRadius, titleBounds.top, titleBounds.right, titleBounds.top + 2.0 * cornerRadius), 270., 360., true);
+ path->closeSubpath();
+
+ dc->setFillColor(titleBackgroundColor_);
+ dc->drawGraphicsPath(path, CDrawContext::kPathFilled);
+
+ dc->setFont(titleFont_);
+ dc->setFontColor(titleFontColor_);
+ dc->drawString(text_.c_str(), titleBounds, kCenterText);
+}
+
+///
+void SFileDropTarget::setFileDropFunction(FileDropFunction f)
+{
+ dropFunction_ = std::move(f);
+}
+
+DragOperation SFileDropTarget::onDragEnter(DragEventData data)
+{
+ op_ = isFileDrop(data.drag) ?
+ DragOperation::Copy : DragOperation::None;
+ return op_;
+}
+
+DragOperation SFileDropTarget::onDragMove(DragEventData data)
+{
+ (void)data;
+ return op_;
+}
+
+void SFileDropTarget::onDragLeave(DragEventData data)
+{
+ (void)data;
+ op_ = DragOperation::None;
+}
+
+bool SFileDropTarget::onDrop(DragEventData data)
+{
+ if (op_ != DragOperation::Copy || !isFileDrop(data.drag))
+ return false;
+
+ IDataPackage::Type type;
+ const void* bytes;
+ uint32_t size = data.drag->getData(0, bytes, type);
+ std::string path(reinterpret_cast(bytes), size);
+
+ if (dropFunction_)
+ dropFunction_(path);
+
+ return true;
+}
+
+bool SFileDropTarget::isFileDrop(IDataPackage* package)
+{
+ return package->getCount() == 1 &&
+ package->getDataType(0) == IDataPackage::kFilePath;
+}
+
+///
+SPiano::SPiano(const CRect& bounds)
+ : CView(bounds), font_(kNormalFont)
+{
+}
+
+void SPiano::setFont(CFontRef font)
+{
+ font_ = font;
+ setDirty();
+}
+
+void SPiano::clearKeyRanges()
+{
+ keyInRange_.reset();
+}
+
+void SPiano::addKeyRange(int start, int end)
+{
+ start = std::min(127, std::max(0, start));
+ end = std::min(127, std::max(0, end));
+
+ for (int x = start; x <= end; ++x)
+ keyInRange_.set(x);
+}
+
+CCoord SPiano::getKeyWidth()
+{
+ return 6.0;
+}
+
+CCoord SPiano::getKeySwitchesHeight()
+{
+ return 20.0;
+}
+
+CCoord SPiano::getKeyRangesHeight()
+{
+ return 11.0;
+}
+
+CCoord SPiano::getKeysHeight() const
+{
+ return getHeight() -
+ (getKeySwitchesHeight() + getKeyRangesHeight() + getOctavesHeight());
+}
+
+CCoord SPiano::getOctavesHeight() const
+{
+ return font_->getSize();
+}
+
+void SPiano::getZoneDimensions(
+ CRect* pKeySwitches,
+ CRect* pKeyboard,
+ CRect* pKeyRanges,
+ CRect* pOctaves)
+{
+ CRect bounds = getViewSize();
+
+ CRect keySwitches(bounds);
+ keySwitches.setHeight(getKeySwitchesHeight());
+
+ CRect keyboard(bounds);
+ keyboard.top = keySwitches.bottom;
+ keyboard.setHeight(getKeysHeight());
+
+ CRect keyRanges(bounds);
+ keyRanges.top = keyboard.bottom;
+ keyRanges.setHeight(getKeyRangesHeight());
+
+ CRect octaves(bounds);
+ octaves.top = keyRanges.bottom;
+ octaves.setHeight(getOctavesHeight());
+
+ // apply some paddings
+ keySwitches.extend(-2.0, -2.0);
+ keyboard.extend(-2.0, -2.0);
+ keyRanges.extend(-2.0, -4.0);
+ octaves.extend(-2.0, -2.0);
+
+ // offsets for centered keyboard
+ CCoord keyWidth = getKeyWidth();
+ CCoord offset = std::round((keyboard.getWidth() - (128.0 * keyWidth)) * 0.5);
+ if (offset > 0) {
+ keySwitches.extend(-offset, 0.0);
+ keyboard.extend(-offset, 0.0);
+ keyRanges.extend(-offset, 0.0);
+ octaves.extend(-offset, 0.0);
+ }
+
+ //
+ if (pKeySwitches)
+ *pKeySwitches = keySwitches;
+ if (pKeyboard)
+ *pKeyboard = keyboard;
+ if (pKeyRanges)
+ *pKeyRanges = keyRanges;
+ if (pOctaves)
+ *pOctaves = octaves;
+}
+
+void SPiano::draw(CDrawContext* dc)
+{
+ CRect bounds = getViewSize();
+
+ dc->setDrawMode(kAntiAliasing);
+
+ SharedPointer path;
+
+ path = owned(dc->createGraphicsPath());
+ path->addRoundRect(bounds, 5.0);
+ dc->setFillColor(CColor(0xca, 0xca, 0xca));
+ dc->drawGraphicsPath(path, CDrawContext::kPathFilled);
+
+ //
+ CRect rectKeySwitches;
+ CRect rectKeyboard;
+ CRect rectKeyRanges;
+ CRect rectOctaves;
+ getZoneDimensions(&rectKeySwitches, &rectKeyboard, &rectKeyRanges, &rectOctaves);
+
+ //
+ path = owned(dc->createGraphicsPath());
+ path->addRoundRect(rectKeyboard, 1.0);
+ dc->setFillColor(CColor(0xff, 0xff, 0xff));
+ dc->drawGraphicsPath(path, CDrawContext::kPathFilled);
+
+ CCoord keyWidth = getKeyWidth();
+ for (int key = 0; key < 128; ++key) {
+ CCoord keyX = rectKeyboard.left + key * keyWidth;
+ int key12 = key % 12;
+ if (key12 == 1 || key12 == 3 ||
+ key12 == 6 || key12 == 8 || key12 == 10)
+ {
+ CRect blackRect(keyX, rectKeyboard.top + 2, keyX + keyWidth, rectKeyboard.bottom - 2);
+ path = owned(dc->createGraphicsPath());
+ path->addRoundRect(blackRect, 1.0);
+ dc->setFillColor(CColor(0x02, 0x02, 0x02));
+ dc->drawGraphicsPath(path, CDrawContext::kPathFilled);
+ }
+ if (key != 0 && key12 == 0) {
+ dc->setLineWidth(1.5);
+ dc->setFrameColor(CColor(0x63, 0x63, 0x63));
+ dc->drawLine(CPoint(keyX, rectKeyboard.top), CPoint(keyX, rectKeyboard.bottom));
+ }
+ if (key12 == 5) {
+ CCoord pad = rectKeyboard.getHeight() * 0.4;
+ dc->setLineWidth(1.0);
+ dc->setFrameColor(CColor(0x63, 0x63, 0x63));
+ dc->drawLine(CPoint(keyX, rectKeyboard.top + pad), CPoint(keyX, rectKeyboard.bottom - pad));
+ }
+ }
+
+ //
+
+ for (int rangeStart = 0; rangeStart < 128;)
+ {
+ if (!keyInRange_[rangeStart]) {
+ ++rangeStart;
+ }
+ else {
+ int rangeEnd = rangeStart;
+ while (rangeEnd + 1 < 128 && keyInRange_[rangeEnd + 1])
+ ++rangeEnd;
+
+ CCoord rangeStartX = rectKeyRanges.left + rangeStart * keyWidth;
+ CCoord rangeEndX = rectKeyRanges.left + (rangeEnd + 1.0) * keyWidth;
+ CRect rectRange(rangeStartX, rectKeyRanges.top, rangeEndX, rectKeyRanges.bottom);
+
+ path = owned(dc->createGraphicsPath());
+ path->addRoundRect(rectRange, 2.0);
+ dc->setFillColor(CColor(0x0f, 0x0f, 0x0f));
+ dc->drawGraphicsPath(path, CDrawContext::kPathFilled);
+
+ rangeStart = rangeEnd + 1;
+ }
+ }
+
+ //
+
+ for (int key = 0; key < 128; ++key) {
+ CCoord keyX = rectOctaves.left + key * keyWidth;
+ int key12 = key % 12;
+ if (key12 == 0) {
+ CRect textRect(keyX, rectOctaves.top, keyX + 12 * keyWidth, rectOctaves.bottom);
+ dc->setFont(font_);
+ dc->setFontColor(CColor(0x63, 0x63, 0x63));
+ dc->drawString(std::to_string(key / 12 - 1).c_str(), textRect, kLeftText);
+ }
+ }
+
+ //
+}
+
+///
+SValueMenu::SValueMenu(const CRect& bounds, IControlListener* listener, int32_t tag)
+ : CParamDisplay(bounds), menuListener_(owned(new MenuListener(*this)))
+{
+ setListener(listener);
+ setTag(tag);
+}
+
+CMenuItem* SValueMenu::addEntry(CMenuItem* item, float value, int32_t index)
+{
+ if (index < 0 || index > getNbEntries()) {
+ menuItems_.emplace_back(owned(item));
+ menuItemValues_.emplace_back(value);
+ }
+ else
+ {
+ menuItems_.insert(menuItems_.begin() + index, owned(item));
+ menuItemValues_.insert(menuItemValues_.begin() + index, value);
+ }
+ return item;
+}
+
+CMenuItem* SValueMenu::addEntry(const UTF8String& title, float value, int32_t index, int32_t itemFlags)
+{
+ if (title == "-")
+ return addSeparator(index);
+ CMenuItem* item = new CMenuItem(title, nullptr, 0, nullptr, itemFlags);
+ return addEntry(item, value, index);
+}
+
+CMenuItem* SValueMenu::addSeparator(int32_t index)
+{
+ CMenuItem* item = new CMenuItem("", nullptr, 0, nullptr, CMenuItem::kSeparator);
+ return addEntry(item, 0.0f, index);
+}
+
+int32_t SValueMenu::getNbEntries() const
+{
+ return static_cast(menuItems_.size());
+}
+
+CMouseEventResult SValueMenu::onMouseDown(CPoint& where, const CButtonState& buttons)
+{
+ (void)where;
+
+ if (buttons & (kLButton|kRButton|kApple)) {
+ CFrame* frame = getFrame();
+ CRect bounds = getViewSize();
+
+ CPoint frameWhere = bounds.getBottomLeft();
+ this->localToFrame(frameWhere);
+
+ auto self = shared(this);
+ frame->doAfterEventProcessing([self, frameWhere]() {
+ if (CFrame* frame = self->getFrame()) {
+ SharedPointer menu = owned(new COptionMenu(CRect(), self->menuListener_, -1, nullptr, nullptr, COptionMenu::kPopupStyle));
+ for (const SharedPointer& item : self->menuItems_) {
+ menu->addEntry(item);
+ item->remember(); // above call does not increment refcount
+ }
+ menu->popup(frame, frameWhere + CPoint(0.0, 1.0));
+ }
+ });
+ return kMouseDownEventHandledButDontNeedMovedOrUpEvents;
+ }
+
+ return kMouseEventNotHandled;
+}
+
+void SValueMenu::onItemClicked(int32_t index)
+{
+ float oldValue = getValue();
+ setValue(menuItemValues_[index]);
+ if (getValue() != oldValue)
+ valueChanged();
}
diff --git a/editor/src/editor/GUIComponents.h b/editor/src/editor/GUIComponents.h
index c76c2e44..8c295f5e 100644
--- a/editor/src/editor/GUIComponents.h
+++ b/editor/src/editor/GUIComponents.h
@@ -5,22 +5,144 @@
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
#pragma once
+#include
#include "utility/vstgui_before.h"
#include "vstgui/lib/controls/cslider.h"
+#include "vstgui/lib/controls/cknob.h"
+#include "vstgui/lib/controls/ctextlabel.h"
+#include "vstgui/lib/controls/coptionmenu.h"
+#include "vstgui/lib/cviewcontainer.h"
#include "vstgui/lib/ccolor.h"
+#include "vstgui/lib/dragging.h"
#include "utility/vstgui_after.h"
using namespace VSTGUI;
-class SimpleSlider : public CSliderBase {
+///
+class SBoxContainer : public CViewContainer {
public:
- SimpleSlider(const CRect& bounds, IControlListener* listener, int32_t tag);
- void draw(CDrawContext* dc) override;
+ explicit SBoxContainer(const CRect& size);
+ virtual ~SBoxContainer() {}
+ void setCornerRadius(CCoord radius);
+ void setBackgroundColor(const CColor& color) override;
+ CColor getBackgroundColor() const override;
- CLASS_METHODS(SimpleSlider, CSliderBase)
+protected:
+ void drawRect(CDrawContext* dc, const CRect& updateRect) override;
+
+protected:
+ CCoord cornerRadius_ = 0.0;
+ CColor backgroundColor_;
+};
+
+///
+class STitleContainer : public SBoxContainer {
+public:
+ explicit STitleContainer(const CRect& size, UTF8StringPtr text = nullptr);
+ ~STitleContainer() {}
+
+ void setTitleFont(CFontRef font);
+ CFontRef getTitleFont() { return titleFont_; }
+
+ void setTitleFontColor(CColor color);
+ CColor getTitleFontColor() const { return titleFontColor_; }
+ void setTitleBackgroundColor(CColor color);
+ CColor getTitleBackgroundColor() const { return titleBackgroundColor_; }
+
+protected:
+ void drawRect(CDrawContext* dc, const CRect& updateRect) override;
private:
- CColor _frame = CColor(0x00, 0x00, 0x00);
- CColor _fill = CColor(0x00, 0x00, 0x00);
+ std::string text_;
+ CColor titleFontColor_;
+ CColor titleBackgroundColor_;
+ SharedPointer titleFont_;
+};
+
+///
+class SFileDropTarget : public IDropTarget,
+ public NonAtomicReferenceCounted {
+public:
+ typedef std::function FileDropFunction;
+ void setFileDropFunction(FileDropFunction f);
+
+protected:
+ DragOperation onDragEnter(DragEventData data) override;
+ DragOperation onDragMove(DragEventData data) override;
+ void onDragLeave(DragEventData data) override;
+ bool onDrop(DragEventData data) override;
+
+private:
+ static bool isFileDrop(IDataPackage* package);
+
+private:
+ DragOperation op_ = DragOperation::None;
+ FileDropFunction dropFunction_;
+};
+
+///
+class SPiano : public CView {
+public:
+ explicit SPiano(const CRect& bounds);
+ CFontRef getFont() const { return font_; }
+ void setFont(CFontRef font);
+
+ void clearKeyRanges();
+ void addKeyRange(int start, int end);
+
+protected:
+ static CCoord getKeyWidth();
+ static CCoord getKeySwitchesHeight();
+ static CCoord getKeyRangesHeight();
+ CCoord getKeysHeight() const;
+ CCoord getOctavesHeight() const;
+
+ void getZoneDimensions(
+ CRect* pKeySwitches,
+ CRect* pKeyboard,
+ CRect* pKeyRanges,
+ CRect* pOctaves);
+
+ void draw(CDrawContext* dc) override;
+
+private:
+ SharedPointer font_;
+ std::bitset<128> keyInRange_;
+};
+
+///
+class SValueMenu : public CParamDisplay {
+public:
+ explicit SValueMenu(const CRect& bounds, IControlListener* listener, int32_t tag);
+ CMenuItem* addEntry(CMenuItem* item, float value, int32_t index = -1);
+ CMenuItem* addEntry(const UTF8String& title, float value, int32_t index = -1, int32_t itemFlags = CMenuItem::kNoFlags);
+ CMenuItem* addSeparator(int32_t index = -1);
+ int32_t getNbEntries() const;
+
+protected:
+ CMouseEventResult onMouseDown(CPoint& where, const CButtonState& buttons);
+
+private:
+ class MenuListener;
+
+ //
+ void onItemClicked(int32_t index);
+
+ //
+ CMenuItemList menuItems_;
+ std::vector menuItemValues_;
+ SharedPointer menuListener_;
+
+ //
+ class MenuListener : public IControlListener, public NonAtomicReferenceCounted {
+ public:
+ explicit MenuListener(SValueMenu& menu) : menu_(menu) {}
+ void valueChanged(CControl* control) override
+ {
+ menu_.onItemClicked(static_cast(control->getValue()));
+ }
+ private:
+ SValueMenu& menu_;
+ };
};
diff --git a/editor/src/editor/NativeHelpers.cpp b/editor/src/editor/NativeHelpers.cpp
new file mode 100644
index 00000000..ab760d73
--- /dev/null
+++ b/editor/src/editor/NativeHelpers.cpp
@@ -0,0 +1,50 @@
+// SPDX-License-Identifier: BSD-2-Clause
+
+// This code is part of the sfizz library and is licensed under a BSD 2-clause
+// license. You should have receive a LICENSE.md file along with the code.
+// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
+
+#include "NativeHelpers.h"
+
+#if defined(_WIN32)
+#include "ghc/fs_std.hpp"
+#include
+#include
+
+bool openFileInExternalEditor(const char *filename)
+{
+ std::wstring path = fs::u8path(filename).wstring();
+
+ SHELLEXECUTEINFOW info;
+ memset(&info, 0, sizeof(info));
+
+ info.cbSize = sizeof(info);
+ info.fMask = SEE_MASK_CLASSNAME;
+ info.lpVerb = L"open";
+ info.lpFile = path.c_str();
+ info.lpClass = L"txtfile";
+ info.nShow = SW_SHOW;
+
+ return ShellExecuteExW(&info);
+}
+#elif defined(__APPLE__)
+ // implemented in NativeHelpers.mm
+#else
+#include
+
+bool openFileInExternalEditor(const char *filename)
+{
+ GAppInfo* appinfo = g_app_info_get_default_for_type("text/plain", FALSE);
+ if (!appinfo)
+ return 1;
+
+ GList* files = nullptr;
+ GFile* file = g_file_new_for_path(filename);
+ files = g_list_append(files, file);
+ gboolean success = g_app_info_launch(appinfo, files, nullptr, nullptr);
+ g_object_unref(file);
+ g_list_free(files);
+ g_object_unref(appinfo);
+ return success == TRUE;
+}
+#endif
diff --git a/editor/src/editor/NativeHelpers.h b/editor/src/editor/NativeHelpers.h
new file mode 100644
index 00000000..b9c29bb6
--- /dev/null
+++ b/editor/src/editor/NativeHelpers.h
@@ -0,0 +1,9 @@
+// SPDX-License-Identifier: BSD-2-Clause
+
+// This code is part of the sfizz library and is licensed under a BSD 2-clause
+// license. You should have receive a LICENSE.md file along with the code.
+// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
+
+#pragma once
+
+bool openFileInExternalEditor(const char *filename);
diff --git a/editor/src/editor/NativeHelpers.mm b/editor/src/editor/NativeHelpers.mm
new file mode 100644
index 00000000..9cb8cd28
--- /dev/null
+++ b/editor/src/editor/NativeHelpers.mm
@@ -0,0 +1,30 @@
+// SPDX-License-Identifier: BSD-2-Clause
+
+// This code is part of the sfizz library and is licensed under a BSD 2-clause
+// license. You should have receive a LICENSE.md file along with the code.
+// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
+
+#include "NativeHelpers.h"
+
+#if defined(__APPLE__)
+#import
+#import
+#import
+
+bool openFileInExternalEditor(const char *fileNameUTF8)
+{
+ BOOL wasOpened = NO;
+
+ NSURL* applicationURL = (__bridge_transfer NSURL*)LSCopyDefaultApplicationURLForContentType(
+ kUTTypePlainText, kLSRolesEditor, nil);
+ if (!applicationURL)
+ return false;
+ if ([applicationURL isFileURL]) {
+ NSWorkspace* workspace = [NSWorkspace sharedWorkspace];
+ NSString* fileName = [NSString stringWithUTF8String:fileNameUTF8];
+ wasOpened = [workspace openFile:fileName withApplication:[applicationURL path]];
+ }
+
+ return wasOpened == YES;
+}
+#endif
diff --git a/editor/src/editor/layout/main.hpp b/editor/src/editor/layout/main.hpp
new file mode 100644
index 00000000..808e5a10
--- /dev/null
+++ b/editor/src/editor/layout/main.hpp
@@ -0,0 +1,167 @@
+/* This file is generated by the layout maker tool. */
+LogicalGroup* const view__0 = createLogicalGroup(CRect(0, 0, 800, 475), -1, "", kCenterText, 14);
+mainView = view__0;
+enterTheme(darkTheme);
+LogicalGroup* const view__1 = createLogicalGroup(CRect(0, 0, 800, 110), -1, "", kCenterText, 14);
+view__0->addView(view__1);
+RoundedGroup* const view__2 = createRoundedGroup(CRect(5, 4, 105, 105), -1, "", kCenterText, 14);
+view__1->addView(view__2);
+SfizzMainButton* const view__3 = createSfizzMainButton(CRect(2, 2, 98, 98), kTagFirstChangePanel+kPanelGeneral, "", kCenterText, 14);
+view__2->addView(view__3);
+RoundedGroup* const view__4 = createRoundedGroup(CRect(110, 5, 490, 105), -1, "", kCenterText, 14);
+view__1->addView(view__4);
+Label* const view__5 = createLabel(CRect(15, 10, 55, 35), -1, "File:", kCenterText, 16);
+view__4->addView(view__5);
+Label* const view__6 = createLabel(CRect(15, 40, 55, 65), -1, "KS:", kCenterText, 16);
+view__4->addView(view__6);
+HLine* const view__7 = createHLine(CRect(10, 35, 365, 40), -1, "", kCenterText, 14);
+view__4->addView(view__7);
+HLine* const view__8 = createHLine(CRect(10, 65, 365, 70), -1, "", kCenterText, 14);
+view__4->addView(view__8);
+Label* const view__9 = createLabel(CRect(80, 10, 310, 35), -1, "DefaultInstrument.sfz", kCenterText, 20);
+sfzFileLabel_ = view__9;
+view__4->addView(view__9);
+Label* const view__10 = createLabel(CRect(80, 40, 310, 65), -1, "Key switch", kCenterText, 20);
+view__4->addView(view__10);
+Label* const view__11 = createLabel(CRect(10, 70, 70, 95), -1, "Voices:", kRightText, 12);
+view__4->addView(view__11);
+LoadFileButton* const view__12 = createLoadFileButton(CRect(315, 10, 340, 35), kTagLoadSfzFile, "", kCenterText, 24);
+view__4->addView(view__12);
+EditFileButton* const view__13 = createEditFileButton(CRect(340, 10, 365, 35), kTagEditSfzFile, "", kCenterText, 24);
+view__4->addView(view__13);
+Label* const view__14 = createLabel(CRect(75, 70, 125, 95), -1, "", kCenterText, 12);
+infoVoicesLabel_ = view__14;
+view__4->addView(view__14);
+Label* const view__15 = createLabel(CRect(130, 70, 190, 95), -1, "Max:", kRightText, 12);
+view__4->addView(view__15);
+Label* const view__16 = createLabel(CRect(195, 70, 245, 95), -1, "", kCenterText, 12);
+numVoicesLabel_ = view__16;
+view__4->addView(view__16);
+Label* const view__17 = createLabel(CRect(250, 70, 310, 95), -1, "Memory:", kRightText, 12);
+view__4->addView(view__17);
+Label* const view__18 = createLabel(CRect(315, 70, 365, 95), -1, "", kCenterText, 12);
+memoryLabel_ = view__18;
+view__4->addView(view__18);
+RoundedGroup* const view__19 = createRoundedGroup(CRect(495, 5, 595, 105), -1, "", kCenterText, 14);
+view__1->addView(view__19);
+LightButton* const view__20 = createLightButton(CRect(15, 37, 85, 62), kTagFirstChangePanel+kPanelSettings, "SETUP", kCenterText, 14);
+view__19->addView(view__20);
+LightButton* const view__21 = createLightButton(CRect(15, 10, 85, 35), kTagFirstChangePanel+kPanelControls, "CC", kCenterText, 14);
+view__19->addView(view__21);
+LightButton* const view__22 = createLightButton(CRect(15, 64, 85, 89), kTagFirstChangePanel+kPanelInfo, "INFO", kCenterText, 14);
+view__19->addView(view__22);
+RoundedGroup* const view__23 = createRoundedGroup(CRect(600, 5, 795, 105), -1, "", kCenterText, 14);
+view__1->addView(view__23);
+Knob48* const view__24 = createKnob48(CRect(15, 15, 63, 63), -1, "", kCenterText, 14);
+view__23->addView(view__24);
+view__24->setVisible(false);
+ValueLabel* const view__25 = createValueLabel(CRect(10, 65, 70, 90), -1, "Center", kCenterText, 12);
+view__23->addView(view__25);
+view__25->setVisible(false);
+Knob48* const view__26 = createKnob48(CRect(80, 15, 128, 63), kTagSetVolume, "", kCenterText, 14);
+volumeSlider_ = view__26;
+view__23->addView(view__26);
+ValueLabel* const view__27 = createValueLabel(CRect(75, 65, 135, 90), -1, "0.0 dB", kCenterText, 12);
+volumeLabel_ = view__27;
+view__23->addView(view__27);
+VMeter* const view__28 = createVMeter(CRect(145, 15, 180, 85), -1, "", kCenterText, 14);
+view__23->addView(view__28);
+enterTheme(defaultTheme);
+LogicalGroup* const view__29 = createLogicalGroup(CRect(5, 110, 795, 395), -1, "", kCenterText, 14);
+subPanels_[kPanelGeneral] = view__29;
+view__0->addView(view__29);
+view__29->setVisible(false);
+RoundedGroup* const view__30 = createRoundedGroup(CRect(0, 0, 120, 285), -1, "", kCenterText, 14);
+view__29->addView(view__30);
+Label* const view__31 = createLabel(CRect(10, 10, 70, 35), -1, "Curves:", kLeftText, 12);
+view__30->addView(view__31);
+Label* const view__32 = createLabel(CRect(10, 35, 70, 60), -1, "Masters:", kLeftText, 12);
+view__30->addView(view__32);
+Label* const view__33 = createLabel(CRect(10, 60, 70, 85), -1, "Groups:", kLeftText, 12);
+view__30->addView(view__33);
+Label* const view__34 = createLabel(CRect(10, 85, 70, 110), -1, "Regions:", kLeftText, 12);
+view__30->addView(view__34);
+Label* const view__35 = createLabel(CRect(10, 110, 70, 135), -1, "Samples:", kLeftText, 12);
+view__30->addView(view__35);
+Label* const view__36 = createLabel(CRect(70, 10, 110, 35), -1, "0", kCenterText, 12);
+infoCurvesLabel_ = view__36;
+view__30->addView(view__36);
+Label* const view__37 = createLabel(CRect(70, 35, 110, 60), -1, "0", kCenterText, 12);
+infoMastersLabel_ = view__37;
+view__30->addView(view__37);
+Label* const view__38 = createLabel(CRect(70, 60, 110, 85), -1, "0", kCenterText, 12);
+infoGroupsLabel_ = view__38;
+view__30->addView(view__38);
+Label* const view__39 = createLabel(CRect(70, 85, 110, 110), -1, "0", kCenterText, 12);
+infoRegionsLabel_ = view__39;
+view__30->addView(view__39);
+Label* const view__40 = createLabel(CRect(70, 110, 110, 135), -1, "0", kCenterText, 12);
+infoSamplesLabel_ = view__40;
+view__30->addView(view__40);
+LogicalGroup* const view__41 = createLogicalGroup(CRect(125, 0, 790, 280), -1, "", kCenterText, 14);
+view__29->addView(view__41);
+SfizzLargePicture* const view__42 = createSfizzLargePicture(CRect(130, 15, 530, 265), -1, "", kCenterText, 14);
+view__41->addView(view__42);
+LogicalGroup* const view__43 = createLogicalGroup(CRect(5, 110, 795, 395), -1, "", kCenterText, 14);
+subPanels_[kPanelControls] = view__43;
+view__0->addView(view__43);
+view__43->setVisible(false);
+RoundedGroup* const view__44 = createRoundedGroup(CRect(0, 0, 790, 285), -1, "", kCenterText, 14);
+view__43->addView(view__44);
+Label* const view__45 = createLabel(CRect(0, 0, 790, 285), -1, "Controls not available", kCenterText, 40);
+view__44->addView(view__45);
+LogicalGroup* const view__46 = createLogicalGroup(CRect(5, 110, 795, 395), -1, "", kCenterText, 14);
+subPanels_[kPanelSettings] = view__46;
+view__0->addView(view__46);
+TitleGroup* const view__47 = createTitleGroup(CRect(255, 15, 535, 125), -1, "Engine", kCenterText, 12);
+view__46->addView(view__47);
+ValueMenu* const view__48 = createValueMenu(CRect(25, 60, 85, 85), kTagSetNumVoices, "", kCenterText, 12);
+numVoicesSlider_ = view__48;
+view__47->addView(view__48);
+ValueLabel* const view__49 = createValueLabel(CRect(15, 20, 95, 45), -1, "Polyphony", kCenterText, 12);
+view__47->addView(view__49);
+ValueMenu* const view__50 = createValueMenu(CRect(110, 60, 170, 85), kTagSetOversampling, "", kCenterText, 12);
+oversamplingSlider_ = view__50;
+view__47->addView(view__50);
+ValueLabel* const view__51 = createValueLabel(CRect(100, 20, 180, 45), -1, "Oversampling", kCenterText, 12);
+view__47->addView(view__51);
+ValueLabel* const view__52 = createValueLabel(CRect(185, 20, 265, 45), -1, "Preload size", kCenterText, 12);
+view__47->addView(view__52);
+ValueMenu* const view__53 = createValueMenu(CRect(195, 60, 255, 85), kTagSetPreloadSize, "", kCenterText, 12);
+preloadSizeSlider_ = view__53;
+view__47->addView(view__53);
+TitleGroup* const view__54 = createTitleGroup(CRect(200, 150, 590, 270), -1, "Tuning", kCenterText, 12);
+view__46->addView(view__54);
+ValueLabel* const view__55 = createValueLabel(CRect(125, 20, 205, 45), -1, "Root key", kCenterText, 12);
+view__54->addView(view__55);
+ValueMenu* const view__56 = createValueMenu(CRect(220, 60, 280, 85), kTagSetTuningFrequency, "", kCenterText, 12);
+tuningFrequencySlider_ = view__56;
+view__54->addView(view__56);
+ValueLabel* const view__57 = createValueLabel(CRect(210, 20, 290, 45), -1, "Frequency", kCenterText, 12);
+view__54->addView(view__57);
+Knob48* const view__58 = createKnob48(CRect(310, 45, 358, 93), kTagSetStretchedTuning, "", kCenterText, 14);
+stretchedTuningSlider_ = view__58;
+view__54->addView(view__58);
+ValueLabel* const view__59 = createValueLabel(CRect(295, 20, 375, 45), -1, "Stretch", kCenterText, 12);
+view__54->addView(view__59);
+ValueLabel* const view__60 = createValueLabel(CRect(20, 20, 120, 45), -1, "Scala file", kCenterText, 12);
+view__54->addView(view__60);
+ValueButton* const view__61 = createValueButton(CRect(20, 60, 120, 85), kTagLoadScalaFile, "DefaultScale", kCenterText, 12);
+scalaFileButton_ = view__61;
+view__54->addView(view__61);
+ValueMenu* const view__62 = createValueMenu(CRect(135, 60, 170, 85), kTagSetScalaRootKey, "", kCenterText, 12);
+scalaRootKeySlider_ = view__62;
+view__54->addView(view__62);
+ValueMenu* const view__63 = createValueMenu(CRect(170, 60, 200, 85), kTagSetScalaRootKey, "", kCenterText, 12);
+scalaRootOctaveSlider_ = view__63;
+view__54->addView(view__63);
+LogicalGroup* const view__64 = createLogicalGroup(CRect(5, 110, 795, 395), -1, "", kCenterText, 14);
+subPanels_[kPanelInfo] = view__64;
+view__0->addView(view__64);
+view__64->setVisible(false);
+RoundedGroup* const view__65 = createRoundedGroup(CRect(0, 0, 790, 285), -1, "", kCenterText, 14);
+view__64->addView(view__65);
+Label* const view__66 = createLabel(CRect(0, 0, 790, 285), -1, "Informative text goes here", kCenterText, 40);
+view__65->addView(view__66);
+Piano* const view__67 = createPiano(CRect(5, 400, 795, 470), -1, "", kCenterText, 14);
+view__0->addView(view__67);
diff --git a/editor/tools/layout-maker/LICENSE b/editor/tools/layout-maker/LICENSE
new file mode 100644
index 00000000..36b7cd93
--- /dev/null
+++ b/editor/tools/layout-maker/LICENSE
@@ -0,0 +1,23 @@
+Boost Software License - Version 1.0 - August 17th, 2003
+
+Permission is hereby granted, free of charge, to any person or organization
+obtaining a copy of the software and accompanying documentation covered by
+this license (the "Software") to use, reproduce, display, distribute,
+execute, and transmit the Software, and to prepare derivative works of the
+Software, and to permit third-parties to whom the Software is furnished to
+do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement, including
+the above license grant, this restriction and the following disclaimer,
+must be included in all copies of the Software, in whole or in part, and
+all derivative works of the Software, unless such copies or derivative
+works are solely in the form of machine-executable object code generated by
+a source language processor.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
diff --git a/editor/tools/layout-maker/README b/editor/tools/layout-maker/README
new file mode 100644
index 00000000..e8402cc3
--- /dev/null
+++ b/editor/tools/layout-maker/README
@@ -0,0 +1,2 @@
+This purpose of this tool is to accept UI designs make with Fluid, the FLTK
+design editor, and convert these designs to music plugin interfaces.
diff --git a/editor/tools/layout-maker/sources/layout.h b/editor/tools/layout-maker/sources/layout.h
new file mode 100644
index 00000000..35a1978e
--- /dev/null
+++ b/editor/tools/layout-maker/sources/layout.h
@@ -0,0 +1,42 @@
+#pragma once
+#include
+#include
+
+struct LayoutImage {
+ std::string filepath;
+ int x = 0;
+ int y = 0;
+ int w = 0;
+ int h = 0;
+};
+
+struct LayoutItem {
+ std::string id;
+ std::string classname;
+ std::string label;
+ int x = 0;
+ int y = 0;
+ int w = 0;
+ int h = 0;
+ std::string box;
+ std::string down_box;
+ int labelfont = 0;
+ int labelsize = 14;
+ std::string labeltype;
+ int textsize = 14;
+ int align = 0;
+ double value = 0;
+ double minimum = 0;
+ double maximum = 0;
+ double step = 0;
+ std::string type;
+ std::string callback;
+ LayoutImage image;
+ bool hidden = false;
+ std::string comment;
+ std::vector items;
+};
+
+struct Layout {
+ std::vector items;
+};
diff --git a/editor/tools/layout-maker/sources/main.cpp b/editor/tools/layout-maker/sources/main.cpp
new file mode 100644
index 00000000..06e5283b
--- /dev/null
+++ b/editor/tools/layout-maker/sources/main.cpp
@@ -0,0 +1,136 @@
+#include "layout.h"
+#include "reader.h"
+#include
+#include
+#include
+#include
+
+///
+typedef std::unordered_map Metadata;
+
+static Metadata metadata_from_comment(absl::string_view comment)
+{
+ Metadata md;
+
+ while (!comment.empty()) {
+ absl::string_view line;
+
+ size_t pos = comment.find_first_of("\r\n");
+ if (pos != comment.npos) {
+ line = comment.substr(0, pos);
+ comment.remove_prefix(pos + 1);
+ }
+ else {
+ line = comment;
+ comment = {};
+ }
+
+ line = absl::StripAsciiWhitespace(line);
+ if (line.empty() || line[0] == '#')
+ continue;
+
+ std::string key, value;
+ pos = line.find_first_of('=');
+ if (pos != comment.npos) {
+ key = std::string(line.substr(0, pos));
+ value = std::string(line.substr(pos + 1));
+ }
+ else
+ key = std::string(line);
+
+ md.emplace(std::move(key), std::move(value));
+ }
+
+ return md;
+}
+
+///
+static void codegen_item(int& idCounter, int parentId, int parentX, int parentY, const LayoutItem& item, absl::string_view oldTheme)
+{
+ const Metadata md = metadata_from_comment(item.comment);
+
+ absl::string_view tag = "-1";
+ absl::string_view newTheme;
+
+ Metadata::const_iterator it;
+ it = md.find("tag");
+ if (it != md.end())
+ tag = it->second;
+ it = md.find("theme");
+ if (it != md.end())
+ newTheme = it->second;
+
+ absl::string_view currentTheme = newTheme.empty() ? oldTheme : newTheme;
+
+ int id = idCounter++;
+ int myX = item.x;
+ int myY = item.y;
+ if (parentId == -1) {
+ myX = 0;
+ myY = 0;
+ }
+ int relX = myX - parentX;
+ int relY = myY - parentY;
+
+ //std::cout << "// Begin " << id << " " << item.classname << " {" << item.label << "}" << "\n";
+
+ if (!newTheme.empty())
+ std::cout << "enterTheme(" << newTheme << ");\n";
+
+ absl::string_view label;
+ if (!item.label.empty() && item.labeltype != "NO_LABEL")
+ label = item.label;
+
+ absl::string_view align = "kCenterText";
+ if (item.align & 4)
+ align = "kLeftText";
+ else if (item.align & 8)
+ align = "kRightText";
+
+ std::cout << item.classname << "* const view__" << id << " = create" << item.classname << "(CRect(" << relX << ", " << relY << ", " << (relX + item.w) << ", " << (relY + item.h) << "), " << tag << ", \"" << label << "\", " << align << ", " << item.labelsize << ");\n";
+
+ if (!item.id.empty())
+ std::cout << item.id << " = view__" << id << ";\n";
+
+ if (parentId != -1)
+ std::cout << "view__" << parentId << "->addView(view__" << id << ");\n";
+
+ if (item.hidden)
+ std::cout << "view__" << id << "->setVisible(false);\n";
+
+ for (const LayoutItem& subItem : item.items)
+ codegen_item(idCounter, id, myX, myY, subItem, currentTheme);
+
+ if (!newTheme.empty())
+ std::cout << "enterTheme(" << oldTheme << ");\n";
+
+ //std::cout << "// End " << id << " " << item.classname << " {" << item.label << "}" << "\n";
+}
+
+static void codegen_layout(const LayoutItem& item)
+{
+ int idCounter = 0;
+ codegen_item(idCounter, -1, 0, 0, item, "defaultTheme");
+}
+
+///
+int main(int argc, char *argv[])
+{
+ if (argc != 2) {
+ std::cerr << "Please indicate a fluid design file.\n";
+ return 1;
+ }
+
+ Layout layout = read_file_layout(argv[1]);
+
+ if (layout.items.size() != 1) {
+ std::cerr << "There must be exactly 1 top level component.";
+ return 1;
+ }
+
+ std::cout << "/* This file is generated by the layout maker tool. */\n";
+
+ codegen_layout(layout.items[0]);
+
+ return 0;
+}
diff --git a/editor/tools/layout-maker/sources/reader.cpp b/editor/tools/layout-maker/sources/reader.cpp
new file mode 100644
index 00000000..788f93d7
--- /dev/null
+++ b/editor/tools/layout-maker/sources/reader.cpp
@@ -0,0 +1,359 @@
+#include "reader.h"
+#include
+#include
+#include
+
+typedef std::vector TokenList;
+static bool read_file_tokens(const char *filename, TokenList &tokens);
+static Layout read_tokens_layout(TokenList::iterator &tok_it, TokenList::iterator tok_end);
+
+Layout read_file_layout(const char *filename)
+{
+ std::vector tokens;
+ if (!read_file_tokens(filename, tokens))
+ throw std::runtime_error("Cannot read fluid design file.");
+
+ TokenList::iterator tok_it = tokens.begin();
+ TokenList::iterator tok_end = tokens.end();
+ return read_tokens_layout(tok_it, tok_end);
+}
+
+static std::string consume_next_token(TokenList::iterator &tok_it, TokenList::iterator tok_end)
+{
+ if (tok_it == tok_end)
+ throw file_format_error("Premature end of tokens");
+ return *tok_it++;
+}
+
+static bool try_consume_next_token(const char *text, TokenList::iterator &tok_it, TokenList::iterator tok_end)
+{
+ if (tok_it == tok_end)
+ return false;
+
+ if (*tok_it != text)
+ return false;
+
+ ++tok_it;
+ return true;
+}
+
+static void ensure_next_token(const char *text, TokenList::iterator &tok_it, TokenList::iterator tok_end)
+{
+ std::string tok = consume_next_token(tok_it, tok_end);
+ if (tok != text)
+ throw file_format_error("Unexpected token: " + tok);
+}
+
+static std::string consume_enclosed_string(TokenList::iterator &tok_it, TokenList::iterator tok_end)
+{
+ ensure_next_token("{", tok_it, tok_end);
+ unsigned depth = 1;
+
+ std::string text;
+ for (;;) {
+ std::string part = consume_next_token(tok_it, tok_end);
+ if (part == "}") {
+ if (--depth == 0)
+ return text;
+ }
+ else if (part == "{")
+ ++depth;
+ if (!text.empty())
+ text.push_back(' ');
+ text.append(part);
+ }
+
+ return text;
+}
+
+static std::string consume_any_string(TokenList::iterator &tok_it, TokenList::iterator tok_end)
+{
+ if (tok_it != tok_end && *tok_it == "{")
+ return consume_enclosed_string(tok_it, tok_end);
+ else
+ return consume_next_token(tok_it, tok_end);
+}
+
+static int consume_int_token(TokenList::iterator &tok_it, TokenList::iterator tok_end)
+{
+ std::string text = consume_next_token(tok_it, tok_end);
+ return std::stoi(text);
+}
+
+static int consume_real_token(TokenList::iterator &tok_it, TokenList::iterator tok_end)
+{
+ std::string text = consume_next_token(tok_it, tok_end);
+ return std::stod(text);
+}
+
+static void consume_image_properties(LayoutImage &image, TokenList::iterator &tok_it, TokenList::iterator tok_end)
+{
+ for (bool have = true; have;) {
+ if (try_consume_next_token("xywh", tok_it, tok_end)) {
+ ensure_next_token("{", tok_it, tok_end);
+ image.x = consume_int_token(tok_it, tok_end);
+ image.y = consume_int_token(tok_it, tok_end);
+ image.w = consume_int_token(tok_it, tok_end);
+ image.h = consume_int_token(tok_it, tok_end);
+ ensure_next_token("}", tok_it, tok_end);
+ }
+ else
+ have = false;
+ }
+}
+
+// static void consume_layout_item_properties(LayoutItem &item, TokenList::iterator &tok_it, TokenList::iterator tok_end)
+// {
+// ensure_next_token("{", tok_it, tok_end);
+// for (std::string text; (text = consume_next_token(tok_it, tok_end)) != "}";) {
+// if (text == "open" || text == "selected")
+// ; // skip
+// else if (text == "label")
+// item.label = consume_any_string(tok_it, tok_end);
+// else if (text == "xywh") {
+// ensure_next_token("{", tok_it, tok_end);
+// item.x = consume_int_token(tok_it, tok_end);
+// item.y = consume_int_token(tok_it, tok_end);
+// item.w = consume_int_token(tok_it, tok_end);
+// item.h = consume_int_token(tok_it, tok_end);
+// ensure_next_token("}", tok_it, tok_end);
+// }
+// else if (text == "box")
+// item.box = consume_next_token(tok_it, tok_end);
+// else if (text == "labelfont")
+// item.labelfont = consume_int_token(tok_it, tok_end);
+// else if (text == "labelsize")
+// item.labelsize = consume_int_token(tok_it, tok_end);
+// else if (text == "labeltype")
+// item.labeltype = consume_any_string(tok_it, tok_end);
+// else if (text == "align")
+// item.align = consume_int_token(tok_it, tok_end);
+// else if (text == "type")
+// item.type = consume_any_string(tok_it, tok_end);
+// else if (text == "callback")
+// item.callback = consume_any_string(tok_it, tok_end);
+// else if (text == "class")
+// item.classname = consume_any_string(tok_it, tok_end);
+// else if (text == "minimum")
+// item.minimum = consume_real_token(tok_it, tok_end);
+// else if (text == "maximum")
+// item.maximum = consume_real_token(tok_it, tok_end);
+// else if (text == "step")
+// item.step = consume_real_token(tok_it, tok_end);
+// else if (text == "image") {
+// item.image.filepath = consume_any_string(tok_it, tok_end);
+// consume_image_properties(item.image, tok_it, tok_end);
+// }
+// }
+// }
+
+static void consume_layout_item_properties(LayoutItem &item, TokenList::iterator &tok_it, TokenList::iterator tok_end)
+{
+ ensure_next_token("{", tok_it, tok_end);
+ for (bool have = true; have;) {
+ if (try_consume_next_token("open", tok_it, tok_end))
+ ; // skip
+ else if (try_consume_next_token("selected", tok_it, tok_end))
+ ; // skip
+ else if (try_consume_next_token("label", tok_it, tok_end))
+ item.label = consume_any_string(tok_it, tok_end);
+ else if (try_consume_next_token("xywh", tok_it, tok_end)) {
+ ensure_next_token("{", tok_it, tok_end);
+ item.x = consume_int_token(tok_it, tok_end);
+ item.y = consume_int_token(tok_it, tok_end);
+ item.w = consume_int_token(tok_it, tok_end);
+ item.h = consume_int_token(tok_it, tok_end);
+ ensure_next_token("}", tok_it, tok_end);
+ }
+ else if (try_consume_next_token("box", tok_it, tok_end))
+ item.box = consume_next_token(tok_it, tok_end);
+ else if (try_consume_next_token("down_box", tok_it, tok_end))
+ item.down_box = consume_next_token(tok_it, tok_end);
+ else if (try_consume_next_token("labelfont", tok_it, tok_end))
+ item.labelfont = consume_int_token(tok_it, tok_end);
+ else if (try_consume_next_token("labelsize", tok_it, tok_end))
+ item.labelsize = consume_int_token(tok_it, tok_end);
+ else if (try_consume_next_token("labeltype", tok_it, tok_end))
+ item.labeltype = consume_any_string(tok_it, tok_end);
+ else if (try_consume_next_token("textsize", tok_it, tok_end))
+ item.textsize = consume_int_token(tok_it, tok_end);
+ else if (try_consume_next_token("align", tok_it, tok_end))
+ item.align = consume_int_token(tok_it, tok_end);
+ else if (try_consume_next_token("type", tok_it, tok_end))
+ item.type = consume_any_string(tok_it, tok_end);
+ else if (try_consume_next_token("callback", tok_it, tok_end))
+ item.callback = consume_any_string(tok_it, tok_end);
+ else if (try_consume_next_token("class", tok_it, tok_end))
+ item.classname = consume_any_string(tok_it, tok_end);
+ else if (try_consume_next_token("value", tok_it, tok_end))
+ item.value = consume_real_token(tok_it, tok_end);
+ else if (try_consume_next_token("minimum", tok_it, tok_end))
+ item.minimum = consume_real_token(tok_it, tok_end);
+ else if (try_consume_next_token("maximum", tok_it, tok_end))
+ item.maximum = consume_real_token(tok_it, tok_end);
+ else if (try_consume_next_token("step", tok_it, tok_end))
+ item.step = consume_real_token(tok_it, tok_end);
+ else if (try_consume_next_token("image", tok_it, tok_end))
+ item.image.filepath = consume_any_string(tok_it, tok_end);
+ else if (try_consume_next_token("hide", tok_it, tok_end))
+ item.hidden = true;
+ else if (try_consume_next_token("visible", tok_it, tok_end))
+ /* skip */;
+ else if (try_consume_next_token("comment", tok_it, tok_end))
+ item.comment = consume_any_string(tok_it, tok_end);
+ else
+ have = false;
+ }
+ ensure_next_token("}", tok_it, tok_end);
+}
+
+static LayoutItem consume_layout_item(const std::string &classname, TokenList::iterator &tok_it, TokenList::iterator tok_end, bool anonymous = false)
+{
+ LayoutItem item;
+ item.classname = classname;
+ if (!anonymous)
+ item.id = consume_any_string(tok_it, tok_end);
+ consume_layout_item_properties(item, tok_it, tok_end);
+ if (tok_it != tok_end && *tok_it == "{") {
+ consume_next_token(tok_it, tok_end);
+ for (std::string text; (text = consume_next_token(tok_it, tok_end)) != "}";) {
+ if (text == "decl") {
+ consume_any_string(tok_it, tok_end);
+ consume_any_string(tok_it, tok_end);
+ }
+ else if (text == "Function") {
+ consume_any_string(tok_it, tok_end);
+ consume_any_string(tok_it, tok_end);
+ consume_any_string(tok_it, tok_end);
+ }
+ else
+ item.items.push_back(consume_layout_item(text, tok_it, tok_end));
+ }
+ }
+ return item;
+}
+
+static Layout read_tokens_layout(TokenList::iterator &tok_it, TokenList::iterator tok_end)
+{
+ Layout layout;
+
+ std::string version_name;
+ std::string header_name;
+ std::string code_name;
+
+ while (tok_it != tok_end) {
+ std::string key = consume_next_token(tok_it, tok_end);
+
+ if (key == "version")
+ version_name = consume_next_token(tok_it, tok_end);
+ else if (key == "header_name") {
+ ensure_next_token("{", tok_it, tok_end);
+ header_name = consume_next_token(tok_it, tok_end);
+ ensure_next_token("}", tok_it, tok_end);
+ }
+ else if (key == "code_name") {
+ ensure_next_token("{", tok_it, tok_end);
+ code_name = consume_next_token(tok_it, tok_end);
+ ensure_next_token("}", tok_it, tok_end);
+ }
+ else if (key == "decl") {
+ consume_any_string(tok_it, tok_end);
+ consume_any_string(tok_it, tok_end);
+ }
+ else if (key == "widget_class") {
+ key = consume_next_token(tok_it, tok_end);
+ layout.items.push_back(consume_layout_item(key, tok_it, tok_end, true));
+ layout.items.back().id = key;
+ }
+ else
+ layout.items.push_back(consume_layout_item(key, tok_it, tok_end));
+ }
+
+ return layout;
+}
+
+///
+class tokenizer {
+public:
+ tokenizer(
+ absl::string_view text,
+ absl::string_view dropped_delims,
+ absl::string_view kept_delims);
+
+ absl::string_view next();
+
+private:
+ absl::string_view text_;
+ absl::string_view dropped_delims_;
+ absl::string_view kept_delims_;
+};
+
+tokenizer::tokenizer(
+ absl::string_view text,
+ absl::string_view dropped_delims,
+ absl::string_view kept_delims)
+ : text_(text), dropped_delims_(dropped_delims), kept_delims_(kept_delims)
+{
+}
+
+absl::string_view tokenizer::next()
+{
+ auto is_dropped = [this](char c) -> bool {
+ return dropped_delims_.find(c) != dropped_delims_.npos;
+ };
+ auto is_kept = [this](char c) -> bool {
+ return kept_delims_.find(c) != kept_delims_.npos;
+ };
+ auto is_delim = [this](char c) -> bool {
+ return dropped_delims_.find(c) != dropped_delims_.npos ||
+ kept_delims_.find(c) != kept_delims_.npos;
+ };
+
+ absl::string_view text = text_;
+
+ while (!text.empty() && is_dropped(text[0]))
+ text.remove_prefix(1);
+
+ if (text.empty())
+ return {};
+
+ size_t pos;
+ {
+ auto it = std::find_if(text.begin(), text.end(), is_delim);
+ if (it == text.end())
+ pos = text.size();
+ else {
+ pos = std::distance(text.begin(), it);
+ pos += is_kept(text[0]);
+ }
+ }
+
+ absl::string_view token = text.substr(0, pos);
+ text_ = text.substr(pos);
+ return token;
+}
+
+///
+static bool read_file_tokens(const char *filename, TokenList &tokens)
+{
+ std::ifstream stream(filename);
+ std::string line;
+
+ std::string text;
+ while (std::getline(stream, line)) {
+ if (!line.empty() && line[0] != '#') {
+ text.append(line);
+ text.push_back('\n');
+ }
+ }
+
+ if (stream.bad())
+ return false;
+
+ tokenizer tok(text, " \t\r\n", "{}");
+ absl::string_view token;
+ while (!(token = tok.next()).empty())
+ tokens.emplace_back(token);
+
+ return !stream.bad();
+}
diff --git a/editor/tools/layout-maker/sources/reader.h b/editor/tools/layout-maker/sources/reader.h
new file mode 100644
index 00000000..589bd307
--- /dev/null
+++ b/editor/tools/layout-maker/sources/reader.h
@@ -0,0 +1,13 @@
+#pragma once
+#include "layout.h"
+#include
+#include
+
+Layout read_file_layout(const char *filename);
+
+///
+struct file_format_error : public std::runtime_error {
+public:
+ explicit file_format_error(const std::string &reason = "Format error")
+ : runtime_error(reason) {}
+};
diff --git a/lv2/CMakeLists.txt b/lv2/CMakeLists.txt
index 3322d3d1..a888ca81 100644
--- a/lv2/CMakeLists.txt
+++ b/lv2/CMakeLists.txt
@@ -98,10 +98,9 @@ endforeach()
if (SFIZZ_LV2_UI)
execute_process (
COMMAND "${CMAKE_COMMAND}" -E make_directory "${PROJECT_BINARY_DIR}/Contents/Resources")
- foreach(res ${EDITOR_RESOURCES})
- file (COPY "${CMAKE_CURRENT_SOURCE_DIR}/../editor/resources/${res}"
- DESTINATION "${PROJECT_BINARY_DIR}/Contents/Resources")
- endforeach()
+ copy_editor_resources(
+ "${CMAKE_CURRENT_SOURCE_DIR}/../editor/resources"
+ "${PROJECT_BINARY_DIR}/Contents/Resources")
endif()
# Installation
diff --git a/scripts/innosetup.iss.in b/scripts/innosetup.iss.in
index b287de6a..7c845a4b 100644
--- a/scripts/innosetup.iss.in
+++ b/scripts/innosetup.iss.in
@@ -50,9 +50,7 @@ Name: "vst3"; Description: "VST3 plugin"; Types: full custom;
[Files]
Source: "sfizz.lv2\Contents\Binary\sfizz.dll"; Components: lv2; DestDir: "{commoncf}\LV2\sfizz.lv2\Contents\Binary"; Flags: ignoreversion
Source: "sfizz.lv2\Contents\Binary\sfizz_ui.dll"; Components: lv2; DestDir: "{commoncf}\LV2\sfizz.lv2\Contents\Binary"; Flags: ignoreversion
-Source: "sfizz.lv2\Contents\Resources\DefaultInstrument.sfz"; Components: lv2; DestDir: "{commoncf}\LV2\sfizz.lv2\Contents\Resources"
-Source: "sfizz.lv2\Contents\Resources\DefaultScale.scl"; Components: lv2; DestDir: "{commoncf}\LV2\sfizz.lv2\Contents\Resources"
-Source: "sfizz.lv2\Contents\Resources\logo.png"; Components: lv2; DestDir: "{commoncf}\LV2\sfizz.lv2\Contents\Resources"
+Source: "sfizz.lv2\Contents\Resources\*"; Components: lv2; DestDir: "{commoncf}\LV2\sfizz.lv2\Contents\Resources"
Source: "sfizz.lv2\manifest.ttl"; Components: lv2; DestDir: "{commoncf}\LV2\sfizz.lv2"
Source: "sfizz.lv2\sfizz.ttl"; Components: lv2; DestDir: "{commoncf}\LV2\sfizz.lv2"
Source: "sfizz.lv2\sfizz_ui.ttl"; Components: lv2; DestDir: "{commoncf}\LV2\sfizz.lv2"
@@ -60,7 +58,7 @@ Source: "sfizz.lv2\lgpl-3.0.txt"; Components: main; DestDir: "{app}"
Source: "sfizz.lv2\LICENSE.md"; Components: main; DestDir: "{app}"
Source: "sfizz.vst3\desktop.ini"; Components: vst3; DestDir: "{commoncf}\VST3\sfizz.vst3"
Source: "sfizz.vst3\Contents\@VST3_PACKAGE_ARCHITECTURE@-win\sfizz.vst3"; Components: vst3; DestDir: "{commoncf}\VST3\sfizz.vst3\Contents\@VST3_PACKAGE_ARCHITECTURE@-win"; Flags: ignoreversion
-Source: "sfizz.vst3\Contents\Resources\logo.png"; Components: vst3; DestDir: "{commoncf}\VST3\sfizz.vst3\Contents\Resources"
+Source: "sfizz.vst3\Contents\Resources\*"; Components: vst3; DestDir: "{commoncf}\VST3\sfizz.vst3\Contents\Resources"
Source: "sfizz.vst3\Plugin.ico"; Components: vst3; DestDir: "{commoncf}\VST3\sfizz.vst3"
Source: "sfizz.vst3\gpl-3.0.txt"; Components: main; DestDir: "{app}"
;Source: "setup\vc_redist.x64.exe"; DestDir: {tmp}; Flags: deleteafterinstall
diff --git a/vst/CMakeLists.txt b/vst/CMakeLists.txt
index c4b3538e..bbd2ff2e 100644
--- a/vst/CMakeLists.txt
+++ b/vst/CMakeLists.txt
@@ -68,10 +68,9 @@ endif()
# Create the bundle (see "VST 3 Locations / Format")
execute_process (
COMMAND "${CMAKE_COMMAND}" -E make_directory "${PROJECT_BINARY_DIR}/${VSTPLUGIN_BUNDLE_NAME}/Contents/Resources")
-foreach(res ${EDITOR_RESOURCES})
- file (COPY "${CMAKE_CURRENT_SOURCE_DIR}/../editor/resources/${res}"
- DESTINATION "${PROJECT_BINARY_DIR}/${VSTPLUGIN_BUNDLE_NAME}/Contents/Resources")
-endforeach()
+copy_editor_resources(
+ "${CMAKE_CURRENT_SOURCE_DIR}/../editor/resources"
+ "${PROJECT_BINARY_DIR}/${VSTPLUGIN_BUNDLE_NAME}/Contents/Resources")
if(WIN32)
set_target_properties(${VSTPLUGIN_PRJ_NAME} PROPERTIES
SUFFIX ".vst3"