diff --git a/.github/workflows/build-lte.yml b/.github/workflows/build-lte.yml
new file mode 100644
index 00000000..16ad1bb3
--- /dev/null
+++ b/.github/workflows/build-lte.yml
@@ -0,0 +1,99 @@
+name: Build Lumatone Editor
+
+on: [push, pull_request]
+
+env: {}
+
+jobs:
+ build-lte-linux:
+ name: Build Lumatone Editor (Linux)
+
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v2
+ - name: Install JUCE to $HOME
+ run: |
+ wget https://github.com/juce-framework/JUCE/releases/download/6.1.6/juce-6.1.6-linux.zip
+ unzip -d ~ juce-6.1.6-linux.zip
+ - name: Install APT dependencies
+ run: |
+ sudo apt update
+ sudo apt install libssh2-1-dev libasound2-dev libjack-jackd2-dev ladspa-sdk libcurl4-openssl-dev libfreetype6-dev libx11-dev libxcomposite-dev libxcursor-dev libxcursor-dev libxext-dev libxinerama-dev libxrandr-dev libxrender-dev libwebkit2gtk-4.0-dev libglu1-mesa-dev mesa-common-dev
+ - name: Build Lumatone Editor
+ run: |
+ ~/JUCE/Projucer --resave TerpstraSysEx.jucer
+ cd Builds/Linux
+ make CONFIG=Release
+ - name: List build directory
+ run: |
+ cd Builds/Linux
+ find
+ - name: Upload Lumatone Editor
+ uses: actions/upload-artifact@v2
+ with:
+ name: LumatoneEditor-Linux
+ path: Builds/Linux/build/Lumatone\ Editor
+ if-no-files-found: error
+
+ build-lte-macos:
+ name: Build Lumatone Editor (macOS)
+
+ runs-on: macos-latest
+
+ steps:
+ - uses: actions/checkout@v2
+ - name: Install JUCE to $HOME
+ run: |
+ wget https://github.com/juce-framework/JUCE/releases/download/6.1.6/juce-6.1.6-osx.zip
+ unzip -d ~ juce-6.1.6-osx.zip
+ - name: Build Lumatone Editor
+ run: |
+ ~/JUCE/Projucer.app/Contents/MacOS/Projucer --resave TerpstraSysEx.jucer
+ cd Builds/MacOSX
+ xcodebuild -configuration Release
+ - name: List build directory
+ run: |
+ cd Builds/MacOSX
+ find .
+ - name: Upload Lumatone Editor
+ uses: actions/upload-artifact@v2
+ with:
+ name: LumatoneEditor-macOS
+ path: Builds/MacOSX/build/Release
+ if-no-files-found: error
+
+ build-lte-windows:
+ name: Build Lumatone Editor (Windows)
+
+ runs-on: windows-latest
+
+ steps:
+ - uses: actions/checkout@v2
+ - uses: microsoft/setup-msbuild@v1.1
+ - name: Install JUCE to C:\
+ run: |
+ Invoke-WebRequest https://github.com/juce-framework/JUCE/releases/download/6.1.6/juce-6.1.6-windows.zip -O juce-6.1.6-windows.zip
+ 7z x juce-6.1.6-windows.zip -oC:\
+ - name: Build Lumatone Editor
+ run: |
+ bash -c '/c/JUCE/Projucer --resave TerpstraSysEx.jucer'
+ cd Builds/VisualStudio2019
+ msbuild "Lumatone Editor.sln" /p:configuration=Release
+ - name: List build directory
+ run: |
+ cd Builds/VisualStudio2019
+ Get-ChildItem -Recurse
+ - name: Upload Lumatone Editor
+ uses: actions/upload-artifact@v2
+ with:
+ name: LumatoneEditor-Windows
+ path: "Builds/VisualStudio2019/x64/Release/App/Lumatone Editor.exe"
+ if-no-files-found: error
+ - name: Upload dynamic libraries
+ uses: actions/upload-artifact@v2
+ with:
+ name: LumatoneEditor-Windows
+ path: Libraries/win64/bin/*.dll
+ if-no-files-found: error
+
diff --git a/.gitignore b/.gitignore
index 1299bd6b..a7128209 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,3 +6,5 @@
/Installers/InnoSetup/Install*
/Installers/InnoSetup/archive*
/Installers/Packages/build*
+.vscode
+/Releases
diff --git a/BinaryData/Localisation/en-gb.txt b/BinaryData/Localisation/en-gb.txt
index 16400c33..c513df86 100644
--- a/BinaryData/Localisation/en-gb.txt
+++ b/BinaryData/Localisation/en-gb.txt
@@ -73,4 +73,7 @@ countries: ca gb au
"FreeDrawing" = "Free Drawing"
"Linear" = "Linear"
"Quadratic" = "Quadratic"
-"Ticks" = "ticks"
\ No newline at end of file
+"Ticks" = "ticks"
+"EditButtonTip" = "Edit palette colours"
+"CloneButtonTip" = "Clone palette"
+"TrashButtonTip" = "Delete palette (currently irreversible)"
\ No newline at end of file
diff --git a/Installers/InnoSetup/LumatoneEditorWinInstallerScript.iss b/Installers/InnoSetup/LumatoneEditorWinInstallerScript.iss
index c80a46d9..d5db9199 100644
--- a/Installers/InnoSetup/LumatoneEditorWinInstallerScript.iss
+++ b/Installers/InnoSetup/LumatoneEditorWinInstallerScript.iss
@@ -2,10 +2,11 @@
; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!
#define MyAppName "Lumatone Editor"
-#define MyAppVersion "1.0.0"
+#define MyAppVersion "1.0.2"
#define MyAppPublisher "Lumatone"
#define MyAppURL "https://www.lumatone.io/"
#define MyAppExeName "Lumatone Editor.exe"
+#define MyAppExeNameDest "Lumatone Editor.exe"
#define MappingAssocName MyAppName + " Mapping"
#define MappingAssocExt ".ltn"
#define MappingAssocKey StringChange(MappingAssocName, " ", "") + MappingAssocExt
@@ -23,6 +24,7 @@ AppPublisher={#MyAppPublisher}
AppPublisherURL={#MyAppURL}
AppSupportURL={#MyAppURL}
AppUpdatesURL={#MyAppURL}
+AllowNoIcons=yes
DefaultDirName={autopf64}\{#MyAppName}
ChangesAssociations=yes
DisableProgramGroupPage=yes
@@ -33,7 +35,7 @@ OutputDir=.\
OutputBaseFilename=Install {#MyAppName} {#MyAppVersion}
Compression=lzma
SolidCompression=yes
-UsePreviousAppDir=no
+UsePreviousAppDir=yes
WizardStyle=modern
[Languages]
@@ -42,8 +44,8 @@ Name: "english"; MessagesFile: "compiler:Default.isl"
[Tasks]
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked
-[Files]
-Source: "..\..\Builds\VisualStudio2019\x64\Release\App\{#MyAppExeName}"; DestDir: "{app}"; Flags: ignoreversion
+[Files]
+Source: "..\..\Builds\VisualStudio2019\x64\Release\App\{#MyAppExeName}"; DestDir: "{app}"; DestName: "{#MyAppExeNameDest}"; Flags: ignoreversion
Source: "..\..\Presets\Mappings\*"; DestDir: "{userdocs}\{#MyAppName}\Mappings"; Flags: ignoreversion recursesubdirs createallsubdirs uninsneveruninstall
Source: "..\..\Presets\Palettes\*"; DestDir: "{userdocs}\{#MyAppName}\Palettes"; Flags: ignoreversion recursesubdirs createallsubdirs uninsneveruninstall
Source: "..\..\Libraries\win64\bin\libssh2-x64.dll"; DestDir: "{app}"; Flags: ignoreversion
@@ -57,14 +59,14 @@ Source: "vc_redist.x64.exe"; DestDir: "{tmp}"; Flags: ignoreversion deleteafteri
Root: HKA; Subkey: "Software\Classes\{#MappingAssocExt}\OpenWithProgIds"; ValueType: string; ValueName: "{#MappingAssocKey}"; ValueData: "%"; Flags: uninsdeletevalue
Root: HKA; Subkey: "Software\Classes\{#MappingAssocExt}"; ValueType: string; ValueName: ""; ValueData: "{#MappingAssocName}"; Flags: uninsdeletekey
Root: HKA; Subkey: "Software\Classes\{#MappingAssocExt}\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\ltn.ico"
-Root: HKA; Subkey: "Software\Classes\{#MappingAssocExt}\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#MyAppExeName}"" ""%1"""
-Root: HKA; Subkey: "Software\Classes\Applications\{#MyAppExeName}\SupportedTypes"; ValueType: string; ValueName: {#MappingAssocExt}; ValueData: ""
+Root: HKA; Subkey: "Software\Classes\{#MappingAssocExt}\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#MyAppExeNameDest}"" ""%1"""
+Root: HKA; Subkey: "Software\Classes\Applications\{#MyAppExeNameDest}\SupportedTypes"; ValueType: string; ValueName: {#MappingAssocExt}; ValueData: ""
Root: HKA; Subkey: "Software\Classes\{#PaletteAssocExt}"; ValueType: string; ValueName: ""; ValueData: "{#PaletteAssocName}"; Flags: uninsdeletekey
Root: HKA; Subkey: "Software\Classes\{#PaletteAssocExt}\DefaultIcon"; ValueType: string; ValueName: ""; ValueData:"{app}\ltp.ico"
[Icons]
-Name: "{autoprograms}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"
-Name: "{autodesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon
+Name: "{autoprograms}\{#MyAppName}"; Filename: "{app}\{#MyAppExeNameDest}"
+Name: "{autodesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeNameDest}"; Tasks: desktopicon
[Code]
// Pulled from https://stackoverflow.com/questions/24574035/how-to-install-microsoft-vc-redistributables-silently-in-inno-setup
@@ -94,5 +96,5 @@ end;
[Run]
Filename: "{tmp}\vc_redist.x64.exe"; Description: "Install VC Redistributable package if necessary"; Parameters: "/q /norestart /c:""msiexec /q /i vcredist.msi"""; \
Check: VCRedistNeedsInstall; StatusMsg: "Installing Microsoft Visual C++ 2015, may take a few minutes..."
-Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall skipifsilent
+Filename: "{app}\{#MyAppExeNameDest}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall skipifsilent
diff --git a/Installers/Packages/Lumatone Editor/LICENSE.txt b/Installers/Packages/Lumatone Editor/LICENSE.txt
new file mode 100644
index 00000000..e478e419
--- /dev/null
+++ b/Installers/Packages/Lumatone Editor/LICENSE.txt
@@ -0,0 +1,11 @@
+Copyright 2022 Lumatone Inc.
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+
+3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
\ No newline at end of file
diff --git a/Installers/Packages/Lumatone Editor/Lumatone Editor.pkgproj b/Installers/Packages/Lumatone Editor/Lumatone Editor.pkgproj
index ec3e3450..9a49d3ac 100644
--- a/Installers/Packages/Lumatone Editor/Lumatone Editor.pkgproj
+++ b/Installers/Packages/Lumatone Editor/Lumatone Editor.pkgproj
@@ -38,7 +38,7 @@
GID
80
PATH
- ../../../Builds/MacOSX/build/Release/Lumatone Editor.app
+ ../../../Releases/macOS/Latest/Lumatone Editor.app
PATH_TYPE
1
PERMISSIONS
@@ -48,6 +48,22 @@
UID
0
+
+ CHILDREN
+
+ GID
+ 80
+ PATH
+ Utilities
+ PATH_TYPE
+ 0
+ PERMISSIONS
+ 493
+ TYPE
+ -1
+ UID
+ 0
+
GID
80
@@ -62,6 +78,22 @@
UID
0
+
+ CHILDREN
+
+ GID
+ 0
+ PATH
+ bin
+ PATH_TYPE
+ 0
+ PERMISSIONS
+ 493
+ TYPE
+ -1
+ UID
+ 0
+
CHILDREN
@@ -81,6 +113,22 @@
UID
0
+
+ CHILDREN
+
+ GID
+ 0
+ PATH
+ Audio
+ PATH_TYPE
+ 0
+ PERMISSIONS
+ 493
+ TYPE
+ 1
+ UID
+ 0
+
CHILDREN
@@ -97,6 +145,22 @@
UID
0
+
+ CHILDREN
+
+ GID
+ 0
+ PATH
+ ColorPickers
+ PATH_TYPE
+ 0
+ PERMISSIONS
+ 493
+ TYPE
+ 1
+ UID
+ 0
+
CHILDREN
@@ -145,6 +209,22 @@
UID
0
+
+ CHILDREN
+
+ GID
+ 80
+ PATH
+ Fonts
+ PATH_TYPE
+ 0
+ PERMISSIONS
+ 1021
+ TYPE
+ 1
+ UID
+ 0
+
CHILDREN
@@ -399,6 +479,71 @@
UID
0
+
+ CHILDREN
+
+
+ CHILDREN
+
+ GID
+ 0
+ PATH
+ etc
+ PATH_TYPE
+ 0
+ PERMISSIONS
+ 493
+ TYPE
+ -1
+ UID
+ 0
+
+
+ CHILDREN
+
+ GID
+ 0
+ PATH
+ var
+ PATH_TYPE
+ 0
+ PERMISSIONS
+ 493
+ TYPE
+ -1
+ UID
+ 0
+
+
+ GID
+ 0
+ PATH
+ private
+ PATH_TYPE
+ 0
+ PERMISSIONS
+ 493
+ TYPE
+ -1
+ UID
+ 0
+
+
+ CHILDREN
+
+ GID
+ 0
+ PATH
+ sbin
+ PATH_TYPE
+ 0
+ PERMISSIONS
+ 493
+ TYPE
+ -1
+ UID
+ 0
+
CHILDREN
@@ -407,50 +552,17 @@
CHILDREN
-
-
- CHILDREN
-
- GID
- 0
- PATH
- /Users/vincenzo/Programming/Projects/TerpstraSysEx.2014/Presets/Mappings
- PATH_TYPE
- 0
- PERMISSIONS
- 493
- TYPE
- 3
- UID
- 0
-
-
- CHILDREN
-
- GID
- 0
- PATH
- /Users/vincenzo/Programming/Projects/TerpstraSysEx.2014/Presets/Palettes
- PATH_TYPE
- 0
- PERMISSIONS
- 493
- TYPE
- 3
- UID
- 0
-
-
+
GID
0
PATH
- Lumatone Editor
+ Extensions
PATH_TYPE
- 2
+ 0
PERMISSIONS
- 509
+ 493
TYPE
- 2
+ 1
UID
0
@@ -458,6 +570,39 @@
GID
0
PATH
+ Library
+ PATH_TYPE
+ 0
+ PERMISSIONS
+ 493
+ TYPE
+ 1
+ UID
+ 0
+
+
+ GID
+ 0
+ PATH
+ System
+ PATH_TYPE
+ 0
+ PERMISSIONS
+ 493
+ TYPE
+ 1
+ UID
+ 0
+
+
+ CHILDREN
+
+
+ CHILDREN
+
+ GID
+ 0
+ PATH
Shared
PATH_TYPE
0
@@ -482,6 +627,136 @@
UID
0
+
+ CHILDREN
+
+
+ CHILDREN
+
+ GID
+ 0
+ PATH
+ bin
+ PATH_TYPE
+ 0
+ PERMISSIONS
+ 493
+ TYPE
+ -1
+ UID
+ 0
+
+
+ CHILDREN
+
+ GID
+ 0
+ PATH
+ include
+ PATH_TYPE
+ 0
+ PERMISSIONS
+ 493
+ TYPE
+ -1
+ UID
+ 0
+
+
+ CHILDREN
+
+ GID
+ 0
+ PATH
+ lib
+ PATH_TYPE
+ 0
+ PERMISSIONS
+ 493
+ TYPE
+ -1
+ UID
+ 0
+
+
+ CHILDREN
+
+
+ CHILDREN
+
+ GID
+ 0
+ PATH
+ bin
+ PATH_TYPE
+ 0
+ PERMISSIONS
+ 493
+ TYPE
+ -1
+ UID
+ 0
+
+
+ GID
+ 0
+ PATH
+ local
+ PATH_TYPE
+ 0
+ PERMISSIONS
+ 493
+ TYPE
+ -1
+ UID
+ 0
+
+
+ CHILDREN
+
+ GID
+ 0
+ PATH
+ sbin
+ PATH_TYPE
+ 0
+ PERMISSIONS
+ 493
+ TYPE
+ -1
+ UID
+ 0
+
+
+ CHILDREN
+
+ GID
+ 0
+ PATH
+ share
+ PATH_TYPE
+ 0
+ PERMISSIONS
+ 493
+ TYPE
+ -1
+ UID
+ 0
+
+
+ GID
+ 0
+ PATH
+ usr
+ PATH_TYPE
+ 0
+ PERMISSIONS
+ 493
+ TYPE
+ -1
+ UID
+ 0
+
GID
0
@@ -501,7 +776,7 @@
PRESERVE_EXTENDED_ATTRIBUTES
SHOW_INVISIBLE
-
+
SPLIT_FORKS
TREAT_MISSING_FILES_AS_WARNING
@@ -513,8 +788,10 @@
POSTINSTALL_PATH
+ PATH
+ ../../../Scripts/pkg-install-presets.sh
PATH_TYPE
- 0
+ 1
PREINSTALL_PATH
@@ -522,7 +799,24 @@
0
RESOURCES
-
+
+
+ CHILDREN
+
+ GID
+ 0
+ PATH
+ ../../../Presets
+ PATH_TYPE
+ 1
+ PERMISSIONS
+ 493
+ TYPE
+ 3
+ UID
+ 0
+
+
PACKAGE_SETTINGS
@@ -531,7 +825,7 @@
CONCLUSION_ACTION
0
FOLLOW_SYMBOLIC_LINKS
-
+
IDENTIFIER
com.Lumatone.pkg.LumatoneEditor
LOCATION
@@ -539,7 +833,7 @@
NAME
Lumatone Editor
OVERWRITE_PERMISSIONS
-
+
PAYLOAD_SIZE
-1
REFERENCE_PATH
@@ -549,7 +843,7 @@
USE_HFS+_COMPRESSION
VERSION
- 0.9.9
+ 1.0.2
TYPE
0
@@ -579,6 +873,43 @@
SHARED_SETTINGS_FOR_ALL_APPAREANCES
+ INSTALLATION TYPE
+
+ HIERARCHIES
+
+ INSTALLER
+
+ LIST
+
+
+ CHILDREN
+
+ DESCRIPTION
+
+ OPTIONS
+
+ HIDDEN
+
+ STATE
+ 1
+
+ PACKAGE_UUID
+ 63444C20-3865-4E17-B60F-C117465E6F52
+ TITLE
+
+ TYPE
+ 0
+ UUID
+ E63EC134-3C02-43B0-B1DA-1F438C914E56
+
+
+ REMOVED
+
+
+
+ MODE
+ 0
+
INSTALLATION_STEPS
@@ -646,7 +977,19 @@
LICENSE
LOCALIZATIONS
-
+
+
+ LANGUAGE
+ English
+ VALUE
+
+ PATH
+ /Users/vito/Programming/Projects/TerpstraSysEx.2014/Installers/Packages/Lumatone Editor/LICENSE.txt
+ PATH_TYPE
+ 1
+
+
+
MODE
0
@@ -655,6 +998,11 @@
LOCALIZATIONS
+ SUMMARY
+
+ LOCALIZATIONS
+
+
TITLE
LOCALIZATIONS
@@ -684,7 +1032,7 @@
BUILD_PATH
PATH
- build
+ ../../..
PATH_TYPE
1
@@ -859,7 +1207,9 @@
NAME
Lumatone Editor
PAYLOAD_ONLY
-
+
+ REFERENCE_FOLDER_PATH
+ /Users/soundtoys/Programming/Vito/TerpstraSysEx.2014
TREAT_MISSING_PRESENTATION_DOCUMENTS_AS_WARNING
diff --git a/Libraries/mac/include/gcrypt.h b/Libraries/mac/include/gcrypt.h
index 084bb286..50be8242 100644
--- a/Libraries/mac/include/gcrypt.h
+++ b/Libraries/mac/include/gcrypt.h
@@ -62,11 +62,11 @@ extern "C" {
return the same version. The purpose of this macro is to let
autoconf (using the AM_PATH_GCRYPT macro) check that this header
matches the installed library. */
-#define GCRYPT_VERSION "1.9.2"
+#define GCRYPT_VERSION "1.9.4-beta25"
/* The version number of this header. It may be used to handle minor
API incompatibilities. */
-#define GCRYPT_VERSION_NUMBER 0x010902
+#define GCRYPT_VERSION_NUMBER 0x010904
/* Internal: We can't use the convenience macros for the multi
@@ -1274,7 +1274,7 @@ enum gcry_md_algos
GCRY_MD_BLAKE2S_128 = 325,
GCRY_MD_SM3 = 326,
GCRY_MD_SHA512_256 = 327,
- GCRY_MD_SHA512_224 = 328,
+ GCRY_MD_SHA512_224 = 328
};
/* Flags used with the open function. */
diff --git a/Libraries/mac/include/gpg-error.h b/Libraries/mac/include/gpg-error.h
index 5643b87a..1750d58c 100644
--- a/Libraries/mac/include/gpg-error.h
+++ b/Libraries/mac/include/gpg-error.h
@@ -18,7 +18,7 @@
* SPDX-License-Identifier: LGPL-2.1+
*
* Do not edit. Generated from gpg-error.h.in for:
- x86_64-apple-darwin19.6.0
+ arm-apple-darwin21.4.0
*/
/* The GnuPG project consists of many components. Error codes are
@@ -66,12 +66,12 @@
#include
/* The version string of this header. */
-#define GPG_ERROR_VERSION "1.41"
-#define GPGRT_VERSION "1.41"
+#define GPG_ERROR_VERSION "1.43-beta11"
+#define GPGRT_VERSION "1.43-beta11"
/* The version number of this header. */
-#define GPG_ERROR_VERSION_NUMBER 0x012900
-#define GPGRT_VERSION_NUMBER 0x012900
+#define GPG_ERROR_VERSION_NUMBER 0x012b00
+#define GPGRT_VERSION_NUMBER 0x012b00
#ifdef __GNUC__
@@ -122,6 +122,7 @@ typedef enum
GPG_ERR_SOURCE_KLEO = 13,
GPG_ERR_SOURCE_G13 = 14,
GPG_ERR_SOURCE_ASSUAN = 15,
+ GPG_ERR_SOURCE_TPM2D = 16,
GPG_ERR_SOURCE_TLS = 17,
GPG_ERR_SOURCE_ANY = 31,
GPG_ERR_SOURCE_USER_1 = 32,
diff --git a/Libraries/mac/include/gpgrt.h b/Libraries/mac/include/gpgrt.h
index 5643b87a..1750d58c 100644
--- a/Libraries/mac/include/gpgrt.h
+++ b/Libraries/mac/include/gpgrt.h
@@ -18,7 +18,7 @@
* SPDX-License-Identifier: LGPL-2.1+
*
* Do not edit. Generated from gpg-error.h.in for:
- x86_64-apple-darwin19.6.0
+ arm-apple-darwin21.4.0
*/
/* The GnuPG project consists of many components. Error codes are
@@ -66,12 +66,12 @@
#include
/* The version string of this header. */
-#define GPG_ERROR_VERSION "1.41"
-#define GPGRT_VERSION "1.41"
+#define GPG_ERROR_VERSION "1.43-beta11"
+#define GPGRT_VERSION "1.43-beta11"
/* The version number of this header. */
-#define GPG_ERROR_VERSION_NUMBER 0x012900
-#define GPGRT_VERSION_NUMBER 0x012900
+#define GPG_ERROR_VERSION_NUMBER 0x012b00
+#define GPGRT_VERSION_NUMBER 0x012b00
#ifdef __GNUC__
@@ -122,6 +122,7 @@ typedef enum
GPG_ERR_SOURCE_KLEO = 13,
GPG_ERR_SOURCE_G13 = 14,
GPG_ERR_SOURCE_ASSUAN = 15,
+ GPG_ERR_SOURCE_TPM2D = 16,
GPG_ERR_SOURCE_TLS = 17,
GPG_ERR_SOURCE_ANY = 31,
GPG_ERR_SOURCE_USER_1 = 32,
diff --git a/Libraries/mac/include/libssh2.h b/Libraries/mac/include/libssh2.h
index d33df03c..d064b316 100644
--- a/Libraries/mac/include/libssh2.h
+++ b/Libraries/mac/include/libssh2.h
@@ -1,5 +1,5 @@
/* Copyright (c) 2004-2009, Sara Golemon
- * Copyright (c) 2009-2015 Daniel Stenberg
+ * Copyright (c) 2009-2021 Daniel Stenberg
* Copyright (c) 2010 Simon Josefsson
* All rights reserved.
*
@@ -40,19 +40,19 @@
#ifndef LIBSSH2_H
#define LIBSSH2_H 1
-#define LIBSSH2_COPYRIGHT "2004-2019 The libssh2 project and its contributors."
+#define LIBSSH2_COPYRIGHT "2004-2021 The libssh2 project and its contributors."
/* We use underscore instead of dash when appending DEV in dev versions just
to make the BANNER define (used by src/session.c) be a valid SSH
banner. Release versions have no appended strings and may of course not
have dashes either. */
-#define LIBSSH2_VERSION "1.9.0"
+#define LIBSSH2_VERSION "1.10.1_DEV"
/* The numeric version number is also available "in parts" by using these
defines: */
-#define LIBSSH2_VERSION_MAJOR 1
-#define LIBSSH2_VERSION_MINOR 9
-#define LIBSSH2_VERSION_PATCH 0
+#define LIBSSH2_VERSION_MAJOR 1
+#define LIBSSH2_VERSION_MINOR 10
+#define LIBSSH2_VERSION_PATCH 1
/* This is the numeric version of the libssh2 version number, meant for easier
parsing and comparions by programs. The LIBSSH2_VERSION_NUM define will
@@ -69,7 +69,7 @@
and it is always a greater number in a more recent release. It makes
comparisons with greater than and less than work.
*/
-#define LIBSSH2_VERSION_NUM 0x010900
+#define LIBSSH2_VERSION_NUM 0x010a01
/*
* This is the date and time when the full source package was created. The
@@ -80,7 +80,7 @@
*
* "Mon Feb 12 11:35:33 UTC 2007"
*/
-#define LIBSSH2_TIMESTAMP "Thu Jun 20 06:19:26 UTC 2019"
+#define LIBSSH2_TIMESTAMP "DEV"
#ifndef RC_INVOKED
@@ -100,7 +100,7 @@ extern "C" {
/* Allow alternate API prefix from CFLAGS or calling app */
#ifndef LIBSSH2_API
# ifdef LIBSSH2_WIN32
-# ifdef _WINDLL
+# if defined(_WINDLL) || defined(libssh2_EXPORTS)
# ifdef LIBSSH2_LIBRARY
# define LIBSSH2_API __declspec(dllexport)
# else
@@ -235,9 +235,11 @@ typedef off_t libssh2_struct_stat_size;
/* Default generate and safe prime sizes for
diffie-hellman-group-exchange-sha1 */
-#define LIBSSH2_DH_GEX_MINGROUP 1024
-#define LIBSSH2_DH_GEX_OPTGROUP 1536
-#define LIBSSH2_DH_GEX_MAXGROUP 2048
+#define LIBSSH2_DH_GEX_MINGROUP 2048
+#define LIBSSH2_DH_GEX_OPTGROUP 4096
+#define LIBSSH2_DH_GEX_MAXGROUP 8192
+
+#define LIBSSH2_DH_MAX_MODULUS_BITS 16384
/* Defaults for pty requests */
#define LIBSSH2_TERM_WIDTH 80
@@ -503,6 +505,7 @@ typedef struct _LIBSSH2_POLLFD {
#define LIBSSH2_ERROR_KNOWN_HOSTS -46
#define LIBSSH2_ERROR_CHANNEL_WINDOW_FULL -47
#define LIBSSH2_ERROR_KEYFILE_AUTH_FAILED -48
+#define LIBSSH2_ERROR_RANDGEN -49
/* this is a define to provide the old (<= 1.2.7) name */
#define LIBSSH2_ERROR_BANNER_NONE LIBSSH2_ERROR_BANNER_RECV
@@ -545,7 +548,7 @@ LIBSSH2_API void libssh2_free(LIBSSH2_SESSION *session, void *ptr);
*
* Fills algs with a list of supported acryptographic algorithms. Returns a
* non-negative number (number of supported algorithms) on success or a
- * negative number (an eror code) on failure.
+ * negative number (an error code) on failure.
*
* NOTE: on success, algs must be deallocated (by calling libssh2_free) when
* not needed anymore
@@ -688,7 +691,7 @@ libssh2_userauth_publickey_frommemory(LIBSSH2_SESSION *session,
* response_callback is provided with filled by library prompts array,
* but client must allocate and fill individual responses. Responses
* array is already allocated. Responses data will be freed by libssh2
- * after callback return, but before subsequent callback invokation.
+ * after callback return, but before subsequent callback invocation.
*/
LIBSSH2_API int
libssh2_userauth_keyboard_interactive_ex(LIBSSH2_SESSION* session,
@@ -718,7 +721,7 @@ LIBSSH2_API int libssh2_poll(LIBSSH2_POLLFD *fds, unsigned int nfds,
#define SSH_EXTENDED_DATA_STDERR 1
-/* Returned by any function that would block during a read/write opperation */
+/* Returned by any function that would block during a read/write operation */
#define LIBSSH2CHANNEL_EAGAIN LIBSSH2_ERROR_EAGAIN
LIBSSH2_API LIBSSH2_CHANNEL *
@@ -761,6 +764,8 @@ LIBSSH2_API int libssh2_channel_setenv_ex(LIBSSH2_CHANNEL *channel,
(unsigned int)strlen(varname), (value), \
(unsigned int)strlen(value))
+LIBSSH2_API int libssh2_channel_request_auth_agent(LIBSSH2_CHANNEL *channel);
+
LIBSSH2_API int libssh2_channel_request_pty_ex(LIBSSH2_CHANNEL *channel,
const char *term,
unsigned int term_len,
@@ -987,7 +992,7 @@ libssh2_knownhost_init(LIBSSH2_SESSION *session);
#define LIBSSH2_KNOWNHOST_KEYENC_RAW (1<<16)
#define LIBSSH2_KNOWNHOST_KEYENC_BASE64 (2<<16)
-/* type of key (3 bits) */
+/* type of key (4 bits) */
#define LIBSSH2_KNOWNHOST_KEY_MASK (15<<18)
#define LIBSSH2_KNOWNHOST_KEY_SHIFT 18
#define LIBSSH2_KNOWNHOST_KEY_RSA1 (1<<18)
@@ -1165,7 +1170,7 @@ libssh2_knownhost_writefile(LIBSSH2_KNOWNHOSTS *hosts,
* libssh2_knownhost_get()
*
* Traverse the internal list of known hosts. Pass NULL to 'prev' to get
- * the first one. Or pass a poiner to the previously returned one to get the
+ * the first one. Or pass a pointer to the previously returned one to get the
* next.
*
* Returns:
@@ -1221,7 +1226,7 @@ libssh2_agent_list_identities(LIBSSH2_AGENT *agent);
* libssh2_agent_get_identity()
*
* Traverse the internal list of public keys. Pass NULL to 'prev' to get
- * the first one. Or pass a poiner to the previously returned one to get the
+ * the first one. Or pass a pointer to the previously returned one to get the
* next.
*
* Returns:
diff --git a/Libraries/mac/include/libssh2_sftp.h b/Libraries/mac/include/libssh2_sftp.h
index 4a750b3e..476ea870 100644
--- a/Libraries/mac/include/libssh2_sftp.h
+++ b/Libraries/mac/include/libssh2_sftp.h
@@ -189,32 +189,32 @@ struct _LIBSSH2_SFTP_STATVFS {
#define LIBSSH2_FXF_EXCL 0x00000020
/* SFTP Status Codes (returned by libssh2_sftp_last_error() ) */
-#define LIBSSH2_FX_OK 0
-#define LIBSSH2_FX_EOF 1
-#define LIBSSH2_FX_NO_SUCH_FILE 2
-#define LIBSSH2_FX_PERMISSION_DENIED 3
-#define LIBSSH2_FX_FAILURE 4
-#define LIBSSH2_FX_BAD_MESSAGE 5
-#define LIBSSH2_FX_NO_CONNECTION 6
-#define LIBSSH2_FX_CONNECTION_LOST 7
-#define LIBSSH2_FX_OP_UNSUPPORTED 8
-#define LIBSSH2_FX_INVALID_HANDLE 9
-#define LIBSSH2_FX_NO_SUCH_PATH 10
-#define LIBSSH2_FX_FILE_ALREADY_EXISTS 11
-#define LIBSSH2_FX_WRITE_PROTECT 12
-#define LIBSSH2_FX_NO_MEDIA 13
-#define LIBSSH2_FX_NO_SPACE_ON_FILESYSTEM 14
-#define LIBSSH2_FX_QUOTA_EXCEEDED 15
-#define LIBSSH2_FX_UNKNOWN_PRINCIPLE 16 /* Initial mis-spelling */
-#define LIBSSH2_FX_UNKNOWN_PRINCIPAL 16
-#define LIBSSH2_FX_LOCK_CONFlICT 17 /* Initial mis-spelling */
-#define LIBSSH2_FX_LOCK_CONFLICT 17
-#define LIBSSH2_FX_DIR_NOT_EMPTY 18
-#define LIBSSH2_FX_NOT_A_DIRECTORY 19
-#define LIBSSH2_FX_INVALID_FILENAME 20
-#define LIBSSH2_FX_LINK_LOOP 21
-
-/* Returned by any function that would block during a read/write opperation */
+#define LIBSSH2_FX_OK 0UL
+#define LIBSSH2_FX_EOF 1UL
+#define LIBSSH2_FX_NO_SUCH_FILE 2UL
+#define LIBSSH2_FX_PERMISSION_DENIED 3UL
+#define LIBSSH2_FX_FAILURE 4UL
+#define LIBSSH2_FX_BAD_MESSAGE 5UL
+#define LIBSSH2_FX_NO_CONNECTION 6UL
+#define LIBSSH2_FX_CONNECTION_LOST 7UL
+#define LIBSSH2_FX_OP_UNSUPPORTED 8UL
+#define LIBSSH2_FX_INVALID_HANDLE 9UL
+#define LIBSSH2_FX_NO_SUCH_PATH 10UL
+#define LIBSSH2_FX_FILE_ALREADY_EXISTS 11UL
+#define LIBSSH2_FX_WRITE_PROTECT 12UL
+#define LIBSSH2_FX_NO_MEDIA 13UL
+#define LIBSSH2_FX_NO_SPACE_ON_FILESYSTEM 14UL
+#define LIBSSH2_FX_QUOTA_EXCEEDED 15UL
+#define LIBSSH2_FX_UNKNOWN_PRINCIPLE 16UL /* Initial mis-spelling */
+#define LIBSSH2_FX_UNKNOWN_PRINCIPAL 16UL
+#define LIBSSH2_FX_LOCK_CONFlICT 17UL /* Initial mis-spelling */
+#define LIBSSH2_FX_LOCK_CONFLICT 17UL
+#define LIBSSH2_FX_DIR_NOT_EMPTY 18UL
+#define LIBSSH2_FX_NOT_A_DIRECTORY 19UL
+#define LIBSSH2_FX_INVALID_FILENAME 20UL
+#define LIBSSH2_FX_LINK_LOOP 21UL
+
+/* Returned by any function that would block during a read/write operation */
#define LIBSSH2SFTP_EAGAIN LIBSSH2_ERROR_EAGAIN
/* SFTP API */
diff --git a/Libraries/mac/lib/libgcrypt.20.dylib b/Libraries/mac/lib/libgcrypt.20.dylib
old mode 100644
new mode 100755
index a332c6d2..9e574876
Binary files a/Libraries/mac/lib/libgcrypt.20.dylib and b/Libraries/mac/lib/libgcrypt.20.dylib differ
diff --git a/Libraries/mac/lib/libgcrypt.dylib b/Libraries/mac/lib/libgcrypt.dylib
new file mode 120000
index 00000000..5984cb08
--- /dev/null
+++ b/Libraries/mac/lib/libgcrypt.dylib
@@ -0,0 +1 @@
+libgcrypt.20.dylib
\ No newline at end of file
diff --git a/Libraries/mac/lib/libgpg-error.0.dylib b/Libraries/mac/lib/libgpg-error.0.dylib
old mode 100644
new mode 100755
index 9afab543..771b7136
Binary files a/Libraries/mac/lib/libgpg-error.0.dylib and b/Libraries/mac/lib/libgpg-error.0.dylib differ
diff --git a/Libraries/mac/lib/libgpg-error.dylib b/Libraries/mac/lib/libgpg-error.dylib
new file mode 120000
index 00000000..36cbccca
--- /dev/null
+++ b/Libraries/mac/lib/libgpg-error.dylib
@@ -0,0 +1 @@
+libgpg-error.0.dylib
\ No newline at end of file
diff --git a/Libraries/mac/lib/libssh2.1.dylib b/Libraries/mac/lib/libssh2.1.dylib
index 581001fc..9b7bf2da 100755
Binary files a/Libraries/mac/lib/libssh2.1.dylib and b/Libraries/mac/lib/libssh2.1.dylib differ
diff --git a/Libraries/mac/lib/libssh2.dylib b/Libraries/mac/lib/libssh2.dylib
new file mode 120000
index 00000000..235229a5
--- /dev/null
+++ b/Libraries/mac/lib/libssh2.dylib
@@ -0,0 +1 @@
+libssh2.1.dylib
\ No newline at end of file
diff --git a/Scripts/install.sh b/Scripts/install.sh
new file mode 100644
index 00000000..96e5001e
--- /dev/null
+++ b/Scripts/install.sh
@@ -0,0 +1,50 @@
+#!/bin/bash
+
+USAGE="Please run in the same directory as the Presets folder without sudo."
+
+if [[ $EUID == 0 ]]; then
+ echo You are running this script as root.
+ echo $USAGE
+ exit 1
+fi
+
+if [[ ! -d "./Presets" ]]; then
+ echo "No ./Presets folder found."
+ echo $USAGE
+ exit 1
+fi
+
+PRESETS=`pwd`/Presets
+
+cd ~/Documents
+
+if [[ ! -d "Lumatone Editor" ]]; then
+ echo 'Creating "~/Documents/Lumatone Editor/"'
+ mkdir "Lumatone Editor" && chmod 777 "Lumatone Editor" && FOLDERCREATED=1
+ if [[ ! $FOLDERCREATED ]]; then
+ echo "Error creating '~/Documents/Lumatone Editor', try creating it manually and run this again."
+ exit 1
+ fi
+fi
+
+echo "Copying preset files..."
+cp -n -r ${PRESETS}/* "Lumatone Editor" && FILESCOPIED=1
+if [[ ! $FILESCOPIED ]]; then
+ echo 'Sorry, there was a problem copying files. Just move the "Mappings" & "Palettes" folders from ./Presets into "~/Documents/Lumatone Editor/" to manually install.'
+ exit 1
+fi
+
+chmod -R 777 "Lumatone Editor"
+
+echo "Preset installation successful! Feel free to delete the local Presets folder."
+echo
+echo "Before running Lumatone Editor, you must install the dependency \"libssh2\"."
+echo
+echo "For debian-based distros use:"
+echo " sudo apt install libssh2"
+echo
+echo "For arch-based distros use:"
+echo " sudo pacman -S libssh2"
+echo
+echo "Or alternatively whatever equivalent command for the package manager you use."
+exit 0
diff --git a/Scripts/package-linux-release.sh b/Scripts/package-linux-release.sh
new file mode 100644
index 00000000..8f4c88c8
--- /dev/null
+++ b/Scripts/package-linux-release.sh
@@ -0,0 +1,57 @@
+#!/bin/bash
+
+USAGE="usage: package-linux-release [ version_id ] ?tidy"
+
+LTE_BIN=`pwd`/../Builds/Linux/build/Lumatone\ Editor
+
+if [[ ! $LTE_BIN ]]; then
+ echo $USAGE
+ echo No binary found.
+ exit 1
+fi
+
+[[ $1 ]] && VERSION=$1
+[[ ! $VERSION ]] && echo "No version specified" && exit 1
+echo "preparing zip for version ${VERSION}"
+
+[[ -d `pwd`/../Releases/ ]] || mkdir `pwd`/../Releases
+
+PARENT_DIR=`pwd`/../Releases/Linux
+TARGET=$PARENT_DIR/$VERSION
+
+([[ -d $PARENT_DIR ]] && rm -r $TARGET/) || mkdir $PARENT_DIR
+mkdir $TARGET
+
+echo copying files... && \
+ cp "${LTE_BIN}" $TARGET/ && \
+ cp ./install.sh $TARGET/ && \
+ cp ../LICENSE $TARGET/ && \
+ cp -r ../Presets $TARGET/ && \
+ # todo install deps
+ # todo readme
+ COPIED=1
+
+[[ ! $COPIED ]] && exit 1
+
+cd ${TARGET}
+
+echo "Run the install.sh script to install presets, or move the Presets child folders to ~/Documents/Lumatone Editor/" > INSTALL
+echo "You will also need to install the libssh2 dependency." >> INSTALL
+echo "Then simply launch 'Lumatone Editor' to get started!" >> INSTALL
+
+ZIP_OUT="LumatoneEditor-${VERSION}-Linux.zip"
+
+zip -r $ZIP_OUT ./* && ZIPPED=1
+if [[ $ZIPPED ]]; then
+ echo wrote $ZIP_OUT
+else
+ echo "Error zipping files for ${ZIP_OUT}"
+ exit 1
+fi
+
+if [[ $2 == "tidy" ]]; then
+ echo cleaning residuals...
+ find ${TARGET}/* \( ! -name "*.zip" \) -delete
+elif [[ $2 ]]; then
+ echo unknown argument \"${2}\"
+fi
diff --git a/Scripts/pkg-install-presets.sh b/Scripts/pkg-install-presets.sh
new file mode 100644
index 00000000..03d5213d
--- /dev/null
+++ b/Scripts/pkg-install-presets.sh
@@ -0,0 +1,13 @@
+#!/bin/bash
+
+PRESETS=`pwd`/Presets
+
+cd ~/Documents
+
+[[ -d "Lumatone Editor" ]] || mkdir "Lumatone Editor" && chmod 777 "Lumatone Editor"
+
+cp -n -r ${PRESETS}/* "Lumatone Editor"
+
+chmod -R 777 "Lumatone Editor"
+
+exit 0
diff --git a/Source/AllKeysOverview.cpp b/Source/AllKeysOverview.cpp
index 3b6cc172..0e418bf4 100644
--- a/Source/AllKeysOverview.cpp
+++ b/Source/AllKeysOverview.cpp
@@ -40,12 +40,12 @@ KeyMiniDisplayInsideAllKeysOverview::KeyMiniDisplayInsideAllKeysOverview(int new
boardIndex = newBoardIndex;
keyIndex = newKeyIndex;
- TerpstraSysExApplication::getApp().getLumatoneController().addMidiListener(this);
+// TerpstraSysExApplication::getApp().getLumatoneController()->addMidiListener(this);
}
KeyMiniDisplayInsideAllKeysOverview::~KeyMiniDisplayInsideAllKeysOverview()
{
- TerpstraSysExApplication::getApp().getLumatoneController().removeMidiListener(this);
+// TerpstraSysExApplication::getApp().getLumatoneController()->removeMidiListener(this);
}
void KeyMiniDisplayInsideAllKeysOverview::paint(Graphics& g)
@@ -61,8 +61,8 @@ void KeyMiniDisplayInsideAllKeysOverview::paint(Graphics& g)
if (colourGraphic && shadowGraphic)
{
- int x = round((getWidth() - colourGraphic->getWidth()) * 0.5f);
- int y = round((getHeight() - colourGraphic->getHeight()) * 0.5f);
+ int x = roundToInt((getWidth() - colourGraphic->getWidth()) * 0.5f);
+ int y = roundToInt((getHeight() - colourGraphic->getHeight()) * 0.5f);
g.drawImageAt(*colourGraphic, x, y, true);
g.drawImageAt(*shadowGraphic, x, y);
@@ -92,7 +92,7 @@ void KeyMiniDisplayInsideAllKeysOverview::mouseDown(const MouseEvent& e)
// Right mouse click: popup menu
PopupMenu menu;
TerpstraSysExApplication::getApp().getMainMenu()->createEditMenu(menu);
- menu.show();
+ menu.showMenuAsync(PopupMenu::Options());
}
// TODO integrate interaction through LumatoneController
@@ -175,43 +175,49 @@ void KeyMiniDisplayInsideAllKeysOverview::setKeyGraphics(Image& colourGraphicIn,
//[/MiscUserDefs]
//==============================================================================
-AllKeysOverview::AllKeysOverview()
- : Component("AllKeysOverview")
+AllKeysOverview::AllKeysOverview ()
{
- //[Constructor_pre] You can add your own custom stuff here..
- //[/Constructor_pre]
+ //[Constructor_pre] You can add your own custom stuff here..
+ //[/Constructor_pre]
- btnLoadFile.reset(new juce::TextButton("btnLoadFile"));
- addAndMakeVisible(btnLoadFile.get());
+ setName ("AllKeysOverview");
+ btnLoadFile.reset (new juce::TextButton ("btnLoadFile"));
+ addAndMakeVisible (btnLoadFile.get());
+ btnLoadFile->setButtonText (TRANS("LoadFile"));
+ btnLoadFile->addListener (this);
- btnLoadFile->setButtonText(translate("LoadFile"));
- btnLoadFile->getProperties().set(LumatoneEditorStyleIDs::textButtonIconHashCode, LumatoneEditorIcon::LoadIcon);
- btnLoadFile->addListener(this);
+ btnLoadFile->setBounds (368, 8, 96, 24);
- btnSaveFile.reset(new juce::TextButton("btnSaveFile"));
- addAndMakeVisible(btnSaveFile.get());
- btnSaveFile->setButtonText(translate("SaveFile"));
- btnSaveFile->getProperties().set(LumatoneEditorStyleIDs::textButtonIconHashCode, LumatoneEditorIcon::SaveIcon);
- btnSaveFile->addListener(this);
+ btnSaveFile.reset (new juce::TextButton ("btnSaveFile"));
+ addAndMakeVisible (btnSaveFile.get());
+ btnSaveFile->setButtonText (TRANS("SaveFile"));
+ btnSaveFile->addListener (this);
- buttonReceive.reset(new juce::TextButton("buttonReceive"));
- addAndMakeVisible(buttonReceive.get());
- buttonReceive->setTooltip(translate("ImportTooltip"));
- buttonReceive->setButtonText(translate("Import from Lumatone"));
- buttonReceive->getProperties().set(LumatoneEditorStyleIDs::textButtonIconHashCode, LumatoneEditorIcon::ArrowUp);
- buttonReceive->getProperties().set(LumatoneEditorStyleIDs::textButtonIconPlacement, LumatoneEditorStyleIDs::TextButtonIconPlacement::RightOfText);
- buttonReceive->addListener(this);
+ btnSaveFile->setBounds (472, 8, 96, 24);
- tilingGeometry.setColumnAngle(LUMATONEGRAPHICCOLUMNANGLE);
- tilingGeometry.setRowAngle(LUMATONEGRAPHICROWANGLE);
+ buttonReceive.reset (new juce::TextButton ("buttonReceive"));
+ addAndMakeVisible (buttonReceive.get());
+ buttonReceive->setTooltip (TRANS("ImportTooltip"));
+ buttonReceive->setButtonText (TRANS("Import from Lumatone"));
+ buttonReceive->addListener (this);
+
+ buttonReceive->setBounds (584, 8, 176, 24);
//[UserPreSize]
+ btnLoadFile->getProperties().set(LumatoneEditorStyleIDs::textButtonIconHashCode, LumatoneEditorIcon::LoadIcon);
+ btnSaveFile->getProperties().set(LumatoneEditorStyleIDs::textButtonIconHashCode, LumatoneEditorIcon::SaveIcon);
+ buttonReceive->getProperties().set(LumatoneEditorStyleIDs::textButtonIconHashCode, LumatoneEditorIcon::ArrowUp);
+ buttonReceive->getProperties().set(LumatoneEditorStyleIDs::textButtonIconPlacement, LumatoneEditorStyleIDs::TextButtonIconPlacement::RightOfText);
lblFirmwareVersion.reset(new Label("FirmwareVersionLabel"));
addChildComponent(lblFirmwareVersion.get());
- TerpstraSysExApplication::getApp().getLumatoneController().addFirmwareListener(this);
+ tilingGeometry.setColumnAngle(LUMATONEGRAPHICCOLUMNANGLE);
+ tilingGeometry.setRowAngle(LUMATONEGRAPHICROWANGLE);
+
+ TerpstraSysExApplication::getApp().getLumatoneController()->addStatusListener(this);
+ TerpstraSysExApplication::getApp().getLumatoneController()->addFirmwareListener(this);
resetOctaveSize();
@@ -222,7 +228,7 @@ AllKeysOverview::AllKeysOverview()
//[Constructor] You can add your own custom stuff here..
currentSetSelection = -1;
-
+ buttonReceive->setVisible(false);
showDeveloperMode(TerpstraSysExApplication::getApp().getPropertiesFile()->getBoolValue("DeveloperMode", false));
//[/Constructor]
}
@@ -283,27 +289,27 @@ void AllKeysOverview::resized()
graphicWidth, graphicHeight
);
- int btnHeight = round(getHeight() * saveLoadH);
- int btnMargin = round(getWidth() * saveloadMarginW);
- int saveLoadWidth = round(getWidth() * saveLoadW);
- int btnY = lumatoneBounds.getY() - round(getHeight() * btnYFromImageTop);
+ int btnHeight = roundToInt(getHeight() * saveLoadH);
+ int btnMargin = roundToInt(getWidth() * saveloadMarginW);
+ int saveLoadWidth = roundToInt(getWidth() * saveLoadW);
+ int btnY = lumatoneBounds.getY() - roundToInt(getHeight() * btnYFromImageTop);
- int halfWidthX = round(getWidth() * 0.5f);
+ int halfWidthX = roundToInt(getWidth() * 0.5f);
btnLoadFile->setBounds(halfWidthX - btnMargin - saveLoadWidth, btnY, saveLoadWidth, btnHeight);
btnSaveFile->setBounds(halfWidthX + btnMargin, btnY, saveLoadWidth, btnHeight);
- octaveLineY = lumatoneBounds.getBottom() + round(getHeight() * octaveLineYRatio);
+ octaveLineY = lumatoneBounds.getBottom() + roundToInt(getHeight() * octaveLineYRatio);
- int importY = lumatoneBounds.getY() - round(getHeight() * importYFromImageTop);
- int importWidth = round(getWidth() * importW);
+ int importY = lumatoneBounds.getY() - roundToInt(getHeight() * importYFromImageTop);
+ int importWidth = roundToInt(getWidth() * importW);
buttonReceive->setBounds(lumatoneBounds.getRight() - importWidth, importY, importWidth, btnHeight);
resizeLabelWithHeight(lblFirmwareVersion.get(), btnHeight * 0.6f);
lblFirmwareVersion->setTopLeftPosition(lumatoneBounds.getX(), lumatoneBounds.getY() - btnHeight * 0.6f);
- int keyWidth = round(lumatoneBounds.getWidth() * keyW);
- int keyHeight = round(lumatoneBounds.getHeight() * keyH);
+ int keyWidth = roundToInt(lumatoneBounds.getWidth() * keyW);
+ int keyHeight = roundToInt(lumatoneBounds.getHeight() * keyH);
// Scale key graphics once
lumatoneGraphic = imageProcessor->resizeImage(ImageCache::getFromHashCode(LumatoneEditorAssets::LumatoneGraphic), lumatoneBounds.getWidth(), lumatoneBounds.getHeight());
@@ -388,12 +394,16 @@ void AllKeysOverview::setFirmwareVersion(FirmwareVersion versionIn)
{
if (versionIn.revision == 55)
{
- lblFirmwareVersion->setText("Prototype 55-keys", NotificationType::dontSendNotification);
+ lblFirmwareVersion->setText("55-keys Prototype", NotificationType::dontSendNotification);
}
}
else
{
+#if JUCE_DEBUG
lblFirmwareVersion->setText("Firmware version: " + versionIn.toString(), NotificationType::dontSendNotification);
+#else
+ lblFirmwareVersion->setText("Firmware version: " + versionIn.toDisplayString(), NotificationType::dontSendNotification);
+#endif
}
lblFirmwareVersion->setVisible(true);
@@ -409,9 +419,22 @@ void AllKeysOverview::setFirmwareVersion(FirmwareVersion versionIn)
void AllKeysOverview::showDeveloperMode(bool developerModeOn)
{
+ if (developerModeOn)
+ buttonReceive->setVisible(true);
+
repaint();
}
+void AllKeysOverview::connectionEstablished(int, int)
+{
+ buttonReceive->setVisible(true);
+}
+
+void AllKeysOverview::connectionLost()
+{
+ buttonReceive->setVisible(false);
+}
+
void AllKeysOverview::firmwareRevisionReceived(FirmwareVersion version)
{
setFirmwareVersion(version);
@@ -456,8 +479,8 @@ void AllKeysOverview::resetOctaveSize()
BEGIN_JUCER_METADATA
-
@@ -469,7 +492,7 @@ BEGIN_JUCER_METADATA
virtualName="" explicitFocusOrder="0" pos="472 8 96 24" buttonText="Save File"
connectedEdges="0" needsCallback="1" radioGroupId="0"/>
diff --git a/Source/AllKeysOverview.h b/Source/AllKeysOverview.h
index 31ee5975..ed37f17e 100644
--- a/Source/AllKeysOverview.h
+++ b/Source/AllKeysOverview.h
@@ -31,7 +31,7 @@
// Representation of a key inside the overview
-class KeyMiniDisplayInsideAllKeysOverview : public Component, public LumatoneController::MidiListener
+class KeyMiniDisplayInsideAllKeysOverview : public Component, public LumatoneEditor::MidiListener
{
public:
KeyMiniDisplayInsideAllKeysOverview(int newBoardIndex, int newKeyIndex);
@@ -82,7 +82,8 @@ class KeyMiniDisplayInsideAllKeysOverview : public Component, public LumatoneCon
//[/Comments]
*/
class AllKeysOverview : public juce::Component,
- public LumatoneController::FirmwareListener,
+ public LumatoneEditor::StatusListener,
+ public LumatoneEditor::FirmwareListener,
public juce::Button::Listener
{
public:
@@ -101,6 +102,12 @@ class AllKeysOverview : public juce::Component,
void setFirmwareVersion(FirmwareVersion versionIn);
void resetOctaveSize();
+
+ // LumatoneEditor::StatusListener
+ void connectionEstablished(int, int) override;
+ void connectionLost() override;
+
+ // LumatoneEditor::FirmwareListener implementation
void firmwareRevisionReceived(FirmwareVersion version) override;
//[/UserMethods]
diff --git a/Source/ApplicationListeners.h b/Source/ApplicationListeners.h
new file mode 100644
index 00000000..d6d8ddca
--- /dev/null
+++ b/Source/ApplicationListeners.h
@@ -0,0 +1,112 @@
+/*
+ ==============================================================================
+
+ ApplicationListeners.h
+ Created: 22 Mar 2022 10:11:10pm
+ Author: Vincenzo Sicurella
+
+ Various listener and enums that should be shared throughout the app
+
+ ==============================================================================
+*/
+
+#pragma once
+#include
+#include "LumatoneFirmwareDefinitions.h"
+
+enum sysExSendingMode
+{
+ liveEditor = 0,
+ offlineEditor = 1,
+ firmwareUpdate = 2
+};
+
+namespace LumatoneEditor
+{
+ //============================================================================
+ // Public interface for Lumatone connection status
+
+ class StatusListener
+ {
+ public:
+
+ virtual ~StatusListener() {}
+
+ virtual void connectionFailed() {}
+ virtual void connectionEstablished(int inputMidiDevice, int outputMidiDevice) {}
+ virtual void connectionLost() {}
+ };
+
+ //============================================================================
+ // Public interface for Lumatone firmware communication
+ class FirmwareListener
+ {
+ public:
+
+ virtual ~FirmwareListener() {}
+
+ // rgbFlag uses 0 for red, 1 for green, 2 for blue
+ virtual void octaveColourConfigReceived(int octaveIndex, uint8 rgbFlag, const int* colourData) {};
+
+ virtual void octaveChannelConfigReceived(int octaveIndex, const int* channelData) {};
+
+ virtual void octaveNoteConfigReceived(int octaveIndex, const int* noteData) {};
+
+ virtual void keyTypeConfigReceived(int octaveIndex, const int* keyTypeData) {};
+
+ virtual void velocityConfigReceived(const int* velocityData) {};
+
+ virtual void aftertouchConfigReceived(const int* aftertouch) {};
+
+ virtual void velocityIntervalConfigReceived(const int* velocityData) {};
+
+ virtual void faderConfigReceived(const int* faderData) {};
+
+ virtual void faderTypeConfigReceived(int octaveIndex, const int* faderTypeData) {};
+
+ virtual void serialIdentityReceived(int inputDeviceIndex, const int* serialBytes) {};
+
+ virtual void calibratePitchModWheelAnswer(TerpstraMIDIAnswerReturnCode code) {};
+
+ virtual void lumatouchConfigReceived(const int* lumatouchData) {};
+
+ virtual void firmwareRevisionReceived(FirmwareVersion version) {};
+
+ virtual void pingResponseReceived(int inputDeviceIndex, unsigned int pingValue) {};
+
+ virtual void peripheralMidiChannelsReceived(PeripheralChannelSettings channelSettings) {};
+
+ virtual void pedalCalibrationDataReceived(int minBound, int maxBound, bool pedalIsActive) {};
+
+ virtual void wheelsCalibrationDataReceived(WheelsCalibrationData calibrationData) {};
+
+ virtual void presetFlagsReceived(PresetFlags presetFlags) {};
+
+ virtual void expressionPedalSensitivityReceived(int sensitivity) {};
+
+ virtual void noAnswerToCommand(int cmd) {};
+ };
+
+
+ class EditorListener
+ {
+ public:
+
+ virtual ~EditorListener() {}
+
+ // TODO - change this to "status" and use "disconnected", "offline", "live", "firmware"
+ virtual void editorModeChanged(sysExSendingMode newEditorMode) {}
+ // virtual void keyFunctionConfigurationChanged(int octaveNumber, int keyNumber, int noteOrCC, int midiChannel, LumatoneKeyType keyType, bool faderUpIsNull) {};
+ // virtual void keyColourConfigurationChanged(int octaveNumber, int keyNumber, Colour keyColour) {};
+ };
+
+
+ class MidiListener
+ {
+ public:
+
+ virtual ~MidiListener() {}
+
+ virtual void handleMidiMessage(const MidiMessage& msg) = 0;
+ };
+}
diff --git a/Source/BoardGeometry.cpp b/Source/BoardGeometry.cpp
index c66791f9..43c5c034 100644
--- a/Source/BoardGeometry.cpp
+++ b/Source/BoardGeometry.cpp
@@ -47,6 +47,9 @@ TerpstraBoardGeometry::TerpstraBoardGeometry()
this->rightUpwardLines.add(StraightLine({ 54, 53 }));
this->firstColumnOffsets = Array({ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 5 });
+ this->rowOffsets = Array({-4, -3, -3, -2, -2, -1, -1, 0, 0, 2, 6});
+ this->boardXOffset = 7;
+ this->boardYOffset = -2;
}
else
{
@@ -77,8 +80,11 @@ TerpstraBoardGeometry::TerpstraBoardGeometry()
this->rightUpwardLines.add(StraightLine({ 55, 53 }));
this->firstColumnOffsets = Array({ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 4 });
+ this->rowOffsets = Array({-4, -3, -3, -2, -2, -1, -1, 0, 0, 2, 5});
+ this->boardXOffset = 7;
+ this->boardYOffset = -2;
}
-
+
maxHorizontalLineSize = 0;
for (auto line : horizontalLines)
@@ -87,6 +93,21 @@ TerpstraBoardGeometry::TerpstraBoardGeometry()
}
+// Given a board number and key index, compute Cartesian coordinates for the given key,
+// the x axis is the shallow diagonal, and the y axis is the right-upward diagonal.
+// (0,0) is at the top left, so most coordinates are negative, but that's okay for our purposes.
+Point TerpstraBoardGeometry::coordinatesForKey (int boardIndex, int keyIndex) const {
+ for (int lineIx = 0; lineIx < this->horizontalLines.size(); lineIx++) {
+ int rowIx = this->horizontalLines[lineIx].indexOf(keyIndex);
+ if (rowIx != -1) {
+ return (Point(this->boardXOffset * boardIndex + rowIx + this->rowOffsets[lineIx], this->boardYOffset * boardIndex - lineIx));
+ }
+ }
+ // If we get here, the requested key was out of range.
+ jassert(false);
+ return Point(0,0);
+}
+
// returns the unique straight line that contains the given field
TerpstraBoardGeometry::StraightLine TerpstraBoardGeometry::getLineOfField(int fieldIndex, StraightLineSet lineSet) const
{
@@ -259,4 +280,4 @@ Array> TerpstraBoardGeometry::getOctaveCoordinates(int boardIndex) co
}
return pointsOut;
-}
\ No newline at end of file
+}
diff --git a/Source/BoardGeometry.h b/Source/BoardGeometry.h
index 3e7aa071..36b1520d 100644
--- a/Source/BoardGeometry.h
+++ b/Source/BoardGeometry.h
@@ -51,12 +51,17 @@ class TerpstraBoardGeometry
Array> getOctaveCoordinates(int boardIndex) const;
+ Point coordinatesForKey (int boardIndex, int keyIndex) const;
+
// Attributes
private:
StraightLineSet horizontalLines;
StraightLineSet rightUpwardLines;
- Array firstColumnOffsets;
- int maxHorizontalLineSize;
+ Array firstColumnOffsets;
+ Array rowOffsets; // Horizonal offset of each horizontal line on the board, relative to the lower left key.
+ int boardXOffset; // Offset along shallow diagonals between analogous keys from one board to the next (typically 7)
+ int boardYOffset; // Offset along up/right diagonals between analogous keys from one board to the next (typically -2)
+ int maxHorizontalLineSize;
};
#endif // BOARDGEOMETRY_H_INCLUDED
diff --git a/Source/ColourPaletteComponent.cpp b/Source/ColourPaletteComponent.cpp
index da1e752c..00b8fc10 100644
--- a/Source/ColourPaletteComponent.cpp
+++ b/Source/ColourPaletteComponent.cpp
@@ -103,11 +103,21 @@ void ColourPaletteComponent::deselectColour()
PaletteControlGroup::PaletteControlGroup(LumatoneEditorColourPalette newPaletteIn)
: palette(ColourPaletteComponent(newPaletteIn)),
- editButton("EditButton_" + newPaletteIn.getName(), translate("EditButtonTip")),
+ editButton("EditButton_" + newPaletteIn.getName()),
+ cloneButton("CloneButton_" + newPaletteIn.getName()),
trashButton("TrashButton_" + newPaletteIn.getName())
{
editButton.setButtonText("Edit");
editButton.getProperties().set(LumatoneEditorStyleIDs::textButtonHyperlinkFlag, 1);
+ editButton.setTooltip(translate("EditButtonTip"));
+
+ const Image cloneIcon = getCachedCloneImage();
+ cloneButton.setImages(false, true, true,
+ cloneIcon, 1.0f, Colour(),
+ cloneIcon, 1.0f, Colours::white.withAlpha(0.4f),
+ cloneIcon, 1.0f, Colour()
+ );
+ cloneButton.setTooltip(translate("CloneButtonTip"));
const Image trashIcon = ImageCache::getFromHashCode(LumatoneEditorAssets::TrashCanIcon);
trashButton.setImages(false, true, true,
@@ -115,4 +125,5 @@ PaletteControlGroup::PaletteControlGroup(LumatoneEditorColourPalette newPaletteI
trashIcon, 1.0f, Colours::white.withAlpha(0.4f),
trashIcon, 1.0f, Colour()
);
+ trashButton.setTooltip(translate("TrashButtonTip"));
}
diff --git a/Source/ColourPaletteComponent.h b/Source/ColourPaletteComponent.h
index 298afe90..49463f72 100644
--- a/Source/ColourPaletteComponent.h
+++ b/Source/ColourPaletteComponent.h
@@ -64,11 +64,14 @@ class PaletteControlGroup
TextButton* getEditButton() { return &editButton; }
+ ImageButton* getCloneButton() { return &cloneButton; }
+
ImageButton* getTrashButton() { return &trashButton; }
private:
ColourPaletteComponent palette;
TextButton editButton;
+ ImageButton cloneButton;
ImageButton trashButton;
-};
\ No newline at end of file
+};
diff --git a/Source/ColourPaletteDataStructure.h b/Source/ColourPaletteDataStructure.h
index 528eb432..c72a04c5 100644
--- a/Source/ColourPaletteDataStructure.h
+++ b/Source/ColourPaletteDataStructure.h
@@ -51,12 +51,11 @@ class LumatoneEditorColourPalette
LumatoneEditorColourPalette(Array colourPaletteIn, String paletteName, String paletteAuthor, String paletteNotes="")
: LumatoneEditorColourPalette(colourPaletteIn, paletteName)
{
- name = paletteName;
author = paletteAuthor;
notes = paletteNotes;
-
- setColours(colourPaletteIn);
}
+
+ LumatoneEditorColourPalette clone() const { return LumatoneEditorColourPalette(colourPalette, name, author, notes); }
int size() const { return colourPalette.size(); }
@@ -127,6 +126,10 @@ class LumatoneEditorColourPalette
return false;
}
+ bool saveToFile()
+ {
+ return saveToFile(File(pathToFile));
+ }
ValueTree toValueTree() const
{
diff --git a/Source/ColourPaletteWindow.cpp b/Source/ColourPaletteWindow.cpp
index a7304dc8..53dd9e69 100644
--- a/Source/ColourPaletteWindow.cpp
+++ b/Source/ColourPaletteWindow.cpp
@@ -15,13 +15,16 @@
// ColourPaletteWindow Definitions
ColourPaletteWindow::ColourPaletteWindow(Array& colourPalettesIn)
- : colourPalettes(colourPalettesIn)
+ : lookAndFeel(TerpstraSysExApplication::getApp().getLookAndFeel()),
+ colourPalettes(colourPalettesIn)
{
+ setLookAndFeel(&lookAndFeel);
+
setName("ColourPaletteWindow");
- paletteGroup.reset(new ColourSelectionGroup());
+ colourSelectorGroup.reset(new ColourSelectionGroup());
- palettePanel.reset(new ColourPalettesPanel(colourPalettes, paletteGroup.get()));
+ palettePanel.reset(new ColourPalettesPanel(colourPalettes, colourSelectorGroup.get()));
palettePanel->addListener(this);
palettePanelViewport.reset(new Viewport("PalettePanelViewport"));
@@ -30,7 +33,8 @@ ColourPaletteWindow::ColourPaletteWindow(Array& col
palettePanelViewport->getVerticalScrollBar().setColour(ScrollBar::ColourIds::thumbColourId, Colour(0xff2d3135));
customPickerPanel.reset(new CustomPickerPanel());
- paletteGroup->addSelector(customPickerPanel.get());
+ colourSelectorGroup->addSelector(customPickerPanel.get());
+ colourSelectorGroup->addColourSelectionListener(customPickerPanel.get());
colourToolTabs.reset(new TabbedComponent(TabbedButtonBar::Orientation::TabsAtTop));
colourToolTabs->setName("ColourSelectionToolTabs");
@@ -39,19 +43,25 @@ ColourPaletteWindow::ColourPaletteWindow(Array& col
colourToolTabs->setColour(TabbedComponent::ColourIds::outlineColourId, Colour());
colourToolTabs->getTabbedButtonBar().getProperties().set(LumatoneEditorStyleIDs::fontHeightScalar, 0.9f);
addAndMakeVisible(*colourToolTabs);
-
+
+ const int firstTabIndex = TerpstraSysExApplication::getApp().getPropertiesFile()->getIntValue("LastColourPopupTabIndex");
+ colourToolTabs->setCurrentTabIndex(firstTabIndex);
colourToolTabs->getTabbedButtonBar().addChangeListener(this);
}
ColourPaletteWindow::~ColourPaletteWindow()
{
- paletteGroup->removeSelector(customPickerPanel.get());
-
- palettePanelViewport = nullptr;
+ paletteEditPanel = nullptr;
colourToolTabs = nullptr;
- palettePanel = nullptr;
+
+ colourSelectorGroup->removeSelector(customPickerPanel.get());
customPickerPanel = nullptr;
- paletteEditPanel = nullptr;
+
+ palettePanelViewport = nullptr;
+ palettePanel = nullptr;
+ colourSelectorGroup = nullptr;
+
+ setLookAndFeel(nullptr);
}
void ColourPaletteWindow::resized()
@@ -62,11 +72,7 @@ void ColourPaletteWindow::resized()
if (paletteEditPanel.get())
paletteEditPanel->setBounds(getLocalBounds());
- palettePanel->setViewUnits(
- palettePanelViewport->getMaximumVisibleWidth(),
- palettePanelViewport->getMaximumVisibleHeight()
- );
- palettePanel->rebuildPanel(colourPalettes);
+ palettePanel->rebuildPanel(colourPalettes, palettePanelViewport->getMaximumVisibleWidth());
}
void ColourPaletteWindow::startEditingPalette(int paletteIndexIn, int selectedSwatchIndex)
@@ -83,6 +89,14 @@ void ColourPaletteWindow::startEditingPalette(int paletteIndexIn, int selectedSw
paletteEditPanel->setSelectedSwatch(selectedSwatchIndex);
}
+void ColourPaletteWindow::duplicatePalette(int paletteIndexIn)
+{
+ auto copiedPalette = colourPalettes[paletteIndexIn].clone();
+ colourPalettes.insert(paletteIndexIn + 1, copiedPalette);
+ TerpstraSysExApplication::getApp().saveColourPalette(copiedPalette);
+ palettePanel->rebuildPanel(colourPalettes);
+}
+
void ColourPaletteWindow::removePalette(int paletteIndexToRemove)
{
// Remove loaded colour palette
@@ -104,6 +118,16 @@ void ColourPaletteWindow::editPaletteRequested(int paletteIndex, int selectedSwa
jassert(true); // Something bad happened!
}
+void ColourPaletteWindow::clonePaletteRequested(int paletteIndex)
+{
+ if (paletteIndex >= 0 && paletteIndex < colourPalettes.size())
+ {
+ duplicatePalette(paletteIndex);
+ }
+ else
+ jassert(true); // Something bad happened!
+}
+
void ColourPaletteWindow::deletePaletteRequested(int paletteIndex)
{
if (paletteIndex >= 0 && paletteIndex < colourPalettes.size())
@@ -118,19 +142,13 @@ void ColourPaletteWindow::newPaletteRequested()
{
paletteEditingIsNew = true;
colourPalettes.insert(0, LumatoneEditorColourPalette());
- startEditingPalette(0);
+ startEditingPalette(0, 0);
}
void ColourPaletteWindow::changeListenerCallback(ChangeBroadcaster* source)
{
- // Custom picker colour changed
- if (source == &colourToolTabs->getTabbedButtonBar())
- {
- customPickerPanel->setCurrentColour(paletteGroup->getSelectedColour());
- }
-
// Palette editing finished
- else if (source == paletteEditPanel.get())
+ if (source == paletteEditPanel.get())
{
if (paletteEditPanel->wasSaveRequested())
{
@@ -140,34 +158,9 @@ void ColourPaletteWindow::changeListenerCallback(ChangeBroadcaster* source)
palette.setColours(paletteEditPanel->getCurrentPalette());
String newName = paletteEditPanel->getPaletteName();
-
- // TODO: Move this elsewhere?
- String paletteFilePath = palette.getPathToFile();
- File paletteFile;
- // File name handling if palette already exists
- if (File::isAbsolutePath(paletteFilePath))
- {
- paletteFile = paletteFilePath;
-
- if (palette.getName() != newName)
- {
- // Delete previous version to ensure name is up to date.
- // This is optional - palettes can retain file name and have palette
- // name changed if that's preferred behavior
- if (paletteFile.existsAsFile())
- {
- paletteFile.deleteFile();
-
- // Rename file with new palette name
- paletteFile = paletteFile.getSiblingFile(newName).withFileExtension(PALETTEFILEEXTENSION);
- }
- }
- }
-
palette.setName(newName);
- // Save to properties
- TerpstraSysExApplication::getApp().saveColourPalette(palette, paletteFile);
+ TerpstraSysExApplication::getApp().saveColourPalette(palette);
}
else
jassert(true); // Something bad happened!
@@ -182,4 +175,10 @@ void ColourPaletteWindow::changeListenerCallback(ChangeBroadcaster* source)
paletteEditingIsNew = false;
paletteEditPanel = nullptr;
}
+
+ else if (source == &colourToolTabs->getTabbedButtonBar())
+ {
+ const int newTab = colourToolTabs->getCurrentTabIndex();
+ TerpstraSysExApplication::getApp().getPropertiesFile()->setValue("LastColourPopupTabIndex", newTab);
+ }
}
diff --git a/Source/ColourPaletteWindow.h b/Source/ColourPaletteWindow.h
index 8c5496c0..b57d42ae 100644
--- a/Source/ColourPaletteWindow.h
+++ b/Source/ColourPaletteWindow.h
@@ -37,7 +37,26 @@ class ColourPaletteWindow : public juce::Component,
/// Registers a listener to receive the colour the user's input resolves to
///
///
- void listenToColourSelection(ColourSelectionListener* listenerIn) { paletteGroup->addColourSelectionListener(listenerIn); }
+ void listenToColourSelection(ColourSelectionListener* listenerIn) { colourSelectorGroup->addColourSelectionListener(listenerIn); }
+
+ ///
+ /// Adds a colour selector component to the colour selection group
+ ///
+ ///
+ void addColourSelectorToGroup(ColourSelectionBroadcaster* broadcaster) { colourSelectorGroup->addSelector(broadcaster); }
+
+ ///
+ /// Force a colour selector (added to the group) to be selected
+ ///
+ ///
+ void setCurrentColourSelector(ColourSelectionBroadcaster* newSelector)
+ {
+ // This statement is kind of a kludge - vsicurella
+ if (colourSelectorGroup->getIndexOfSelector(newSelector) < 0)
+ colourSelectorGroup->addSelector(newSelector);
+
+ colourSelectorGroup->setCurrentSelector(newSelector);
+ }
private:
@@ -47,6 +66,12 @@ class ColourPaletteWindow : public juce::Component,
///
void startEditingPalette(int paletteIndexIn, int selectedSwatchIndex = -1);
+ ///
+ /// Creates a new palette with the same name and swatches
+ ///
+ ///
+ void duplicatePalette(int paletteIndexIn);
+
///
/// Removes a palette and associated buttons
///
@@ -57,11 +82,15 @@ class ColourPaletteWindow : public juce::Component,
void editPaletteRequested(int paletteIndex, int selectedSwatchIndex) override;
+ void clonePaletteRequested(int paletteIndex) override;
+
void deletePaletteRequested(int paletteIndex) override;
void newPaletteRequested() override;
private:
+
+ LumatoneEditorLookAndFeel lookAndFeel;
Array& colourPalettes;
@@ -72,7 +101,7 @@ class ColourPaletteWindow : public juce::Component,
std::unique_ptr palettePanelViewport;
- std::unique_ptr paletteGroup;
+ std::unique_ptr colourSelectorGroup;
int paletteIndexEditing = -1;
bool paletteEditingIsNew = false;
diff --git a/Source/ColourSelectionGroup.h b/Source/ColourSelectionGroup.h
index 6e9cdac5..3521ad78 100644
--- a/Source/ColourSelectionGroup.h
+++ b/Source/ColourSelectionGroup.h
@@ -18,6 +18,7 @@ class ColourSelectionBroadcaster;
class ColourSelectionListener
{
public:
+ virtual ~ColourSelectionListener() {}
virtual void colourChangedCallback(ColourSelectionBroadcaster* source, Colour newColour) = 0;
};
@@ -26,14 +27,14 @@ class ColourSelectionBroadcaster
public:
ColourSelectionBroadcaster() {};
- virtual ~ColourSelectionBroadcaster() {};
+ virtual ~ColourSelectionBroadcaster() {}
virtual Colour getSelectedColour() = 0;
virtual void deselectColour() = 0;
void addColourSelectionListener(ColourSelectionListener* listenerIn) { selectorListeners.add(listenerIn); }
- void removeColourSelectionListener(ColourSelectionListener* listenerIn) { selectorListeners.add(listenerIn); }
+ void removeColourSelectionListener(ColourSelectionListener* listenerIn) { selectorListeners.remove(listenerIn); }
protected:
@@ -45,6 +46,12 @@ class ColourSelectionGroup : public ColourSelectionBroadcaster,
{
public:
+ ~ColourSelectionGroup()
+ {
+ for (auto selector : colourSelectors)
+ selector->removeColourSelectionListener(this);
+ }
+
///
/// Adds a ColourPaletteComponent and returns the palette's group index
///
@@ -60,12 +67,12 @@ class ColourSelectionGroup : public ColourSelectionBroadcaster,
return colourSelectors.indexOf(selectorToAdd);
}
- void removeSelector(ColourSelectionBroadcaster* paletteComponentToRemove)
+ void removeSelector(ColourSelectionBroadcaster* selectorToRemove)
{
- int removed = colourSelectors.removeAllInstancesOf(paletteComponentToRemove);
+ int removed = colourSelectors.removeAllInstancesOf(selectorToRemove);
if (removed > 0)
- paletteComponentToRemove->removeColourSelectionListener(this);
+ selectorToRemove->removeColourSelectionListener(this);
}
///
@@ -105,6 +112,23 @@ class ColourSelectionGroup : public ColourSelectionBroadcaster,
return colourSelectors.indexOf(selectorIn);
}
+ void setCurrentSelector(ColourSelectionBroadcaster* selector)
+ {
+ auto index = colourSelectors.indexOf(selector);
+
+ if (index >= 0 && index < colourSelectors.size())
+ {
+ if (selectedBroadcasterIndex >= 0 && getSelectedBroadcaster() != selector)
+ {
+ getSelectedBroadcaster()->deselectColour();
+ }
+
+ selectedBroadcasterIndex = index;
+
+ selectorListeners.call(&ColourSelectionListener::colourChangedCallback, selector, selector->getSelectedColour());
+ }
+ }
+
//=========================================================================
///
@@ -114,14 +138,7 @@ class ColourSelectionGroup : public ColourSelectionBroadcaster,
///
void colourChangedCallback(ColourSelectionBroadcaster* source, Colour newColour) override
{
- if (getSelectedBroadcaster() && getSelectedBroadcaster() != source)
- {
- getSelectedBroadcaster()->deselectColour();
- }
-
- selectedBroadcasterIndex = colourSelectors.indexOf(source);
-
- selectorListeners.call(&ColourSelectionListener::colourChangedCallback, source, newColour);
+ setCurrentSelector(source);
}
private:
@@ -129,4 +146,4 @@ class ColourSelectionGroup : public ColourSelectionBroadcaster,
Array colourSelectors;
int selectedBroadcasterIndex = -1;
-};
\ No newline at end of file
+};
diff --git a/Source/ColourSelectionPanels.h b/Source/ColourSelectionPanels.h
index 17b88eb6..9ff0bafb 100644
--- a/Source/ColourSelectionPanels.h
+++ b/Source/ColourSelectionPanels.h
@@ -32,88 +32,116 @@ class ColourPalettesPanel : public Component
newPaletteBtn->setButtonText(translate("NewPalette"));
newPaletteBtn->getProperties().set(LumatoneEditorStyleIDs::textButtonHyperlinkFlag, 1);
newPaletteBtn->onClick = [&] { listeners.call(&ColourPalettesPanel::Listener::newPaletteRequested); };
-
- flexBox.flexWrap = FlexBox::Wrap::wrap;
- flexBox.justifyContent = FlexBox::JustifyContent::flexStart;
+
};
- // Used to make this component resize itself depending on how many swatches there are
- void setViewUnits(int widthIn, int heightIn)
+ ~ColourPalettesPanel()
{
- viewableWidth = widthIn;
- viewableHeight = heightIn;
+ for (auto palette : allPalettes)
+ selectionGroup->removeSelector(palette);
- setSize(viewableWidth, getHeightFromNumRows(numRows));
+ paletteLabels.clear();
+ controlGroups.clear();
+ newPaletteBtn = nullptr;
+ newPalette = nullptr;
}
- int getHeightFromNumRows(int numRowsIn)
+ // Used to make this component resize itself depending on how many swatches there are
+ //void setViewUnits(int widthIn, int heightIn)
+ //{
+ // viewableWidth = widthIn;
+ // viewableHeight = heightIn;
+
+ // setSize(viewableWidth, getHeightFromNumRows(numRows));
+ //}
+
+ int getHeightFromNumRows(int widthIn, int numRowsIn)
{
- return round(viewableHeight * 0.5f * numRowsIn);
+ float rowHeight = widthIn * (itemHeightScalar + topMarginScalar + bottomMarginScalar);
+ auto height = roundToInt(numRowsIn * rowHeight);
+ return height;
}
void paint(Graphics& g) override
{
- ////Draws rectangles around items and margins
-// for (auto item : flexBox.items)
-// {
-// g.setColour(Colours::red);
-// g.drawRect(item.currentBounds);
-//
-// g.setColour(Colours::green);
-// g.drawRect(item.currentBounds.getX(), item.currentBounds.getBottom(), item.width, item.margin.bottom, 1.0f);
-//
-// g.setColour(Colours::yellow);
-// g.drawRect(item.currentBounds.getX() - item.margin.left - 1, item.currentBounds.getY(), item.margin.left - 1, item.currentBounds.getHeight(), 1.0f);
-// g.drawRect(item.currentBounds.getRight() + 1, item.currentBounds.getY(), item.margin.right - 1, item.currentBounds.getHeight(), 1.0f);
-//
-// g.setColour(Colours::violet);
-// g.drawRect(item.currentBounds.getX(), item.currentBounds.getY() - item.margin.top, item.width, item.margin.top);
-// }
+ //Draws rectangles around items and margins
+ //for (auto item : dbgItems)
+ //{
+ // g.setColour(Colours::red);
+ // g.drawRect(item.currentBounds);
+
+ // g.setColour(Colours::green);
+ // g.drawRect(item.currentBounds.getX(), item.currentBounds.getBottom(), item.width, item.margin.bottom, 1.0f);
+
+ // g.setColour(Colours::yellow);
+ // g.drawRect(item.currentBounds.getX() - item.margin.left - 1, item.currentBounds.getY(), item.margin.left - 1, item.currentBounds.getHeight(), 1.0f);
+ // g.drawRect(item.currentBounds.getRight() + 1, item.currentBounds.getY(), item.margin.right - 1, item.currentBounds.getHeight(), 1.0f);
+
+ // g.setColour(Colours::violet);
+ // g.drawRect(item.currentBounds.getX(), item.currentBounds.getY() - item.margin.top, item.width, item.margin.top);
+ //}
};
void resized() override
{
- Rectangle viewportBounds(viewableWidth, viewableHeight);
+ Rectangle viewportBounds(getWidth(), viewableHeight);
+ FlexBox flexBox(FlexBox::Direction::row, FlexBox::Wrap::wrap, FlexBox::AlignContent::flexStart, FlexBox::AlignItems::center, FlexBox::JustifyContent::flexStart);
- float horizontalMargin = viewportBounds.proportionOfWidth(horizontalMarginScalar);
float itemWidth = viewportBounds.proportionOfWidth(itemWidthScalar);
float itemHeight = viewportBounds.proportionOfWidth(itemHeightScalar);
- float topMargin = viewportBounds.proportionOfHeight(topMarginScalar);
- float bottomMargin = viewportBounds.proportionOfHeight(bottomMarginScalar);
+ float topMargin = viewportBounds.proportionOfWidth(topMarginScalar);
+ float bottomMargin = viewportBounds.proportionOfWidth(bottomMarginScalar);
+ float horizontalMargin = (viewportBounds.getWidth() - (3 * itemWidth)) * 0.143f;
- for (int i = 0; i < flexBox.items.size(); i++)
+ for (auto palette : allPalettes)
{
- FlexItem& item = flexBox.items.getReference(i);
- item.width = itemWidth;
- item.height = itemHeight;
+ FlexItem item(itemWidth, itemHeight, *palette);
item.margin = FlexItem::Margin(topMargin, horizontalMargin, bottomMargin, horizontalMargin);
+ flexBox.items.add(item);
}
flexBox.performLayout(viewportBounds);
- float bottomMarginControlHeight = roundToInt(viewportBounds.proportionOfHeight(btmMarginCtrlScalar));
+ float bottomMarginControlHeight = roundToInt(viewportBounds.proportionOfWidth(btmMarginCtrlScalar));
float bottomMarginControlSpace = (bottomMargin - bottomMarginControlHeight) * 0.5f;
+ float labelYItemOffset = itemHeight * 0.8f;
for (int i = 0; i < controlGroups.size(); i++)
{
FlexItem& item = flexBox.items.getReference(i + 1);
Rectangle bottomMarginBounds(item.currentBounds.getX(), item.currentBounds.getBottom(), itemWidth, bottomMargin);
- int halfItemWidth = bottomMarginBounds.proportionOfWidth(0.5f);
+ int fourthWidthItem = bottomMarginBounds.proportionOfWidth(0.25f);
- auto label = paletteLabels[i];
- label->setBounds(item.currentBounds.withTrimmedTop(itemHeight * 0.8f).toNearestInt());
+ Rectangle labelBounds = item.currentBounds.withTrimmedTop(labelYItemOffset);
+ Point controlsPosition;
+ if (paletteLabels[i]->getText().isNotEmpty())
+ {
+ auto label = paletteLabels[i];
+ label->setBounds(labelBounds.toNearestInt());
+ controlsPosition = bottomMarginBounds.getPosition().translated(0, bottomMarginControlSpace);
+ }
+ else
+ {
+ controlsPosition = labelBounds.getPosition();
+ }
auto group = controlGroups.getUnchecked(i);
- group->getEditButton()->setSize(halfItemWidth, bottomMarginControlHeight);
- group->getEditButton()->setTopLeftPosition(bottomMarginBounds.getPosition().translated(0, bottomMarginControlSpace).roundToInt());
- group->getTrashButton()->setBounds(group->getEditButton()->getBounds().translated(halfItemWidth, 0));
+ group->getEditButton()->setSize(fourthWidthItem, bottomMarginControlHeight);
+ group->getEditButton()->setTopLeftPosition(controlsPosition.roundToInt().translated(bottomMarginBounds.proportionOfWidth(0.125f), 0));
+ group->getCloneButton()->setBounds(group->getEditButton()->getBounds().translated(fourthWidthItem, 0));
+ group->getTrashButton()->setBounds(group->getEditButton()->getBounds().translated(fourthWidthItem * 2.0f, 0));
- Rectangle controlBounds = Rectangle(item.currentBounds.getTopLeft().translated(-horizontalMargin, -topMargin), bottomMarginBounds.getBottomRight());
- controlGroupHitBoxes.set(i, controlBounds.toNearestInt());
+ auto hitBox = Rectangle(item.currentBounds.getTopLeft().translated(-horizontalMargin, -topMargin), bottomMarginBounds.getBottomRight()).toNearestInt();
+ controlGroupHitBoxes.set(i, hitBox);
}
+ dbgItems = flexBox.items;
+
newPaletteBtn->setSize(itemWidth, bottomMarginControlHeight);
- newPaletteBtn->setTopLeftPosition(newPalette->getX(), newPalette->getBottom() + bottomMarginControlSpace);
+ newPaletteBtn->setTopLeftPosition(newPalette->getX(), newPalette->getY() + labelYItemOffset);
+ newPaletteBtn->toFront(false);
+
+ needsResize = false;
}
void mouseMove(const MouseEvent& mouse) override
@@ -125,7 +153,7 @@ class ColourPalettesPanel : public Component
}
else
{
- for (int i = 0; i < controlGroupHitBoxes.size(); i++)
+ for (int i = 0; i < controlGroups.size(); i++)
{
if (controlGroupHitBoxes[i].contains(mouse.getEventRelativeTo(this).position.roundToInt()))
{
@@ -146,25 +174,19 @@ class ColourPalettesPanel : public Component
}
// Setup panels from scratch
- void rebuildPanel(Array palettesIn, bool resize = true)
+ void rebuildPanel(Array palettesIn, int width = 0, bool resize = true)
{
- removeAllChildren();
- flexBox.items.clear();
-
- for (auto label : paletteLabels)
- {
- removeChildComponent(label);
- }
+ for (auto group : controlGroups)
+ selectionGroup->removeSelector(group->getPaletteComponent());
+ removeAllChildren();
+ addAndMakeVisible(newPaletteBtn.get());
addAndMakeVisible(newPalette.get());
- flexBox.items.add(*newPalette);
- addAndMakeVisible(newPaletteBtn.get());
-
- // Remove old ones from colour selection group?
controlGroups.clear();
paletteLabels.clear();
- controlGroupHitBoxes.clear();
+
+ allPalettes = Array(newPalette.get());
// Palettes with colour
for (int i = 0; i < palettesIn.size(); i++)
@@ -174,7 +196,8 @@ class ColourPalettesPanel : public Component
auto paletteComponent = group->getPaletteComponent();
paletteComponent->getProperties().set("index", i);
addAndMakeVisible(paletteComponent);
- flexBox.items.add(*paletteComponent);
+
+ allPalettes.add(paletteComponent);
if (selectionGroup)
selectionGroup->addSelector(paletteComponent);
@@ -183,8 +206,12 @@ class ColourPalettesPanel : public Component
group->getEditButton()->onClick = [&, i, paletteComponent] { listeners.call(&ColourPalettesPanel::Listener::editPaletteRequested, i, paletteComponent->getSelectedSwatchNumber()); };
addChildComponent(group->getEditButton());
+ group->getCloneButton()->getProperties().set("index", i);
+ group->getCloneButton()->onClick = [&, i, paletteComponent] { listeners.call(&ColourPalettesPanel::Listener::clonePaletteRequested, i); };
+ addChildComponent(group->getCloneButton());
+
group->getTrashButton()->getProperties().set("index", i);
- group->getTrashButton()->onClick = [&, i] { listeners.call(&ColourPalettesPanel::Listener::deletePaletteRequested, i); };
+ group->getTrashButton()->onClick = [&, group, i] { listeners.call(&ColourPalettesPanel::Listener::deletePaletteRequested, i); };
addChildComponent(group->getTrashButton());
String name = palettesIn[i].getName();
@@ -194,21 +221,29 @@ class ColourPalettesPanel : public Component
label->getProperties().set(LumatoneEditorStyleIDs::labelMaximumLineCount, 2);
addAndMakeVisible(label);
- controlGroupHitBoxes.add(Rectangle());
+ controlGroupHitBoxes.set(i, Rectangle());
}
int rows = ceil((palettesIn.size() + 1) * 0.333333f);
+ int w = getWidth();
+
// Set height depending on how many rows
+ width = (width < 1) ? w : width;
+ viewableHeight = getHeightFromNumRows(width, rows);
+
if (resize)
{
- if (rows != numRows)
- setSize(viewableWidth, getHeightFromNumRows(rows));
- else
+ needsResize = true;
+ setSize(width, viewableHeight);
+
+ // Force resize
+ if (needsResize)
resized();
}
numRows = rows;
+
}
private:
@@ -217,6 +252,7 @@ class ColourPalettesPanel : public Component
{
auto group = controlGroups.getUnchecked(paletteIndex);
group->getEditButton()->setVisible(areVisible);
+ group->getCloneButton()->setVisible(areVisible);
group->getTrashButton()->setVisible(areVisible);
}
@@ -224,35 +260,34 @@ class ColourPalettesPanel : public Component
ColourSelectionGroup* selectionGroup;
- FlexBox flexBox;
-
std::unique_ptr newPalette;
std::unique_ptr newPaletteBtn;
OwnedArray controlGroups;
+ Array allPalettes;
OwnedArray paletteLabels;
+ Array dbgItems;
+
int numRows = 1;
- int viewableWidth = 0;
int viewableHeight = 0;
+ bool needsResize = false;
Array> controlGroupHitBoxes;
int lastPaletteMouseOver = -1;
- const float itemWidthScalar = 0.28f;
- const float itemHeightScalar = 0.25f;
+ const float itemWidthScalar = 0.265f;
+ const float itemHeightScalar = 0.24f;
- const float topMarginScalar = 0.042f;
- const float horizontalMarginScalar = 0.025f;
- const float bottomMarginScalar = 0.075f;
- const float btmMarginCtrlScalar = 0.06f;
+ const float topMarginScalar = 0.04f;
+ const float horizontalMarginScalar = 0.0367f;
+ const float bottomMarginScalar = 0.06f;
+ const float btmMarginCtrlScalar = 0.04f;
const float buttonWidthScalar = 0.333333f;
const float buttonHeightScalar = 0.166667f;
const float panelLeftMarginWidth = 0.020833f;
-
-
//==============================================================================
public:
@@ -260,8 +295,10 @@ class ColourPalettesPanel : public Component
class Listener
{
public:
+ virtual ~Listener() {}
virtual void editPaletteRequested(int paletteIndex, int selectedSwatchIndex) = 0;
+ virtual void clonePaletteRequested(int paletteIndex) = 0;
virtual void deletePaletteRequested(int paletteIndex) = 0;
virtual void newPaletteRequested() = 0;
};
@@ -280,7 +317,8 @@ class ColourPalettesPanel : public Component
*/
class CustomPickerPanel : public Component,
public ChangeListener,
- public ColourSelectionBroadcaster
+ public ColourSelectionBroadcaster,
+ public ColourSelectionListener
{
public:
@@ -292,7 +330,7 @@ class CustomPickerPanel : public Component,
+ ColourSelector::ColourSelectorOptions::showColourspace
));
-
+ colourPicker->setName("ColourPicker");
addAndMakeVisible(*colourPicker);
colourPicker->addChangeListener(this);
}
@@ -319,6 +357,12 @@ class CustomPickerPanel : public Component,
selectorListeners.call(&ColourSelectionListener::colourChangedCallback, this, colourPicker->getCurrentColour());
}
+ void colourChangedCallback(ColourSelectionBroadcaster* source, Colour newColour) override
+ {
+ if (this != source)
+ colourPicker->setCurrentColour(newColour, dontSendNotification);
+ }
+
//==============================================================================
Colour getSelectedColour() override
@@ -401,19 +445,19 @@ class PaletteEditPanel : public Component,
float leftCenter = leftWidth * 0.5f;
resizeLabelWithHeight(editPaletteLabel.get(), proportionOfHeight(editPaletteHeight));
- editPaletteLabel->setCentrePosition(leftCenter, round(editPaletteLabel->getHeight() * 0.5f + proportionOfHeight(editPaletteLabelY)));
+ editPaletteLabel->setCentrePosition(leftCenter, roundToInt(editPaletteLabel->getHeight() * 0.5f + proportionOfHeight(editPaletteLabelY)));
float paletteWidth = proportionOfWidth(paletteWidthScalar);
float paletteHeight = proportionOfHeight(paletteHeightScalar);
paletteControl->setSize(paletteWidth, paletteHeight);
- paletteControl->setCentrePosition(leftCenter, round(paletteHeight * 0.5f + proportionOfHeight(paletteY)));
+ paletteControl->setCentrePosition(leftCenter, roundToInt(paletteHeight * 0.5f + proportionOfHeight(paletteY)));
saveButton->setSize(proportionOfWidth(buttonWidth), proportionOfHeight(buttonHeight));
- saveButton->setCentrePosition(leftCenter, round(saveButton->getHeight() * 0.5f + proportionOfHeight(buttonY)));
+ saveButton->setCentrePosition(leftCenter, roundToInt(saveButton->getHeight() * 0.5f + proportionOfHeight(buttonY)));
cancelButton->setBounds(saveButton->getBounds().translated(0, saveButton->getHeight() * 1.125f));
colourPicker->setSize(proportionOfWidth(pickerWidth), proportionOfHeight(pickerHeight));
- colourPicker->setTopLeftPosition(leftWidth, round((getHeight() - colourPicker->getHeight()) * 0.5f));
+ colourPicker->setTopLeftPosition(leftWidth, roundToInt((getHeight() - colourPicker->getHeight()) * 0.5f));
float leftMargin = colourPicker->getRight() * 0.03f * 0.5f;
paletteNameEditor->setBounds(Rectangle(
@@ -542,7 +586,7 @@ class PaletteEditPanel : public Component,
const float editPaletteHeight = 0.0606f;
const float paletteY = 0.26f;
- const float paletteWidthScalar = 0.27f;
+ const float paletteWidthScalar = 0.25f;
const float paletteHeightScalar = 0.25f;
const float buttonY = 0.6739f;
diff --git a/Source/CurvesArea.cpp b/Source/CurvesArea.cpp
index 90a29859..73b75183 100644
--- a/Source/CurvesArea.cpp
+++ b/Source/CurvesArea.cpp
@@ -7,7 +7,7 @@
the "//[xyz]" and "//[/xyz]" sections will be retained when the file is loaded
and re-saved.
- Created with Projucer version: 6.0.5
+ Created with Projucer version: 6.0.8
------------------------------------------------------------------------------
@@ -54,23 +54,27 @@ void CurvesArea::CurvesTabComponent::resized()
//==============================================================================
CurvesArea::CurvesArea ()
- : Component("CurvesArea")
{
//[Constructor_pre] You can add your own custom stuff here..
showDeveloperMode = TerpstraSysExApplication::getApp().getPropertiesFile()->getBoolValue("DeveloperMode", false);
- //[/Constructor_pre]
+ //[/Constructor_pre]
- labelWindowTitle.reset (new juce::Label ("labelWindowTitle", translate("Curves")));
+ setName ("CurvesArea");
+ labelWindowTitle.reset (new juce::Label ("labelWindowTitle",
+ TRANS("Curves")));
addAndMakeVisible (labelWindowTitle.get());
- labelWindowTitle->setFont(TerpstraSysExApplication::getApp().getAppFont(LumatoneEditorFont::UniviaProBold));
- labelWindowTitle->setColour (Label::backgroundColourId, Colour());
+ labelWindowTitle->setFont (juce::Font (18.00f, juce::Font::plain).withTypefaceStyle ("Regular"));
+ labelWindowTitle->setJustificationType (juce::Justification::centredLeft);
+ labelWindowTitle->setEditable (false, false, false);
+ labelWindowTitle->setColour (juce::TextEditor::textColourId, juce::Colours::black);
+ labelWindowTitle->setColour (juce::TextEditor::backgroundColourId, juce::Colour (0x00000000));
+
+ labelWindowTitle->setBounds (8, 8, 150, 24);
curvesTab.reset (new CurvesTabComponent (juce::TabbedButtonBar::TabsAtTop));
addAndMakeVisible (curvesTab.get());
curvesTab->setTabBarDepth (30);
- curvesTab->setColour(TabbedComponent::ColourIds::outlineColourId, Colour());
- curvesTab->setColour(TabbedComponent::ColourIds::backgroundColourId, Colour());
- curvesTab->addTab (TRANS("Note Velocity"), Colour(), new NoteOnOffVelocityCurveDialog(), true);
+ curvesTab->addTab (TRANS("Note Velocity"), juce::Colours::lightgrey, new NoteOnOffVelocityCurveDialog(), true);
curvesTab->setCurrentTabIndex (0);
curvesTab->setBounds (8, 40, 464, 200);
@@ -85,9 +89,27 @@ CurvesArea::CurvesArea ()
//[UserPreSize]
setDeveloperMode(showDeveloperMode);
+
+ labelWindowTitle->setFont(TerpstraSysExApplication::getApp().getAppFont(LumatoneEditorFont::UniviaProBold));
+ labelWindowTitle->setColour(Label::backgroundColourId, Colour());
+
+ curvesTab->setColour(TabbedComponent::ColourIds::outlineColourId, Colour());
+ curvesTab->setColour(TabbedComponent::ColourIds::backgroundColourId, Colour());
+ //curvesTab->addTab(TRANS("Note Velocity"), Colour(), new NoteOnOffVelocityCurveDialog(), true);
+ curvesTab->setTabBackgroundColour(0, Colour());
+
+
+ /* Don't want to resize here
+ *
//[/UserPreSize]
+ setSize (472, 240);
+
+
//[Constructor] You can add your own custom stuff here..
+ */
+
+
//[/Constructor]
}
@@ -109,33 +131,39 @@ CurvesArea::~CurvesArea()
void CurvesArea::paint (juce::Graphics& g)
{
//[UserPrePaint] Add your own custom painting code here..
+ /*
//[/UserPrePaint]
- //g.fillAll (juce::Colour (0xff323e44));
+ g.fillAll (juce::Colour (0xff323e44));
//[UserPaint] Add your own custom painting code here..
-
+ */
//[/UserPaint]
}
void CurvesArea::resized()
{
+ //[UserPreResize] Add your own custom resize code here..
int tabBarDepth = roundToInt(getHeight() * tabDepth);
int tabY = proportionOfHeight(tabYScalar);
+ //[/UserPreResize]
- btnDeveloperMode->setBounds(
- getWidth() - btnDeveloperMode->getWidth(),
- proportionOfHeight(0.0f),
- btnDeveloperMode->getWidth(),
- tabY);
+ //[UserResized] Add your own custom resize handling here..
+
+ btnDeveloperMode->setBounds(
+ getWidth() - btnDeveloperMode->getWidth(),
+ proportionOfHeight(0.0f),
+ btnDeveloperMode->getWidth(),
+ tabY);
curvesTab->setTabBarDepth(tabBarDepth);
curvesTab->setTabsIndent(roundToInt(getWidth() * tabXScalar));
-
+
curvesTab->setBounds(0, tabY, getWidth(), getHeight() - tabY);
resizeLabelWithHeight(labelWindowTitle.get(), tabBarDepth * 0.9f);
labelWindowTitle->setTopLeftPosition(roundToInt(getWidth() * 0.01f), tabY);
+ //[/UserResized]
}
void CurvesArea::buttonClicked (juce::Button* buttonThatWasClicked)
@@ -204,7 +232,7 @@ void CurvesArea::setDeveloperMode(bool devModeOn)
BEGIN_JUCER_METADATA
-
diff --git a/Source/CurvesArea.h b/Source/CurvesArea.h
index 4a5024d9..ebab1030 100644
--- a/Source/CurvesArea.h
+++ b/Source/CurvesArea.h
@@ -7,7 +7,7 @@
the "//[xyz]" and "//[/xyz]" sections will be retained when the file is loaded
and re-saved.
- Created with Projucer version: 6.0.5
+ Created with Projucer version: 6.0.8
------------------------------------------------------------------------------
@@ -68,15 +68,17 @@ class CurvesArea : public juce::Component,
void sendConfigToController();
void setDeveloperMode(bool devModeOn);
- //[/UserMethods]
+ //[/UserMethods]
void paint (juce::Graphics& g) override;
void resized() override;
void buttonClicked (juce::Button* buttonThatWasClicked) override;
+
+
private:
//[UserVariables] -- You can add your own custom variables in this section.
-
+
bool showDeveloperMode = false;
//==============================================================================
@@ -87,7 +89,7 @@ class CurvesArea : public juce::Component,
const float tabYScalar = 0.06f;
const float tabWidth = 0.65f;
const float tabFontHeight = 0.058f;
-
+
//[/UserVariables]
//==============================================================================
diff --git a/Source/DeviceActivityMonitor.cpp b/Source/DeviceActivityMonitor.cpp
index b0e66cd6..c7d09497 100644
--- a/Source/DeviceActivityMonitor.cpp
+++ b/Source/DeviceActivityMonitor.cpp
@@ -13,14 +13,25 @@
DeviceActivityMonitor::DeviceActivityMonitor(TerpstraMidiDriver& midiDriverIn)
- : midiDriver(midiDriverIn)
+ : midiDriver(midiDriverIn), readQueueSize(0)
{
- midiDriver.addListener(this);
+ detectDevicesIfDisconnected = TerpstraSysExApplication::getApp().getPropertiesFile()->getBoolValue("DetectDeviceIfDisconnected", true);
+ checkConnectionOnInactivity = TerpstraSysExApplication::getApp().getPropertiesFile()->getBoolValue("CheckConnectionIfInactive", true);
+ responseTimeoutMs = TerpstraSysExApplication::getApp().getPropertiesFile()->getIntValue("DetectDevicesTimeout", responseTimeoutMs);
+
+// midiDriver.addListener(this);
+ reset(readBlockSize);
+ midiDriver.addMessageCollector(this);
+
+ // avoid resizing during communication
+ ensureStorageAllocated(2000);
+ testResponseDeviceIndices.resize(2000);
}
DeviceActivityMonitor::~DeviceActivityMonitor()
{
-
+ removeAllChangeListeners();
+ midiDriver.removeMessageCollector(this);
}
void DeviceActivityMonitor::setDetectDeviceIfDisconnected(bool doDetection)
@@ -45,7 +56,9 @@ void DeviceActivityMonitor::setCheckForInactivity(bool monitorActivity)
TerpstraSysExApplication::getApp().getPropertiesFile()->setValue("CheckConnectionIfInactive", checkConnectionOnInactivity);
if (checkConnectionOnInactivity && isConnectionEstablished())
+ {
startTimer(inactivityTimeoutMs);
+ }
}
void DeviceActivityMonitor::pingAllDevices()
@@ -58,11 +71,10 @@ void DeviceActivityMonitor::pingAllDevices()
waitingForTestResponse = true;
- // Impossible...but why not limit anyway?
- const unsigned int maxDevices = jmin(outputDevices.size(), (1 << 28) - 1);
- for (unsigned int i = 0; i < maxDevices; i++)
+ int maxDevices = jmin(outputDevices.size(), 128);
+ for (int i = 0; i < maxDevices; i++)
{
- const unsigned int id = i + 1;
+ unsigned int id = (unsigned int)i + 1;
midiDriver.ping(id, i);
outputPingIds.add(id);
}
@@ -128,7 +140,11 @@ void DeviceActivityMonitor::testNextOutput()
if (testOutputIndex >= 0 && testOutputIndex < outputDevices.size())
{
DBG("Testing " + outputDevices[testOutputIndex].name);
- midiDriver.sendGetSerialIdentityRequest(testOutputIndex);
+ if (sendCalibratePitchModOff)
+ midiDriver.sendCalibratePitchModWheel(false, testOutputIndex);
+ else
+ midiDriver.sendGetSerialIdentityRequest(testOutputIndex);
+
waitingForTestResponse = true;
startTimer(responseTimeoutMs);
}
@@ -181,10 +197,9 @@ void DeviceActivityMonitor::stopMonitoringDevice()
bool DeviceActivityMonitor::initializeConnectionTest()
{
- TerpstraSysExApplication::getApp().getLumatoneController().testCurrentDeviceConnection();
-
+ TerpstraSysExApplication::getApp().getLumatoneController()->testCurrentDeviceConnection();
waitingForTestResponse = true;
-
+ startTimer(inactivityTimeoutMs);
return true;
}
@@ -193,8 +208,9 @@ void DeviceActivityMonitor::onSerialIdentityResponse(const MidiMessage& msg, int
{
waitingForTestResponse = false;
- if (deviceConnectionMode == DetectConnectionMode::lookingForDevice)
+ switch (deviceConnectionMode)
{
+ case DetectConnectionMode::lookingForDevice:
if (midiDriver.hasDevicesDefined())
{
confirmedOutputIndex = midiDriver.getMidiOutputIndex();
@@ -206,11 +222,15 @@ void DeviceActivityMonitor::onSerialIdentityResponse(const MidiMessage& msg, int
confirmedInputIndex = deviceIndexResponded;
}
- startTimer(10);
- }
- else if (deviceConnectionMode == DetectConnectionMode::waitingForInactivity)
- {
+ break;
+
+ case DetectConnectionMode::waitingForInactivity:
startTimer(inactivityTimeoutMs);
+ break;
+
+ default:
+ // TODO review
+ break;
}
}
@@ -245,7 +265,7 @@ void DeviceActivityMonitor::onPingResponse(const MidiMessage& msg, int deviceInd
{
confirmedOutputIndex = pingId - 1;
confirmedInputIndex = deviceIndexResponded;
- startTimer(10);
+ // startTimer(10);
}
}
@@ -257,9 +277,14 @@ void DeviceActivityMonitor::onPingResponse(const MidiMessage& msg, int deviceInd
void DeviceActivityMonitor::onTestResponseReceived()
{
- stopTimer();
-
waitingForTestResponse = false;
+
+ if (sendCalibratePitchModOff)
+ {
+ sendCalibratePitchModOff = false;
+ checkDetectionStatus();
+ return;
+ }
if (deviceConnectionMode == DetectConnectionMode::lookingForDevice)
{
@@ -337,67 +362,84 @@ void DeviceActivityMonitor::timerCallback()
{
stopTimer();
- if (deviceConnectionMode < DetectConnectionMode::noDeviceMonitoring)
+ MidiBuffer readBuffer;
+ removeNextBlockOfMessages(readBuffer, readBlockSize);
+
+ handleMessageQueue(readBuffer, testResponseDeviceIndices);
+ auto size = readQueueSize.load();
+ readQueueSize.store(jlimit(0, 999999, size - readBlockSize));
+
+ switch (deviceConnectionMode)
{
+ case DetectConnectionMode::noDeviceActivity:
+ case DetectConnectionMode::lookingForDevice:
if (detectDevicesIfDisconnected)
{
checkDetectionStatus();
+ break;
}
- else
+
+ stopDeviceDetection();
+ break;
+
+ case DetectConnectionMode::noDeviceMonitoring:
+ case DetectConnectionMode::waitingForInactivity:
+ jassert(isConnectionEstablished() && midiDriver.hasDevicesDefined());
+ if (!checkConnectionOnInactivity)
{
- stopDeviceDetection();
+ stopMonitoringDevice();
+ break;
}
- }
- else if (deviceConnectionMode >= DetectConnectionMode::noDeviceMonitoring)
- {
- jassert(isConnectionEstablished() && midiDriver.hasDevicesDefined());
- if (checkConnectionOnInactivity)
+
+ if (!waitingForTestResponse)
{
- if (!waitingForTestResponse)
+ if (sentQueueSize > 0)
{
- if (midiQueueSize == 0)
- {
- if (deviceConnectionMode == DetectConnectionMode::noDeviceMonitoring)
- deviceConnectionMode = DetectConnectionMode::waitingForInactivity;
-
- initializeConnectionTest();
- }
- else
- {
- startTimer(inactivityTimeoutMs);
- }
+ startTimer(inactivityTimeoutMs);
+ break;
}
+
+ if (deviceConnectionMode == DetectConnectionMode::noDeviceMonitoring)
+ deviceConnectionMode = DetectConnectionMode::waitingForInactivity;
+
+ initializeConnectionTest();
}
else
{
- stopMonitoringDevice();
+ DBG("waiting for test response...");
}
- }
- else
+
+ break;
+
+ default:
jassertfalse;
+ }
}
-//=========================================================================
-// TerpstraMidiDriver::Listener Implementation
-
-void DeviceActivityMonitor::midiMessageReceived(MidiInput* source, const MidiMessage& msg)
+void DeviceActivityMonitor::handleResponse(int inputDeviceIndex, const MidiMessage& msg)
{
- stopTimer();
-
if (msg.isSysEx())
{
- if (waitingForTestResponse)
+ auto sysExData = msg.getSysExData();
+ auto cmd = sysExData[CMD_ID];
+
+ if (cmd == PERIPHERAL_CALBRATION_DATA && !isConnectionEstablished())
{
- auto sysExData = msg.getSysExData();
- auto cmd = sysExData[CMD_ID];
+ sendCalibratePitchModOff = true;
+ // startTimer(100);
+ return;
+ }
+ if (waitingForTestResponse) switch (sysExData[MSG_STATUS])
+ {
// Skip echos, or mark as a failed ping
- if (sysExData[MSG_STATUS] == TEST_ECHO)
+ case TEST_ECHO:
{
switch (cmd)
{
case LUMA_PING:
{
+ DBG("Ignoring Ping Echo");
onFailedPing(msg);
break;
}
@@ -405,65 +447,110 @@ void DeviceActivityMonitor::midiMessageReceived(MidiInput* source, const MidiMes
break;
case GET_FIRMWARE_REVISION:
- onTestResponseReceived();
+ DBG("Ignoring Firmware Echo");
+ // onTestResponseReceived();
break;
-
+
default:
return;
}
-
- waitingForTestResponse = false;
- startTimer(10);
+ break;
}
- else if (sysExData[MSG_STATUS] == TerpstraMIDIAnswerReturnCode::ACK)
- {
- int deviceIndex = midiDriver.getMidiInputList().indexOf(source->getDeviceInfo());
+ case TerpstraMIDIAnswerReturnCode::ACK:
+ {
switch (cmd)
{
case GET_SERIAL_IDENTITY:
- onSerialIdentityResponse(msg, deviceIndex);
+ onSerialIdentityResponse(msg, inputDeviceIndex);
+ break;
+
+ case CALIBRATE_PITCH_MOD_WHEEL:
+ if (sysExData[PAYLOAD_INIT] != TEST_ECHO)
+ {
+ if (!isConnectionEstablished())
+ {
+ sendCalibratePitchModOff = true;
+ }
+ }
break;
case GET_FIRMWARE_REVISION:
- // TODO
break;
case LUMA_PING:
- onPingResponse(msg, deviceIndex);
+ onPingResponse(msg, inputDeviceIndex);
break;
default:
break;
}
+
+ waitingForTestResponse = false;
+ break;
+ }
+
+ // Consider a response from a different firmware state as successful
+ case TerpstraMIDIAnswerReturnCode::STATE:
+ {
+ // Find 'off' message if possible
+ //onTestResponseReceived();
}
}
-
+
// Edge case if we're disconnected but get a response
else if (!isConnectionEstablished())
{
confirmedInputIndex = midiDriver.getMidiInputIndex();
confirmedOutputIndex = midiDriver.getMidiOutputIndex();
-
+
// Maybe not best solution?
deviceConnectionMode = DetectConnectionMode::lookingForDevice;
- startTimer(10);
}
else
{
startTimer(inactivityTimeoutMs);
}
}
+
}
-void DeviceActivityMonitor::noAnswerToMessage(const MidiMessage& midiMessage)
+void DeviceActivityMonitor::handleMessageQueue(const MidiBuffer& readBuffer, const Array& devices)
+{
+ int smpl = 0;
+ for (auto event : readBuffer)
+ {
+ auto msg = event.getMessage();
+ handleResponse(devices[smpl], msg);
+ smpl++;
+ }
+}
+
+//=========================================================================
+// TerpstraMidiDriver::Listener Implementation
+
+void DeviceActivityMonitor::midiMessageReceived(MidiInput* source, const MidiMessage& msg)
+{
+ if (!msg.isSysEx())
+ return;
+
+ int deviceIndex = (source == nullptr) ? -1
+ : midiDriver.getMidiInputList().indexOf(source->getDeviceInfo());
+
+ addMessageToQueue(msg);
+
+ auto size = readQueueSize.load();
+ testResponseDeviceIndices.set(size, deviceIndex);
+ readQueueSize.store(size + 1);
+}
+
+void DeviceActivityMonitor::noAnswerToMessage(MidiInput* expectedDevice, const MidiMessage& midiMessage)
{
stopTimer();
if (waitingForTestResponse && deviceConnectionMode < DetectConnectionMode::noDeviceMonitoring)
{
waitingForTestResponse = false;
-
auto sysExData = midiMessage.getSysExData();
if (sysExData[CMD_ID] == LUMA_PING && outputPingIds.size() > 0)
{
diff --git a/Source/DeviceActivityMonitor.h b/Source/DeviceActivityMonitor.h
index 5fcd526b..4435d282 100644
--- a/Source/DeviceActivityMonitor.h
+++ b/Source/DeviceActivityMonitor.h
@@ -19,7 +19,9 @@
#include "TerpstraMidiDriver.h"
-class DeviceActivityMonitor : public juce::Timer, public juce::ChangeBroadcaster, protected TerpstraMidiDriver::Listener
+class DeviceActivityMonitor : public juce::Timer,
+ public juce::ChangeBroadcaster,
+ protected TerpstraMidiDriver::Collector
{
public:
@@ -106,11 +108,20 @@ class DeviceActivityMonitor : public juce::Timer, public juce::ChangeBroadcaster
/// Returns false if devices are not valid, and true if it an attempt to connect was made
bool initializeConnectionTest();
+ ///
+ /// Handle a response from a test or confirmed MidiInput device
+ ///
+ ///
+ ///
+ void handleResponse(int inputDeviceIndex, const MidiMessage& midiMessage);
+
private:
//=========================================================================
// Callback functions
+ void handleMessageQueue(const MidiBuffer& readBuffer, const Array& devices);
+
void onSerialIdentityResponse(const MidiMessage& msg, int deviceIndexResponded);
void onFailedPing(const MidiMessage& msg);
@@ -121,17 +132,17 @@ class DeviceActivityMonitor : public juce::Timer, public juce::ChangeBroadcaster
void onSuccessfulDetection();
void onDisconnection();
-
+
protected:
//=========================================================================
// TerpstraMidiDriver::Listener Implementation
void midiMessageReceived(MidiInput* source, const MidiMessage& midiMessage) override;
- void midiMessageSent(const MidiMessage& midiMessage) override {};
- void midiSendQueueSize(int queueSizeIn) override { midiQueueSize = queueSizeIn; };
- void generalLogMessage(String textMessage, HajuErrorVisualizer::ErrorLevel errorLevel) override {};
- void noAnswerToMessage(const MidiMessage& midiMessage) override;
+ void midiMessageSent(MidiOutput* target, const MidiMessage& midiMessage) override {}
+ void midiSendQueueSize(int queueSizeIn) override { sentQueueSize = queueSizeIn; }
+ void generalLogMessage(String textMessage, HajuErrorVisualizer::ErrorLevel errorLevel) override {}
+ void noAnswerToMessage(MidiInput* expectedDevice, const MidiMessage& midiMessage) override;
private:
@@ -141,10 +152,15 @@ class DeviceActivityMonitor : public juce::Timer, public juce::ChangeBroadcaster
bool deviceDetectInProgress = false;
bool waitingForTestResponse = false;
- int responseTimeoutMs = 500;
- int detectRoutineTimeoutMs = 500;
+ int responseTimeoutMs = 600;
+ int detectRoutineTimeoutMs = 1000;
int inactivityTimeoutMs = 1500;
-
+
+ Array testResponseDeviceIndices;
+ std::atomic readQueueSize;
+ const int readBlockSize = 64;
+ int sentQueueSize = 0;
+
int testOutputIndex = -1;
Array outputDevices;
Array inputDevices;
@@ -153,10 +169,10 @@ class DeviceActivityMonitor : public juce::Timer, public juce::ChangeBroadcaster
int confirmedInputIndex = -1;
int confirmedOutputIndex = -1;
- int midiQueueSize = 0;
-
bool detectDevicesIfDisconnected = true;
bool checkConnectionOnInactivity = true;
+ bool sendCalibratePitchModOff = false;
+
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(DeviceActivityMonitor)
};
diff --git a/Source/EditActions.cpp b/Source/EditActions.cpp
index c58e781a..2eeb6ca2 100644
--- a/Source/EditActions.cpp
+++ b/Source/EditActions.cpp
@@ -13,6 +13,8 @@
namespace Lumatone {
+ // ==============================================================================
+ // Implementation of SingleNoteAssignAction
SingleNoteAssignAction::SingleNoteAssignAction(
int setSelection,
int keySelection,
@@ -20,13 +22,15 @@ namespace Lumatone {
bool setChannel,
bool setNote,
bool setColour,
+ bool setCCPolarity,
LumatoneKeyType newKeyType,
int newChannelNumber,
int newNoteNumber,
- TerpstraKey::COLOURTYPE newColour)
+ TerpstraKey::COLOURTYPE newColour,
+ bool newCCFaderIsDefault)
: setSelection(setSelection), keySelection(keySelection)
- , setKeyType(setKeyType), setChannel(setChannel), setNote(setNote), setColour(setColour)
- , newData(newKeyType, newChannelNumber, newNoteNumber, newColour)
+ , setKeyType(setKeyType), setChannel(setChannel), setNote(setNote), setColour(setColour), setCCFaderPolarity(setCCPolarity)
+ , newData(newKeyType, newChannelNumber, newNoteNumber, newColour, newCCFaderIsDefault)
{
auto mainComponent = TerpstraSysExApplication::getApp().getMainContentComponent();
jassert(mainComponent != nullptr);
@@ -43,7 +47,7 @@ namespace Lumatone {
{
if (setSelection >= 0 && setSelection < NUMBEROFBOARDS && keySelection >= 0 && keySelection < TerpstraSysExApplication::getApp().getOctaveBoardSize())
{
- if (setKeyType || setChannel || setNote || setColour)
+ if (setKeyType || setChannel || setNote || setColour || setCCFaderPolarity)
{
auto mainComponent = TerpstraSysExApplication::getApp().getMainContentComponent();
jassert(mainComponent != nullptr);
@@ -65,9 +69,13 @@ namespace Lumatone {
{
mappingInEdit.sets[setSelection].theKeys[keySelection].colour = newData.colour;
}
+ if (setCCFaderPolarity)
+ {
+ mappingInEdit.sets[setSelection].theKeys[keySelection].ccFaderDefault = newData.ccFaderDefault;
+ }
// Send to device
- TerpstraSysExApplication::getApp().getLumatoneController().sendKeyParam(
+ TerpstraSysExApplication::getApp().getLumatoneController()->sendKeyParam(
setSelection + 1,
keySelection,
mappingInEdit.sets[setSelection].theKeys[keySelection]);
@@ -93,7 +101,7 @@ namespace Lumatone {
{
if (setSelection >= 0 && setSelection < NUMBEROFBOARDS && keySelection >= 0 && keySelection < TerpstraSysExApplication::getApp().getOctaveBoardSize())
{
- if (setKeyType || setChannel || setNote || setColour)
+ if (setKeyType || setChannel || setNote || setColour || setCCFaderPolarity)
{
auto mainComponent = TerpstraSysExApplication::getApp().getMainContentComponent();
jassert(mainComponent != nullptr);
@@ -115,14 +123,19 @@ namespace Lumatone {
{
mappingInEdit.sets[setSelection].theKeys[keySelection].colour = previousData.colour;
}
+ if (setCCFaderPolarity)
+ {
+ mappingInEdit.sets[setSelection].theKeys[keySelection].ccFaderDefault = previousData.ccFaderDefault;
+ }
+
// Send to device
- TerpstraSysExApplication::getApp().getLumatoneController().sendKeyParam(
+ TerpstraSysExApplication::getApp().getLumatoneController()->sendKeyParam(
setSelection + 1,
keySelection,
mappingInEdit.sets[setSelection].theKeys[keySelection]);
- // Notfy that there are changes: in calling function
+ // Notify that there are changes: in calling function
}
else
{
@@ -139,9 +152,195 @@ namespace Lumatone {
}
}
- int SingleNoteAssignAction::getSizeInUnits()
+
+ // ==============================================================================
+ // Implementation of SectionEditAction
+
+ SectionEditAction::SectionEditAction(int setSelection, TerpstraKeys& newSectionValue)
+ : setSelection(setSelection)
+ , newData(newSectionValue)
+ {
+ auto mainComponent = TerpstraSysExApplication::getApp().getMainContentComponent();
+ jassert(mainComponent != nullptr);
+
+ previousData = mainComponent->getMappingInEdit().sets[setSelection];
+ }
+
+ bool SectionEditAction::isValid() const
+ {
+ return setSelection >= 0 && setSelection < NUMBEROFBOARDS;
+ }
+
+ bool SectionEditAction::perform()
+ {
+ if (setSelection >= 0 && setSelection < NUMBEROFBOARDS)
+ {
+ auto mainComponent = TerpstraSysExApplication::getApp().getMainContentComponent();
+ jassert(mainComponent != nullptr);
+ TerpstraKeyMapping& mappingInEdit = mainComponent->getMappingInEdit();
+
+ mappingInEdit.sets[setSelection] = newData;
+
+ // Send to device
+ TerpstraSysExApplication::getApp().getLumatoneController()->sendAllParamsOfBoard(setSelection + 1, mappingInEdit.sets[setSelection]);
+
+ // Notify that there are changes: in calling function
+ return true;
+ }
+ else
+ {
+ jassertfalse;
+ return false;
+ }
+ }
+
+ bool SectionEditAction::undo()
+ {
+ if (setSelection >= 0 && setSelection < NUMBEROFBOARDS)
+ {
+ auto mainComponent = TerpstraSysExApplication::getApp().getMainContentComponent();
+ jassert(mainComponent != nullptr);
+ TerpstraKeyMapping& mappingInEdit = mainComponent->getMappingInEdit();
+
+ mappingInEdit.sets[setSelection] = previousData;
+
+ // Send to device
+ TerpstraSysExApplication::getApp().getLumatoneController()->sendAllParamsOfBoard(setSelection + 1, mappingInEdit.sets[setSelection]);
+
+ // Notify that there are changes: in calling function
+ return true;
+ }
+ else
+ {
+ jassertfalse;
+ return false;
+ }
+
+ }
+
+ // ==============================================================================
+ // Implementation of InvertFootControllerEditAction
+
+ InvertFootControllerEditAction::InvertFootControllerEditAction(bool newValue)
+ : newData(newValue)
{
- return 2 * sizeof(int) + 6 * sizeof(bool) + 2 * sizeof(TerpstraKey);
+ auto mainComponent = TerpstraSysExApplication::getApp().getMainContentComponent();
+ jassert(mainComponent != nullptr);
+
+ previousData = mainComponent->getMappingInEdit().invertExpression;
}
-}
\ No newline at end of file
+ bool InvertFootControllerEditAction::perform()
+ {
+ auto mainComponent = TerpstraSysExApplication::getApp().getMainContentComponent();
+ jassert(mainComponent != nullptr);
+ TerpstraKeyMapping& mappingInEdit = mainComponent->getMappingInEdit();
+
+ mappingInEdit.invertExpression = newData;
+
+ // Send to device
+ TerpstraSysExApplication::getApp().getLumatoneController()->sendInvertFootController(newData);
+
+ // Notify that there are changes: in calling function
+ return true;
+ }
+
+ bool InvertFootControllerEditAction::undo()
+ {
+ auto mainComponent = TerpstraSysExApplication::getApp().getMainContentComponent();
+ jassert(mainComponent != nullptr);
+ TerpstraKeyMapping& mappingInEdit = mainComponent->getMappingInEdit();
+
+ mappingInEdit.invertExpression = previousData;
+
+ // Send to device
+ TerpstraSysExApplication::getApp().getLumatoneController()->sendInvertFootController(previousData);
+
+ // Notify that there are changes: in calling function
+ return true;
+ }
+
+ // ==============================================================================
+ // Implementation of ExprPedalSensivityEditAction
+
+ ExprPedalSensivityEditAction::ExprPedalSensivityEditAction(int newValue)
+ : newData(newValue)
+ {
+ auto mainComponent = TerpstraSysExApplication::getApp().getMainContentComponent();
+ jassert(mainComponent != nullptr);
+
+ previousData = mainComponent->getMappingInEdit().expressionControllerSensivity;
+ }
+
+ bool ExprPedalSensivityEditAction::perform()
+ {
+ auto mainComponent = TerpstraSysExApplication::getApp().getMainContentComponent();
+ jassert(mainComponent != nullptr);
+ TerpstraKeyMapping& mappingInEdit = mainComponent->getMappingInEdit();
+
+ mappingInEdit.expressionControllerSensivity = newData;
+
+ // Send to device
+ TerpstraSysExApplication::getApp().getLumatoneController()->sendExpressionPedalSensivity(newData);
+
+ // Notify that there are changes: in calling function
+ return true;
+ }
+
+ bool ExprPedalSensivityEditAction::undo()
+ {
+ auto mainComponent = TerpstraSysExApplication::getApp().getMainContentComponent();
+ jassert(mainComponent != nullptr);
+ TerpstraKeyMapping& mappingInEdit = mainComponent->getMappingInEdit();
+
+ mappingInEdit.expressionControllerSensivity = previousData;
+
+ // Send to device
+ TerpstraSysExApplication::getApp().getLumatoneController()->sendExpressionPedalSensivity(previousData);
+
+ // Notify that there are changes: in calling function
+ return true;
+ }
+
+ // ==============================================================================
+ // Implementation of InvertSustainEditAction
+
+ InvertSustainEditAction::InvertSustainEditAction(bool newValue)
+ : newData(newValue)
+ {
+ auto mainComponent = TerpstraSysExApplication::getApp().getMainContentComponent();
+ jassert(mainComponent != nullptr);
+
+ previousData = mainComponent->getMappingInEdit().invertSustain;
+ }
+
+ bool InvertSustainEditAction::perform()
+ {
+ auto mainComponent = TerpstraSysExApplication::getApp().getMainContentComponent();
+ jassert(mainComponent != nullptr);
+ TerpstraKeyMapping& mappingInEdit = mainComponent->getMappingInEdit();
+
+ mappingInEdit.invertSustain = newData;
+
+ // Send to device
+ TerpstraSysExApplication::getApp().getLumatoneController()->invertSustainPedal(newData);
+
+ // Notify that there are changes: in calling function
+ return true;
+ }
+
+ bool InvertSustainEditAction::undo()
+ {
+ auto mainComponent = TerpstraSysExApplication::getApp().getMainContentComponent();
+ jassert(mainComponent != nullptr);
+ TerpstraKeyMapping& mappingInEdit = mainComponent->getMappingInEdit();
+
+ mappingInEdit.invertSustain = previousData;
+
+ // Send to device
+ TerpstraSysExApplication::getApp().getLumatoneController()->invertSustainPedal(previousData);
+
+ // Notify that there are changes: in calling function
+ return true;
+ }
+}
diff --git a/Source/EditActions.h b/Source/EditActions.h
index aea31e55..ce03210e 100644
--- a/Source/EditActions.h
+++ b/Source/EditActions.h
@@ -25,18 +25,21 @@ namespace Lumatone {
bool setChannel,
bool setNote,
bool setColour,
+ bool ccFaderDefault,
LumatoneKeyType newKeyType = LumatoneKeyType::noteOnNoteOff,
int newChannelNumber = 0,
int newNoteNumber = 0,
- TerpstraKey::COLOURTYPE newColour = juce::Colour());
+ TerpstraKey::COLOURTYPE newColour = juce::Colour(),
+ bool newCCFaderDefault = true);
- SingleNoteAssignAction(SingleNoteAssignAction& second)
+ SingleNoteAssignAction(const SingleNoteAssignAction& second)
: setSelection(second.setSelection)
, keySelection(second.keySelection)
, setKeyType(second.setKeyType)
, setChannel(second.setChannel)
, setNote(second.setNote)
, setColour(second.setColour)
+ , setCCFaderPolarity(second.setCCFaderPolarity)
, previousData(second.previousData)
, newData(second.newData)
{}
@@ -45,7 +48,8 @@ namespace Lumatone {
virtual bool perform() override;
virtual bool undo() override;
- int getSizeInUnits() override;
+
+ int getSizeInUnits() override { return sizeof(SingleNoteAssignAction); }
private:
int setSelection = - 1;
@@ -55,9 +59,88 @@ namespace Lumatone {
bool setChannel = false;
bool setNote = false;
bool setColour = false;
+ bool setCCFaderPolarity = false;
TerpstraKey previousData;
TerpstraKey newData;
};
-}
\ No newline at end of file
+ class SectionEditAction : public UndoableAction
+ {
+ public:
+ SectionEditAction(int setSelection, TerpstraKeys& newSectionValue);
+
+ SectionEditAction(const SectionEditAction& second)
+ : setSelection(second.setSelection)
+ , previousData(second.previousData)
+ , newData(second.newData)
+ {}
+
+ bool isValid() const;
+
+ virtual bool perform() override;
+ virtual bool undo() override;
+ int getSizeInUnits() override { return sizeof(SectionEditAction); }
+
+ private:
+ int setSelection = -1;
+
+ TerpstraKeys previousData;
+ TerpstraKeys newData;
+ };
+
+ class InvertFootControllerEditAction : public UndoableAction
+ {
+ public:
+ InvertFootControllerEditAction(bool newValue);
+
+ InvertFootControllerEditAction(const InvertFootControllerEditAction& second)
+ : previousData(second.previousData), newData(second.newData)
+ {}
+
+ virtual bool perform() override;
+ virtual bool undo() override;
+ int getSizeInUnits() override { return sizeof(InvertFootControllerEditAction); }
+
+ private:
+ bool previousData;
+ bool newData;
+ };
+
+ class ExprPedalSensivityEditAction : public UndoableAction
+ {
+ public:
+ ExprPedalSensivityEditAction(int newValue);
+
+ ExprPedalSensivityEditAction(const ExprPedalSensivityEditAction& second)
+ : previousData(second.previousData), newData(second.newData)
+ {}
+
+ virtual bool perform() override;
+ virtual bool undo() override;
+ int getSizeInUnits() override { return sizeof(ExprPedalSensivityEditAction); }
+
+ private:
+ int previousData;
+ int newData;;
+ };
+
+ class InvertSustainEditAction : public UndoableAction
+ {
+ public:
+ InvertSustainEditAction(bool newValue);
+
+ InvertSustainEditAction(const InvertSustainEditAction& second)
+ : previousData(second.previousData), newData(second.newData)
+ {}
+
+ virtual bool perform() override;
+ virtual bool undo() override;
+ int getSizeInUnits() override { return sizeof(InvertSustainEditAction); }
+
+ private:
+ int previousData;
+ int newData;;
+ };
+
+}
diff --git a/Source/FileBrowserComponent.h b/Source/FileBrowserComponent.h
index 798d1979..a96816af 100644
--- a/Source/FileBrowserComponent.h
+++ b/Source/FileBrowserComponent.h
@@ -18,11 +18,14 @@ class PathBrowserComponent : public Component, public Button::Listener
{
public:
- PathBrowserComponent(const String dialogBoxTitle, const File& fileIn = File::getSpecialLocation(File::SpecialLocationType::userDocumentsDirectory), String fileTypeFilter = "")
- : chooser(dialogBoxTitle, File(), fileTypeFilter)
+ PathBrowserComponent(const String dialogBoxTitle,
+ String fileTypeFilter,
+ File fileIn = File::getSpecialLocation(File::SpecialLocationType::userDocumentsDirectory))
{
setName(dialogBoxTitle);
+ chooser = std::make_unique(dialogBoxTitle, fileIn, fileTypeFilter);
+
editor.reset(new TextEditor(dialogBoxTitle + "Editor"));
editor->setMultiLine(false, false);
editor->setReadOnly(true);
@@ -51,11 +54,16 @@ class PathBrowserComponent : public Component, public Button::Listener
void buttonClicked(Button* buttonThatWasClicked) override
{
- if (chooser.browseForFileToOpen())
- {
- editor->setText(chooser.getResult().getFullPathName(), false);
- listeners.call(&PathBrowserComponent::Listener::fileChanged, this, chooser.getResult());
- }
+ chooser->launchAsync(FileBrowserComponent::FileChooserFlags::openMode | FileBrowserComponent::FileChooserFlags::canSelectFiles,
+ [&](const FileChooser& chooser)
+ {
+ auto result = chooser.getResult();
+ if (result.existsAsFile())
+ {
+ editor->setText(chooser.getResult().getFullPathName(), false);
+ listeners.call(&PathBrowserComponent::Listener::fileChanged, this, chooser.getResult());
+ }
+ });
}
TextEditor* getEditor() { return editor.get(); }
@@ -93,7 +101,7 @@ class PathBrowserComponent : public Component, public Button::Listener
private:
- FileChooser chooser;
+ std::unique_ptr chooser;
std::unique_ptr editor;
std::unique_ptr openButton;
diff --git a/Source/FirmwareTransfer.cpp b/Source/FirmwareTransfer.cpp
index a64fb632..7b287610 100644
--- a/Source/FirmwareTransfer.cpp
+++ b/Source/FirmwareTransfer.cpp
@@ -97,7 +97,7 @@ bool FirmwareTransfer::requestFirmwareUpdate(String firmwareFilePath)
selectedFileToTransfer = firmwareFilePath;
transferRequested = true;
- runThread();
+ launchThread();
return true;
}
@@ -142,7 +142,7 @@ void FirmwareTransfer::run()
else if (transferRequested)
{
- prepareForUpdate();
+ prepareAndRunUpdate();
transferRequested = false;
}
}
@@ -193,12 +193,15 @@ static FirmwareTransfer::StatusCode shutdownSSHSession(LIBSSH2_SESSION* session,
fclose(localFile);
DBG("All done.");
- libssh2_exit();
-
return returnCode;
}
-bool FirmwareTransfer::prepareForUpdate()
+void FirmwareTransfer::exitLibSsh2()
+{
+ libssh2_exit();
+}
+
+bool FirmwareTransfer::prepareAndRunUpdate()
{
StatusCode returnStatus = StatusCode::Initialize;
listeners.call(&FirmwareTransfer::ProcessListener::firmwareTransferUpdate, returnStatus, statusCodeToMessage(returnStatus));
@@ -314,9 +317,10 @@ FirmwareTransfer::StatusCode FirmwareTransfer::performFirmwareUpdate()
return StatusCode::StartupErr;
}
+ // Make sure we release libssh2 before app is shutdown
+ TerpstraSysExApplication::getApp().setFirmwareUpdatePerformed(true);
-
-#ifdef JUCE_WIN
+#if JUCE_WINDOWS
// Create socket and connect to port 22
sock = socket(AF_INET, SOCK_STREAM, 0);
@@ -339,11 +343,11 @@ FirmwareTransfer::StatusCode FirmwareTransfer::performFirmwareUpdate()
{
DBG("failed to connect!");
-#if WIN32
+ #if WIN32
closesocket(sock);
-#else
+ #else
close(sock);
-#endif
+ #endif
return StatusCode::HostConnectErr;
}
diff --git a/Source/FirmwareTransfer.h b/Source/FirmwareTransfer.h
index f3017bcb..d09ebc77 100644
--- a/Source/FirmwareTransfer.h
+++ b/Source/FirmwareTransfer.h
@@ -74,11 +74,18 @@ class FirmwareTransfer : public juce::ThreadWithProgressWindow
// TODO use error codes
static bool checkFirmwareFileIntegrity(String filePathIn);
+
+public:
+
+ // Deinitialize libssh2 library
+ static void exitLibSsh2();
+
public:
class ProcessListener
{
public:
+ virtual ~ProcessListener() {}
virtual void firmwareTransferUpdate(FirmwareTransfer::StatusCode statusCode, String msg)=0;
};
@@ -94,7 +101,7 @@ class FirmwareTransfer : public juce::ThreadWithProgressWindow
private:
// Return true if update was successful
- bool prepareForUpdate();
+ bool prepareAndRunUpdate();
StatusCode performFirmwareUpdate();
// header only in .cpp
@@ -122,7 +129,7 @@ class FirmwareTransfer : public juce::ThreadWithProgressWindow
int numberOfWaitIncrements = 0;
// Estimation based on boot time of ~85 seconds, plus transfer time, and overhead
- const int maxUpdateIncrements = 300000 / UPDATETIMEOUT;
+ const int maxUpdateIncrements = 250000 / UPDATETIMEOUT;
public:
@@ -158,7 +165,8 @@ class FirmwareTransfer : public juce::ThreadWithProgressWindow
return translate("Error: Could not prepare device communication protool");
case FirmwareTransfer::StatusCode::HostConnectErr:
- return translate("Error: Could not communicate with Lumatone");
+ return translate("Error: Could not communicate with Lumatone"
+ "\nPlease make sure you are connected over USB.");
case FirmwareTransfer::StatusCode::SessionEstErr:
return translate("Error: Could not verify connection with Lumatone");
diff --git a/Source/GeneralOptionsDlg.cpp b/Source/GeneralOptionsDlg.cpp
index 2e0ffe6c..06c45f15 100644
--- a/Source/GeneralOptionsDlg.cpp
+++ b/Source/GeneralOptionsDlg.cpp
@@ -7,7 +7,7 @@
the "//[xyz]" and "//[/xyz]" sections will be retained when the file is loaded
and re-saved.
- Created with Projucer version: 6.0.5
+ Created with Projucer version: 6.0.8
------------------------------------------------------------------------------
@@ -29,29 +29,46 @@
//==============================================================================
GeneralOptionsDlg::GeneralOptionsDlg ()
- : Component("GeneralOptionsDlg")
{
//[Constructor_pre] You can add your own custom stuff here..
//[/Constructor_pre]
- labelGeneralSettingslTitle.reset (new juce::Label ("labelGeneralSettingslTitle", translate("GeneralSettings")));
+ labelGeneralSettingslTitle.reset (new juce::Label ("labelGeneralSettingslTitle",
+ TRANS("General Settings")));
addAndMakeVisible (labelGeneralSettingslTitle.get());
- labelGeneralSettingslTitle->setFont(TerpstraSysExApplication::getApp().getAppFont(LumatoneEditorFont::UniviaProBold));
+ labelGeneralSettingslTitle->setFont (juce::Font (18.00f, juce::Font::plain).withTypefaceStyle ("Regular"));
+ labelGeneralSettingslTitle->setJustificationType (juce::Justification::centredLeft);
+ labelGeneralSettingslTitle->setEditable (false, false, false);
+ labelGeneralSettingslTitle->setColour (juce::Label::textColourId, juce::Colour (0xff61acc8));
+ labelGeneralSettingslTitle->setColour (juce::TextEditor::textColourId, juce::Colours::black);
+ labelGeneralSettingslTitle->setColour (juce::TextEditor::backgroundColourId, juce::Colour (0x00000000));
+ labelGeneralSettingslTitle->setBounds (8, 0, 104, 24);
buttonAfterTouchActive.reset (new juce::ToggleButton ("buttonAfterTouchActive"));
addAndMakeVisible (buttonAfterTouchActive.get());
- buttonAfterTouchActive->setButtonText (translate("PolyphonicAftertouch"));
+ buttonAfterTouchActive->setButtonText (TRANS("Polyphonic Aftertouch"));
buttonAfterTouchActive->addListener (this);
+ buttonAfterTouchActive->setBounds (8, 32, 176, 24);
+
buttonLightOnKeyStrokes.reset (new juce::ToggleButton ("buttonLightOnKeyStrokes"));
addAndMakeVisible (buttonLightOnKeyStrokes.get());
- buttonLightOnKeyStrokes->setButtonText (translate("LightOnKeystrokes"));
+ buttonLightOnKeyStrokes->setButtonText (TRANS("Light on Keystrokes"));
buttonLightOnKeyStrokes->addListener (this);
+ buttonLightOnKeyStrokes->setBounds (8, 64, 176, 24);
+
+
//[UserPreSize]
+ labelGeneralSettingslTitle->setFont(TerpstraSysExApplication::getApp().getAppFont(LumatoneEditorFont::UniviaProBold));
+
+ TerpstraSysExApplication::getApp().getLumatoneController()->addFirmwareListener(this);
//[/UserPreSize]
+ setSize (188, 96);
+
+
//[Constructor] You can add your own custom stuff here..
//[/Constructor]
}
@@ -59,6 +76,7 @@ GeneralOptionsDlg::GeneralOptionsDlg ()
GeneralOptionsDlg::~GeneralOptionsDlg()
{
//[Destructor_pre]. You can add your own custom destruction code here..
+ TerpstraSysExApplication::getApp().getLumatoneController()->removeFirmwareListener(this);
//[/Destructor_pre]
labelGeneralSettingslTitle = nullptr;
@@ -74,11 +92,11 @@ GeneralOptionsDlg::~GeneralOptionsDlg()
void GeneralOptionsDlg::paint (juce::Graphics& g)
{
//[UserPrePaint] Add your own custom painting code here..
- //[/UserPrePaint]
- //g.fillAll (juce::Colour (0xffbad0de));
+ //[/UserPrePaint]
//[UserPaint] Add your own custom painting code here..
+
g.setColour(Colour(0xff212626));
g.fillRoundedRectangle(getLocalBounds().toFloat().withTop(proportionOfHeight(SETTINGSAREAMARGINHEIGHT)), roundedCornerSize);
//[/UserPaint]
@@ -91,7 +109,7 @@ void GeneralOptionsDlg::resized()
//[UserResized] Add your own custom resize handling here..
- roundedCornerSize = round(getParentHeight() * ROUNDEDCORNERTOAPPHEIGHT);
+ roundedCornerSize = roundToInt(getParentHeight() * ROUNDEDCORNERTOAPPHEIGHT);
resizeLabelWithHeight(labelGeneralSettingslTitle.get(), roundToInt(getHeight() * SETTINGSLABELHEIGHT));
labelGeneralSettingslTitle->setTopLeftPosition(roundToInt(getWidth() * SETTINGSLABELMARGINWIDTH), 0);
@@ -119,7 +137,7 @@ void GeneralOptionsDlg::buttonClicked (juce::Button* buttonThatWasClicked)
//[UserButtonCode_buttonAfterTouchActive] -- add your button handler code here..
((MainContentComponent*)getParentComponent())->getMappingInEdit().afterTouchActive = buttonAfterTouchActive->getToggleState();
TerpstraSysExApplication::getApp().setHasChangesToSave(true);
- TerpstraSysExApplication::getApp().getLumatoneController().setAftertouchEnabled(buttonAfterTouchActive->getToggleState());
+ TerpstraSysExApplication::getApp().getLumatoneController()->setAftertouchEnabled(buttonAfterTouchActive->getToggleState());
//[/UserButtonCode_buttonAfterTouchActive]
}
else if (buttonThatWasClicked == buttonLightOnKeyStrokes.get())
@@ -127,7 +145,7 @@ void GeneralOptionsDlg::buttonClicked (juce::Button* buttonThatWasClicked)
//[UserButtonCode_buttonLightOnKeyStrokes] -- add your button handler code here..
((MainContentComponent*)getParentComponent())->getMappingInEdit().lightOnKeyStrokes = buttonLightOnKeyStrokes->getToggleState();
TerpstraSysExApplication::getApp().setHasChangesToSave(true);
- TerpstraSysExApplication::getApp().getLumatoneController().sendLightOnKeyStrokes(buttonLightOnKeyStrokes->getToggleState());
+ TerpstraSysExApplication::getApp().getLumatoneController()->sendLightOnKeyStrokes(buttonLightOnKeyStrokes->getToggleState());
//[/UserButtonCode_buttonLightOnKeyStrokes]
}
@@ -155,6 +173,12 @@ void GeneralOptionsDlg::loadFromMapping()
buttonLightOnKeyStrokes->setToggleState(mappingInEdit.lightOnKeyStrokes, juce::NotificationType::dontSendNotification);
}
+void GeneralOptionsDlg::presetFlagsReceived(PresetFlags presetFlags)
+{
+ buttonAfterTouchActive->setToggleState(presetFlags.polyphonicAftertouch, dontSendNotification);
+ buttonLightOnKeyStrokes->setToggleState(presetFlags.lightsOnKeystroke, dontSendNotification);
+}
+
//[/MiscUserCode]
@@ -168,10 +192,11 @@ void GeneralOptionsDlg::loadFromMapping()
BEGIN_JUCER_METADATA
-
+ parentClasses="public Component, public LumatoneEditor::FirmwareListener"
+ constructorParams="" variableInitialisers="" snapPixels="8" snapActive="1"
+ snapShown="1" overlayOpacity="0.330" fixedSize="1" initialWidth="188"
+ initialHeight="96">
+
void cacheImages()
{
- //ImageCache::addImageToCache(ImageCache::getFromMemory(BinaryData::ImportIcon4x_png, BinaryData::ImportIcon4x_pngSize), LumatoneEditorAssets::ImportIcon);
- //ImageCache::addImageToCache(ImageCache::getFromMemory(BinaryData::SaveIcon4x_png, BinaryData::SaveIcon4x_pngSize), LumatoneEditorAssets::SaveIcon);
- //ImageCache::addImageToCache(ImageCache::getFromMemory(BinaryData::LoadIcon4x_png, BinaryData::LoadIcon4x_pngSize), LumatoneEditorAssets::LoadIcon);
ImageCache::addImageToCache(ImageCache::getFromMemory(BinaryData::KeyboardBase_png, BinaryData::KeyboardBase_pngSize), LumatoneEditorAssets::LumatoneGraphic);
ImageCache::addImageToCache(ImageCache::getFromMemory(BinaryData::KeybedShadows_png, BinaryData::KeybedShadows_pngSize), LumatoneEditorAssets::KeybedShadows);
ImageCache::addImageToCache(ImageCache::getFromMemory(BinaryData::KeyShape2x_png, BinaryData::KeyShape2x_pngSize), LumatoneEditorAssets::KeyShape);
@@ -1271,9 +1317,6 @@ class LumatoneEditorLookAndFeel : public LookAndFeel_V4
setColour(AlertWindow::ColourIds::outlineColourId, findColour(LumatoneEditorColourIDs::MediumBackground));
}
-public:
- LumatoneEditorCompactWindow compactWindowStyle;
-
private:
LumatoneEditorFonts::Library appFonts;
@@ -1289,6 +1332,10 @@ class LumatoneEditorLookAndFeel : public LookAndFeel_V4
const float comboBoxRoundedCornerScalar = 0.304878f;
- const float rotaryAngleStart = float_Pi * -0.64f; // pi * -2/3
+ const float rotaryAngleStart = MathConstants::pi * -0.64f; // pi * -2/3
const float rotaryAngleEnd = -rotaryAngleStart;
+
+public:
+ LumatoneEditorCompactWindow compactWindowStyle;
+
};
diff --git a/Source/LumatoneEditorStyleCommon.h b/Source/LumatoneEditorStyleCommon.h
index 8f722e5e..f23654da 100644
--- a/Source/LumatoneEditorStyleCommon.h
+++ b/Source/LumatoneEditorStyleCommon.h
@@ -32,7 +32,7 @@
#define SETTINGSAREAMARGINHEIGHT 0.1714f
-#define SETTINGSLABELHEIGHT 0.14f
+#define SETTINGSLABELHEIGHT 0.13f
#define SETTINGSLABELMARGINWIDTH 0.01f
#define SETTINGSCONTROLMARGINTOAPPWIDTH 0.01171875f
@@ -40,7 +40,7 @@
#if JUCE_MAC
#define GLOBALFONTSCALAR 0.9f
- #define CONTROLBOXFONTHEIGHTSCALAR 0.66f
+ #define CONTROLBOXFONTHEIGHTSCALAR 0.7f
#elif JUCE_WINDOWS
#define GLOBALFONTSCALAR 1.0f
#define CONTROLBOXFONTHEIGHTSCALAR 0.8f
@@ -101,7 +101,7 @@ static Path getConnectedRoundedRectPath(Rectangle bounds, float roundedCo
{
yTo = endpoint.y - roundedCornerSize;
rect.lineTo(endpoint.x, yTo);
- rect.addArc(endpoint.x - roundedCornerSize, yTo, roundedCornerSize, roundedCornerSize, PATH_PI_2_CW, float_Pi);
+ rect.addArc(endpoint.x - roundedCornerSize, yTo, roundedCornerSize, roundedCornerSize, PATH_PI_2_CW, MathConstants::pi);
}
if (connectedFlags & Button::ConnectedEdgeFlags::ConnectedOnBottom || connectedFlags & Button::ConnectedEdgeFlags::ConnectedOnLeft)
@@ -111,7 +111,7 @@ static Path getConnectedRoundedRectPath(Rectangle bounds, float roundedCo
else
{
rect.lineTo(roundedCornerSize, endpoint.y);
- rect.addArc(origin.x, endpoint.y - roundedCornerSize, roundedCornerSize, roundedCornerSize, -float_Pi, PATH_PI_2_CCW);
+ rect.addArc(origin.x, endpoint.y - roundedCornerSize, roundedCornerSize, roundedCornerSize, -MathConstants::pi, PATH_PI_2_CCW);
}
rect.closeSubPath();
@@ -143,7 +143,7 @@ static Path getDiagonalRoundedCornersPath(Rectangle bounds, float rounded
{
xTo += roundedCornerSize;
rect.lineTo(xTo, yTo);
- rect.addArc(xTo - roundedCornerSize, yTo - roundedCornerSize, roundedCornerSize, roundedCornerSize, -float_Pi, PATH_PI_2_CCW);
+ rect.addArc(xTo - roundedCornerSize, yTo - roundedCornerSize, roundedCornerSize, roundedCornerSize, -MathConstants::pi, PATH_PI_2_CCW);
}
else
{
@@ -161,7 +161,7 @@ static Path getDiagonalRoundedCornersPath(Rectangle bounds, float rounded
{
yTo -= roundedCornerSize;
rect.lineTo(xTo, yTo);
- rect.addArc(xTo - roundedCornerSize, yTo, roundedCornerSize, roundedCornerSize, PATH_PI_2_CW, float_Pi);
+ rect.addArc(xTo - roundedCornerSize, yTo, roundedCornerSize, roundedCornerSize, PATH_PI_2_CW, MathConstants::pi);
}
else
{
@@ -271,10 +271,10 @@ static void addArcToPath(Path& pathIn, Rectangle& ellipseBounds, float fr
///
///
///
-static void setWidthRetainingAspectRatio(Component* component, const Image& image, int widthIn)
-{
- component->setSize(widthIn, round(image.getHeight() / (float) image.getWidth() * widthIn));
-}
+//static void setWidthRetainingAspectRatio(Component* component, const Image& image, int widthIn)
+//{
+// component->setSize(widthIn, round(image.getHeight() / (float) image.getWidth() * widthIn));
+//}
///
/// Sets the height of a component while retaining the aspect ratio of a given image
@@ -282,10 +282,10 @@ static void setWidthRetainingAspectRatio(Component* component, const Image& imag
///
///
///
-static void setHeightRetainingAspectRatio(Component* component, const Image& image, int heightIn)
-{
- component->setSize(round(image.getWidth() / (float) image.getHeight() * heightIn), heightIn);
-}
+//static void setHeightRetainingAspectRatio(Component* component, const Image& image, int heightIn)
+//{
+// component->setSize(round(image.getWidth() / (float) image.getHeight() * heightIn), heightIn);
+//}
///
/// Sets the width of an ImageComponent while retaining the aspect ratio of its image
@@ -304,10 +304,10 @@ static void setHeightRetainingAspectRatio(Component* component, const Image& ima
///
///
///
-static void setHeightRetainingAspectRatio(ImageComponent* component, int heightIn)
-{
- setHeightRetainingAspectRatio(component, component->getImage(), heightIn);
-}
+//static void setHeightRetainingAspectRatio(ImageComponent* component, int heightIn)
+//{
+// setHeightRetainingAspectRatio(component, component->getImage(), heightIn);
+//}
///
/// Sets the width of an ImageButton while retaining the aspect ratio of its normal image
@@ -315,10 +315,10 @@ static void setHeightRetainingAspectRatio(ImageComponent* component, int heightI
///
///
///
-static void setWidthRetainingAspectRatio(ImageButton* component, int widthIn)
-{
- setWidthRetainingAspectRatio(component, component->getNormalImage(), widthIn);
-}
+//static void setWidthRetainingAspectRatio(ImageButton* component, int widthIn)
+//{
+// setWidthRetainingAspectRatio(component, component->getNormalImage(), widthIn);
+//}
///
/// Sets the height of an ImageButton while retaining the aspect ratio of its normal image
@@ -326,10 +326,10 @@ static void setWidthRetainingAspectRatio(ImageButton* component, int widthIn)
///
///
///
-static void setHeightRetainingAspectRatio(ImageButton* component, int heightIn)
-{
- setHeightRetainingAspectRatio(component, component->getNormalImage(), heightIn);
-}
+//static void setHeightRetainingAspectRatio(ImageButton* component, int heightIn)
+//{
+// setHeightRetainingAspectRatio(component, component->getNormalImage(), heightIn);
+//}
static void resizeLabelWithHeight(Label* label, int height, float fontHeightScalar = 1.0f, String textSuffix = "_")
{
@@ -381,7 +381,7 @@ static float scalarToFitString(Label& labelIn)
static void resizeToggleButtonWithHeight(ToggleButton* btn, Font font, int heightIn, String textSuffix = "_")
{
font.setHeight(font.getHeight() * GLOBALFONTSCALAR);
- btn->setSize(btn->getHeight() + round(font.getStringWidth(btn->getButtonText() + textSuffix)), heightIn);
+ btn->setSize(btn->getHeight() + roundToInt(font.getStringWidth(btn->getButtonText() + textSuffix)), heightIn);
}
static void drawPathToFillBounds(Graphics& g, const Path& path, Rectangle boundsToFill)
@@ -403,7 +403,7 @@ static Path createLogomark()
float phi2 = PHI * 2;
float innerRad = 1.0f / phi2;
float outerRad = phi2 * 0.125f;
- float ang = float_Pi * 0.083333f;
+ float ang = MathConstants::pi * 0.083333f;
float angOff = ang * 0.5f;
logo.addPolygon(center, 6, innerRad, ang - angOff);
@@ -550,8 +550,32 @@ static void getCCPolarityIconPath(bool inverted, Path& arrowPath, Path& faderPat
}
}
-//static void drawSaveIconAt(Graphics& g, int x, int y)
+static Path getCloneIconPath()
+{
+ // side-by-side
+// float yMargin = 0.1f;
+// float height = 1.0f - yMargin * 2.0f;
+//
+// float width = 5.0f/12.0f;
+//
+// auto leftRect = Rectangle(1.0/12.0f, yMargin, width, height);
+// auto rightRect = Rectangle(width, yMargin, width, height);
+
+ // bottom left overlapping top right
+
+ float xMargin = 0.1f;
+ float yMargin = 0.1f;
+ float size = 0.5f;
+
+ auto leftRect = Rectangle(xMargin, 1.0f - yMargin - size, size, size);
+ auto rightRect = Rectangle(1.0f - xMargin - size, yMargin, size, size);
+
+ auto path = Path();
+ path.addRoundedRectangle(leftRect, 0.1f, 0.1f);
+ path.addRoundedRectangle(rightRect, 0.1f, 0.1f);
+ return path;
+}
// Hash codes for use with ImageCache::getFromHashCode()
enum LumatoneEditorAssets
@@ -566,9 +590,31 @@ enum LumatoneEditorAssets
TickBox = 0x0003100,
SavePalette = 0x0005000,
CancelPalette = 0x0005001,
- TrashCanIcon = 0x0005002
+ TrashCanIcon = 0x0005002,
+ CloneIcon = 0x0005003
};
+// TODO: clean up / make a better routine with ImageCache usage
+static Image getCachedCloneImage()
+{
+ auto cloneImg = ImageCache::getFromHashCode(LumatoneEditorAssets::CloneIcon);
+ if (cloneImg.isValid())
+ return cloneImg;
+
+ // Create duplicate icon
+ auto cloneIcon = getCloneIconPath();
+ cloneIcon.scaleToFit(0, 0, 80, 80, true);
+
+ cloneImg = Image(Image::PixelFormat::ARGB, 100, 100, true);
+ Graphics cloneG(cloneImg);
+ cloneG.setColour(Colours::white.darker(0.1f));
+ cloneG.setOrigin(Point(10, 10));
+ auto stroke = PathStrokeType(8.0f, PathStrokeType::JointStyle::curved);
+ cloneG.strokePath(cloneIcon, stroke);
+ ImageCache::addImageToCache(cloneImg, LumatoneEditorAssets::CloneIcon);
+ return cloneImg;
+}
+
enum LumatoneEditorIcon
{
Checkmark = 0x01,
@@ -650,3 +696,32 @@ namespace LumatoneEditorStyleIDs
static Identifier roundedDiagonalCorners = Identifier("RoundedDiagonalCorners");
}
+
+// LookAndFeel doesn't have Slider IncDec button access in drawIncDecButtonsBackground
+class TextButtonMouseHighlight : public TextButton
+{
+
+ Colour highlightColour;
+
+public:
+
+ TextButtonMouseHighlight(Colour highlightColourIn = Colour())
+ : highlightColour(highlightColourIn) {}
+
+ ~TextButtonMouseHighlight() {}
+
+ void paint(Graphics& g) override
+ {
+ TextButton::paint(g);
+
+ if (isEnabled() && isMouseOver())
+ {
+ auto bounds = getLocalBounds().toFloat();
+
+ Colour c = (isMouseButtonDown()) ? highlightColour.overlaidWith(Colours::white.withAlpha(0.1f))
+ : highlightColour;
+ g.setColour(c);
+ g.fillRoundedRectangle(bounds, getHeight() * 0.25f);
+ }
+ }
+};
diff --git a/Source/LumatoneFirmwareDefinitions.h b/Source/LumatoneFirmwareDefinitions.h
index 86cfb332..e5efb480 100644
--- a/Source/LumatoneFirmwareDefinitions.h
+++ b/Source/LumatoneFirmwareDefinitions.h
@@ -146,25 +146,35 @@ System exclusive command bytes
#define CALLIBRATE_EXPRESSION_PEDAL 0x38
#define RESET_EXPRESSION_PEDAL_BOUNDS 0x39
-// Firmware Version 1.0.12
+// Firmware Version 1.0.12 / 1.1.0
#define GET_BOARD_THRESHOLD_VALUES 0x3A
#define GET_BOARD_SENSITIVITY_VALUES 0x3B
-// Firmware Version 1.0.13
#define SET_PERIPHERAL_CHANNELS 0x3C
#define GET_PERIPHERAL_CHANNELS 0x3D
#define PERIPHERAL_CALBRATION_DATA 0x3E
-// Firmware Version 1.0.14
#define SET_AFTERTOUCH_TRIGGER_DELAY 0x3F
#define GET_AFTERTOUCH_TRIGGER_DELAY 0x40
-// Firmware Version 1.0.15
#define SET_LUMATOUCH_NOTE_OFF_DELAY 0x41
#define GET_LUMATOUCH_NOTE_OFF_DELAY 0x42
#define SET_EXPRESSION_PEDAL_THRESHOLD 0x43
#define GET_EXPRESSION_PEDAL_THRESHOLD 0x44
#define INVERT_SUSTAIN_PEDAL 0x45
+#define RESET_DEFAULT_PRESETS 0x46
+#define GET_PRESET_FLAGS 0x47
+#define GET_EXPRESSION_PEDAL_SENSITIVIY 0x48
+
+#define GET_MACRO_LIGHT_INTENSITY 0x49
+#define RESET_MACRO_LIGHT_INTENSITY 0x4A
+
+#define RESET_BOARD_KEYS 0x4B
+#define RESET_AFTERTOUCH_TRIGGER_DELAY 0x4C
+
+#define RESET_LUMATOUCH_NOTE_OFF_DELAY 0x4D
+#define GET_PITCH_AND_MOD_BOUNDS 0x4E
+#define GET_EXPRESSION_PEDAL_BOUNDS 0x4F
typedef enum
{
@@ -177,12 +187,19 @@ typedef enum
typedef enum
{
+ disabledDefault = 0,
noteOnNoteOff = 1,
continuousController = 2,
lumaTouch = 3,
disabled = 4
} LumatoneKeyType;
+typedef enum
+{
+ ExpressionPedal = 0,
+ PitchAndModWheels
+} PeripheralCalibrationDataMode;
+
enum class LumatoneFirmwareVersion
{
NO_VERSION = 0, // Used for instantiation
@@ -198,10 +215,9 @@ enum class LumatoneFirmwareVersion
VERSION_1_0_10,
VERSION_1_0_11,
VERSION_1_0_12,
- VERSION_1_0_13,
- VERSION_1_0_14,
- VERSION_1_0_15,
- LAST_VERSION = VERSION_1_0_15,
+ VERSION_1_1_0 = VERSION_1_0_12,
+ VERSION_1_2_0,
+ LAST_VERSION = VERSION_1_2_0,
FUTURE_VERSION = 0xFFFF // Used when version is nonnegative and below 9.9.999
} ;
@@ -218,6 +234,15 @@ struct FirmwareVersion
String toString() const { return String(major) + "." + String(minor) + "." + String(revision); }
+ String toDisplayString() const
+ {
+ String str = String(major) + "." + String(minor);
+ if (revision > 0)
+ str += ("." + String(revision));
+
+ return str;
+ }
+
//============================================================================
static FirmwareVersion fromString(String firmwareVersion)
@@ -289,7 +314,8 @@ struct FirmwareSupport
messageIsNotResponseToCommand,
messageIsNotSysEx,
unknownCommand,
- externalError
+ externalError,
+ commandNotImplemented
};
String errorToString(Error err)
@@ -322,6 +348,8 @@ struct FirmwareSupport
return "Unknown command / Not Acknowledged";
case Error::externalError:
return "Error from device";
+ case Error::commandNotImplemented:
+ return "Command handling not implemented";
default:
return "Undefined error..";
}
@@ -335,21 +363,40 @@ struct FirmwareSupport
else if ((versionIn.major < 0) | (versionIn.minor < 0) | (versionIn.revision < 0))
return LumatoneFirmwareVersion::UNKNOWN_VERSION;
+ // MAJOR: 1
else if (versionIn.major == 1)
{
+ // MINOR: 0
if (versionIn.minor == 0)
{
if (versionIn.revision < 3)
return LumatoneFirmwareVersion::VERSION_55_KEYS;
+ // Computing is probably not the best thing to do but edge cases are extremely unlikely here
else if (versionIn.revision - 3 > (int)LumatoneFirmwareVersion::LAST_VERSION - (int)LumatoneFirmwareVersion::VERSION_1_0_3)
return LumatoneFirmwareVersion::FUTURE_VERSION;
else if (versionIn.revision >= 3)
return (LumatoneFirmwareVersion)((int)LumatoneFirmwareVersion::VERSION_1_0_3 + (versionIn.revision - 3));
}
+
+ // MINOR: 1
+ else if (versionIn.minor == 1)
+ {
+ if (versionIn.revision == 0)
+ return LumatoneFirmwareVersion::VERSION_1_1_0;
+ }
+
+ else if (versionIn.minor == 2)
+ {
+ if (versionIn.revision == 0)
+ return LumatoneFirmwareVersion::VERSION_1_2_0;
+ }
+
+ return LumatoneFirmwareVersion::FUTURE_VERSION;
}
+ // Unsure if this is needed, or if returning FUTURE_VERSION without a condition is better
else if (versionIn.major < 9 && versionIn.minor < 9 && versionIn.revision < 999)
return LumatoneFirmwareVersion::FUTURE_VERSION;
@@ -361,22 +408,22 @@ struct FirmwareSupport
LumatoneFirmwareVersion getLowestVersionAcknowledged(unsigned int CMD)
{
if (CMD < CHANGE_KEY_NOTE) // 0x00
- LumatoneFirmwareVersion::UNKNOWN_VERSION;
+ return LumatoneFirmwareVersion::UNKNOWN_VERSION;
else if (CMD <= GET_SERIAL_IDENTITY) // 0x23
- LumatoneFirmwareVersion::NO_VERSION;
+ return LumatoneFirmwareVersion::NO_VERSION;
else if (CMD <= DEMO_MODE) // 0x25
- LumatoneFirmwareVersion::VERSION_1_0_5;
+ return LumatoneFirmwareVersion::VERSION_1_0_5;
else if (CMD <= CALIBRATE_PITCH_MOD_WHEEL) // 0x26
- LumatoneFirmwareVersion::VERSION_1_0_6;
+ return LumatoneFirmwareVersion::VERSION_1_0_6;
else if (CMD <= SET_KEY_MAX_THRESHOLD) // 0x29
- LumatoneFirmwareVersion::VERSION_1_0_7;
+ return LumatoneFirmwareVersion::VERSION_1_0_7;
else if (CMD <= GET_FIRMWARE_REVISION) // 0x31
- LumatoneFirmwareVersion::VERSION_1_0_8;
+ return LumatoneFirmwareVersion::VERSION_1_0_8;
else if (CMD <= LUMA_PING) // 0x33
return LumatoneFirmwareVersion::VERSION_1_0_9;
@@ -387,17 +434,8 @@ struct FirmwareSupport
else if (CMD <= RESET_EXPRESSION_PEDAL_BOUNDS) // 0x39
return LumatoneFirmwareVersion::VERSION_1_0_11;
- else if (CMD <= GET_BOARD_SENSITIVITY_VALUES) // 0x3B
- return LumatoneFirmwareVersion::VERSION_1_0_12;
-
- else if (CMD <= PERIPHERAL_CALBRATION_DATA) // 0x3E
- return LumatoneFirmwareVersion::VERSION_1_0_13;
-
- else if (CMD <= SET_AFTERTOUCH_TRIGGER_DELAY) // 0x3F
- return LumatoneFirmwareVersion::VERSION_1_0_14;
-
- else if (CMD <= INVERT_SUSTAIN_PEDAL) //0x45
- return LumatoneFirmwareVersion::VERSION_1_0_15;
+ else if (CMD <= GET_EXPRESSION_PEDAL_BOUNDS) // 0x4F
+ return LumatoneFirmwareVersion::VERSION_1_1_0;
else
return LumatoneFirmwareVersion::FUTURE_VERSION;
@@ -434,3 +472,104 @@ struct FirmwareSupport
}
};
+
+typedef enum
+{
+ PitchWheel = 0,
+ ModWheel,
+ Expression,
+ Sustain
+} PeripheralChannel;
+
+struct PeripheralChannelSettings
+{
+ int pitchWheel = 1;
+ int modWheel = 1;
+ int expressionPedal = 1;
+ int sustainPedal = 1;
+
+ void setChannel(PeripheralChannel controlId, int channelIn)
+ {
+ if (channelIn < 1)
+ channelIn = 1;
+
+ if (channelIn > 16)
+ channelIn = 16;
+
+ switch (controlId)
+ {
+ case PeripheralChannel::PitchWheel:
+ pitchWheel = channelIn;
+ break;
+
+ case PeripheralChannel::ModWheel:
+ modWheel = channelIn;
+ break;
+
+ case PeripheralChannel::Expression:
+ expressionPedal = channelIn;
+ break;
+
+ case PeripheralChannel::Sustain:
+ sustainPedal = channelIn;
+ break;
+ }
+ }
+
+ int getChannel(PeripheralChannel controlId)
+ {
+ switch (controlId)
+ {
+ case PeripheralChannel::PitchWheel:
+ return pitchWheel;
+
+ case PeripheralChannel::ModWheel:
+ return modWheel;
+
+ case PeripheralChannel::Expression:
+ return expressionPedal;
+
+ case PeripheralChannel::Sustain:
+ return sustainPedal;
+ }
+
+ return 0;
+ }
+};
+
+#define ADCSCALAR 2.44140625e-4;
+
+struct WheelsCalibrationData
+{
+ int centerPitch = 0;
+ int minPitch = 0;
+ int maxPitch = 0;
+
+ int minMod = 0;
+ int maxMod = 0;
+
+ float getCentrePitchNorm() const { return centerPitch * ADCSCALAR; }
+ float getMinPitchNorm() const { return minPitch * ADCSCALAR; }
+ float getMaxPitchNorm() const { return maxPitch * ADCSCALAR; }
+ float getMinModNorm() const { return minMod * ADCSCALAR; }
+ float getMaxModNorm() const { return maxMod * ADCSCALAR; }
+
+ String toString() const
+ {
+ String str;
+ str += ("Center Pitch: " + String(centerPitch) + newLine);
+ str += (" Min Pitch: " + String(minPitch) + newLine);
+ str += (" Max Pitch: " + String(maxPitch) + newLine);
+ str += (" Min Mod: " + String(minMod) + newLine);
+ str += (" Max Mod: " + String(maxMod) + newLine);
+ return str;
+ }
+};
+
+struct PresetFlags
+{
+ bool expressionPedalInverted = false;
+ bool lightsOnKeystroke = false;
+ bool polyphonicAftertouch = false;
+ bool sustainPedalInverted = false;
+};
diff --git a/Source/LumatoneMenu.cpp b/Source/LumatoneMenu.cpp
index 655244d6..85173e40 100644
--- a/Source/LumatoneMenu.cpp
+++ b/Source/LumatoneMenu.cpp
@@ -51,6 +51,10 @@ namespace Lumatone {
menu.addCommandItem(theManager, deleteOctaveBoard);
menu.addCommandItem(theManager, copyOctaveBoard);
menu.addCommandItem(theManager, pasteOctaveBoard);
+ menu.addCommandItem(theManager, pasteOctaveBoardChannels);
+ menu.addCommandItem(theManager, pasteOctaveBoardNotes);
+ menu.addCommandItem(theManager, pasteOctaveBoardColours);
+ menu.addCommandItem(theManager, pasteOctaveBoardTypes);
menu.addCommandItem(theManager, undo);
menu.addCommandItem(theManager, redo);
}
@@ -82,4 +86,4 @@ namespace Lumatone {
}
}
-}
\ No newline at end of file
+}
diff --git a/Source/LumatoneMenu.h b/Source/LumatoneMenu.h
index 19589155..6ed939bd 100644
--- a/Source/LumatoneMenu.h
+++ b/Source/LumatoneMenu.h
@@ -21,15 +21,20 @@ namespace Lumatone {
saveSysExMappingAs = 0x200012,
resetSysExMapping = 0x200013,
- deleteOctaveBoard = 0x200017,
- copyOctaveBoard = 0x200018,
- pasteOctaveBoard = 0x200019,
- undo = 0x200020,
- redo = 0x200021,
-
- recentFilesBaseID = 0x200100,
-
- aboutSysEx = 0x200040
+ deleteOctaveBoard = 0x200100,
+ copyOctaveBoard = 0x200101,
+ pasteOctaveBoard = 0x200102,
+ pasteOctaveBoardNotes = 0x200103,
+ pasteOctaveBoardChannels = 0x200104,
+ pasteOctaveBoardColours = 0x200105,
+ pasteOctaveBoardTypes = 0x200106,
+
+ undo = 0x200200,
+ redo = 0x200201,
+
+ recentFilesBaseID = 0x200300,
+
+ aboutSysEx = 0x200400
};
class MainMenuModel : public MenuBarModel
@@ -55,4 +60,4 @@ namespace Lumatone {
toggleDeveloperMode = 0xA00001
};
}
-}
\ No newline at end of file
+}
diff --git a/Source/Main.cpp b/Source/Main.cpp
index 701fbba1..3f4db957 100644
--- a/Source/Main.cpp
+++ b/Source/Main.cpp
@@ -17,7 +17,7 @@
//==============================================================================
-MainContentComponent* TerpstraSysExApplication::getMainContentComponent()
+MainContentComponent* TerpstraSysExApplication::getMainContentComponent() const
{
jassert(mainWindow != nullptr);
return (MainContentComponent*)(mainWindow->getContentComponent());
@@ -38,16 +38,13 @@ TerpstraSysExApplication::TerpstraSysExApplication()
propertiesFile = new PropertiesFile(options);
jassert(propertiesFile != nullptr);
+ lumatoneController = std::make_unique();
+
// Localisation
String localisation = getLocalisation(SystemStats::getDisplayLanguage());
LocalisedStrings::setCurrentMappings(new LocalisedStrings(localisation, false));
LocalisedStrings::getCurrentMappings()->setFallback(new LocalisedStrings(BinaryData::engb_txt, false));
- // Window aspect ratio
- boundsConstrainer.reset(new ComponentBoundsConstrainer());
- boundsConstrainer->setFixedAspectRatio(DEFAULTMAINWINDOWASPECT);
- boundsConstrainer->setMinimumSize(800, round(800 / DEFAULTMAINWINDOWASPECT));
-
// Colour scheme
//lookAndFeel.setColourScheme(lookAndFeel.getDarkColourScheme());
@@ -77,7 +74,7 @@ TerpstraSysExApplication::TerpstraSysExApplication()
userDocumentsDirectory = File::getSpecialLocation(File::userDocumentsDirectory).getChildFile("Lumatone Editor");
userDocumentsDirectory.createDirectory();
}
-
+
possibleDirectory = propertiesFile->getValue("UserMappingsDirectory");
if (File::isAbsolutePath(possibleDirectory))
{
@@ -88,7 +85,7 @@ TerpstraSysExApplication::TerpstraSysExApplication()
userMappingsDirectory = userDocumentsDirectory.getChildFile("Mappings");
userMappingsDirectory.createDirectory();
}
-
+
possibleDirectory = propertiesFile->getValue("UserPalettesDirectory");
if (File::isAbsolutePath(possibleDirectory))
{
@@ -102,9 +99,6 @@ TerpstraSysExApplication::TerpstraSysExApplication()
reloadColourPalettes();
- lumatoneController.setDeviceDetectionTimeout(propertiesFile->getIntValue("DetectDevicesTimeout", 500));
- lumatoneController.checkConnectionWhenInactive(propertiesFile->getBoolValue("CheckConnectionIfInactive", true));
-
// State of main window will be read from properties file when main window is created
}
@@ -123,10 +117,10 @@ void TerpstraSysExApplication::initialise(const String& commandLine)
// ToDo switch on/off isomorphic mass assign mode
// Try to open a config file
- if (File::isAbsolutePath(commandLineParameter))
- {
- currentFile = File(commandLineParameter);
- }
+ if (File::isAbsolutePath(commandLineParameter))
+ {
+ currentFile = File(commandLineParameter);
+ }
else
{
// If file name is with quotes, try removing the quotes
@@ -143,7 +137,9 @@ void TerpstraSysExApplication::initialise(const String& commandLine)
commandManager->registerAllCommandsForTarget(this);
menuModel.reset(new Lumatone::Menu::MainMenuModel(commandManager.get()));
- mainWindow.reset(new MainWindow());
+ boundsConstrainer = std::make_unique();
+
+ mainWindow.reset(new MainWindow(boundsConstrainer.get()));
mainWindow->addKeyListener(commandManager->getKeyMappings());
mainWindow->restoreStateFromPropertiesFile(propertiesFile);
@@ -163,12 +159,12 @@ void TerpstraSysExApplication::initialise(const String& commandLine)
void TerpstraSysExApplication::shutdown()
{
- // Add your application's shutdown code here..
-
+ // Add your application's shutdown code here..
+
// Save documents directories (Future: provide option to change them and save after changed by user)
propertiesFile->setValue("UserDocumentsDirectory", userDocumentsDirectory.getFullPathName());
- propertiesFile->setValue("UserMappingsDirectory", userMappingsDirectory.getFullPathName());
- propertiesFile->setValue("UserPalettesDirectory", userPalettesDirectory.getFullPathName());
+ propertiesFile->setValue("UserMappingsDirectory", userMappingsDirectory.getFullPathName());
+ propertiesFile->setValue("UserPalettesDirectory", userPalettesDirectory.getFullPathName());
// Save recent files list
recentFiles.removeNonExistentFiles();
@@ -186,35 +182,54 @@ void TerpstraSysExApplication::shutdown()
#if JUCE_MAC
MenuBarModel::setMacMainMenu(nullptr);
+#else
+ mainWindow->setMenuBarComponent(nullptr);
#endif
menuModel = nullptr;
mainWindow = nullptr; // (deletes our window)
+
+ if (firmwareUpdateWasPerformed)
+ FirmwareTransfer::exitLibSsh2();
+
// commandManager = nullptr;
}
//==============================================================================
void TerpstraSysExApplication::systemRequestedQuit()
{
- // This is called when the app is being asked to quit: you can ignore this
- // request and let the app carry on running, or call quit() to allow the app to close.
+ // This is called when the app is being asked to quit: you can ignore this
+ // request and let the app carry on running, or call quit() to allow the app to close.
// If there are changes: ask for save
if (hasChangesToSave)
{
- int retc = AlertWindow::showYesNoCancelBox(AlertWindow::AlertIconType::QuestionIcon, "Quitting the application", "Do you want to save your changes?");
- if (retc == 0)
- {
- // "Cancel". Do not quit.
- return;
- }
- else if (retc == 1)
- {
- // "Yes". Try to save. Cancel if unsuccessful
- if (!saveSysExMapping())
- return;
- }
- // retc == 2: "No" -> end without saving
+ AlertWindow::showYesNoCancelBox(
+ AlertWindow::AlertIconType::QuestionIcon,
+ "Quitting the application",
+ "Do you want to save your changes?",
+ "Yes", "No", "Cancel", nullptr,
+ ModalCallbackFunction::create([&](int retc)
+ {
+ if (retc == 0)
+ {
+ // "Cancel". Do not quit.
+ return;
+ }
+ else if (retc == 1)
+ {
+ // "Yes". Try to save. Cancel if unsuccessful
+ saveSysExMapping([&](bool success) { if (success) quit(); });
+ }
+ else
+ {
+ // retc == 2: "No" -> end without saving
+ quit();
+ }
+ })
+ );
+
+ return;
}
quit();
@@ -222,9 +237,9 @@ void TerpstraSysExApplication::systemRequestedQuit()
void TerpstraSysExApplication::anotherInstanceStarted(const String& commandLine)
{
- // When another instance of the app is launched while this one is running,
- // this method is invoked, and the commandLine parameter tells you what
- // the other instance's command-line arguments were.
+ // When another instance of the app is launched while this one is running,
+ // this method is invoked, and the commandLine parameter tells you what
+ // the other instance's command-line arguments were.
}
void TerpstraSysExApplication::reloadColourPalettes()
@@ -250,27 +265,41 @@ bool TerpstraSysExApplication::saveColourPalette(LumatoneEditorColourPalette& pa
{
ValueTree paletteNode = palette.toValueTree();
+ if (pathToFile == File())
+ pathToFile = File(palette.getPathToFile());
+
+ // If name changed, delete the old one
+ if (pathToFile.exists())
+ {
+ auto currentName = pathToFile.getFileName();
+ if (currentName != palette.getName())
+ {
+ pathToFile.deleteFile();
+ }
+ }
+
// New file
if (!pathToFile.existsAsFile())
{
- if (palette.getName() != String())
- pathToFile = userPalettesDirectory.getChildFile(palette.getName());
- else
- pathToFile = userPalettesDirectory.getChildFile("UnnamedPalette");
+ String fileName = "UnnamedPalette";
+
+ if (palette.getName().isNotEmpty())
+ fileName = palette.getName();
+
+ pathToFile = userPalettesDirectory.getChildFile(fileName);
// Make sure filename is unique since saving happens automatically
- int nameId = 0;
-
- // One thousand should be enough...right?
- while (pathToFile.withFileExtension(PALETTEFILEEXTENSION).existsAsFile() && nameId < 1000)
+ // Sorry programmers, we're using cardinal numbers here, and the original is implicitly #1 ;)
+ int nameId = 1;
+ while (pathToFile.withFileExtension(PALETTEFILEEXTENSION).existsAsFile() && nameId < 999999)
{
- nameId++;
- pathToFile = userPalettesDirectory.getChildFile("UnnamedPalette" + String(nameId));
+ auto fileNameToSave = fileName + "_" + String(++nameId);
+ pathToFile = userPalettesDirectory.getChildFile(fileNameToSave);
}
}
- success = palette.saveToFile(pathToFile);
-
+ success = palette.saveToFile(pathToFile);
+
// TODO error handling?
}
@@ -305,6 +334,11 @@ void TerpstraSysExApplication::getAllCommands(Array & commands)
Lumatone::Menu::commandIDs::deleteOctaveBoard,
Lumatone::Menu::commandIDs::copyOctaveBoard,
Lumatone::Menu::commandIDs::pasteOctaveBoard,
+ Lumatone::Menu::commandIDs::pasteOctaveBoardChannels,
+ Lumatone::Menu::commandIDs::pasteOctaveBoardNotes,
+ Lumatone::Menu::commandIDs::pasteOctaveBoardColours,
+ Lumatone::Menu::commandIDs::pasteOctaveBoardTypes,
+
Lumatone::Menu::commandIDs::undo,
Lumatone::Menu::commandIDs::redo,
@@ -322,22 +356,22 @@ void TerpstraSysExApplication::getCommandInfo(CommandID commandID, ApplicationCo
{
case Lumatone::Menu::commandIDs::openSysExMapping:
result.setInfo("Load file mapping", "Open a Lumatone key mapping", "File", 0);
- result.addDefaultKeypress('o', ModifierKeys::ctrlModifier);
+ result.addDefaultKeypress('o', ModifierKeys::commandModifier);
break;
case Lumatone::Menu::commandIDs::saveSysExMapping:
result.setInfo("Save mapping", "Save the current mapping to file", "File", 0);
- result.addDefaultKeypress('s', ModifierKeys::ctrlModifier);
+ result.addDefaultKeypress('s', ModifierKeys::commandModifier);
break;
case Lumatone::Menu::commandIDs::saveSysExMappingAs:
result.setInfo("Save mapping as...", "Save the current mapping to new file", "File", 0);
- result.addDefaultKeypress('a', ModifierKeys::ctrlModifier);
+ result.addDefaultKeypress('a', ModifierKeys::commandModifier);
break;
case Lumatone::Menu::commandIDs::resetSysExMapping:
result.setInfo("New", "Start new mapping. Clear all edit fields, do not save current edits.", "File", 0);
- result.addDefaultKeypress('n', ModifierKeys::ctrlModifier);
+ result.addDefaultKeypress('n', ModifierKeys::commandModifier);
break;
case Lumatone::Menu::commandIDs::deleteOctaveBoard:
@@ -346,24 +380,50 @@ void TerpstraSysExApplication::getCommandInfo(CommandID commandID, ApplicationCo
break;
case Lumatone::Menu::commandIDs::copyOctaveBoard:
- result.setInfo("Copy", "Copy section data", "Edit", 0);
- result.addDefaultKeypress('c', ModifierKeys::ctrlModifier);
+ result.setInfo("Copy section", "Copy current octave board data", "Edit", 0);
+ result.addDefaultKeypress('c', ModifierKeys::commandModifier);
break;
case Lumatone::Menu::commandIDs::pasteOctaveBoard:
- result.setInfo("Paste", "Paste section data", "Edit", 0);
- result.addDefaultKeypress('v', ModifierKeys::ctrlModifier);
+ result.setInfo("Paste section", "Paste copied section data", "Edit", 0);
+ result.addDefaultKeypress('v', ModifierKeys::commandModifier);
+ result.setActive(canPasteSubBoardData());
break;
+ case Lumatone::Menu::commandIDs::pasteOctaveBoardNotes:
+ result.setInfo("Paste notes", "Paste copied section notes", "Edit", 0);
+ result.addDefaultKeypress('v', ModifierKeys::commandModifier | ModifierKeys::shiftModifier);
+ result.setActive(canPasteSubBoardData());
+ break;
+
+ case Lumatone::Menu::commandIDs::pasteOctaveBoardChannels:
+ result.setInfo("Paste channels", "Paste copied section channels", "Edit", 0);
+ result.addDefaultKeypress('v', ModifierKeys::commandModifier | ModifierKeys::altModifier);
+ result.setActive(canPasteSubBoardData());
+ break;
+
+ case Lumatone::Menu::commandIDs::pasteOctaveBoardColours:
+ result.setInfo("Paste colours", "Paste copied section colours", "Edit", 0);
+ result.addDefaultKeypress('v', ModifierKeys::altModifier);
+ result.setActive(canPasteSubBoardData());
+ break;
+
+ case Lumatone::Menu::commandIDs::pasteOctaveBoardTypes:
+ result.setInfo("Paste types", "Paste copied section key types", "Edit", 0);
+ result.addDefaultKeypress('v', ModifierKeys::altModifier | ModifierKeys::shiftModifier);
+ result.setActive(canPasteSubBoardData());
+ break;
+
case Lumatone::Menu::commandIDs::undo:
result.setInfo("Undo", "Undo latest edit", "Edit", 0);
- result.addDefaultKeypress('z', ModifierKeys::ctrlModifier);
+ result.addDefaultKeypress('z', ModifierKeys::commandModifier);
result.setActive(undoManager.canUndo());
break;
case Lumatone::Menu::commandIDs::redo:
result.setInfo("Redo", "Redo latest edit", "Edit", 0);
- result.addDefaultKeypress('y', ModifierKeys::ctrlModifier);
+ result.addDefaultKeypress('y', ModifierKeys::commandModifier);
+ result.addDefaultKeypress('z', ModifierKeys::commandModifier + ModifierKeys::shiftModifier);
result.setActive(undoManager.canRedo());
break;
@@ -373,7 +433,9 @@ void TerpstraSysExApplication::getCommandInfo(CommandID commandID, ApplicationCo
case Lumatone::Debug::commandIDs::toggleDeveloperMode:
result.setInfo("Toggle Developer Mode", "Show/hide controls for tweaking internal parameters", "Edit", 0);
- result.addDefaultKeypress('m', juce::ModifierKeys::allKeyboardModifiers);
+ result.addDefaultKeypress('m',
+ juce::ModifierKeys::ctrlModifier + juce::ModifierKeys::altModifier + juce::ModifierKeys::shiftModifier);
+ result.setActive(true);
break;
default:
@@ -394,13 +456,18 @@ bool TerpstraSysExApplication::perform(const InvocationInfo& info)
return saveSysExMappingAs();
case Lumatone::Menu::commandIDs::resetSysExMapping:
return resetSysExMapping();
- case Lumatone::Menu::commandIDs::deleteOctaveBoard:
+ case Lumatone::Menu::commandIDs::deleteOctaveBoard:
return deleteSubBoardData();
case Lumatone::Menu::commandIDs::copyOctaveBoard:
return copySubBoardData();
case Lumatone::Menu::commandIDs::pasteOctaveBoard:
return pasteSubBoardData();
+ case Lumatone::Menu::commandIDs::pasteOctaveBoardNotes:
+ case Lumatone::Menu::commandIDs::pasteOctaveBoardChannels:
+ case Lumatone::Menu::commandIDs::pasteOctaveBoardColours:
+ case Lumatone::Menu::commandIDs::pasteOctaveBoardTypes:
+ return pasteModifiedSubBoardData(info.commandID);
case Lumatone::Menu::commandIDs::undo:
return undo();
@@ -420,39 +487,44 @@ bool TerpstraSysExApplication::perform(const InvocationInfo& info)
bool TerpstraSysExApplication::openSysExMapping()
{
- FileChooser chooser("Open a Lumatone key mapping", recentFiles.getFile(0).getParentDirectory(), "*.ltn;*.tsx");
- if (chooser.browseForFileToOpen())
- {
- currentFile = chooser.getResult();
- return openFromCurrentFile();
- }
+ chooser = std::make_unique("Open a Lumatone key mapping", recentFiles.getFile(0).getParentDirectory(), "*.ltn;*.tsx");
+ chooser->launchAsync(FileBrowserComponent::FileChooserFlags::canSelectFiles | FileBrowserComponent::FileChooserFlags::openMode,
+ [&](const FileChooser& chooser)
+ {
+ currentFile = chooser.getResult();
+ openFromCurrentFile();
+ });
+
return true;
}
-bool TerpstraSysExApplication::saveSysExMapping()
+bool TerpstraSysExApplication::saveSysExMapping(std::function saveFileCallback)
{
if (currentFile.getFileName().isEmpty())
- return saveSysExMappingAs();
+ return saveSysExMappingAs(saveFileCallback);
else
- return saveCurrentFile();
+ return saveCurrentFile(saveFileCallback);
}
-bool TerpstraSysExApplication::saveSysExMappingAs()
+bool TerpstraSysExApplication::saveSysExMappingAs(std::function saveFileCallback)
{
- FileChooser chooser("Lumatone Key Mapping Files", recentFiles.getFile(0).getParentDirectory(), "*.ltn");
- if (chooser.browseForFileToSave(true))
- {
- currentFile = chooser.getResult();
- if (saveCurrentFile() )
+ chooser = std::make_unique("Lumatone Key Mapping Files", recentFiles.getFile(0).getParentDirectory(), "*.ltn");
+ chooser->launchAsync(FileBrowserComponent::FileChooserFlags::saveMode | FileBrowserComponent::FileChooserFlags::warnAboutOverwriting,
+ [this, saveFileCallback](const FileChooser& chooser)
{
- // Window title
- updateMainTitle();
- return true;
- }
- }
+ currentFile = chooser.getResult();
+ bool saved = saveCurrentFile();
+ if (saved)
+ {
+ // Window title
+ updateMainTitle();
+ }
- return false;
+ saveFileCallback(saved);
+ });
+
+ return true;
}
bool TerpstraSysExApplication::resetSysExMapping()
@@ -478,8 +550,7 @@ bool TerpstraSysExApplication::resetSysExMapping()
bool TerpstraSysExApplication::deleteSubBoardData()
{
- return ((MainContentComponent*)(mainWindow->getContentComponent()))->deleteCurrentSubBoardData();
- // ToDo Add to undo history
+ return performUndoableAction(((MainContentComponent*)(mainWindow->getContentComponent()))->createDeleteCurrentSectionAction());
}
bool TerpstraSysExApplication::copySubBoardData()
@@ -489,19 +560,45 @@ bool TerpstraSysExApplication::copySubBoardData()
bool TerpstraSysExApplication::pasteSubBoardData()
{
- return ((MainContentComponent*)(mainWindow->getContentComponent()))->pasteCurrentSubBoardData();
- // ToDo Add to undo history
+ return performUndoableAction(((MainContentComponent*)(mainWindow->getContentComponent()))->createPasteCurrentSectionAction());
+}
+
+bool TerpstraSysExApplication::pasteModifiedSubBoardData(CommandID commandID)
+{
+ switch (commandID)
+ {
+ case Lumatone::Menu::pasteOctaveBoardNotes:
+ case Lumatone::Menu::pasteOctaveBoardColours:
+ case Lumatone::Menu::pasteOctaveBoardChannels:
+ case Lumatone::Menu::pasteOctaveBoardTypes:
+ return performUndoableAction(((MainContentComponent*)(mainWindow->getContentComponent()))->createModifiedPasteCurrentSectionAction(commandID));
+ default:
+ jassertfalse;
+ return false;
+ }
+}
+
+bool TerpstraSysExApplication::canPasteSubBoardData() const
+{
+ if (mainWindow != nullptr)
+ return getMainContentComponent()->canPasteCopiedSubBoard();
+ return false;
}
bool TerpstraSysExApplication::performUndoableAction(UndoableAction* editAction)
{
- if (undoManager.perform(editAction)) // UndoManager will check for nullptr and also for disposing of the object
+ if (editAction != nullptr)
{
- setHasChangesToSave(true);
- ((MainContentComponent*)(mainWindow->getContentComponent()))->refreshKeyDataFields();
+ undoManager.beginNewTransaction();
+ if (undoManager.perform(editAction)) // UndoManager will check for nullptr and also for disposing of the object
+ {
+ setHasChangesToSave(true);
+ ((MainContentComponent*)(mainWindow->getContentComponent()))->refreshAllFields();
+ return true;
+ }
}
- else
- return false;
+
+ return false;
}
bool TerpstraSysExApplication::undo()
@@ -509,7 +606,8 @@ bool TerpstraSysExApplication::undo()
if (undoManager.undo())
{
setHasChangesToSave(true);
- ((MainContentComponent*)(mainWindow->getContentComponent()))->refreshKeyDataFields();
+ ((MainContentComponent*)(mainWindow->getContentComponent()))->refreshAllFields();
+ return true;
}
else
return false;
@@ -520,7 +618,8 @@ bool TerpstraSysExApplication::redo()
if (undoManager.redo())
{
setHasChangesToSave(true);
- ((MainContentComponent*)(mainWindow->getContentComponent()))->refreshKeyDataFields();
+ ((MainContentComponent*)(mainWindow->getContentComponent()))->refreshAllFields();
+ return true;
}
else
return false;
@@ -533,6 +632,11 @@ bool TerpstraSysExApplication::toggleDeveloperMode()
return ((MainContentComponent*)(mainWindow->getContentComponent()))->setDeveloperMode(newMode);
}
+void TerpstraSysExApplication::setEditMode(sysExSendingMode editMode)
+{
+ lumatoneController->setSysExSendingMode(editMode);
+}
+
bool TerpstraSysExApplication::generalOptionsDialog()
{
GeneralOptionsDlg* optionsWindow = new GeneralOptionsDlg();
@@ -667,15 +771,21 @@ bool TerpstraSysExApplication::openFromCurrentFile()
else
{
// Show error message
- AlertWindow::showMessageBox(AlertWindow::AlertIconType::WarningIcon, "Open File Error", "The file " + currentFile.getFullPathName() + " could not be opened.");
+ AlertWindow::showMessageBoxAsync(AlertWindow::AlertIconType::WarningIcon, "Open File Error", "The file " + currentFile.getFullPathName() + " could not be opened.");
// XXX Update Window title in any case? Make file name empty/make data empty in case of error?
return false;
}
}
+bool TerpstraSysExApplication::setCurrentFile(File fileToOpen)
+{
+ currentFile = fileToOpen;
+ return openFromCurrentFile();
+}
+
// Saves the current mapping to file, specified in currentFile.
-bool TerpstraSysExApplication::saveCurrentFile()
+bool TerpstraSysExApplication::saveCurrentFile(std::function saveFileCallback)
{
if (currentFile.existsAsFile())
currentFile.deleteFile();
@@ -685,11 +795,13 @@ bool TerpstraSysExApplication::saveCurrentFile()
TerpstraKeyMapping keyMapping;
((MainContentComponent*)(mainWindow->getContentComponent()))->getData(keyMapping);
+ bool appendSuccess = true;
StringArray stringArray = keyMapping.toStringArray();
for (int i = 0; i < stringArray.size(); i++)
- currentFile.appendText(stringArray[i] + "\n");
-
- setHasChangesToSave(false);
+ appendSuccess = appendSuccess && currentFile.appendText(stringArray[i] + "\n");
+
+ setHasChangesToSave(!appendSuccess);
+ saveFileCallback(appendSuccess);
// ToDo undo history?
@@ -702,18 +814,19 @@ bool TerpstraSysExApplication::saveCurrentFile()
void TerpstraSysExApplication::sendCurrentConfigurationToDevice()
{
auto theConfig = ((MainContentComponent*)(mainWindow->getContentComponent()))->getMappingInEdit();
-
+
// MIDI channel, MIDI note, colour and key type config for all keys
- getLumatoneController().sendCompleteMapping(theConfig);
+ getLumatoneController()->sendCompleteMapping(theConfig);
// General options
- getLumatoneController().setAftertouchEnabled(theConfig.afterTouchActive);
- getLumatoneController().sendLightOnKeyStrokes(theConfig.lightOnKeyStrokes);
- getLumatoneController().sendInvertFootController(theConfig.invertExpression);
- getLumatoneController().sendExpressionPedalSensivity(theConfig.expressionControllerSensivity);
+ getLumatoneController()->setAftertouchEnabled(theConfig.afterTouchActive);
+ getLumatoneController()->sendLightOnKeyStrokes(theConfig.lightOnKeyStrokes);
+ getLumatoneController()->sendInvertFootController(theConfig.invertExpression);
+ getLumatoneController()->sendExpressionPedalSensivity(theConfig.expressionControllerSensivity);
+ getLumatoneController()->invertSustainPedal(theConfig.invertSustain);
// Velocity curve config
- getLumatoneController().setVelocityIntervalConfig(theConfig.velocityIntervalTableValues);
+ getLumatoneController()->setVelocityIntervalConfig(theConfig.velocityIntervalTableValues);
((MainContentComponent*)(mainWindow->getContentComponent()))->getCurvesArea()->sendConfigToController();
}
@@ -723,46 +836,65 @@ void TerpstraSysExApplication::requestConfigurationFromDevice()
// if editing operations were done that have not been saved, give the possibility to save them
if (hasChangesToSave)
{
- int retc = AlertWindow::showYesNoCancelBox(
+ AlertWindow::showYesNoCancelBox(
AlertWindow::AlertIconType::QuestionIcon,
"Request configuration from device",
- "The controller's current configuration will be received now. This will overwrite all edits you have done. Do you want to save them first?");
- if (retc == 0)
- {
- // "Cancel". Do not receive config
- return;
- }
- else if (retc == 1)
- {
- // "Yes". Try to save. Cancel if unsuccessful
- if (!saveSysExMapping())
- return;
- }
- // retc == 2: "No" -> no saving, overwrite
+ "Lumatone's layout will now be imported. This will overwrite your unsaved changes. Do you want to save them first?",
+ "Save to file", "Import anyway", "Cancel import", nullptr,
+ ModalCallbackFunction::create([&](int retc)
+ {
+ if (retc == 0)
+ {
+ // "Cancel". Do not receive config, go offline
+ DBG("Layout import cancelled");
+ setEditMode(sysExSendingMode::offlineEditor);
+ return;
+ }
+ else if (retc == 1)
+ {
+ // "Yes". Try to save. Cancel if unsuccessful
+ saveSysExMapping([this](bool success)
+ {
+ if (success)
+ this->requestConfigurationFromDevice();
+ else
+ DBG("Cancelled layout import");
+ });
+ }
+ else
+ {
+ // retc == 2: "No" -> no saving, overwrite
+ DBG("Overwriting current edits");
+ setHasChangesToSave(false);
+ requestConfigurationFromDevice();
+ }
+ })
+ );
+
+ return;
}
TerpstraSysExApplication::getApp().resetSysExMapping();
// Request MIDI channel, MIDI note, colour and key type config for all keys
- getLumatoneController().sendGetCompleteMappingRequest();
+ getLumatoneController()->sendGetCompleteMappingRequest();
// General options
- // ToDo AfterTouchActive
- // ToDo LightOnKeyStrokes
- // ToDo invertFootController
- // ToDO expressionControllerSensivity
+ getLumatoneController()->getPresetFlags();
+ getLumatoneController()->getExpressionPedalSensitivity();
// Velocity curve config
- getLumatoneController().sendVelocityIntervalConfigRequest();
- getLumatoneController().sendVelocityConfigRequest();
- getLumatoneController().sendFaderConfigRequest();
- getLumatoneController().sendAftertouchConfigRequest();
+ getLumatoneController()->sendVelocityIntervalConfigRequest();
+ getLumatoneController()->sendVelocityConfigRequest();
+ getLumatoneController()->sendFaderConfigRequest();
+ getLumatoneController()->sendAftertouchConfigRequest();
+
}
void TerpstraSysExApplication::updateMainTitle()
{
String windowTitle("Lumatone Editor");
- if (!currentFile.getFileName().isEmpty() )
+ if (!currentFile.getFileName().isEmpty())
windowTitle << " - " << currentFile.getFileName();
if (hasChangesToSave)
windowTitle << "*";
@@ -778,37 +910,54 @@ void TerpstraSysExApplication::setHasChangesToSave(bool value)
}
}
+//https://forum.juce.com/t/closing-dialog-windows-on-shutdown/27326/6
+void TerpstraSysExApplication::setOpenDialogWindow(DialogWindow* dialogWindowIn)
+{
+ dialogWindow.reset(dialogWindowIn);
+
+ // attach callback to window to release std::unique_ptr when the window is closed
+ ModalComponentManager::getInstance()->attachCallback(dialogWindow.get(), ModalCallbackFunction::create([&](int r)
+ {
+ dialogWindow.release();
+ }));
+}
+
bool TerpstraSysExApplication::aboutTerpstraSysEx()
{
String m;
m << "Lumatone Editor" << newLine
+ << "Version " << getApplicationVersion() << newLine
<< newLine
- << "Version " << String((JUCE_APP_VERSION_HEX >> 16) & 0xff) << "."
- << String((JUCE_APP_VERSION_HEX >> 8) & 0xff) << "."
- << String(JUCE_APP_VERSION_HEX & 0xff) << newLine
-
- << "@ Hans Straub, Vincenzo Sicurella 2014 - 2021" << newLine
+ << "@ Hans Straub 2014 - 2021," << newLine
+ << "& Vincenzo Sicurella 2020 - 2022" << newLine
<< newLine
<< "Based on the program 'TerpstraSysEx' @ Dylan Horvath 2007" << newLine
<< newLine
- << "For help on using this program, or any questions relating to the Lumatone keyboard, go to" << newLine
- << "http://lumatone.io";
- //<< "or";
- //<< newLine
- //<< "http://terpstrakeyboard.com";
+ << "For help on using this program, or any questions relating to the Lumatone keyboard, go to:" << newLine
+ << newLine
+ << "https://www.lumatone.io/" << newLine
+ << newLine
+ << "Built " << Time::getCompilationDate().toString(true, true, false, true)
+ << " with " << SystemStats::getOperatingSystemName() << newLine
+ << "on " << SystemStats::getCpuModel() << newLine
+ << SystemStats::getJUCEVersion();
DialogWindow::LaunchOptions options;
- Label* label = new Label();
- label->setLookAndFeel(&lookAndFeel);
- label->setText(m, dontSendNotification);
- label->setFont(lookAndFeel.getAppFont(LumatoneEditorFont::FranklinGothic));
- options.content.setOwned(label);
+ auto textDisplay = new TextEditor();
+ //textDisplay->setLookAndFeel(&lookAndFeel);
+ lookAndFeel.setupTextEditor(*textDisplay);
+ textDisplay->setMultiLine(true, true);
+ textDisplay->setText(m, dontSendNotification);
+ textDisplay->setFont(lookAndFeel.getAppFont(LumatoneEditorFont::FranklinGothic));
+ textDisplay->setReadOnly(true);
+ textDisplay->setCaretVisible(false);
+ options.content.setOwned(textDisplay);
juce::Rectangle area(0, 0, 400, 200);
options.content->setSize(area.getWidth(), area.getHeight());
- resizeLabelWithHeight(label, roundToInt(area.getHeight() * 0.24f));
+ //resizeLabelWithHeight(label, roundToInt(area.getHeight() * 0.24f));
options.dialogTitle = "About Lumatone Editor";
options.dialogBackgroundColour = lookAndFeel.findColour(LumatoneEditorColourIDs::DarkBackground);
@@ -818,13 +967,15 @@ bool TerpstraSysExApplication::aboutTerpstraSysEx()
options.resizable = false;
- DialogWindow* dw = options.launchAsync();
- dw->setLookAndFeel(&lookAndFeel);
+ auto dw = options.launchAsync();
+ //dw->setLookAndFeel(&lookAndFeel);
dw->centreWithSize(400, 260);
+ setOpenDialogWindow(dw);
+
return true;
}
//==============================================================================
// This macro generates the main() routine that launches the app.
-START_JUCE_APPLICATION (TerpstraSysExApplication)
+START_JUCE_APPLICATION(TerpstraSysExApplication)
diff --git a/Source/Main.h b/Source/Main.h
index 14025d05..2312d758 100644
--- a/Source/Main.h
+++ b/Source/Main.h
@@ -10,9 +10,9 @@
#pragma once
-#include "../JuceLibraryCode/JuceHeader.h"
+#include
#include "LumatoneMenu.h"
-#include "MainComponent.h"
+#include "MainWindow.h"
#include "LumatoneController.h"
#include "ViewConstants.h"
@@ -21,6 +21,8 @@
#include "LocalisationMap.h"
#include "FirmwareTransfer.h"
+#define CHOOSE_FILE_NOOP [](bool) -> void {}
+
//==============================================================================
class TerpstraSysExApplication : public JUCEApplication
{
@@ -28,14 +30,14 @@ class TerpstraSysExApplication : public JUCEApplication
//==============================================================================
TerpstraSysExApplication();
- const String getApplicationName() { return ProjectInfo::projectName; }
- const String getApplicationVersion() { return ProjectInfo::versionString; }
- bool moreThanOneInstanceAllowed() { return true; }
+ const String getApplicationName() override { return ProjectInfo::projectName; }
+ const String getApplicationVersion() override { return ProjectInfo::versionString; }
+ bool moreThanOneInstanceAllowed() override { return true; }
- void initialise(const String& commandLine);
- void shutdown();
- void systemRequestedQuit();
- void anotherInstanceStarted(const String& commandLine);
+ void initialise(const String& commandLine) override;
+ void shutdown() override;
+ void systemRequestedQuit() override;
+ void anotherInstanceStarted(const String& commandLine) override;
static TerpstraSysExApplication& getApp()
{
@@ -48,16 +50,19 @@ class TerpstraSysExApplication : public JUCEApplication
LumatoneEditorLookAndFeel& getLookAndFeel() { return lookAndFeel; }
ComponentBoundsConstrainer* getBoundsConstrainer() { return boundsConstrainer.get(); };
RecentlyOpenedFilesList& getRecentFileList() { return recentFiles; }
- LumatoneController& getLumatoneController() { return lumatoneController; }
+ LumatoneController* getLumatoneController() { return lumatoneController.get(); }
Array& getColourPalettes() { return colourPalettes; }
Font getAppFont(LumatoneEditorFont fontIdIn, float height = 12.0f) { return appFonts.getFont(fontIdIn, height); }
- int getOctaveBoardSize() const { return lumatoneController.getOctaveSize(); }
+ int getOctaveBoardSize() const { return lumatoneController->getOctaveSize(); }
+ int getNumBoards() const { return lumatoneController->getNumBoards(); }
+
+ FirmwareVersion getFirmwareVersion() const { return lumatoneController->getFirmwareVersion(); }
+ String getFirmwareVersionStr() const { return lumatoneController->getFirmwareVersion().toDisplayString(); }
- FirmwareVersion getFirmwareVersion() const { return lumatoneController.getFirmwareVersion(); }
- String getFirmwareVersionStr() const { return lumatoneController.getFirmwareVersion().toString(); }
+ void setFirmwareUpdatePerformed(bool updateWasRun) { firmwareUpdateWasPerformed = true; }
void reloadColourPalettes();
- bool saveColourPalette(LumatoneEditorColourPalette& palette, File pathToPalette);
+ bool saveColourPalette(LumatoneEditorColourPalette& palette, File pathToPalette=File());
bool deletePaletteFile(File pathToPalette);
// Menu functionality
@@ -67,13 +72,17 @@ class TerpstraSysExApplication : public JUCEApplication
bool perform(const InvocationInfo& info) override;
bool openSysExMapping();
- bool saveSysExMapping();
- bool saveSysExMappingAs();
+ bool saveSysExMapping(std::function saveFileCallback = CHOOSE_FILE_NOOP);
+ bool saveSysExMappingAs(std::function saveFileCallback = CHOOSE_FILE_NOOP);
bool resetSysExMapping();
bool deleteSubBoardData();
bool copySubBoardData();
bool pasteSubBoardData();
+ bool pasteModifiedSubBoardData(CommandID commandID);
+ bool canPasteSubBoardData() const;
+
+ void setEditMode(sysExSendingMode editMode);
void setCalibrationMode(bool calibrationStarted) { inCalibrationMode = calibrationStarted; }
bool getInCalibrationMode() const { return inCalibrationMode; }
@@ -91,86 +100,23 @@ class TerpstraSysExApplication : public JUCEApplication
bool openRecentFile(int recentFileIndex);
bool openFromCurrentFile();
- bool saveCurrentFile();
+ bool setCurrentFile(File fileToOpen);
+ bool saveCurrentFile(std::function saveFileCallback = CHOOSE_FILE_NOOP);
void sendCurrentConfigurationToDevice();
void requestConfigurationFromDevice();
-
void updateMainTitle();
bool getHasChangesToSave() const { return hasChangesToSave; }
void setHasChangesToSave(bool value);
+ void setOpenDialogWindow(DialogWindow* dialogWindowIn);
+
bool aboutTerpstraSysEx();
- //==============================================================================
- /*
- This class implements the desktop window that contains an instance of
- our MainContentComponent class.
- */
- class MainWindow : public DocumentWindow
- {
- public:
- MainWindow() : DocumentWindow("Lumatone Editor",
- TerpstraSysExApplication::getApp().getLookAndFeel().findColour(LumatoneEditorColourIDs::MediumBackground),
- DocumentWindow::minimiseButton + DocumentWindow::closeButton)
- {
- setContentOwned(new MainContentComponent(), true);
-
- setResizable(true, true);
- setConstrainer(TerpstraSysExApplication::getApp().getBoundsConstrainer());
- centreWithSize(getWidth(), getHeight());
-#if JUCE_ANDROID
- setFullScreen(true);
-#endif
- setLookAndFeel(&TerpstraSysExApplication::getApp().getLookAndFeel());
- setVisible(true);
- }
-
- void closeButtonPressed() override
- {
- // This is called when the user tries to close this window. Here, we'll just
- // ask the app to quit when this happens, but you can change this to do
- // whatever you need.
- JUCEApplication::getInstance()->systemRequestedQuit();
- }
-
- BorderSize getBorderThickness() override
- {
- return BorderSize (1);
- }
-
- /* Note: Be careful if you override any DocumentWindow methods - the base
- class uses a lot of them, so by overriding you might break its functionality.
- It's best to do all your work in your content component instead, but if
- you really have to override any DocumentWindow methods, make sure your
- subclass also calls the superclass's method.
- */
-
- void saveStateToPropertiesFile(PropertiesFile* propertiesFile)
- {
- // Save state of main window
- propertiesFile->setValue("MainWindowState", getWindowStateAsString());
- ((MainContentComponent*)(getContentComponent()))->saveStateToPropertiesFile(propertiesFile);
- }
-
- void restoreStateFromPropertiesFile(PropertiesFile* propertiesFile)
- {
- if (!restoreWindowStateFromString(propertiesFile->getValue("MainWindowState")))
- {
- // Default window state
- setSize(DEFAULTMAINWINDOWWIDTH, DEFAULTMAINWINDOWHEIGHT);
- }
-
- ((MainContentComponent*)(getContentComponent()))->restoreStateFromPropertiesFile(propertiesFile);
- }
-
- private:
- JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(MainWindow)
- };
-
- MainContentComponent* getMainContentComponent();
+
+ MainContentComponent* getMainContentComponent() const;
private:
std::unique_ptr mainWindow;
@@ -199,7 +145,13 @@ class TerpstraSysExApplication : public JUCEApplication
Array colourPalettes;
+ // Make sure an open dialog window is deleted on shutdown
+ std::unique_ptr dialogWindow;
+
// Communication with Lumatone
- LumatoneController lumatoneController;
-};
+ std::unique_ptr lumatoneController;
+ std::unique_ptr chooser;
+
+ bool firmwareUpdateWasPerformed = false; // Allows us to deinitialize libssh2 a single time
+};
diff --git a/Source/MainComponent.cpp b/Source/MainComponent.cpp
index a075d759..99c3c827 100644
--- a/Source/MainComponent.cpp
+++ b/Source/MainComponent.cpp
@@ -11,6 +11,7 @@
#include "MainComponent.h"
#include "ViewConstants.h"
#include "Main.h"
+#include "EditActions.h"
//==============================================================================
@@ -46,7 +47,7 @@ MainContentComponent::MainContentComponent()
addAndMakeVisible(globalSettingsArea.get());
globalSettingsArea->listenToColourEditButtons(this);
- TerpstraSysExApplication::getApp().getLumatoneController().addFirmwareListener(this);
+ TerpstraSysExApplication::getApp().getLumatoneController()->addFirmwareListener(this);
//lblAppName.reset(new Label("lblAppName", TerpstraSysExApplication::getApp().getApplicationName()));
lblAppName.reset(new Label("lblAppName", "lumatone editor"));
@@ -109,11 +110,7 @@ void MainContentComponent::setData(TerpstraKeyMapping& newData, bool withRefresh
if (withRefresh)
{
- refreshKeyDataFields();
- generalOptionsArea->loadFromMapping();
- pedalSensitivityDlg->loadFromMapping();
- curvesArea->loadFromMapping();
- curvesArea->repaint();
+ refreshAllFields();
}
}
@@ -129,24 +126,16 @@ void MainContentComponent::getData(TerpstraKeyMapping& newData)
newData = mappingData;
}
-bool MainContentComponent::deleteCurrentSubBoardData()
+UndoableAction* MainContentComponent::createDeleteCurrentSectionAction()
{
auto currentSetSelection = noteEditArea->getOctaveBoardSelectorTab()->getCurrentTabIndex();
if (currentSetSelection >= 0 && currentSetSelection < TerpstraSysExApplication::getApp().getOctaveBoardSize())
- {
- // Delete subboard data
- mappingData.sets[currentSetSelection] = TerpstraKeys();
-
- // Refresh display
- refreshKeyDataFields();
-
- // Mark that there are changes
- TerpstraSysExApplication::getApp().setHasChangesToSave(true);
-
- return true;
+ {
+ auto keySet = TerpstraKeys();
+ return new Lumatone::SectionEditAction(currentSetSelection, keySet);
}
else
- return false;
+ return nullptr;
}
bool MainContentComponent::copyCurrentSubBoardData()
@@ -161,25 +150,66 @@ bool MainContentComponent::copyCurrentSubBoardData()
return false;
}
-bool MainContentComponent::pasteCurrentSubBoardData()
+UndoableAction* MainContentComponent::createPasteCurrentSectionAction()
{
auto currentSetSelection = noteEditArea->getOctaveBoardSelectorTab()->getCurrentTabIndex();
- if (currentSetSelection >= 0 && currentSetSelection < TerpstraSysExApplication::getApp().getOctaveBoardSize())
- {
- if (!copiedSubBoardData.isEmpty())
- {
- mappingData.sets[currentSetSelection] = copiedSubBoardData;
-
- // Refresh display
- refreshKeyDataFields();
-
- // Mark that there are changes
- TerpstraSysExApplication::getApp().setHasChangesToSave(true);
- }
- return true;
+ if (currentSetSelection >= 0 && currentSetSelection < TerpstraSysExApplication::getApp().getNumBoards()
+ && !copiedSubBoardData.isEmpty())
+ {
+ return new Lumatone::SectionEditAction(currentSetSelection, copiedSubBoardData);
}
else
- return false;
+ return nullptr;
+}
+
+UndoableAction* MainContentComponent::createModifiedPasteCurrentSectionAction(CommandID commandID)
+{
+ auto currentSetSelectionIndex = noteEditArea->getOctaveBoardSelectorTab()->getCurrentTabIndex();
+ if (currentSetSelectionIndex >= 0 && currentSetSelectionIndex < TerpstraSysExApplication::getApp().getNumBoards()
+ && !copiedSubBoardData.isEmpty())
+ {
+ auto modifiedSubBoardData = copiedSubBoardData;
+ auto octaveSize = TerpstraSysExApplication::getApp().getOctaveBoardSize();
+
+ for (int i = 0; i < octaveSize; i++)
+ {
+ auto currentSectionKey = mappingData.sets[currentSetSelectionIndex].theKeys[i];
+ auto modifiedKey = modifiedSubBoardData.theKeys[i];
+
+ switch (commandID)
+ {
+ case Lumatone::Menu::commandIDs::pasteOctaveBoardNotes:
+ modifiedKey = currentSectionKey.withNoteOrCC(modifiedKey.noteNumber);
+ break;
+
+ case Lumatone::Menu::commandIDs::pasteOctaveBoardChannels:
+ modifiedKey = currentSectionKey.withChannelNumber(modifiedKey.channelNumber);
+ break;
+
+ case Lumatone::Menu::commandIDs::pasteOctaveBoardColours:
+ modifiedKey = currentSectionKey.withColour(modifiedKey.colour);
+ break;
+
+ case Lumatone::Menu::commandIDs::pasteOctaveBoardTypes:
+ modifiedKey = currentSectionKey.withKeyType(modifiedKey.keyType).withInvertCCFader(modifiedKey.ccFaderDefault);
+ break;
+
+ default:
+ jassertfalse;
+ }
+
+ modifiedSubBoardData.theKeys[i] = modifiedKey;
+ }
+
+ return new Lumatone::SectionEditAction(currentSetSelectionIndex, modifiedSubBoardData);
+ }
+ else
+ return nullptr;
+}
+
+bool MainContentComponent::canPasteCopiedSubBoard() const
+{
+ return !copiedSubBoardData.isEmpty();
}
bool MainContentComponent::setDeveloperMode(bool developerModeOn)
@@ -277,6 +307,14 @@ void MainContentComponent::faderConfigReceived(const int* faderData)
curvesArea->loadFromMapping();
}
+void MainContentComponent::faderTypeConfigReceived(int octaveIndex, const int* faderTypeData)
+{
+ for (int keyIndex = 0; keyIndex < TerpstraSysExApplication::getApp().getOctaveBoardSize(); keyIndex++)
+ {
+ this->mappingData.sets[octaveIndex - 1].theKeys[keyIndex].ccFaderDefault = faderTypeData[keyIndex];
+ }
+}
+
void MainContentComponent::lumatouchConfigReceived(const int* lumatouchData)
{
this->mappingData.lumaTouchConfig.editStrategy = TerpstraVelocityCurveConfig::EDITSTRATEGYINDEX::freeDrawing;
@@ -315,11 +353,19 @@ void MainContentComponent::buttonClicked(Button* btn)
{
colourEdit = noteEditArea->getColourEditComponent();
paletteWindow->listenToColourSelection(noteEditArea->getSingleNoteColourTextEditor());
+
+ // Shouldn't be necessary when Isomorphic is moved from dev to public
+ auto isomorphicPanel = noteEditArea->getIsomorphicMassAssignPanel();
+ if (isomorphicPanel != nullptr)
+ paletteWindow->listenToColourSelection(isomorphicPanel);
+
+ paletteWindow->addColourSelectorToGroup(noteEditArea.get());
+ paletteWindow->setCurrentColourSelector(noteEditArea->getSingleNoteColourTextEditor());
}
Rectangle componentArea = colourEdit->getScreenBounds().translated(-getScreenX(), -getScreenY());
- CallOutBox& popupBox = CallOutBox::launchAsynchronously(
+ CallOutBox::launchAsynchronously(
std::unique_ptr(paletteWindow),
componentArea,
this
@@ -327,7 +373,6 @@ void MainContentComponent::buttonClicked(Button* btn)
// else, a preset button colour button was pressed
paletteWindow->listenToColourSelection(colourEdit);
- popupBox.setLookAndFeel(&getLookAndFeel());
// TODO: Set swatch # or custom colour as current colour
}
}
@@ -358,10 +403,6 @@ void MainContentComponent::resized()
controlsArea = getBounds().withTop(proportionOfHeight(controlsAreaY)).withBottom(footerY);
// All keys overview/virtual keyboard playing
- // New height of subset field area, with minimal value
- int noteEditAreaWidth = noteEditArea->getWidth();
- int noteEditAreaHeight = noteEditArea->getHeight();
-
int newKeysOverviewAreaHeight = jmax(controlsArea.getY() - midiAreaHeight, MINIMALTERPSTRAKEYSETAREAHEIGHT);
allKeysOverview->setBounds(0, midiAreaHeight, newWidth, newKeysOverviewAreaHeight);
@@ -392,3 +433,12 @@ void MainContentComponent::refreshKeyDataFields()
noteEditArea->refreshKeyFields();
}
+void MainContentComponent::refreshAllFields()
+{
+ refreshKeyDataFields();
+ generalOptionsArea->loadFromMapping();
+ pedalSensitivityDlg->loadFromMapping();
+ curvesArea->loadFromMapping();
+ curvesArea->repaint();
+}
+
diff --git a/Source/MainComponent.h b/Source/MainComponent.h
index 528d22b5..61368d46 100644
--- a/Source/MainComponent.h
+++ b/Source/MainComponent.h
@@ -33,7 +33,7 @@
your controls and content.
*/
class MainContentComponent : public Component,
- public LumatoneController::FirmwareListener,
+ public LumatoneEditor::FirmwareListener,
public ChangeListener,
public Button::Listener
{
@@ -56,9 +56,11 @@ class MainContentComponent : public Component,
CurvesArea* getCurvesArea() { return curvesArea.get(); }
// Board edit operations
- bool deleteCurrentSubBoardData();
+ UndoableAction* createDeleteCurrentSectionAction();
bool copyCurrentSubBoardData();
- bool pasteCurrentSubBoardData();
+ UndoableAction* createPasteCurrentSectionAction();
+ UndoableAction* createModifiedPasteCurrentSectionAction(CommandID commandID);
+ bool canPasteCopiedSubBoard() const;
bool setDeveloperMode(bool developerModeOn);
@@ -69,12 +71,13 @@ class MainContentComponent : public Component,
void buttonClicked(Button* btn) override;
// GUI implementation
- void paint (Graphics&);
- void resized();
+ void paint (Graphics&) override;
+ void resized() override;
void refreshKeyDataFields();
+ void refreshAllFields();
- // Implementation of LumatoneController::FirmwareListener
+ // Implementation of LumatoneEditor::FirmwareListener
void octaveColourConfigReceived(int octaveIndex, uint8 rgbFlag, const int* colourData) override;
@@ -92,6 +95,8 @@ class MainContentComponent : public Component,
void faderConfigReceived(const int* faderData) override;
+ void faderTypeConfigReceived(int octaveIndex, const int* faderTypeData) override;
+
void lumatouchConfigReceived(const int* lumatouchData) override;
void firmwareRevisionReceived(FirmwareVersion version) override;
@@ -148,8 +153,8 @@ class MainContentComponent : public Component,
const float footerAreaY = 0.96f;
- const float popupWidth = 0.2879f;
- const float popupHeight = 0.2615f;
+ const float popupWidth = 0.4f;
+ const float popupHeight = 0.333f;
const float lumatoneVersionMarginX = 0.02f;
const float lumatoneVersionWidth = 0.2f;
@@ -166,4 +171,4 @@ class MainContentComponent : public Component,
const Rectangle pedalSettingsBounds = { 0.777778f, settingsAreaY, 0.18f, settingsAreaHeight };
const Rectangle curvesAreaBounds = { settingsColumnX, 0.7174f, 0.3626f, 0.21f };
-};
\ No newline at end of file
+};
diff --git a/Source/MainWindow.cpp b/Source/MainWindow.cpp
new file mode 100644
index 00000000..ca1ecfb9
--- /dev/null
+++ b/Source/MainWindow.cpp
@@ -0,0 +1,147 @@
+/*
+ ==============================================================================
+
+ MainWindow.cpp
+ Created: 2 Apr 2022 3:24:02pm
+
+ ==============================================================================
+*/
+
+#include "MainWindow.h"
+#include "Main.h"
+
+MainWindow::MainWindow(ComponentBoundsConstrainer* constrainerIn) : DocumentWindow("Lumatone Editor",
+ TerpstraSysExApplication::getApp().getLookAndFeel().findColour(LumatoneEditorColourIDs::MediumBackground),
+ DocumentWindow::minimiseButton + DocumentWindow::closeButton),
+ constrainer(constrainerIn)
+{
+ setContentOwned(new MainContentComponent(), true);
+
+ setResizable(true, true);
+#if JUCE_ANDROID
+ setFullScreen(true);
+#else
+ // Window aspect ratio
+ constrainer->setFixedAspectRatio(DEFAULTMAINWINDOWASPECT);
+ constrainer->setMinimumSize(800, round(800 / DEFAULTMAINWINDOWASPECT));
+
+ setConstrainer(constrainer);
+ updateBounds();
+ startTimer(2000);
+#endif
+
+ setLookAndFeel(&TerpstraSysExApplication::getApp().getLookAndFeel());
+}
+
+MainWindow::~MainWindow()
+{
+ stopTimer();
+ setConstrainer(nullptr);
+}
+
+void MainWindow::closeButtonPressed()
+{
+ // This is called when the user tries to close this window. Here, we'll just
+ // ask the app to quit when this happens, but you can change this to do
+ // whatever you need.
+ JUCEApplication::getInstance()->systemRequestedQuit();
+}
+
+BorderSize MainWindow::getBorderThickness()
+{
+ return BorderSize (1);
+}
+
+bool MainWindow::isLargerThanCurrentScreen() const
+{
+ if (maxWindowHeight > 0)
+ return getHeight() > maxWindowHeight;
+
+ return false; // shouldn't happen
+}
+
+bool MainWindow::isOutOfVerticalBounds() const
+{
+ return getScreenY() < 0
+ || getScreenY() >= (maxWindowHeight - verticalBoundsThreshold);
+}
+
+bool MainWindow::isOutOfHorizontalBounds() const
+{
+ return getScreenBounds().getRight() < horizontalBoundsThreshold
+ || getScreenX() >= (maxWindowWidth - horizontalBoundsThreshold);
+}
+
+void MainWindow::saveStateToPropertiesFile(PropertiesFile* propertiesFile)
+{
+ // Save state of main window
+ propertiesFile->setValue("MainWindowState", getWindowStateAsString());
+ ((MainContentComponent*)(getContentComponent()))->saveStateToPropertiesFile(propertiesFile);
+}
+
+void MainWindow::restoreStateFromPropertiesFile(PropertiesFile* propertiesFile)
+{
+ bool useSavedState = restoreWindowStateFromString(propertiesFile->getValue("MainWindowState"));
+
+ fixWindowPositionAndSize(!useSavedState);
+
+ setVisible(true);
+
+ ((MainContentComponent*)(getContentComponent()))->restoreStateFromPropertiesFile(propertiesFile);
+}
+
+void MainWindow::updateBounds()
+{
+ auto thisDisplay = Desktop::getInstance().getDisplays().getDisplayForRect(getScreenBounds());
+
+ if (thisDisplay != nullptr)
+ {
+ int screenWidth = thisDisplay->userArea.getWidth();
+ int screenHeight = thisDisplay->userArea.getHeight();
+ if (screenWidth != maxWindowWidth || screenHeight != maxWindowHeight)
+ {
+ maxWindowWidth = screenWidth;
+ maxWindowHeight = screenHeight;
+ constrainer->setMaximumHeight(maxWindowHeight);
+
+ fixWindowPositionAndSize();
+ return;
+ }
+ }
+
+ if (isOutOfVerticalBounds())
+ fixWindowPositionAndSize();
+}
+
+void MainWindow::fixWindowPositionAndSize(bool setToDefault)
+{
+ if (setToDefault || isLargerThanCurrentScreen())
+ {
+ // Default window state
+ setSize(DEFAULTMAINWINDOWWIDTH, DEFAULTMAINWINDOWHEIGHT);
+ return;
+ }
+
+ if (isOutOfVerticalBounds())
+ {
+ int correctedY = (getScreenY() < 0) ? 0
+ : maxWindowHeight - verticalBoundsThreshold;
+ setTopLeftPosition(getScreenX(), correctedY);
+ }
+
+ if (isOutOfHorizontalBounds())
+ {
+ auto bounds = getScreenBounds();
+ int correctedX = (bounds.getX() < 0) ? horizontalBoundsThreshold - maxWindowWidth
+ : maxWindowWidth - horizontalBoundsThreshold;
+ setTopLeftPosition(correctedX, getScreenY());
+ }
+}
+
+void MainWindow::timerCallback()
+{
+ // Set threshold to be a quarter of the window handle height
+ verticalBoundsThreshold = round(getTitleBarHeight() * 0.25f);
+
+ updateBounds();
+}
diff --git a/Source/MainWindow.h b/Source/MainWindow.h
new file mode 100644
index 00000000..35fe7e52
--- /dev/null
+++ b/Source/MainWindow.h
@@ -0,0 +1,67 @@
+/*
+ ==============================================================================
+
+ MainWindow.h
+ Created: 2 Apr 2022 3:24:02pm
+
+ ==============================================================================
+*/
+
+#pragma once
+#include "MainComponent.h"
+
+//==============================================================================
+/*
+This class implements the desktop window that contains an instance of
+our MainContentComponent class.
+*/
+class MainWindow : public DocumentWindow, public Timer
+{
+public:
+ MainWindow(ComponentBoundsConstrainer* constrainerIn);
+
+ virtual ~MainWindow();
+
+ void closeButtonPressed() override;
+
+ BorderSize getBorderThickness() override;
+
+ /* Note: Be careful if you override any DocumentWindow methods - the base
+ class uses a lot of them, so by overriding you might break its functionality.
+ It's best to do all your work in your content component instead, but if
+ you really have to override any DocumentWindow methods, make sure your
+ subclass also calls the superclass's method.
+ */
+
+ // Returns true if the height is larger than current screen
+ bool isLargerThanCurrentScreen() const;
+
+ // Returns true if the top of the window is out of the screen
+ bool isOutOfVerticalBounds() const;
+
+ // Returns true if left side of window is too far right, or if right side of window is too far left
+ bool isOutOfHorizontalBounds() const;
+
+ void saveStateToPropertiesFile(PropertiesFile* propertiesFile);
+
+ void restoreStateFromPropertiesFile(PropertiesFile* propertiesFile);
+
+ // Checks size of current display and updates constraint
+ void updateBounds();
+
+ void fixWindowPositionAndSize(bool setToDefault=false);
+
+ void timerCallback() override;
+
+private:
+
+ ComponentBoundsConstrainer* constrainer;
+
+ int maxWindowWidth = 0;
+ int maxWindowHeight = 0;
+
+ int horizontalBoundsThreshold = 10;
+ int verticalBoundsThreshold = 10;
+
+ JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(MainWindow)
+};
diff --git a/Source/MappingLogic.cpp b/Source/MappingLogic.cpp
index d2f0125d..818ffc76 100644
--- a/Source/MappingLogic.cpp
+++ b/Source/MappingLogic.cpp
@@ -61,6 +61,7 @@ juce::Colour MappingLogicBase::indexToColour(int inx) const
void MappingLogicBase::indexToTerpstraKey(int inx, TerpstraKey& keyData) const
{
+ keyData.keyType = LumatoneKeyType::noteOnNoteOff;
keyData.channelNumber = indexToMIDIChannel(inx);
keyData.noteNumber = indexToMIDINote(inx);
@@ -126,7 +127,7 @@ void IncrMidiNotesMappingLogic::setValues(int newPeriodSize, int newChannelInCas
int IncrMidiNotesMappingLogic::globalMappingSize() const
{
if (isSingleChannel())
- return this->periodSize; // notes start at 0 and go until periodSize-1
+ return 128; // notes start at 0 and go until periodSize-1
else
return this->periodSize * 16;
}
diff --git a/Source/MappingLogic.h b/Source/MappingLogic.h
index bad55821..739d4aaf 100644
--- a/Source/MappingLogic.h
+++ b/Source/MappingLogic.h
@@ -21,10 +21,13 @@ class MappingLogicBase
{
public:
MappingLogicBase(ScaleStructure& scaleStructureIn, Array& colourTableIn);
+ virtual ~MappingLogicBase() {}
void setPeriodSize(int newPeriodSize, bool forceRefresh = false);
void setAssignColours(bool value) { assignColours = value; };
+ void setColourTable(Array& colourTableIn) { colourTable = colourTableIn; }
+
// Global number of notes in the mapping
virtual int globalMappingSize() const = 0;
@@ -134,7 +137,8 @@ class KBMFilesMappingLogic: public MappingLogicBase
public:
KBMFilesMappingLogic(ScaleStructure& scaleStructureIn, Array& colourTableIn);
-
+ virtual ~KBMFilesMappingLogic() {}
+
//===============================
// Set parameters
diff --git a/Source/MidiEditArea.cpp b/Source/MidiEditArea.cpp
index 599abbaa..e3286ba1 100644
--- a/Source/MidiEditArea.cpp
+++ b/Source/MidiEditArea.cpp
@@ -28,7 +28,7 @@
//[MiscUserDefs] You can add your own user definitions and misc code here...
// Index in edit mode tab coincides with sysExSendingMode. In case that changes in the future, modify this here.
-LumatoneController::sysExSendingMode editModeTabIndexToMidiSysExSendingMode(int tabIndex) { return static_cast(tabIndex); }
+sysExSendingMode editModeTabIndexToMidiSysExSendingMode(int tabIndex) { return static_cast(tabIndex); }
//[/MiscUserDefs]
@@ -167,14 +167,15 @@ MidiEditArea::MidiEditArea (LumatoneEditorLookAndFeel& lookAndFeelIn)
//[Constructor] You can add your own custom stuff here..
- TerpstraSysExApplication::getApp().getLumatoneController().addStatusListener(this);
- auto inputs = TerpstraSysExApplication::getApp().getLumatoneController().getMidiInputList();
- auto outputs = TerpstraSysExApplication::getApp().getLumatoneController().getMidiOutputList();
+ TerpstraSysExApplication::getApp().getLumatoneController()->addStatusListener(this);
+ TerpstraSysExApplication::getApp().getLumatoneController()->addEditorListener(this);
+ auto inputs = TerpstraSysExApplication::getApp().getLumatoneController()->getMidiInputList();
+ auto outputs = TerpstraSysExApplication::getApp().getLumatoneController()->getMidiOutputList();
refreshInputMenuAndSetSelected(0, dontSendNotification);
refreshOutputMenuAndSetSelected(0, dontSendNotification);
setConnectivity(false);
- btnAutoConnect->setToggleState(TerpstraSysExApplication::getApp().getLumatoneController().isDetectingLumatone(), sendNotificationSync);
+ btnAutoConnect->setToggleState(TerpstraSysExApplication::getApp().getLumatoneController()->isDetectingLumatone(), sendNotificationSync);
//[/Constructor]
}
@@ -198,7 +199,7 @@ MidiEditArea::~MidiEditArea()
//[Destructor]. You can add your own custom destruction code here..
//deviceMonitor.stopThread(100);
- TerpstraSysExApplication::getApp().getLumatoneController().removeStatusListener(this);
+ TerpstraSysExApplication::getApp().getLumatoneController()->removeStatusListener(this);
//[/Destructor]
}
@@ -213,7 +214,7 @@ void MidiEditArea::paint (juce::Graphics& g)
//[UserPaint] Add your own custom painting code here..
g.fillAll(lookAndFeel.findColour(LumatoneEditorColourIDs::LightBackground));
- // Dark backrgound for title and logomark
+ // Dark background for title and logomark
g.setColour(lookAndFeel.findColour(LumatoneEditorColourIDs::DarkBackground));
g.fillRect(lumatoneLabelBounds);
g.fillRect(connectivityArea);
@@ -222,10 +223,10 @@ void MidiEditArea::paint (juce::Graphics& g)
if (!isConnected)
{
g.setColour(lookAndFeel.findColour(LumatoneEditorColourIDs::LightBackground));
- g.fillRoundedRectangle(ioBounds, round(getHeight() * controlBoundsCornerRadius));
+ g.fillRoundedRectangle(ioBounds, roundToInt(getHeight() * controlBoundsCornerRadius));
}
- g.setColour(connectedColours[isConnected]);
+ g.setColour(connectedColours[(int)(isConnected && liveEditorBtn->getToggleState())]);
drawPathToFillBounds(g, logomarkPath, logomarkBounds);
//[/UserPaint]
}
@@ -239,52 +240,54 @@ void MidiEditArea::resized()
//[UserResized] Add your own custom resize handling here..
- lumatoneLabelBounds = getBounds().withRight(round(w * lumatoneLabelAreaWidth));
+ lumatoneLabelBounds = getBounds().withRight(roundToInt(w * lumatoneLabelAreaWidth));
resizeLabelWithWidth(lumatoneLabel.get(), lumatoneLabelBounds.proportionOfWidth(lumatoneLabelWidthInArea));
lumatoneLabel->setCentrePosition(lumatoneLabelBounds.getCentre());
// Also used to position logomark
ioBounds.setBounds(
- round(w * controlBoundsX), round(h * controlBoundsY),
- round(w * controlBoundsWidth), round(h * controlBoundsHeight)
+ roundToInt(w * controlBoundsX), roundToInt(h * controlBoundsY),
+ roundToInt(w * controlBoundsWidth), roundToInt(h * controlBoundsHeight)
);
+ int logomarkSize = roundToInt(h * logomarkHeight);
+ logomarkBounds.setSize(logomarkSize, logomarkSize);
+ logomarkBounds.setCentre(ioBounds.getRight() + roundToInt((getWidth() - ioBounds.getRight()) * 0.5f), roundToInt(h * 0.5f));
+
if (isConnected)
{
- resizeLabelWithHeight(lblEditMode.get(), round(h* editModeHeight));
+ int lblHeight = roundToInt(h * editModeHeight);
+ resizeLabelWithHeight(lblEditMode.get(), lblHeight);
lblEditMode->setTopLeftPosition(
- lumatoneLabelBounds.getRight() + round(w * editModeX),
- round((h - lblEditMode->getHeight()) * 0.5f)
+ lumatoneLabelBounds.getRight() + roundToInt(w * editModeX),
+ roundToInt((h - lblEditMode->getHeight()) * 0.5f)
);
- liveEditorBtn->setSize(round(w * liveEditButtonWidth), round(h* editModeButtonHeight));
+ liveEditorBtn->setSize(roundToInt(w * liveEditButtonWidth), roundToInt(h* editModeButtonHeight));
liveEditorBtn->setTopLeftPosition(
lblEditMode->getRight(),
- round((h - liveEditorBtn->getHeight()) * 0.5f)
+ roundToInt((h - liveEditorBtn->getHeight()) * 0.5f)
);
offlineEditorBtn->setBounds(
- liveEditorBtn->getRight(), liveEditorBtn->getY(), round(w * offlineEditButtonWidth), liveEditorBtn->getHeight()
+ liveEditorBtn->getRight(), liveEditorBtn->getY(), roundToInt(w * offlineEditButtonWidth), liveEditorBtn->getHeight()
);
+ connectivityArea = getBounds().toFloat().withLeft(roundToInt(w * connectedAreaX));
- connectivityArea = getBounds().toFloat().withLeft(round(w * connectedAreaX));
-
- lblConnectionState->setJustificationType (juce::Justification::centredLeft);
- resizeLabelWithHeight(lblConnectionState.get(), round(h * connectivityHeight));
- lblConnectionState->setTopLeftPosition(
- round(w * connectedX),
- round((h - lblConnectionState->getHeight()) * 0.5f)
- );
+ int logoMargin = w - logomarkBounds.getRight();
+ lblConnectionState->setTopLeftPosition(connectivityArea.getX(), roundToInt((h - lblHeight) * 0.5f));
+ lblConnectionState->setSize(logomarkBounds.getX() - connectivityArea.getX() - logoMargin, lblHeight);
+ lblConnectionState->setJustificationType (juce::Justification::centredRight);
}
else
{
int controlHeight = roundToInt(ioBounds.getHeight() * midiDeviceControlBoundsHeight);
- connectivityArea = getBounds().toFloat().withLeft(round(w * disconnectedAreaX));
+ connectivityArea = getBounds().toFloat().withLeft(roundToInt(w * disconnectedAreaX));
- int lblMarginX = round(ioBounds.getWidth() * controlBoundsMarginScalar);
- int lblMarginY = round((ioBounds.getHeight() - h * connectivityHeight) * 0.5f);
+ int lblMarginX = roundToInt(ioBounds.getWidth() * controlBoundsMarginScalar);
+ int lblMarginY = roundToInt((ioBounds.getHeight() - h * connectivityHeight) * 0.5f);
ioAreaFlexBox.items.clear();
ioAreaFlexBox.items.add(FlexItem(*btnAutoConnect).withFlex(0).withWidth(controlHeight * 1.6f).withHeight(controlHeight));
@@ -315,18 +318,13 @@ void MidiEditArea::resized()
ioBounds.reduced(lblMarginX, lblMarginY)
);
- pleaseConnectLabel->setTopLeftPosition(round(w * pleaseConnectX), round(h * pleaseConnectY));
- resizeLabelWithHeight(pleaseConnectLabel.get(), round(h * pleaseConnectHeight));
+ pleaseConnectLabel->setTopLeftPosition(roundToInt(w * pleaseConnectX), roundToInt(h * pleaseConnectY));
+ resizeLabelWithHeight(pleaseConnectLabel.get(), roundToInt(h * pleaseConnectHeight));
- offlineMsgLabel->setTopLeftPosition(round(w * connectionDirectionsX), round(h * connectionDirectionsY));
- offlineMsgLabel->setSize(connectivityArea.getX() - pleaseConnectLabel->getX(), round(h * connectionDirectionsHeight));
+ offlineMsgLabel->setTopLeftPosition(roundToInt(w * connectionDirectionsX), roundToInt(h * connectionDirectionsY));
+ offlineMsgLabel->setSize(connectivityArea.getX() - pleaseConnectLabel->getX(), roundToInt(h * connectionDirectionsHeight));
offlineMsgLabel->setFont(offlineMsgLabel->getFont().withHeight(offlineMsgLabel->getHeight()));
}
-
-
- int logomarkSize = round(h * logomarkHeight);
- logomarkBounds.setSize(logomarkSize, logomarkSize);
- logomarkBounds.setCentre(ioBounds.getRight() + roundToInt((getWidth() - ioBounds.getRight()) * 0.5f), round(h* 0.5f));
//[/UserResized]
}
@@ -339,7 +337,7 @@ void MidiEditArea::comboBoxChanged (juce::ComboBox* comboBoxThatHasChanged)
{
//[UserComboBoxCode_cbMidiInput] -- add your combo box handling code here..
if (cbMidiInput->getSelectedItemIndex() >= 0)
- TerpstraSysExApplication::getApp().getLumatoneController().setMidiInput(cbMidiInput->getSelectedItemIndex());
+ TerpstraSysExApplication::getApp().getLumatoneController()->setMidiInput(cbMidiInput->getSelectedItemIndex());
if (cbMidiInput->getSelectedItemIndex() < 0 || cbMidiOutput->getSelectedItemIndex() < 0)
{
@@ -351,7 +349,8 @@ void MidiEditArea::comboBoxChanged (juce::ComboBox* comboBoxThatHasChanged)
}
else
{
- attemptDeviceConnection();
+ jassert(!isConnected);
+ lblConnectionState->setText("Connecting...", NotificationType::dontSendNotification);
}
//[/UserComboBoxCode_cbMidiInput]
}
@@ -359,7 +358,7 @@ void MidiEditArea::comboBoxChanged (juce::ComboBox* comboBoxThatHasChanged)
{
//[UserComboBoxCode_cbMidiOutput] -- add your combo box handling code here..
if (cbMidiOutput->getSelectedItemIndex() >= 0)
- TerpstraSysExApplication::getApp().getLumatoneController().setMidiOutput(cbMidiOutput->getSelectedItemIndex());
+ TerpstraSysExApplication::getApp().getLumatoneController()->setMidiOutput(cbMidiOutput->getSelectedItemIndex());
if (cbMidiInput->getSelectedItemIndex() < 0 || cbMidiOutput->getSelectedItemIndex() < 0)
{
@@ -371,7 +370,8 @@ void MidiEditArea::comboBoxChanged (juce::ComboBox* comboBoxThatHasChanged)
}
else
{
- attemptDeviceConnection();
+ jassert(!isConnected);
+ lblConnectionState->setText("Connecting...", NotificationType::dontSendNotification);
}
//[/UserComboBoxCode_cbMidiOutput]
}
@@ -394,7 +394,7 @@ void MidiEditArea::buttonClicked (juce::Button* buttonThatWasClicked)
if (btnAutoConnect->getToggleState())
{
- TerpstraSysExApplication::getApp().getLumatoneController().detectAndConnectToLumatone();
+ TerpstraSysExApplication::getApp().getLumatoneController()->detectAndConnectToLumatone();
lblConnectionState->setText(translate("Searching for Lumatone..."), dontSendNotification);
//errorVisualizer.setErrorLevel(
// *lblConnectionState.get(),
@@ -403,7 +403,7 @@ void MidiEditArea::buttonClicked (juce::Button* buttonThatWasClicked)
}
else
{
- TerpstraSysExApplication::getApp().getLumatoneController().stopAutoConnection();
+ TerpstraSysExApplication::getApp().getLumatoneController()->stopAutoConnection();
lblConnectionState->setText(translate("Disconnected"), dontSendNotification);
startTimer(deviceRefreshTimeoutMs);
}
@@ -417,26 +417,7 @@ void MidiEditArea::buttonClicked (juce::Button* buttonThatWasClicked)
{
auto sysExSendingMode = editModeTabIndexToMidiSysExSendingMode((int)!liveEditorBtn->getToggleState());
- TerpstraSysExApplication::getApp().getLumatoneController().setSysExSendingMode(sysExSendingMode);
-
- switch (sysExSendingMode)
- {
- case LumatoneController::sysExSendingMode::liveEditor:
- onOpenConnectionToDevice();
- break;
-
- case LumatoneController::sysExSendingMode::offlineEditor:
- lblConnectionState->setText(translate("Offline"), NotificationType::dontSendNotification);
- //errorVisualizer.setErrorLevel(
- // *lblConnectionState.get(),
- // HajuErrorVisualizer::ErrorLevel::noError,
- // "Offline");
- break;
-
- default:
- jassertfalse;
- break;
- }
+ TerpstraSysExApplication::getApp().setEditMode(sysExSendingMode);
}
//[/UserbuttonClicked_Post]
}
@@ -452,7 +433,7 @@ void MidiEditArea::lookAndFeelChanged()
connectedColours.add(getLookAndFeel().findColour(LumatoneEditorColourIDs::ConnectedGreen));
}
-void MidiEditArea::setConnectivity(bool isConnectedIn)
+void MidiEditArea::setConnectivity(bool isConnectedIn, String connectionStatus)
{
bool isNotConnected = !isConnectedIn;
@@ -471,20 +452,32 @@ void MidiEditArea::setConnectivity(bool isConnectedIn)
if (isConnected)
{
- stopTimer();
-
if (liveEditorBtn->getToggleState())
- lblConnectionState->setText(translate("Connected"), dontSendNotification);
+ {
+ if (connectionStatus.isEmpty())
+ connectionStatus = "Connected";
+ lblConnectionState->setText(translate(connectionStatus), dontSendNotification);
+ }
else
- lblConnectionState->setText(translate("Offline"), dontSendNotification);
+ {
+ if (connectionStatus.isEmpty())
+ connectionStatus = "Offline";
+ lblConnectionState->setText(translate(connectionStatus), dontSendNotification);
+ }
}
else
{
if (btnAutoConnect->getToggleState())
- lblConnectionState->setText(translate("Searching for Lumatone..."), dontSendNotification);
+ {
+ if (connectionStatus.isEmpty())
+ connectionStatus = "Searching for Lumatone...";
+ lblConnectionState->setText(translate(connectionStatus), dontSendNotification);
+ }
else
{
- lblConnectionState->setText(translate("Disconnected"), dontSendNotification);
+ if (connectionStatus.isEmpty())
+ connectionStatus = "Disconnected";
+ lblConnectionState->setText(translate(connectionStatus), dontSendNotification);
startTimer(deviceRefreshTimeoutMs);
}
}
@@ -495,17 +488,16 @@ void MidiEditArea::setConnectivity(bool isConnectedIn)
repaint();
}
-// Called when DeviceActivityMonitor detects a change in devices
-
-//void MidiEditArea::connectionChanged(bool hasConnection)
-//{
-// if (hasConnection && btnAutoConnect->getToggleState() == false)
-// {
-// DBG("MIDIAREA SETTING DEVICES");
-// TerpstraSysExApplication::getApp().getLumatoneController().setMidiInput(cbMidiInput->getSelectedId() - 1);
-// TerpstraSysExApplication::getApp().getLumatoneController().setMidiOutput(cbMidiOutput->getSelectedId() - 1);
-// }
-//}
+void MidiEditArea::connectionFailed()
+{
+ setConnectivity(false, "No answer");
+ //errorVisualizer.setErrorLevel(
+ // *lblConnectionState.get(),
+ // HajuErrorVisualizer::ErrorLevel::error,
+ // "No answer...");
+ TerpstraSysExApplication::getApp().getLumatoneController()->setMidiInput(-1);
+ TerpstraSysExApplication::getApp().getLumatoneController()->setMidiOutput(-1);
+}
void MidiEditArea::connectionEstablished(int inputDevice, int outputDevice)
{
@@ -527,102 +519,105 @@ void MidiEditArea::connectionEstablished(int inputDevice, int outputDevice)
void MidiEditArea::connectionLost()
{
+ // Lost should only happen after connection is established
+ jassert(isConnected);
+
if (!btnAutoConnect->getToggleState())
startTimer(deviceRefreshTimeoutMs);
- if (isWaitingForConnectionTest)
- {
- lblConnectionState->setText(translate("No answer"), NotificationType::dontSendNotification);
- //errorVisualizer.setErrorLevel(
- // *lblConnectionState.get(),
- // HajuErrorVisualizer::ErrorLevel::error,
- // "No answer");
- }
else
- {
- setConnectivity(false);
- }
+ {
+ refreshInputMenuAndSetSelected(0, NotificationType::dontSendNotification);
+ refreshOutputMenuAndSetSelected(0, NotificationType::sendNotificationAsync);
+ }
+
+ setConnectivity(false);
}
-void MidiEditArea::attemptDeviceConnection()
+void MidiEditArea::editorModeChanged(sysExSendingMode editMode)
{
- jassert(!isConnected);
-
- lblConnectionState->setText("Connecting...", NotificationType::dontSendNotification);
- //errorVisualizer.setErrorLevel(
- // *lblConnectionState.get(),
- // HajuErrorVisualizer::ErrorLevel::noError,
- // "Connecting...");
+ switch (editMode)
+ {
+ case sysExSendingMode::liveEditor:
+ liveEditorBtn->setToggleState(true, NotificationType::dontSendNotification);
+ if (TerpstraSysExApplication::getApp().getHasChangesToSave())
+ onOpenConnectionToDevice(translate("Switch to Live Mode with unsaved changes"));
+ break;
+
+ case sysExSendingMode::offlineEditor:
+ offlineEditorBtn->setToggleState(true, NotificationType::dontSendNotification);
+ lblConnectionState->setText(translate("Offline"), NotificationType::dontSendNotification);
+ //errorVisualizer.setErrorLevel(
+ // *lblConnectionState.get(),
+ // HajuErrorVisualizer::ErrorLevel::noError,
+ // "Offline");
+ break;
+
+ default:
+ jassertfalse;
+ break;
+ }
- if (TerpstraSysExApplication::getApp().getLumatoneController().isConnected())
- {
- onOpenConnectionToDevice();
- }
- else
- {
- //isWaitingForConnectionTest = true;
- //TerpstraSysExApplication::getApp().getLumatoneController().testCurrentDeviceConnection();
- //jassert(isWaitingForConnectionTest); // Triggered if a test is requested before opening any devices
- }
+ lblConnectionState->setColour(Label::ColourIds::textColourId, connectedColours[(int)liveEditorBtn->getToggleState()]);
+ repaint();
}
-void MidiEditArea::onOpenConnectionToDevice()
+void MidiEditArea::onOpenConnectionToDevice(String dialogTitle)
{
jassert(cbMidiInput->getSelectedItemIndex() >= 0 && cbMidiOutput->getSelectedItemIndex() >= 0);
- jassert(alert == nullptr);
- // This can get spammed, and needs a real solution, but for now this will prevent it in releases - Vito
- if (alert == nullptr)
- {
- // Get firmware version so we can use the correct commands
- TerpstraSysExApplication::getApp().getLumatoneController().sendGetFirmwareRevisionRequest();
-
- alert.reset(new AlertWindow("Connection established!", translate("Do you want to send the current setup to your Lumatone?"), AlertWindow::AlertIconType::QuestionIcon, getParentComponent()));
- alert->addButton("Send Editor layout", 1);
- alert->addButton("Keep Editing Offline", 0);
- alert->addButton("Import From Lumatone", 2);
- alert->setLookAndFeel(&lookAndFeel);
-
-/* alert = lookAndFeel.createAlertWindow("Connection established!", translate("Do you want to send the current setup to your Lumatone?"),
- "Import from Lumatone",
- "Send layout",
- "Edit Offline",
- AlertWindow::AlertIconType::WarningIcon,
- 3, getParentComponent())*/;
-
- auto retc = alert->runModalLoop();
+ if (dialogTitle.length() == 0)
+ dialogTitle = translate("Connection Established!");
- if (retc == 1)
- {
- TerpstraSysExApplication::getApp().requestConfigurationFromDevice();
- liveEditorBtn->setToggleState(true, NotificationType::dontSendNotification);
- lblConnectionState->setText("Connected", NotificationType::dontSendNotification);
- }
- else if (retc == 2)
- {
- TerpstraSysExApplication::getApp().sendCurrentConfigurationToDevice();
- liveEditorBtn->setToggleState(true, dontSendNotification);
- lblConnectionState->setText("Connected", NotificationType::dontSendNotification);
- }
- else
+ jassert(!isWaitingForUserChoice);
+ if (isWaitingForUserChoice)
+ {
+ DBG("Bad connection loop detected");
+ return;
+ }
+
+ isWaitingForUserChoice = true;
+
+ auto alertOptions = MessageBoxOptions().withTitle(dialogTitle)
+ .withMessage(translate("Do you want to send the current setup to your Lumatone?"))
+ .withIconType(AlertWindow::AlertIconType::QuestionIcon)
+ .withAssociatedComponent(getParentComponent())
+ .withButton("Send Editor Layout")
+ .withButton("Keep Editing Offline")
+ .withButton("Import From Lumatone");
+
+ AlertWindow::showAsync(alertOptions, [&](int retc)
{
- offlineEditorBtn->setToggleState(true, NotificationType::sendNotification);
- lblConnectionState->setText("Offline", NotificationType::dontSendNotification);
- }
-
- //alert->setLookAndFeel(nullptr);
- alert = nullptr;
- }
+ isWaitingForUserChoice = false;
+
+ if (retc == 0) // Import
+ {
+ TerpstraSysExApplication::getApp().requestConfigurationFromDevice();
+ liveEditorBtn->setToggleState(true, NotificationType::sendNotification);
+ lblConnectionState->setText("Connected", NotificationType::dontSendNotification);
+ }
+ else if (retc == 1) // Send
+ {
+ TerpstraSysExApplication::getApp().sendCurrentConfigurationToDevice();
+ liveEditorBtn->setToggleState(true, NotificationType::sendNotification);
+ lblConnectionState->setText("Connected", NotificationType::dontSendNotification);
+ }
+ else if (retc == 2) // Offline
+ {
+ offlineEditorBtn->setToggleState(true, NotificationType::sendNotification);
+ lblConnectionState->setText("Offline", NotificationType::dontSendNotification);
+ }
+ });
}
void MidiEditArea::refreshInputMenuAndSetSelected(int inputDeviceIndex, juce::NotificationType notificationType)
{
cbMidiInput->clear(NotificationType::dontSendNotification);
int i = 1;
- for (auto device : TerpstraSysExApplication::getApp().getLumatoneController().getMidiInputList())
+ for (auto device : TerpstraSysExApplication::getApp().getLumatoneController()->getMidiInputList())
cbMidiInput->addItem(device.name, i++);
- if (inputDeviceIndex > 0)
+ if (inputDeviceIndex >= 0)
cbMidiInput->setSelectedId(inputDeviceIndex, notificationType);
}
@@ -630,10 +625,10 @@ void MidiEditArea::refreshOutputMenuAndSetSelected(int outputDeviceIndex, juce::
{
cbMidiOutput->clear(NotificationType::dontSendNotification);
int i = 1;
- for (auto device : TerpstraSysExApplication::getApp().getLumatoneController().getMidiOutputList())
+ for (auto device : TerpstraSysExApplication::getApp().getLumatoneController()->getMidiOutputList())
cbMidiOutput->addItem(device.name, i++);
- if (outputDeviceIndex > 0)
+ if (outputDeviceIndex >= 0)
cbMidiOutput->setSelectedId(outputDeviceIndex, notificationType);
}
@@ -645,15 +640,15 @@ void MidiEditArea::timerCallback()
}
else
{
- TerpstraSysExApplication::getApp().getLumatoneController().refreshAvailableMidiDevices();
+ TerpstraSysExApplication::getApp().getLumatoneController()->refreshAvailableMidiDevices();
refreshInputMenuAndSetSelected(
- TerpstraSysExApplication::getApp().getLumatoneController().getMidiInputIndex() + 1,
+ TerpstraSysExApplication::getApp().getLumatoneController()->getMidiInputIndex() + 1,
juce::NotificationType::dontSendNotification
);
refreshOutputMenuAndSetSelected(
- TerpstraSysExApplication::getApp().getLumatoneController().getMidiOutputIndex() + 1,
+ TerpstraSysExApplication::getApp().getLumatoneController()->getMidiOutputIndex() + 1,
juce::NotificationType::dontSendNotification
);
}
diff --git a/Source/MidiEditArea.h b/Source/MidiEditArea.h
index 2ce58a49..dba95248 100644
--- a/Source/MidiEditArea.h
+++ b/Source/MidiEditArea.h
@@ -21,10 +21,9 @@
//[Headers] -- You can add your own extra header files here --
#include "JuceHeader.h"
-#include "TerpstraMidiDriver.h"
#include "HajuLib/HajuErrorVisualizer.h"
+#include "ApplicationListeners.h"
#include "LumatoneEditorLookAndFeel.h"
-#include "DeviceActivityMonitor.h"
//[/Headers]
@@ -38,7 +37,8 @@
//[/Comments]
*/
class MidiEditArea : public Component,
- public LumatoneController::StatusListener,
+ public LumatoneEditor::StatusListener,
+ public LumatoneEditor::EditorListener,
public juce::ComboBox::Listener,
public juce::Button::Listener,
public juce::Timer
@@ -52,24 +52,25 @@ class MidiEditArea : public Component,
//[UserMethods] -- You can add your own custom methods in this section.
void lookAndFeelChanged() override;
- void attemptDeviceConnection();
- void onOpenConnectionToDevice();
+ void onOpenConnectionToDevice(String dialogTitle = "");
- // For now, preserve connection functionality and make sure internal combo boxes are up to date
void refreshInputMenuAndSetSelected(int inputDeviceIndex, juce::NotificationType notificationType = NotificationType::sendNotification);
void refreshOutputMenuAndSetSelected(int outputDeviceIndex, juce::NotificationType notificationType = NotificationType::sendNotification);
- // Implementation of LumatoneController::StatusListener
- //void availableDevicesChanged(const Array& inputDevices, int lastInputDevice, const Array& outputDevices, int lastOutputDevice) override;
+ // Implementation of LumatoneEditor::StatusListener
+ void connectionFailed() override;
void connectionEstablished(int inputDevice, int outputDevice) override;
void connectionLost() override;
+
+ // Implementation of LumatoneEditor::EditorListener
+ void editorModeChanged(sysExSendingMode editMode) override;
void timerCallback() override;
private:
- void setConnectivity(bool isConnected);
+ void setConnectivity(bool isConnected, String connectionStatus=String());
public:
//[/UserMethods]
@@ -92,6 +93,7 @@ class MidiEditArea : public Component,
bool isConnected = false;
bool isWaitingForConnectionTest = false;
+ bool isWaitingForUserChoice = false;
LumatoneEditorLookAndFeel& lookAndFeel;
@@ -111,7 +113,7 @@ class MidiEditArea : public Component,
//==============================================================================
// Helpers
- std::unique_ptr alert;
+ //std::unique_ptr alert;
FlexBox ioAreaFlexBox;
@@ -169,11 +171,8 @@ class MidiEditArea : public Component,
const float connectedAreaX = 0.8387f;
const float controlBoundsMarginScalar = 0.0325f;
- const float connectedX = 0.871f;
const float connectivityHeight = 0.1957f;
- const float logomarkX = 0.9559f;
- const float logomarkY = 0.2222f;
const float logomarkHeight = 0.5f;
//[/UserVariables]
diff --git a/Source/NoteEditArea.cpp b/Source/NoteEditArea.cpp
index 847ab694..e601d3d8 100644
--- a/Source/NoteEditArea.cpp
+++ b/Source/NoteEditArea.cpp
@@ -7,7 +7,7 @@
the "//[xyz]" and "//[/xyz]" sections will be retained when the file is loaded
and re-saved.
- Created with Projucer version: 6.0.5
+ Created with Projucer version: 6.0.8
------------------------------------------------------------------------------
@@ -19,8 +19,6 @@
//[Headers] You can add your own extra header files here...
#include "ViewConstants.h"
-#include "SingleNoteAssign.h"
-#include "IsomorphicMassAssign.h"
#include "Main.h"
//[/Headers]
@@ -33,28 +31,42 @@
//==============================================================================
NoteEditArea::NoteEditArea ()
- : Component("NoteEditArea"),
- currentSingleKeySelection(-1)
+ : currentSingleKeySelection(-1)
{
- //[Constructor_pre] You can add your own custom stuff here..;
+ //[Constructor_pre] You can add your own custom stuff here..
showIsomorphicMassAssign = TerpstraSysExApplication::getApp().getPropertiesFile()->getBoolValue("IsomorphicMassAssign", false);
//[/Constructor_pre]
- editFunctionsTab.reset(new juce::TabbedComponent(juce::TabbedButtonBar::TabsAtTop));
- editFunctionsTab->setName("EditFunctionsTab");
- editFunctionsTab->setColour(TabbedComponent::ColourIds::outlineColourId, Colour());
- editFunctionsTab->setColour(TabbedComponent::ColourIds::backgroundColourId, Colour());
- addAndMakeVisible(editFunctionsTab.get());
- editFunctionsTab->addTab(translate("ManualAssign"), juce::Colours::lightgrey, new SingleNoteAssign(), true);
- editFunctionsTab->setCurrentTabIndex(0);
+ setName ("NoteEditArea");
+ editFunctionsTab.reset (new juce::TabbedComponent (juce::TabbedButtonBar::TabsAtTop));
+ addAndMakeVisible (editFunctionsTab.get());
+ editFunctionsTab->setTabBarDepth (30);
+ editFunctionsTab->addTab (TRANS("Manual Assign"), juce::Colours::lightgrey, new SingleNoteAssign(), true);
+ editFunctionsTab->setCurrentTabIndex (0);
+
+ editFunctionsTab->setBounds (8, 48, 320, 422);
+
+ labelWindowTitle.reset (new juce::Label ("labelWindowTitle",
+ TRANS("Assign Keys")));
+ addAndMakeVisible (labelWindowTitle.get());
+ labelWindowTitle->setFont (juce::Font (18.00f, juce::Font::plain).withTypefaceStyle ("Regular"));
+ labelWindowTitle->setJustificationType (juce::Justification::centredLeft);
+ labelWindowTitle->setEditable (false, false, false);
+ labelWindowTitle->setColour (juce::Label::textColourId, juce::Colour (0xff61acc8));
+ labelWindowTitle->setColour (juce::TextEditor::textColourId, juce::Colours::black);
+ labelWindowTitle->setColour (juce::TextEditor::backgroundColourId, juce::Colour (0x00000000));
+
+ labelWindowTitle->setBounds (8, 8, 104, 24);
+
+
+ //[UserPreSize]
editFunctionsTab->setIndent(0);
editFunctionsTab->setOutline(0);
- labelWindowTitle.reset (new juce::Label ("labelWindowTitle", translate("AssignKeys")));
- labelWindowTitle->setFont(TerpstraSysExApplication::getApp().getAppFont(LumatoneEditorFont::UniviaProBold));
- addAndMakeVisible (labelWindowTitle.get());
+ editFunctionsTab->setColour(TabbedComponent::ColourIds::outlineColourId, Colour());
+ editFunctionsTab->setColour(TabbedComponent::ColourIds::backgroundColourId, Colour());
- //[UserPreSize]
+ labelWindowTitle->setFont(TerpstraSysExApplication::getApp().getAppFont(LumatoneEditorFont::UniviaProBold));
if (showIsomorphicMassAssign)
editFunctionsTab->addTab(TRANS("Isomorphic Assign"), juce::Colours::lightgrey, new IsomorphicMassAssign(), true);
@@ -73,13 +85,24 @@ NoteEditArea::NoteEditArea ()
// Single Key fields
resetOctaveSize(false);
+ /* Don't want to resize now
+ /*
//[/UserPreSize]
+ setSize (760, 470);
+
//[Constructor] You can add your own custom stuff here..
+ */
// First octaveboard selection, selection on first key: see MainComponent (Has to be done after change listener has been established)
+ auto singleNoteAssign = dynamic_cast(editFunctionsTab->getTabContentComponent(0));
+ if (singleNoteAssign != nullptr)
+ {
+ addColourSelectionListener(singleNoteAssign);
+ }
+
//[/Constructor]
}
@@ -108,9 +131,15 @@ NoteEditArea::~NoteEditArea()
void NoteEditArea::paint (juce::Graphics& g)
{
//[UserPrePaint] Add your own custom painting code here..
+
+ /*
//[/UserPrePaint]
+ g.fillAll (juce::Colour (0xffbad0de));
+
//[UserPaint] Add your own custom painting code here..
+ */
+
g.setColour(backgroundColour);
g.fillRoundedRectangle(contentBackground, roundedCornerLayout);
@@ -153,7 +182,7 @@ void NoteEditArea::resized()
// Single Key fields
- keyEditBounds = contentBackground.withLeft(assignControlsBounds.getRight() + assignControlsBounds.getX() / 2);
+ keyEditBounds = contentBackground.withLeft(assignControlsBounds.getRight() + assignControlsBounds.getX() * 0.5f);
tilingGeometry.fitTilingTo(
keyEditBounds,
@@ -167,7 +196,7 @@ void NoteEditArea::resized()
jassert(keyCentres.size() == TerpstraSysExApplication::getApp().getOctaveBoardSize());
float keySize = tilingGeometry.getKeySize();
-
+
int keyIndex = 0;
for (keyIndex = 0; keyIndex < keyCentres.size(); keyIndex++)
{
@@ -193,35 +222,44 @@ void NoteEditArea::mouseDown (const juce::MouseEvent& e)
// Select field
changeSingleKeySelection(keyIndex);
- // Perform the edit, according to edit mode. Including sending to device
- auto setSelection = octaveBoardSelectorTab->getCurrentTabIndex();
- jassert(setSelection >= 0 && setSelection < NUMBEROFBOARDS && keyIndex >= 0 && keyIndex < TerpstraSysExApplication::getApp().getOctaveBoardSize());
-
- int editMode = editFunctionsTab->getCurrentTabIndex();
- switch (editMode)
- {
- case noteEditMode::SingleNoteAssignMode:
+ // Grab key colour - may be replaced with eyedropper tool
+ if (e.mods.isAltDown())
{
- auto editAction = dynamic_cast(editFunctionsTab->getTabContentComponent(editMode))->createEditAction(setSelection, keyIndex);
- TerpstraSysExApplication::getApp().performUndoableAction(editAction);
- break;
+ auto colour = terpstraKeyFields[keyIndex]->getValue().colour;
+ selectorListeners.call(&ColourSelectionListener::colourChangedCallback, this, colour);
}
- case noteEditMode::IsomorphicMassAssignMode:
+ // Standard assign action
+ else
{
- bool mappingChanged = dynamic_cast(editFunctionsTab->getTabContentComponent(editMode))->performMouseDown(setSelection, keyIndex);
- if (mappingChanged)
- {
- TerpstraSysExApplication::getApp().setHasChangesToSave(true);
+ // Perform the edit, according to edit mode. Including sending to device
+ auto setSelection = octaveBoardSelectorTab->getCurrentTabIndex();
+ jassert(setSelection >= 0 && setSelection < NUMBEROFBOARDS&& keyIndex >= 0 && keyIndex < TerpstraSysExApplication::getApp().getOctaveBoardSize());
- // Refresh key fields (all may be affected)
- ((MainContentComponent*)getParentComponent())->refreshKeyDataFields();
+ int editMode = editFunctionsTab->getCurrentTabIndex();
+ switch (editMode)
+ {
+ case noteEditMode::SingleNoteAssignMode:
+ {
+ auto editAction = dynamic_cast(editFunctionsTab->getTabContentComponent(noteEditMode::SingleNoteAssignMode))->createEditAction(setSelection, keyIndex);
+ TerpstraSysExApplication::getApp().performUndoableAction(editAction);
+ break;
+ }
+ case noteEditMode::IsomorphicMassAssignMode:
+ {
+ bool mappingChanged = dynamic_cast(editFunctionsTab->getTabContentComponent(editMode))->performMouseDown(setSelection, keyIndex);
+ if (mappingChanged)
+ {
+ TerpstraSysExApplication::getApp().setHasChangesToSave(true);
+
+ // Refresh key fields (all may be affected)
+ ((MainContentComponent*)getParentComponent())->refreshKeyDataFields();
+ }
+ break;
+ }
+ default:
+ break;
}
- break;
- }
- default:
- break;
}
-
break;
}
}
@@ -249,9 +287,11 @@ void NoteEditArea::setControlsTopLeftPosition(int controlsAreaX, int controlsAre
void NoteEditArea::restoreStateFromPropertiesFile(PropertiesFile* propertiesFile)
{
dynamic_cast(editFunctionsTab->getTabContentComponent(noteEditMode::SingleNoteAssignMode))->restoreStateFromPropertiesFile(propertiesFile);
-
+
if (showIsomorphicMassAssign)
dynamic_cast(editFunctionsTab->getTabContentComponent(noteEditMode::IsomorphicMassAssignMode))->restoreStateFromPropertiesFile(propertiesFile);
+
+ resized();
}
void NoteEditArea::saveStateToPropertiesFile(PropertiesFile* propertiesFile)
@@ -291,7 +331,7 @@ ColourEditComponent* NoteEditArea::getColourEditComponent()
return dynamic_cast(editFunctionsTab->getTabContentComponent(noteEditMode::SingleNoteAssignMode))->getColourEditComponent();
}
-ColourTextEditor* NoteEditArea::getSingleNoteColourTextEditor()
+ColourTextEditor* NoteEditArea::getSingleNoteColourTextEditor()
{
return dynamic_cast(editFunctionsTab->getTabContentComponent(noteEditMode::SingleNoteAssignMode))->getColourTextEditor();;
}
@@ -344,6 +384,23 @@ void NoteEditArea::resetOctaveSize(bool refreshAndResize)
}
}
}
+
+Colour NoteEditArea::getSelectedColour()
+{
+ if (currentSingleKeySelection >= 0 && currentSingleKeySelection < TerpstraSysExApplication::getApp().getOctaveBoardSize())
+ {
+ return terpstraKeyFields[currentSingleKeySelection]->getValue().colour;
+ }
+
+ auto singleNoteAssign = dynamic_cast(editFunctionsTab->getTabContentComponent(SingleNoteAssignMode));
+ if (singleNoteAssign != nullptr)
+ {
+ return singleNoteAssign->getColourEditComponent()->getColourAsObject();
+ }
+
+ return Colour();
+}
+
//[/MiscUserCode]
@@ -356,12 +413,11 @@ void NoteEditArea::resetOctaveSize(bool refreshAndResize)
BEGIN_JUCER_METADATA
-
+
diff --git a/Source/NoteEditArea.h b/Source/NoteEditArea.h
index c07e950e..b85431c9 100644
--- a/Source/NoteEditArea.h
+++ b/Source/NoteEditArea.h
@@ -7,7 +7,7 @@
the "//[xyz]" and "//[/xyz]" sections will be retained when the file is loaded
and re-saved.
- Created with Projucer version: 6.0.5
+ Created with Projucer version: 6.0.8
------------------------------------------------------------------------------
@@ -31,6 +31,9 @@
#include "BoardGeometry.h"
+#include "SingleNoteAssign.h"
+#include "IsomorphicMassAssign.h"
+
//[/Headers]
@@ -44,7 +47,8 @@
//[/Comments]
*/
class NoteEditArea : public Component,
- public ChangeListener
+ public ChangeListener,
+ public ColourSelectionBroadcaster
{
public:
//==============================================================================
@@ -71,6 +75,8 @@ class NoteEditArea : public Component,
ColourTextEditor* getSingleNoteColourTextEditor();
+ IsomorphicMassAssign* getIsomorphicMassAssignPanel() { return dynamic_cast(editFunctionsTab->getTabContentComponent(1)); }
+
void changeSingleKeySelection(int newSelection);
void refreshKeyFields();
@@ -82,6 +88,10 @@ class NoteEditArea : public Component,
void resetOctaveSize(bool refreshAndResize=true);
+ // ColourSelectionBroadcaster Implementation
+ Colour getSelectedColour() override;
+ void deselectColour() override {};
+
//[/UserMethods]
void paint (juce::Graphics& g) override;
@@ -89,6 +99,7 @@ class NoteEditArea : public Component,
void mouseDown (const juce::MouseEvent& e) override;
+
private:
//[UserVariables] -- You can add your own custom variables in this section.
enum noteEditMode
diff --git a/Source/NoteOnOffVelocityCurveDialog.cpp b/Source/NoteOnOffVelocityCurveDialog.cpp
index 28e2857d..ad361dee 100644
--- a/Source/NoteOnOffVelocityCurveDialog.cpp
+++ b/Source/NoteOnOffVelocityCurveDialog.cpp
@@ -17,32 +17,3 @@ NoteOnOffVelocityCurveDialog::NoteOnOffVelocityCurveDialog()
: VelocityCurveDlgBase(TerpstraVelocityCurveConfig::VelocityCurveType::noteOnNoteOff)
{
}
-
-//NoteOnOffVelocityCurveDialog::~NoteOnOffVelocityCurveDialog()
-//{
-//}
-
-//float NoteOnOffVelocityCurveDialog::beamWidth(int xPos)
-//{
-// auto mappingInEdit = getMappingInEdit();
-// if ( mappingInEdit == nullptr) // Security at start of program
-// return 1; // ad-hoc
-//
-// if (xPos == 0)
-// {
-// return (getWidth() - 2.0f * cbEditMode->getX()) * mappingInEdit->velocityIntervalTableValues[0] / 2048.0f;
-// }
-// else if (xPos < VELOCITYINTERVALTABLESIZE)
-// {
-// return (getWidth() - 2.0f * cbEditMode->getX()) * (mappingInEdit->velocityIntervalTableValues[xPos] - mappingInEdit->velocityIntervalTableValues[xPos-1]) / 2048.0f;
-// }
-// else if (xPos == VELOCITYINTERVALTABLESIZE)
-// {
-// return (getWidth() - 2.0f * cbEditMode->getX()) * (2048.0f - mappingInEdit->velocityIntervalTableValues[xPos-1]) / 2048.0f;
-// }
-// else
-// {
-// jassertfalse;
-// return 1;
-// }
-//}
\ No newline at end of file
diff --git a/Source/NoteOnOffVelocityCurveDialog.h b/Source/NoteOnOffVelocityCurveDialog.h
index cfa7a2b0..47e5292b 100644
--- a/Source/NoteOnOffVelocityCurveDialog.h
+++ b/Source/NoteOnOffVelocityCurveDialog.h
@@ -15,13 +15,7 @@
// Note on/on velocity curve dialog. Horizontal axis stands for ticks
class NoteOnOffVelocityCurveDialog : public VelocityCurveDlgBase {
public:
- //==============================================================================
NoteOnOffVelocityCurveDialog();
- //~NoteOnOffVelocityCurveDialog() override;
-
- //protected:
- // virtual float beamWidth(int xPos) override;
-
};
class FaderVelocityCurveDialog : public VelocityCurveDlgBase {
diff --git a/Source/Palette.h b/Source/Palette.h
index c92148c6..526903fd 100644
--- a/Source/Palette.h
+++ b/Source/Palette.h
@@ -26,17 +26,11 @@ class Palette : public juce::Component, protected juce::ChangeListener
// Setup paths needed to draw swatches
}
- ~Palette() override
- {
- }
+ virtual ~Palette() {}
//======================================================================
// Implementation of juce::Component
- virtual void paint(Graphics& g) = 0;
-
- virtual void resized() = 0;
-
virtual void mouseDown(const MouseEvent& e) override
{
if (isEnabled())
@@ -46,12 +40,6 @@ class Palette : public juce::Component, protected juce::ChangeListener
if (s.contains(e.position))
{
setSelectedSwatchNumber(swatchPaths.indexOf(s));
-
- if (selector)
- {
- selector->setCurrentColour(palette[selectedSwatch], dontSendNotification);
- }
-
return;
}
}
@@ -99,6 +87,10 @@ class Palette : public juce::Component, protected juce::ChangeListener
virtual void setSelectedSwatchNumber(int swatchIndex)
{
selectedSwatch = swatchIndex;
+ if (selector)
+ {
+ selector->setCurrentColour(palette[selectedSwatch], dontSendNotification);
+ }
repaint();
}
diff --git a/Source/PedalSensitivityDlg.cpp b/Source/PedalSensitivityDlg.cpp
index d94e8f93..4bdf1844 100644
--- a/Source/PedalSensitivityDlg.cpp
+++ b/Source/PedalSensitivityDlg.cpp
@@ -19,6 +19,7 @@
//[Headers] You can add your own extra header files here...
#include "Main.h"
+#include "EditActions.h"
//[/Headers]
#include "PedalSensitivityDlg.h"
@@ -34,7 +35,7 @@ PedalSensitivityDlg::PedalSensitivityDlg ()
//[/Constructor_pre]
labelExprContrSensitivity.reset (new juce::Label ("new label",
- TRANS("Sensitivity:")));
+ TRANS("Sensitivity")));
addAndMakeVisible (labelExprContrSensitivity.get());
labelExprContrSensitivity->setFont (juce::Font (15.00f, juce::Font::plain).withTypefaceStyle ("Regular"));
labelExprContrSensitivity->setJustificationType (juce::Justification::centredLeft);
@@ -42,49 +43,62 @@ PedalSensitivityDlg::PedalSensitivityDlg ()
labelExprContrSensitivity->setColour (juce::TextEditor::textColourId, juce::Colours::black);
labelExprContrSensitivity->setColour (juce::TextEditor::backgroundColourId, juce::Colour (0x00000000));
- labelExprContrSensitivity->setBounds (6, 35, 64, 24);
+ labelExprContrSensitivity->setBounds (13, 152, 74, 24);
btnInvertExpression.reset (new juce::ToggleButton ("btnInvertExpression"));
addAndMakeVisible (btnInvertExpression.get());
- btnInvertExpression->setButtonText (TRANS("Invert Expression"));
+ btnInvertExpression->setButtonText (TRANS("Invert"));
btnInvertExpression->addListener (this);
- btnInvertExpression->setBounds (8, 96, 162, 24);
+ btnInvertExpression->setBounds (10, 32, 99, 24);
- labelPedalTitle.reset (new juce::Label ("labelPedalTitle",
- TRANS("Pedal Settings")));
- addAndMakeVisible (labelPedalTitle.get());
- labelPedalTitle->setFont (juce::Font (18.00f, juce::Font::plain).withTypefaceStyle ("Regular"));
- labelPedalTitle->setJustificationType (juce::Justification::centredLeft);
- labelPedalTitle->setEditable (false, false, false);
- labelPedalTitle->setColour (juce::Label::textColourId, juce::Colour (0xff61acc8));
- labelPedalTitle->setColour (juce::TextEditor::textColourId, juce::Colours::black);
- labelPedalTitle->setColour (juce::TextEditor::backgroundColourId, juce::Colour (0x00000000));
+ lblExpression.reset (new juce::Label ("lblExpression",
+ TRANS("Expression")));
+ addAndMakeVisible (lblExpression.get());
+ lblExpression->setFont (juce::Font (18.00f, juce::Font::plain).withTypefaceStyle ("Regular"));
+ lblExpression->setJustificationType (juce::Justification::centredLeft);
+ lblExpression->setEditable (false, false, false);
+ lblExpression->setColour (juce::Label::textColourId, juce::Colour (0xff61acc8));
+ lblExpression->setColour (juce::TextEditor::textColourId, juce::Colours::black);
+ lblExpression->setColour (juce::TextEditor::backgroundColourId, juce::Colour (0x00000000));
- labelPedalTitle->setBounds (6, 3, 104, 24);
+ lblExpression->setBounds (6, 3, 104, 24);
sldExprCtrlSensitivity.reset (new juce::Slider ("sldExprCtrlSensitivity"));
addAndMakeVisible (sldExprCtrlSensitivity.get());
sldExprCtrlSensitivity->setRange (0, 127, 1);
sldExprCtrlSensitivity->setSliderStyle (juce::Slider::RotaryHorizontalVerticalDrag);
- sldExprCtrlSensitivity->setTextBoxStyle (juce::Slider::TextBoxLeft, false, 60, 20);
+ sldExprCtrlSensitivity->setTextBoxStyle (juce::Slider::TextBoxBelow, false, 60, 20);
sldExprCtrlSensitivity->addListener (this);
- sldExprCtrlSensitivity->setBounds (77, 12, 160, 72);
+ sldExprCtrlSensitivity->setBounds (-32, 49, 160, 97);
btnInvertSustain.reset (new juce::ToggleButton ("btnInvertSustain"));
addAndMakeVisible (btnInvertSustain.get());
- btnInvertSustain->setButtonText (TRANS("Invert Sustain"));
+ btnInvertSustain->setButtonText (TRANS("Invert"));
btnInvertSustain->addListener (this);
- btnInvertSustain->setBounds (8, 72, 150, 24);
+ btnInvertSustain->setBounds (125, 32, 86, 24);
+
+ lblSustain.reset (new juce::Label ("lblSustain",
+ TRANS("Sustain")));
+ addAndMakeVisible (lblSustain.get());
+ lblSustain->setFont (juce::Font (18.00f, juce::Font::plain).withTypefaceStyle ("Regular"));
+ lblSustain->setJustificationType (juce::Justification::centredLeft);
+ lblSustain->setEditable (false, false, false);
+ lblSustain->setColour (juce::Label::textColourId, juce::Colour (0xff61acc8));
+ lblSustain->setColour (juce::TextEditor::textColourId, juce::Colours::black);
+ lblSustain->setColour (juce::TextEditor::backgroundColourId, juce::Colour (0x00000000));
+
+ lblSustain->setBounds (121, 3, 99, 24);
//[UserPreSize]
- labelPedalTitle->setFont(TerpstraSysExApplication::getApp().getAppFont(LumatoneEditorFont::UniviaProBold));
+ lblExpression->setFont(TerpstraSysExApplication::getApp().getAppFont(LumatoneEditorFont::UniviaProBold));
+ lblSustain->setFont(TerpstraSysExApplication::getApp().getAppFont(LumatoneEditorFont::UniviaProBold));
labelExprContrSensitivity->setFont(TerpstraSysExApplication::getApp().getAppFont(LumatoneEditorFont::GothamNarrowMedium));
-
- TerpstraSysExApplication::getApp().getLumatoneController().addFirmwareListener(this);
+ labelExprContrSensitivity->setJustificationType(Justification::centred);
+ TerpstraSysExApplication::getApp().getLumatoneController()->addFirmwareListener(this);
//[/UserPreSize]
setSize (134, 96);
@@ -101,9 +115,10 @@ PedalSensitivityDlg::~PedalSensitivityDlg()
labelExprContrSensitivity = nullptr;
btnInvertExpression = nullptr;
- labelPedalTitle = nullptr;
+ lblExpression = nullptr;
sldExprCtrlSensitivity = nullptr;
btnInvertSustain = nullptr;
+ lblSustain = nullptr;
//[Destructor]. You can add your own custom destruction code here..
@@ -118,7 +133,8 @@ void PedalSensitivityDlg::paint (juce::Graphics& g)
//[UserPaint] Add your own custom painting code here..
g.setColour(Colour(0xff212626));
- g.fillRoundedRectangle(getLocalBounds().toFloat().withTop(proportionOfHeight(SETTINGSAREAMARGINHEIGHT)), roundedCornerSize);
+ g.fillRoundedRectangle(expressionBounds, roundedCornerSize);
+ g.fillRoundedRectangle(sustainBounds, roundedCornerSize);
//[/UserPaint]
}
@@ -131,20 +147,38 @@ void PedalSensitivityDlg::resized()
//[UserResized] Add your own custom resize handling here..
- roundedCornerSize = round(getParentHeight() * ROUNDEDCORNERTOAPPHEIGHT);
+ roundedCornerSize = roundToInt(getParentHeight() * ROUNDEDCORNERTOAPPHEIGHT);
- resizeLabelWithHeight(labelPedalTitle.get(), roundToInt(getHeight() * SETTINGSLABELHEIGHT));
- labelPedalTitle->setTopLeftPosition(roundToInt(getWidth() * SETTINGSLABELMARGINWIDTH), 0);
+ int areaMarginWidth = roundToInt(w * sectionMarginWidth) * 0.5f;
+ int areaMarginHeight = roundToInt(h * SETTINGSAREAMARGINHEIGHT);
- int marginX = roundToInt(getParentWidth() * SETTINGSCONTROLMARGINTOAPPWIDTH);
- int buttonHeight = roundToInt(h * SETTINGSTOGGLEHEIGHTSCALAR);
- btnInvertExpression->setBounds(marginX, proportionOfHeight(0.3f), w, buttonHeight);
- btnInvertSustain->setBounds(marginX, proportionOfHeight(0.5), w, buttonHeight);
+ expressionBounds = getLocalBounds().toFloat().withTop(areaMarginHeight).withRight(roundToInt(w * 0.5f - areaMarginWidth));
+ sustainBounds = getLocalBounds().toFloat().withTop(areaMarginHeight).withLeft(roundToInt(w * 0.5f + areaMarginWidth));
- sldExprCtrlSensitivity->setBounds(getLocalBounds().toFloat().getProportion(sliderBoundsProps).toNearestInt());
+ int lblMarginX = roundToInt(w * SETTINGSLABELMARGINWIDTH);
+ int lblWidth = roundToInt(w * 0.5f);
+ int lblHeight = roundToInt(h * SETTINGSLABELHEIGHT);
+ lblExpression->setBounds(expressionBounds.getX() + lblMarginX, 0, lblWidth, lblHeight);
+ lblSustain->setBounds(sustainBounds.getX() + lblMarginX, 0, lblWidth, lblHeight);
- resizeLabelWithHeight(labelExprContrSensitivity.get(), buttonHeight * 1.2f, 1.0f, "");
- labelExprContrSensitivity->setCentrePosition(sldExprCtrlSensitivity->getBounds().getCentreX(), btnInvertExpression->getBounds().getCentreY());
+ int controlMargin = roundToInt(getParentWidth() * SETTINGSCONTROLMARGINTOAPPWIDTH);
+ int buttonHeight = roundToInt(h * SETTINGSTOGGLEHEIGHTSCALAR);
+ int buttonY = roundToInt(h * 0.3f);
+ btnInvertExpression->setBounds(expressionBounds.withTrimmedLeft(controlMargin).withTop(buttonY).withHeight(buttonHeight).toNearestInt());
+ btnInvertSustain->setBounds(sustainBounds.withTrimmedLeft(controlMargin).withTop(buttonY).withHeight(buttonHeight).toNearestInt());
+
+ sldExprCtrlSensitivity->setBounds(
+ expressionBounds.reduced(expressionBounds.getWidth() * 0.2f, 0)
+ .withTop(btnInvertExpression->getBottom() + buttonHeight)
+ .withTrimmedBottom(buttonHeight * 1.5f)
+ .toNearestInt()
+ );
+
+ labelExprContrSensitivity->setBounds(
+ expressionBounds.withTop(sldExprCtrlSensitivity->getBottom() + buttonHeight * 0.1f)
+ .withTrimmedBottom(buttonHeight * 0.5f)
+ .toNearestInt()
+ );
//[/UserResized]
}
@@ -156,19 +190,13 @@ void PedalSensitivityDlg::buttonClicked (juce::Button* buttonThatWasClicked)
if (buttonThatWasClicked == btnInvertExpression.get())
{
//[UserButtonCode_btnInvertExpression] -- add your button handler code here..
- bool invert = btnInvertExpression->getToggleState();
- ((MainContentComponent*)getParentComponent())->getMappingInEdit().invertExpression = invert;
- TerpstraSysExApplication::getApp().setHasChangesToSave(true);
- TerpstraSysExApplication::getApp().getLumatoneController().sendInvertFootController(invert);
+ TerpstraSysExApplication::getApp().performUndoableAction(new Lumatone::InvertFootControllerEditAction(btnInvertExpression->getToggleState()));
//[/UserButtonCode_btnInvertExpression]
}
else if (buttonThatWasClicked == btnInvertSustain.get())
{
//[UserButtonCode_btnInvertSustain] -- add your button handler code here..
- bool invert = btnInvertSustain->getToggleState();
- ((MainContentComponent*)getParentComponent())->getMappingInEdit().invertSustain = invert;
- TerpstraSysExApplication::getApp().setHasChangesToSave(true);
- TerpstraSysExApplication::getApp().getLumatoneController().invertSustainPedal(invert);
+ TerpstraSysExApplication::getApp().performUndoableAction(new Lumatone::InvertSustainEditAction(btnInvertSustain->getToggleState()));
//[/UserButtonCode_btnInvertSustain]
}
@@ -184,6 +212,24 @@ void PedalSensitivityDlg::sliderValueChanged (juce::Slider* sliderThatWasMoved)
if (sliderThatWasMoved == sldExprCtrlSensitivity.get())
{
//[UserSliderCode_sldExprCtrlSensitivity] -- add your slider handling code here..
+ int newSensitvity = sldExprCtrlSensitivity->getValue();
+ // ToDo value checking: encapsulate in keyboard data structure?
+ if (newSensitvity < 0)
+ {
+ newSensitvity = 0;
+ sldExprCtrlSensitivity->setValue(newSensitvity);
+ }
+
+ if (newSensitvity > 0x7f)
+ {
+ newSensitvity = 0x7f;
+ sldExprCtrlSensitivity->setValue(newSensitvity);
+ }
+
+ ((MainContentComponent*)getParentComponent())->getMappingInEdit().expressionControllerSensivity = newSensitvity;
+ TerpstraSysExApplication::getApp().setHasChangesToSave(true);
+ TerpstraSysExApplication::getApp().getLumatoneController()->sendExpressionPedalSensivity(newSensitvity);
+
//[/UserSliderCode_sldExprCtrlSensitivity]
}
@@ -195,40 +241,13 @@ void PedalSensitivityDlg::sliderValueChanged (juce::Slider* sliderThatWasMoved)
//[MiscUserCode] You can add your own definitions of your custom methods or any other code here...
-
-//void PedalSensitivityDlg::textEditorTextChanged(TextEditor& textEdit)
-//{
-//}
-//
-//void PedalSensitivityDlg::textEditorFocusLost(TextEditor& textEdit)
-//{
-// if (&textEdit == txtExprCtrlSensivity.get())
-// {
-// int newSensitvity = textEdit.getText().getIntValue();
-// if (newSensitvity < 0)
-// {
-// newSensitvity = 0;
-// textEdit.setText(String(newSensitvity));
-// }
-//
-// if (newSensitvity > 0x7f)
-// {
-// newSensitvity = 0x7f;
-// textEdit.setText(String(newSensitvity));
-// }
-//
-// ((MainContentComponent*)getParentComponent())->getMappingInEdit().expressionControllerSensivity = newSensitvity;
-// TerpstraSysExApplication::getApp().setHasChangesToSave(true);
-// TerpstraSysExApplication::getApp().getMidiDriver().sendExpressionPedalSensivity(newSensitvity);
-// }
-//}
-
void PedalSensitivityDlg::lookAndFeelChanged()
{
auto newLookAndFeel = dynamic_cast(&getLookAndFeel());
if (newLookAndFeel)
{
- labelPedalTitle->setColour(Label::ColourIds::textColourId, newLookAndFeel->findColour(LumatoneEditorColourIDs::LabelBlue));
+ lblExpression->setColour(Label::ColourIds::textColourId, newLookAndFeel->findColour(LumatoneEditorColourIDs::LabelBlue));
+ lblSustain->setColour(Label::ColourIds::textColourId, newLookAndFeel->findColour(LumatoneEditorColourIDs::LabelBlue));
labelExprContrSensitivity->setColour(Label::ColourIds::textColourId, newLookAndFeel->findColour(LumatoneEditorColourIDs::DescriptionText));
}
}
@@ -248,14 +267,27 @@ void PedalSensitivityDlg::firmwareRevisionReceived(FirmwareVersion version)
if (firmwareSupport.versionAcknowledgesCommand(version, INVERT_SUSTAIN_PEDAL))
{
- btnInvertSustain->setVisible(true);
+ btnInvertSustain->setEnabled(true);
+ btnInvertSustain->setTooltip("");
}
else
{
- btnInvertSustain->setVisible(false);
+ btnInvertSustain->setEnabled(false);
+ btnInvertSustain->setTooltip("This feature is not supported by the firmware version of your Lumatone.");
}
}
+void PedalSensitivityDlg::presetFlagsReceived(PresetFlags presetFlags)
+{
+ btnInvertExpression->setToggleState(presetFlags.expressionPedalInverted, dontSendNotification);
+ btnInvertSustain->setToggleState(presetFlags.sustainPedalInverted, dontSendNotification);
+}
+
+void PedalSensitivityDlg::expressionPedalSensitivityReceived(int sensitivity)
+{
+ sldExprCtrlSensitivity->setValue(sensitivity, dontSendNotification);
+}
+
//[/MiscUserCode]
@@ -269,33 +301,37 @@ void PedalSensitivityDlg::firmwareRevisionReceived(FirmwareVersion version)
BEGIN_JUCER_METADATA
-
+ edTextCol="ff000000" edBkgCol="0" labelText="Expression" editableSingleClick="0"
+ editableDoubleClick="0" focusDiscardsChanges="0" fontname="Default font"
+ fontsize="18.0" kerning="0.0" bold="0" italic="0" justification="33"/>
+
END_JUCER_METADATA
diff --git a/Source/PedalSensitivityDlg.h b/Source/PedalSensitivityDlg.h
index 784f5826..1adf63a1 100644
--- a/Source/PedalSensitivityDlg.h
+++ b/Source/PedalSensitivityDlg.h
@@ -36,7 +36,7 @@
//[/Comments]
*/
class PedalSensitivityDlg : public juce::Component,
- public LumatoneController::FirmwareListener,
+ public LumatoneEditor::FirmwareListener,
public juce::Button::Listener,
public juce::Slider::Listener
{
@@ -52,8 +52,10 @@ class PedalSensitivityDlg : public juce::Component,
void lookAndFeelChanged() override;
- // LumatoneController::FirmwareListener implementation
+ // LumatoneEditor::FirmwareListener implementation
void firmwareRevisionReceived(FirmwareVersion version) override;
+ void presetFlagsReceived(PresetFlags presetFlags) override;
+ void expressionPedalSensitivityReceived(int sensitivity) override;
//[/UserMethods]
@@ -66,20 +68,20 @@ class PedalSensitivityDlg : public juce::Component,
private:
//[UserVariables] -- You can add your own custom variables in this section.
- int roundedCornerSize;
- Rectangle controlBounds;
-
- // Style Constants
- const Rectangle sliderBoundsProps = { 0.54f, 0.38f, 0.4f, 0.52f };
+ int roundedCornerSize = 0;
+ Rectangle expressionBounds;
+ Rectangle sustainBounds;
+ const float sectionMarginWidth = 0.05f;
//[/UserVariables]
//==============================================================================
std::unique_ptr labelExprContrSensitivity;
std::unique_ptr btnInvertExpression;
- std::unique_ptr labelPedalTitle;
+ std::unique_ptr lblExpression;
std::unique_ptr sldExprCtrlSensitivity;
std::unique_ptr btnInvertSustain;
+ std::unique_ptr lblSustain;
//==============================================================================
diff --git a/Source/PolygonPalette.h b/Source/PolygonPalette.h
index b92c386b..f38563f3 100644
--- a/Source/PolygonPalette.h
+++ b/Source/PolygonPalette.h
@@ -19,7 +19,7 @@
class PolygonPalette : public Palette
{
public:
- PolygonPalette(int numSidesIn = 6, float angleOffsetIn = float_Pi / 12.0f)
+ PolygonPalette(int numSidesIn = 6, float angleOffsetIn = MathConstants::pi / 12.0f)
: Palette(numSidesIn), angleOffset(angleOffsetIn)
{
createSwatches();
@@ -76,7 +76,7 @@ class PolygonPalette : public Palette
void createSwatches()
{
- float angInc = 2 * float_Pi / getNumberOfSwatches();
+ float angInc = 2 * MathConstants::pi / getNumberOfSwatches();
float angMargin = angInc * margin * 0.5f;
for (int i = 0; i < getNumberOfSwatches(); i++)
diff --git a/Source/ScaleStructureController/GroupHandle.cpp b/Source/ScaleStructureController/GroupHandle.cpp
index 2cc05dcd..ff5dd7f7 100644
--- a/Source/ScaleStructureController/GroupHandle.cpp
+++ b/Source/ScaleStructureController/GroupHandle.cpp
@@ -80,7 +80,7 @@ Path GroupHandle::getLine(float lineThickness) const
Path diamond;
if (size > 0)
- diamond.addPolygon(line.getPointAlongLineProportionally(1/size), 4, lineThickness, position.x + float_Pi / 2);
+ diamond.addPolygon(line.getPointAlongLineProportionally(1/size), 4, lineThickness, position.x + MathConstants::pi / 2);
return diamond;
}
diff --git a/Source/ScaleStructureController/GroupingCircle.cpp b/Source/ScaleStructureController/GroupingCircle.cpp
index 7a7060fd..d1d2f90a 100644
--- a/Source/ScaleStructureController/GroupingCircle.cpp
+++ b/Source/ScaleStructureController/GroupingCircle.cpp
@@ -284,7 +284,7 @@ void GroupingCircle::resized()
groupInnerCircleBounds = groupOuterCircleBounds.reduced(groupRingWidth);
degreeInnerCircleBounds = groupInnerCircleBounds.reduced(degreeRingWidth);
- angleIncrement = 2 * double_Pi / groupChain.size();
+ angleIncrement = 2 * MathConstants::pi / groupChain.size();
angleHalf = angleIncrement / 2.0f;
handleDragThreshold = angleIncrement * 2.0 / 3.0f;
diff --git a/Source/ScaleStructureController/GroupingCircle.h b/Source/ScaleStructureController/GroupingCircle.h
index 72d271f2..d37aabf1 100644
--- a/Source/ScaleStructureController/GroupingCircle.h
+++ b/Source/ScaleStructureController/GroupingCircle.h
@@ -22,7 +22,7 @@ class GroupingCircle : public Component
{
public:
GroupingCircle(const ScaleStructure& structureIn, Array& colourTableIn);
- ~GroupingCircle();
+ virtual ~GroupingCircle();
float getInnerRadius() const;
float getMiddleRadius() const;
@@ -57,7 +57,7 @@ class GroupingCircle : public Component
class Listener
{
public:
- ~Listener() {};
+ virtual ~Listener() {}
virtual void offsetChanged(int newOffset) = 0;
@@ -123,7 +123,7 @@ class GroupingCircle : public Component
Array highlightedDegreeEdges; // TODO: turn this into Array> to differentiate symmetric edges
Array> highlightedEdgeLines;
- const float handleDotAngRatio = float_Pi / 100.0f;
+ const float handleDotAngRatio = MathConstants::pi / 100.0f;
float handleDotRadius;
float handleHighlightMult = 1.5f;
float handlePlacementRadius;
@@ -150,7 +150,7 @@ class GroupingCircle : public Component
float degreeMiddleRadius;
Point center;
- float circleOffset = float_Pi / 2.0f;
+ float circleOffset = MathConstants::pi / 2.0f;
float groupRingWidth;
float degreeRingWidth;
@@ -161,8 +161,8 @@ class GroupingCircle : public Component
double angleIncrement;
double angleHalf;
- const float float_HalfPi = float_Pi / 2;
- const float float_Tau = float_Pi * 2;
+ const float float_HalfPi = MathConstants::pi / 2;
+ const float float_Tau = MathConstants::pi * 2;
Array> radiLines;
Array degreeArcPaths;
diff --git a/Source/ScaleStructureController/ScaleStructure.cpp b/Source/ScaleStructureController/ScaleStructure.cpp
index cf7cc202..87a7b64c 100644
--- a/Source/ScaleStructureController/ScaleStructure.cpp
+++ b/Source/ScaleStructureController/ScaleStructure.cpp
@@ -573,7 +573,7 @@ bool ScaleStructure::setChromaAlterations(Array> chromaAlterationsIn)
void ScaleStructure::calculateProperties()
{
- DBG("~~~ ScaleStructure is calculating properties ~~~");
+ //DBG("~~~ ScaleStructure is calculating properties ~~~")
// Clear all data dependent on Generator and Size choices
scaleSizes.clear();
keyboardTypes.clear();
@@ -711,7 +711,7 @@ void ScaleStructure::calculateGeneratorChain()
}
}
- DBG("SS Generator Chain: " + dbgstr);
+// DBG("SS Generator Chain: " + dbgstr);
calculateIntervalScales();
}
@@ -758,32 +758,32 @@ void ScaleStructure::fillSymmetricGrouping(bool applyAlterations)
// DEBUG PRINTING
- String dbgstr = "";
- int size, sum = 0;
- for (int i = 0; i < degreeGroupIndexedSizes.size(); i++) {
- size = degreeGroupScaleSizes[i];
- dbgstr += String(size) + ", ";
- sum += size;
- }
- dbgstr += " = " + String(sum);
- DBG("SS Updated grouping: " + dbgstr);
-
- dbgstr = "\t";
- for (int group = 0; group < degreeGroupIndexedSizes.size(); group++)
- {
- Array degreeGroup = degreeGroupings[group];
- dbgstr += "Tier " + String(group) + ": ";
- for (int deg = 0; deg < degreeGroup.size(); deg++)
- {
- dbgstr += String(degreeGroup[deg]) + ", ";
- }
-
- if (group + 1 < degreeGroupIndexedSizes.size())
- dbgstr += "\n\t";
- }
-
- DBG("SS Degree groupings: ");
- DBG(dbgstr);
+// String dbgstr = "";
+// int size, sum = 0;
+// for (int i = 0; i < degreeGroupIndexedSizes.size(); i++) {
+// size = degreeGroupScaleSizes[i];
+// dbgstr += String(size) + ", ";
+// sum += size;
+// }
+// dbgstr += " = " + String(sum);
+// DBG("SS Updated grouping: " + dbgstr);
+
+// dbgstr = "\t";
+// for (int group = 0; group < degreeGroupIndexedSizes.size(); group++)
+// {
+// Array degreeGroup = degreeGroupings[group];
+// dbgstr += "Tier " + String(group) + ": ";
+// for (int deg = 0; deg < degreeGroup.size(); deg++)
+// {
+// dbgstr += String(degreeGroup[deg]) + ", ";
+// }
+//
+// if (group + 1 < degreeGroupIndexedSizes.size())
+// dbgstr += "\n\t";
+// }
+//
+// DBG("SS Degree groupings: ");
+// DBG(dbgstr);
}
void ScaleStructure::applyChromaAlterations()
@@ -876,16 +876,16 @@ void ScaleStructure::applyChromaAlterations()
groupChain.add(deg);
}
- String dbgstr = "";
-
- for (auto alteration : chromaAlterations)
- {
- dbgstr += "(" + alteration.toString() + "), ";
- }
- dbgstr = alterationsAttachedToDegree
- ? "SS MODMOS Properties by degree:\t" + dbgstr
- : "SS MODMOS Properties by gIndex:\t" + dbgstr;
- DBG(dbgstr);
+// String dbgstr = "";
+//
+// for (auto alteration : chromaAlterations)
+// {
+// dbgstr += "(" + alteration.toString() + "), ";
+// }
+// dbgstr = alterationsAttachedToDegree
+// ? "SS MODMOS Properties by degree:\t" + dbgstr
+// : "SS MODMOS Properties by gIndex:\t" + dbgstr;
+// DBG(dbgstr);
}
int ScaleStructure::getSymmetricGroup(int groupIndexIn) const
@@ -1742,7 +1742,9 @@ String ScaleStructure::getIntervalSteps(Point& stepSizesOut, bool withModif
for (int i = 1; i <= getScaleSize() * periodFactorSelected; i++)
{
sizes.set(i - 1, sizes[i] - sizes[i - 1]);
+#if JUCE_DEBUG
steps += String(sizes[i - 1]) + " ";
+#endif
}
// Extract step sizes
diff --git a/Source/ScaleStructureController/ScaleStructureComponent.cpp b/Source/ScaleStructureController/ScaleStructureComponent.cpp
index 942e4c71..7a51f3ae 100644
--- a/Source/ScaleStructureController/ScaleStructureComponent.cpp
+++ b/Source/ScaleStructureController/ScaleStructureComponent.cpp
@@ -165,7 +165,7 @@ void ScaleStructureComponent::resized()
periodSlider->setCentrePosition(circle->getIntPointFromCenter(circle->getInnerRadius() * 0.4f, 0));
generatorSlider->setSize(proportionOfWidth(0.2f), proportionOfHeight(sliderHeight));
- generatorSlider->setCentrePosition(circle->getIntPointFromCenter(circle->getInnerRadius() * 0.125f, float_Pi));
+ generatorSlider->setCentrePosition(circle->getIntPointFromCenter(circle->getInnerRadius() * 0.125f, MathConstants::pi));
offsetLabel->setFont(Font().withHeight(offsetFontHeightScalar));
offsetLabel->setSize(offsetLabel->getFont().getStringWidth("Offset") * 2, offsetLabel->getFont().getHeight() * 3);
@@ -190,11 +190,11 @@ void ScaleStructureComponent::resized()
resizeLabelWithWidth(generatorValueLbl.get(), proportionOfWidth(0.25f));
generatorValueLbl->setSize(proportionOfWidth(0.4f), generatorValueLbl->getHeight());
- generatorValueLbl->setCentrePosition(circle->getIntPointFromCenter(circle->getInnerRadius() * 3.0f * controlUnit, float_Pi));
+ generatorValueLbl->setCentrePosition(circle->getIntPointFromCenter(circle->getInnerRadius() * 3.0f * controlUnit, MathConstants::pi));
resizeLabelWithWidth(stepSizePatternLbl.get(), proportionOfWidth(0.25f));
stepSizePatternLbl->setSize(proportionOfWidth(0.4f), stepSizePatternLbl->getHeight());
- stepSizePatternLbl->setCentrePosition(circle->getIntPointFromCenter(circle->getInnerRadius() * 4.5 * controlUnit, float_Pi));
+ stepSizePatternLbl->setCentrePosition(circle->getIntPointFromCenter(circle->getInnerRadius() * 4.5 * controlUnit, MathConstants::pi));
//[/UserResized]
}
diff --git a/Source/ScaleStructureController/ScaleStructureComponent.h b/Source/ScaleStructureController/ScaleStructureComponent.h
index 866d473b..160ac381 100644
--- a/Source/ScaleStructureController/ScaleStructureComponent.h
+++ b/Source/ScaleStructureController/ScaleStructureComponent.h
@@ -163,9 +163,9 @@ class ScaleStructureComponent : public Component,
const float sizeRadiusScalar = 0.493827f;
const float offsetFontHeightScalar = 0.0208333f;
- const float offsetArrowAngle1 = float_Pi * 0.083333f;
+ const float offsetArrowAngle1 = MathConstants::pi * 0.083333f;
const float offsetArrowAngle2 = offsetArrowAngle1 * 0.5f;
- const float offsetArrowAngle3 = float_Pi * 0.071429f;
+ const float offsetArrowAngle3 = MathConstants::pi * 0.071429f;
const float offsetArrowRadScalar0 = 0.0769231f;
const float offsetArrowRadScalar1 = 0.92857f;
diff --git a/Source/Settings/CalibrationDlg.cpp b/Source/Settings/CalibrationDlg.cpp
index aed8eb4d..31ba2297 100644
--- a/Source/Settings/CalibrationDlg.cpp
+++ b/Source/Settings/CalibrationDlg.cpp
@@ -7,7 +7,7 @@
the "//[xyz]" and "//[/xyz]" sections will be retained when the file is loaded
and re-saved.
- Created with Projucer version: 6.0.5
+ Created with Projucer version: 6.0.8
------------------------------------------------------------------------------
@@ -52,7 +52,7 @@ CalibrationDlg::CalibrationDlg ()
//[UserPreSize]
- updateCalibrationStatus();
+ updateWheelCalibrationStatus();
// Calibration type selector
calibrationSelectorTab.reset(new TabbedButtonBar(TabbedButtonBar::Orientation::TabsAtTop));
@@ -60,10 +60,12 @@ CalibrationDlg::CalibrationDlg ()
calibrationSelectorTab->addTab(translate("Keys"), juce::Colours::lightgrey, 1);
calibrationSelectorTab->addTab(translate("Aftertouch"), juce::Colours::lightgrey, 2);
- calibrationSelectorTab->addTab(translate("Modulation Wheel"), juce::Colours::lightgrey, 3);
+ calibrationSelectorTab->addTab(translate("Pitch & Mod Wheels"), juce::Colours::lightgrey, 3);
calibrationSelectorTab->addChangeListener(this);
+ TerpstraSysExApplication::getApp().getLumatoneController()->addFirmwareListener(this);
+
//[/UserPreSize]
setSize (524, 212);
@@ -79,6 +81,7 @@ CalibrationDlg::CalibrationDlg ()
CalibrationDlg::~CalibrationDlg()
{
//[Destructor_pre]. You can add your own custom destruction code here..
+ TerpstraSysExApplication::getApp().getLumatoneController()->removeFirmwareListener(this);
//[/Destructor_pre]
btnStart = nullptr;
@@ -87,6 +90,7 @@ CalibrationDlg::~CalibrationDlg()
//[Destructor]. You can add your own custom destruction code here..
calibrationSelectorTab = nullptr;
+ wheelsCalibrationComponent = nullptr;
//[/Destructor]
}
@@ -113,22 +117,45 @@ void CalibrationDlg::resized()
//[/UserPreResize]
//[UserResized] Add your own custom resize handling here..
- calibrationSelectorTab->setBounds(0, 0, getWidth() - generalRim, OCTAVEBOARDTABHEIGHT);
+ auto currentTab = calibrationSelectorTab->getCurrentTabIndex();
- instructionsBounds.setBounds(
- generalRim,
- calibrationSelectorTab->getBottom() + generalRim,
- getWidth() - 2 * generalRim,
- btnStart->getY() - calibrationSelectorTab->getBottom() - 2 * generalRim);
- instructionsFont.setHeight(instructionsBounds.getHeight() * fontHeightInBounds);
+ calibrationSelectorTab->setBounds(0, 0, getWidth() - generalRim, OCTAVEBOARDTABHEIGHT);
int buttonWidth = proportionOfWidth(0.3f);
int buttonHeight = proportionOfHeight(0.125f);
+
btnStart->setSize(buttonWidth, buttonHeight);
btnStart->setTopLeftPosition(generalRim, getHeight() - generalRim - buttonHeight);
-
btnStop->setBounds(btnStart->getBounds().withX(getWidth() - generalRim - buttonWidth));
+ if (currentTab == calibrateModulationWheel && wheelsCalibrationComponent != nullptr)
+ {
+ int panelHeight = btnStart->getY() - calibrationSelectorTab->getBottom() - 2 * generalRim;
+
+ wheelsCalibrationComponent->setBounds(
+ generalRim,
+ calibrationSelectorTab->getBottom() + generalRim,
+ proportionOfWidth(wheelsGraphicWidthScalar),
+ panelHeight);
+
+ instructionsBounds.setBounds(
+ wheelsCalibrationComponent->getRight() + generalRim,
+ calibrationSelectorTab->getBottom() + generalRim,
+ getWidth() - wheelsCalibrationComponent->getRight() - 2 * generalRim,
+ panelHeight);
+ }
+
+ else
+ {
+ instructionsBounds.setBounds(
+ generalRim,
+ calibrationSelectorTab->getBottom() + generalRim,
+ getWidth() - 2 * generalRim,
+ btnStart->getY() - calibrationSelectorTab->getBottom() - 2 * generalRim);
+ instructionsFont.setHeight(instructionsBounds.getHeight() * fontHeightInBounds);
+ }
+
+
//[/UserResized]
}
@@ -146,19 +173,16 @@ void CalibrationDlg::buttonClicked (juce::Button* buttonThatWasClicked)
switch (tabSelection)
{
case calibrateKeys:
- TerpstraSysExApplication::getApp().getLumatoneController().startCalibrateKeys();
+ TerpstraSysExApplication::getApp().getLumatoneController()->startCalibrateKeys();
break;
case calibrateAftertouch:
- TerpstraSysExApplication::getApp().getLumatoneController().startCalibrateAftertouch();
+ TerpstraSysExApplication::getApp().getLumatoneController()->startCalibrateAftertouch();
break;
case calibrateModulationWheel:
- TerpstraSysExApplication::getApp().getLumatoneController().setCalibratePitchModWheel(true);
-
- // Todo - wait for ack
- TerpstraSysExApplication::getApp().setCalibrationMode(true);
- updateCalibrationStatus();
+ TerpstraSysExApplication::getApp().getLumatoneController()->setCalibratePitchModWheel(true);
+ startCalibration = true;
break;
default:
@@ -182,10 +206,8 @@ void CalibrationDlg::buttonClicked (juce::Button* buttonThatWasClicked)
jassertfalse;
break;
case calibrateModulationWheel:
- TerpstraSysExApplication::getApp().getLumatoneController().setCalibratePitchModWheel(false);
- // Todo - wait for ack
- TerpstraSysExApplication::getApp().setCalibrationMode(false);
- updateCalibrationStatus();
+ TerpstraSysExApplication::getApp().getLumatoneController()->setCalibratePitchModWheel(false);
+ startCalibration = false;
break;
default:
jassertfalse;
@@ -219,6 +241,7 @@ void CalibrationDlg::changeListenerCallback(ChangeBroadcaster *source)
{
if (source == calibrationSelectorTab.get())
{
+ wheelsCalibrationComponent = nullptr;
btnStart->setEnabled(true);
// Instructions depending on tab selection
auto tabSelection = calibrationSelectorTab->getCurrentTabIndex();
@@ -230,7 +253,7 @@ void CalibrationDlg::changeListenerCallback(ChangeBroadcaster *source)
<< newLine
<< translate("To return to normal operating state, the five submodule boards must exit out calibration mode by pressing their corresponding macro buttons to save or cancel calibration.");
btnStop->setVisible(false);
- repaint();
+ resized();
break;
case calibrateAftertouch:
@@ -239,7 +262,7 @@ void CalibrationDlg::changeListenerCallback(ChangeBroadcaster *source)
<< newLine
<< translate("To return to normal operating state, the five submodule boards must exit out calibration mode by pressing their corresponding macro buttons to save or cancel calibration.");
btnStop->setVisible(false);
- repaint();
+ resized();
break;
case calibrateModulationWheel:
@@ -248,23 +271,60 @@ void CalibrationDlg::changeListenerCallback(ChangeBroadcaster *source)
<< newLine
<< translate("Click \'End calibration\' to stop.");
btnStop->setVisible(true);
- updateCalibrationStatus();
- repaint();
+ setupWheelCalibrationLayout();
+ updateWheelCalibrationStatus();
break;
+
default:
jassertfalse;
break;
}
+ repaint();
+ }
+}
+
+void CalibrationDlg::setupWheelCalibrationLayout()
+{
+ FirmwareSupport support;
+ if (support.versionAcknowledgesCommand(TerpstraSysExApplication::getApp().getFirmwareVersion(), PERIPHERAL_CALBRATION_DATA))
+ {
+ wheelsCalibrationComponent.reset(new WheelsCalibrationComponent());
+ addAndMakeVisible(wheelsCalibrationComponent.get());
+ resized();
}
}
-void CalibrationDlg::updateCalibrationStatus()
+
+void CalibrationDlg::updateWheelCalibrationStatus()
{
bool inCalibration = TerpstraSysExApplication::getApp().getInCalibrationMode();
btnStart->setEnabled(!inCalibration);
btnStop->setEnabled(inCalibration);
}
+void CalibrationDlg::calibratePitchModWheelAnswer(TerpstraMIDIAnswerReturnCode code)
+{
+ if (code == TerpstraMIDIAnswerReturnCode::ACK)
+ {
+ if (startCalibration)
+ TerpstraSysExApplication::getApp().setCalibrationMode(true);
+ else
+ TerpstraSysExApplication::getApp().setCalibrationMode(false);
+ updateWheelCalibrationStatus();
+ startCalibration = false;
+ }
+}
+
+void CalibrationDlg::wheelsCalibrationDataReceived(WheelsCalibrationData calibrationData)
+{
+ if (wheelsCalibrationComponent != nullptr)
+ {
+ wheelsCalibrationComponent->updateCalibrationData(calibrationData);
+ }
+ //else
+ //jassertfalse;
+}
+
//[/MiscUserCode]
@@ -278,7 +338,7 @@ void CalibrationDlg::updateCalibrationStatus()
BEGIN_JUCER_METADATA
diff --git a/Source/Settings/CalibrationDlg.h b/Source/Settings/CalibrationDlg.h
index 2c42f9bd..cc8c0c4d 100644
--- a/Source/Settings/CalibrationDlg.h
+++ b/Source/Settings/CalibrationDlg.h
@@ -7,7 +7,7 @@
the "//[xyz]" and "//[/xyz]" sections will be retained when the file is loaded
and re-saved.
- Created with Projucer version: 6.0.5
+ Created with Projucer version: 6.0.8
------------------------------------------------------------------------------
@@ -20,7 +20,8 @@
#pragma once
//[Headers] -- You can add your own extra header files here --
-#include
+#include "../LumatoneController.h"
+#include "WheelsCalibrationComponent.h"
//[/Headers]
@@ -35,6 +36,7 @@
*/
class CalibrationDlg : public juce::Component,
public ChangeListener,
+ public LumatoneEditor::FirmwareListener,
public juce::Button::Listener
{
public:
@@ -48,7 +50,17 @@ class CalibrationDlg : public juce::Component,
// Implementation of ChangeListener
void changeListenerCallback(ChangeBroadcaster *source) override;
- void updateCalibrationStatus();
+ void setupWheelCalibrationLayout();
+ void updateWheelCalibrationStatus();
+
+ //==============================================================================
+ // LumatoneEditor::FirmwareListener Implementation
+
+ void calibratePitchModWheelAnswer(TerpstraMIDIAnswerReturnCode code) override;
+
+ void wheelsCalibrationDataReceived(WheelsCalibrationData calibrationData) override;
+
+
//[/UserMethods]
void paint (juce::Graphics& g) override;
@@ -68,6 +80,9 @@ class CalibrationDlg : public juce::Component,
};
std::unique_ptr calibrationSelectorTab;
+ std::unique_ptr wheelsCalibrationComponent;
+
+ bool startCalibration = false;
String instructionText;
@@ -77,6 +92,8 @@ class CalibrationDlg : public juce::Component,
const float fontHeightInBounds = 0.125f;
const int generalRim = 12;
+ const float wheelsGraphicWidthScalar = 0.15f;
+
//[/UserVariables]
//==============================================================================
diff --git a/Source/Settings/FirmwareDlg.cpp b/Source/Settings/FirmwareDlg.cpp
index 38ef9573..ba284f2e 100644
--- a/Source/Settings/FirmwareDlg.cpp
+++ b/Source/Settings/FirmwareDlg.cpp
@@ -21,7 +21,14 @@ FirmwareDlg::FirmwareDlg()
auto properties = TerpstraSysExApplication::getApp().getPropertiesFile();
File lastFirmwareLocation = properties->getValue("LastFirmwareBinPath", properties->getValue("UserDocumentsLocation", File::getSpecialLocation(File::SpecialLocationType::userDocumentsDirectory).getFullPathName()));
- fileBrowser.reset(new PathBrowserComponent("Lumatone Firmware Update", lastFirmwareLocation));
+ String openFileType =
+#if JUCE_DEBUG
+ ""
+#else
+ "*.tgz"
+#endif
+ ;
+ fileBrowser.reset(new PathBrowserComponent("Lumatone Firmware Update", openFileType, lastFirmwareLocation));
fileBrowser->getEditor()->setColour(TextEditor::ColourIds::backgroundColourId, TerpstraSysExApplication::getApp().getLookAndFeel().findColour(LumatoneEditorColourIDs::ControlBoxBackground));
fileBrowser->getEditor()->setColour(TextEditor::ColourIds::textColourId, TerpstraSysExApplication::getApp().getLookAndFeel().findColour(LumatoneEditorColourIDs::DescriptionText));
fileBrowser->getEditor()->getProperties().set(LumatoneEditorStyleIDs::connectedEdgeFlags, Button::ConnectedEdgeFlags::ConnectedOnRight);
@@ -37,7 +44,6 @@ FirmwareDlg::FirmwareDlg()
infoBox->setMouseClickGrabsKeyboardFocus(false);
infoBox->setReadOnly(true);
infoBox->setMultiLine(true);
- infoBox->insertTextAtCaret(translate("Select a firmware file and then click \"Begin Update\""));
infoBox->setColour(TextEditor::ColourIds::backgroundColourId, TerpstraSysExApplication::getApp().getLookAndFeel().findColour(LumatoneEditorColourIDs::ControlBoxBackground));
infoBox->setColour(TextEditor::ColourIds::textColourId, TerpstraSysExApplication::getApp().getLookAndFeel().findColour(LumatoneEditorColourIDs::DescriptionText));
infoBox->setColour(ScrollBar::ColourIds::thumbColourId, Colour(0xff2d3135));
@@ -48,7 +54,9 @@ FirmwareDlg::FirmwareDlg()
addAndMakeVisible(firmwareStatusLabel.get());
updateFirmwareVersionLabel();
- TerpstraSysExApplication::getApp().getLumatoneController().addFirmwareListener(this);
+ TerpstraSysExApplication::getApp().getLumatoneController()->addFirmwareListener(this);
+
+ postMessage(translate("Select a firmware file and then click \"Begin Update\""));
//if (!updateIsAvailable)
//{
@@ -58,7 +66,7 @@ FirmwareDlg::FirmwareDlg()
FirmwareDlg::~FirmwareDlg()
{
- TerpstraSysExApplication::getApp().getLumatoneController().removeFirmwareListener(this);
+ TerpstraSysExApplication::getApp().getLumatoneController()->removeFirmwareListener(this);
}
void FirmwareDlg::paint(Graphics& g)
@@ -68,10 +76,7 @@ void FirmwareDlg::paint(Graphics& g)
void FirmwareDlg::resized()
{
- int margin = 12;
- int doubleMargin = margin * 2;
- int buttonWidth = proportionOfWidth(0.3f);
- int buttonHeight = 30;
+ int buttonWidth = proportionOfWidth(buttonWidthScalar);
//checkUpdateBtn->setBounds(margin, margin, buttonWidth, buttonHeight);
@@ -92,9 +97,9 @@ void FirmwareDlg::buttonClicked(Button* btn)
//}
if (btn == doUpdateBtn.get())
{
- if (TerpstraSysExApplication::getApp().getLumatoneController().getMidiInputIndex() < 0 || TerpstraSysExApplication::getApp().getLumatoneController().getMidiOutputIndex() < 0)
+ if (TerpstraSysExApplication::getApp().getLumatoneController()->getMidiInputIndex() < 0 || TerpstraSysExApplication::getApp().getLumatoneController()->getMidiOutputIndex() < 0)
{
- AlertWindow::showMessageBox(AlertWindow::AlertIconType::NoIcon, "Not connected", "Please connect the Lumatone via USB before performing a firmware update.", "Ok", this);
+ AlertWindow::showMessageBoxAsync(AlertWindow::AlertIconType::NoIcon, "Not connected", "Please connect the Lumatone via USB before performing a firmware update.", "Ok", this);
return;
}
@@ -103,11 +108,12 @@ void FirmwareDlg::buttonClicked(Button* btn)
if (firmwareFileSelected.existsAsFile())
{
TerpstraSysExApplication::getApp().getPropertiesFile()->setValue("LastFirmwareBinPath", firmwareFileSelected.getParentDirectory().getFullPathName());
- TerpstraSysExApplication::getApp().getLumatoneController().requestFirmwareUpdate(firmwareFileSelected, this);
+ TerpstraSysExApplication::getApp().getLumatoneController()->requestFirmwareUpdate(firmwareFileSelected, this);
}
else
{
- infoBox->setText(infoBox->getText() + "Error: Not a valid firmware file...");
+ postMessage("Error: Not a valid firmware file...");
+ //infoBox->setText();
}
}
}
@@ -138,11 +144,9 @@ void FirmwareDlg::firmwareTransferUpdate(FirmwareTransfer::StatusCode statusCode
firmwareUpdateInProgress = false;
}
- if (msg!= "")
+ if (msg != "")
{
- msgLog += (msg + "\n");
- infoNeedsUpdate = true;
- startTimer(infoUpdateTimeoutMs);
+ postMessage(msg);
}
}
@@ -158,7 +162,7 @@ void FirmwareDlg::timerCallback()
void FirmwareDlg::firmwareRevisionReceived(FirmwareVersion version)
{
updateFirmwareVersionLabel();
- postMessage("Firmware update complete! Lumatone is now running firmware version " + version.toString());
+ postMessage("Firmware update complete! Lumatone is now running firmware version " + version.toDisplayString());
}
void FirmwareDlg::postMessage(String msg)
@@ -166,4 +170,4 @@ void FirmwareDlg::postMessage(String msg)
msgLog += msg + '\n';
infoNeedsUpdate = true;
startTimer(infoUpdateTimeoutMs);
-}
\ No newline at end of file
+}
diff --git a/Source/Settings/FirmwareDlg.h b/Source/Settings/FirmwareDlg.h
index 677932e4..64993aab 100644
--- a/Source/Settings/FirmwareDlg.h
+++ b/Source/Settings/FirmwareDlg.h
@@ -18,7 +18,7 @@ class FirmwareDlg : public Component,
protected Button::Listener,
protected PathBrowserComponent::Listener,
protected FirmwareTransfer::ProcessListener,
- protected LumatoneController::FirmwareListener,
+ protected LumatoneEditor::FirmwareListener,
private Timer
{
public:
@@ -45,7 +45,7 @@ class FirmwareDlg : public Component,
void firmwareTransferUpdate(FirmwareTransfer::StatusCode statusCode, String msg) override;
//=========================================================================
- // LumatoneController::FirmwareListener implementation
+ // LumatoneEditor::FirmwareListener implementation
void firmwareRevisionReceived(FirmwareVersion version) override;
@@ -70,4 +70,10 @@ class FirmwareDlg : public Component,
String msgLog;
bool infoNeedsUpdate = false;
const int infoUpdateTimeoutMs = 100;
+
+ // Style helpers
+ int margin = 12;
+ int doubleMargin = margin * 2;
+ float buttonWidthScalar = 0.3f;
+ int buttonHeight = 30;
};
diff --git a/Source/Settings/MidiSettingsDlg.cpp b/Source/Settings/MidiSettingsDlg.cpp
new file mode 100644
index 00000000..ddd01c71
--- /dev/null
+++ b/Source/Settings/MidiSettingsDlg.cpp
@@ -0,0 +1,176 @@
+/*
+ ==============================================================================
+
+ MidiSettingsDlg.cpp
+ Created: 29 Jun 2021 10:02:43pm
+ Author: Vincenzo
+
+ ==============================================================================
+*/
+
+#include "MidiSettingsDlg.h"
+#include "../Main.h"
+
+MidiSettingsDlg::MidiSettingsDlg()
+{
+ setMidiChannelHeader.reset(new Label("SetMidiChannelHeader", "Set Controller MIDI Channel"));
+ setMidiChannelHeader->setJustificationType(Justification::centred);
+ setMidiChannelHeader->setFont(TerpstraSysExApplication::getApp().getAppFont(LumatoneEditorFont::UniviaPro));
+ addAndMakeVisible(setMidiChannelHeader.get());
+
+ controlLabelFont = TerpstraSysExApplication::getApp().getAppFont(LumatoneEditorFont::GothamNarrowMedium);
+
+ for (auto controlName : ControlNames)
+ {
+ auto lbl = setMidiChannelLabels.add(new Label(controlName + " Label", controlName + ": "));
+ lbl->setJustificationType(Justification::centredRight);
+ lbl->setFont(controlLabelFont);
+ addAndMakeVisible(*lbl);
+
+ auto sld = setMidiChannelSliders.add(new Slider(Slider::SliderStyle::IncDecButtons, Slider::TextEntryBoxPosition::TextBoxLeft));
+ sld->setRange(1, 16, 1);
+ sld->setName(controlName);
+ sld->setTooltip("Set MIDI Channel for " + controlName + " messages.");
+ sld->addListener(this);
+ addAndMakeVisible(*sld);
+
+ // Help with resizing
+ if (controlName.length() > longestControlName.length())
+ longestControlName = controlName;
+ }
+
+ flexBox.flexWrap = FlexBox::Wrap::noWrap;
+ flexBox.flexDirection = FlexBox::Direction::column;
+ flexBox.justifyContent = FlexBox::JustifyContent::flexStart;
+ flexBox.alignContent = FlexBox::AlignContent::flexStart;
+
+ TerpstraSysExApplication::getApp().getLumatoneController()->addFirmwareListener(this);
+
+ setSupportedControls(TerpstraSysExApplication::getApp().getFirmwareVersion());
+}
+
+MidiSettingsDlg::~MidiSettingsDlg()
+{
+ // Not good when app closes with this window open...
+ TerpstraSysExApplication::getApp().getLumatoneController()->removeFirmwareListener(this);
+
+ setMidiChannelHeader = nullptr;
+ setMidiChannelLabels.clear();
+ setMidiChannelSliders.clear();
+}
+
+void MidiSettingsDlg::paint(Graphics& g)
+{
+
+}
+
+void MidiSettingsDlg::resized()
+{
+ int rowHeight = proportionOfHeight(fontHeightInBounds);
+
+ auto setMidiChannelControlBounds = getLocalBounds().withTrimmedTop(rowHeight + margin * 2);
+
+ controlLabelFont.setHeight(rowHeight);
+ int labelWidth = controlLabelFont.getStringWidth(longestControlName);
+ int sldWidth = labelWidth * 0.667f;
+
+ flexRows.clear();
+ flexBox.items.clear();
+
+ auto controlMargin = FlexItem::Margin(0, margin * 0.5f, 0, 0);
+ int toggleMargins = margin * 0.2f;
+
+ for (int i = 0; i < ControlNames.size(); i++)
+ {
+ flexRows.add(FlexBox(
+ FlexBox::Direction::row,
+ FlexBox::Wrap::noWrap,
+ FlexBox::AlignContent::center,
+ FlexBox::AlignItems::center,
+ FlexBox::JustifyContent::center
+ ));
+
+ FlexBox& flexRow = flexRows.getReference(i);
+
+ auto lbl = setMidiChannelLabels[i];
+ auto lblItem = FlexItem(labelWidth, rowHeight, *lbl).withMargin(controlMargin);
+ flexRow.items.add(lblItem);
+
+ auto sld = setMidiChannelSliders[i];
+ sld->setTextBoxStyle(Slider::TextEntryBoxPosition::TextBoxLeft, false, sldWidth * 0.5f, rowHeight);
+ auto sldItem = FlexItem(labelWidth, rowHeight, *sld).withMargin(controlMargin);
+ flexRow.items.add(sldItem);
+
+ auto rowItem = FlexItem(getWidth(), rowHeight);
+ rowItem.associatedFlexBox = &flexRow;
+ rowItem.margin = FlexItem::Margin(0, 0, margin, 0);
+
+ flexBox.items.add(rowItem);
+ }
+
+ flexBox.performLayout(setMidiChannelControlBounds);
+
+ setMidiChannelHeader->setBounds(setMidiChannelControlBounds.withY(margin).withHeight(rowHeight));
+}
+
+void MidiSettingsDlg::sliderValueChanged(Slider* sld)
+{
+ int controlIndex = setMidiChannelSliders.indexOf(sld);
+
+ if (controlIndex >= 0 && controlIndex < ControlNames.size())
+ {
+ channelSettings.setChannel((PeripheralChannel)controlIndex, sld->getValue());
+ sendChannelSettings();
+ }
+}
+
+void MidiSettingsDlg::setSupportedControls(FirmwareVersion version)
+{
+ bool setChannelsEnabled = false;
+
+ FirmwareSupport support;
+ if (support.versionAcknowledgesCommand(version, SET_PERIPHERAL_CHANNELS))
+ {
+ setChannelsEnabled = true;
+ TerpstraSysExApplication::getApp().getLumatoneController()->getPeripheralChannels();
+ }
+
+ for (int i = 0; i < ControlNames.size(); i++)
+ {
+ setMidiChannelSliders[i]->setEnabled(setChannelsEnabled);
+
+ if (!setChannelsEnabled)
+ {
+ setMidiChannelSliders[i]->setTooltip(translate("This feature is not supported by your Lumatone's firmware version."));
+ }
+ }
+}
+
+void MidiSettingsDlg::updateChannelSettings(PeripheralChannelSettings channelSettingsIn)
+{
+ channelSettings = channelSettingsIn;
+
+ for (int i = 0; i < ControlNames.size(); i++)
+ {
+ auto channel = channelSettings.getChannel((PeripheralChannel)i);
+ setMidiChannelSliders[i]->setValue(channel, dontSendNotification);
+ }
+}
+
+void MidiSettingsDlg::sendChannelSettings()
+{
+ TerpstraSysExApplication::getApp().getLumatoneController()->setPeripheralChannels(channelSettings);
+}
+
+//=========================================================================
+// LumatoneEditor::FirmwareListener implementation
+
+void MidiSettingsDlg::firmwareRevisionReceived(FirmwareVersion version)
+{
+ setSupportedControls(version);
+}
+
+void MidiSettingsDlg::peripheralMidiChannelsReceived(PeripheralChannelSettings channelSettings)
+{
+ updateChannelSettings(channelSettings);
+}
diff --git a/Source/Settings/MidiSettingsDlg.h b/Source/Settings/MidiSettingsDlg.h
new file mode 100644
index 00000000..4f847afe
--- /dev/null
+++ b/Source/Settings/MidiSettingsDlg.h
@@ -0,0 +1,75 @@
+/*
+ ==============================================================================
+
+ MidiSettingsDlg.h
+ Created: 29 Jun 2021 10:02:43pm
+ Author: Vincenzo
+
+ ==============================================================================
+*/
+
+#pragma once
+
+#include "../LumatoneController.h"
+
+class MidiSettingsDlg : public Component,
+ protected Slider::Listener,
+ protected LumatoneEditor::FirmwareListener
+{
+public:
+
+ MidiSettingsDlg();
+ ~MidiSettingsDlg();
+
+ void paint(Graphics & g) override;
+
+ void resized() override;
+
+ void sliderValueChanged(Slider* sld) override;
+
+ void setSupportedControls(FirmwareVersion version);
+
+ void updateChannelSettings(PeripheralChannelSettings channelSettings);
+
+ void sendChannelSettings();
+
+ //=========================================================================
+ // LumatoneEditor::FirmwareListener implementation
+
+ void firmwareRevisionReceived(FirmwareVersion version) override;
+
+ void peripheralMidiChannelsReceived(PeripheralChannelSettings channelSettings) override;
+
+
+
+private:
+ //=========================================================================
+
+ const StringArray ControlNames =
+ {
+ "Pitch Wheel",
+ "Mod Wheel",
+ "Expression Pedal",
+ "Sustain Pedal"
+ };
+
+private:
+
+ std::unique_ptr setMidiChannelHeader;
+
+ OwnedArray setMidiChannelSliders;
+ OwnedArray setMidiChannelLabels;
+
+ PeripheralChannelSettings channelSettings;
+
+ FlexBox flexBox;
+ Array flexRows;
+
+ // Style helpers
+ const int margin = 12;
+ const int buttonHeight = 30;
+ const float fontHeightInBounds = 0.08f;
+
+ String longestControlName;
+ Font controlLabelFont;
+};
\ No newline at end of file
diff --git a/Source/Settings/PresetSettingsDlg.cpp b/Source/Settings/PresetSettingsDlg.cpp
new file mode 100644
index 00000000..7af2e3f5
--- /dev/null
+++ b/Source/Settings/PresetSettingsDlg.cpp
@@ -0,0 +1,84 @@
+/*
+ ==============================================================================
+
+ PresetSettingsDlg.cpp
+ Created: 29 Jun 2021 9:10:00pm
+ Author: Vincenzo
+
+ ==============================================================================
+*/
+
+#include "PresetSettingsDlg.h"
+#include "../Main.h"
+
+PresetSettingsDlg::PresetSettingsDlg()
+{
+ resetPresetsBtn.reset(new TextButton(
+ translate("Reset Presets To Factory Default"),
+ translate("Clear the mapping presets stored on the device and replace with factory mappings.")
+ ));
+ addAndMakeVisible(resetPresetsBtn.get());
+ resetPresetsBtn->addListener(this);
+
+ TerpstraSysExApplication::getApp().getLumatoneController()->addFirmwareListener(this);
+
+ setSupportedControls(TerpstraSysExApplication::getApp().getFirmwareVersion());
+
+ flexBox.justifyContent = FlexBox::JustifyContent::flexStart;
+ flexBox.alignContent = FlexBox::AlignContent::flexStart;
+}
+
+PresetSettingsDlg::~PresetSettingsDlg()
+{
+ TerpstraSysExApplication::getApp().getLumatoneController()->removeFirmwareListener(this);
+ resetPresetsBtn = nullptr;
+}
+
+void PresetSettingsDlg::paint(Graphics& g)
+{
+
+}
+
+void PresetSettingsDlg::resized()
+{
+ Font btnFont = getLookAndFeel().getTextButtonFont(*resetPresetsBtn.get(), buttonHeight);
+
+ int btnWidth = btnFont.getStringWidth(resetPresetsBtn->getButtonText()) * 1.2f;
+
+ flexBox.items.clear();
+
+ FlexItem btnItem = FlexItem().withWidth(btnWidth).withHeight(buttonHeight);
+ btnItem.associatedComponent = resetPresetsBtn.get();
+ flexBox.items.add(btnItem);
+
+ flexBox.performLayout(getLocalBounds().reduced(margin));
+}
+
+void PresetSettingsDlg::buttonClicked(Button* btn)
+{
+ if (btn == resetPresetsBtn.get())
+ {
+ TerpstraSysExApplication::getApp().getLumatoneController()->resetPresetsToFactoryDefault();
+ }
+}
+
+void PresetSettingsDlg::firmwareRevisionReceived(FirmwareVersion version)
+{
+ setSupportedControls(version);
+}
+
+void PresetSettingsDlg::setSupportedControls(FirmwareVersion version)
+{
+ FirmwareSupport support;
+ if (support.versionAcknowledgesCommand(version, RESET_DEFAULT_PRESETS))
+ {
+ resetPresetsBtn->setEnabled(true);
+ resetPresetsBtn->setTooltip(translate("Clear the mapping presets stored on the device and replace with factory mappings."));
+ }
+ else
+ {
+ resetPresetsBtn->setEnabled(false);
+ resetPresetsBtn->setTooltip(translate("This feature is not supported by your Lumatone firmware version."));
+ // TODO: better approach for changing tooltips
+ }
+}
\ No newline at end of file
diff --git a/Source/Settings/PresetSettingsDlg.h b/Source/Settings/PresetSettingsDlg.h
new file mode 100644
index 00000000..fd65d2e9
--- /dev/null
+++ b/Source/Settings/PresetSettingsDlg.h
@@ -0,0 +1,46 @@
+/*
+ ==============================================================================
+
+ PresetSettingsDlg.h
+ Created: 29 Jun 2021 9:10:00pm
+ Author: Vincenzo
+
+ ==============================================================================
+*/
+
+#pragma once
+#include "../LumatoneController.h"
+
+class PresetSettingsDlg : public Component,
+ protected Button::Listener,
+ protected LumatoneEditor::FirmwareListener
+
+{
+public:
+
+ PresetSettingsDlg();
+ ~PresetSettingsDlg();
+
+ void paint(Graphics& g) override;
+
+ void resized() override;
+
+ void buttonClicked(Button* btn) override;
+
+ void setSupportedControls(FirmwareVersion version);
+
+ //=========================================================================
+ // LumatoneEditor::FirmwareListener implementation
+
+ void firmwareRevisionReceived(FirmwareVersion version) override;
+
+
+private:
+
+ std::unique_ptr resetPresetsBtn;
+ FlexBox flexBox;
+
+ // Style helpers
+ int margin = 12;
+ int buttonHeight = 30;
+};
\ No newline at end of file
diff --git a/Source/Settings/SettingsContainer.cpp b/Source/Settings/SettingsContainer.cpp
index 38603d2a..76acbe1c 100644
--- a/Source/Settings/SettingsContainer.cpp
+++ b/Source/Settings/SettingsContainer.cpp
@@ -31,8 +31,13 @@ void SettingsCategoryModel::paintListBoxItem(int rowNumber, Graphics& g, int wid
SettingsContainer::SettingsContainer()
: Component("SettingsContainer"),
- model({"Calibrate", "Firmware"})
-{
+ model({
+ translate("Calibrate"),
+ translate("Firmware"),
+ translate("MIDI"),
+ translate("Presets")
+ })
+{
categoryList.reset(new ListBox("CategoryList"));
categoryList->setModel(&model);
@@ -45,7 +50,9 @@ SettingsContainer::SettingsContainer()
SettingsContainer::~SettingsContainer()
{
+ settingsPanel = nullptr;
categoryList = nullptr;
+
}
void SettingsContainer::paint(Graphics& g)
@@ -65,14 +72,9 @@ void SettingsContainer::resized()
void SettingsContainer::lookAndFeelChanged()
{
- auto* lookAndFeel = dynamic_cast(&getLookAndFeel());
- if (lookAndFeel)
- {
- setColour(ResizableWindow::ColourIds::backgroundColourId, lookAndFeel->findColour(LumatoneEditorColourIDs::LightBackground));
- categoryList->setColour(ListBox::ColourIds::backgroundColourId, lookAndFeel->findColour(LumatoneEditorColourIDs::MediumBackground));
- }
+ setColour(ResizableWindow::ColourIds::backgroundColourId, getLookAndFeel().findColour(LumatoneEditorColourIDs::LightBackground));
+ categoryList->setColour(ListBox::ColourIds::backgroundColourId, getLookAndFeel().findColour(LumatoneEditorColourIDs::MediumBackground));
}
-
void SettingsContainer::changeListenerCallback(ChangeBroadcaster* source)
{
auto panelIndex = categoryList->getSelectedRow();
@@ -91,6 +93,14 @@ void SettingsContainer::showPanel(int editorSettingCategory)
case LumatoneEditorSettingCategories::Firmware:
newPanel = new FirmwareDlg();
break;
+
+ case LumatoneEditorSettingCategories::Midi:
+ newPanel = new MidiSettingsDlg();
+ break;
+
+ case LumatoneEditorSettingCategories::Presets:
+ newPanel = new PresetSettingsDlg();
+ break;
}
if (newPanel)
diff --git a/Source/Settings/SettingsContainer.h b/Source/Settings/SettingsContainer.h
index 5b25a0aa..9cef9815 100644
--- a/Source/Settings/SettingsContainer.h
+++ b/Source/Settings/SettingsContainer.h
@@ -12,10 +12,14 @@
#include "../LumatoneEditorLookAndFeel.h"
#include "CalibrationDlg.h"
#include "FirmwareDlg.h"
+#include "PresetSettingsDlg.h"
+#include "MidiSettingsDlg.h"
typedef enum {
Calibration = 0,
- Firmware = 1
+ Firmware = 1,
+ Midi = 2,
+ Presets = 3
} LumatoneEditorSettingCategories;
class SettingsCategoryModel : public ListBoxModel, public ChangeBroadcaster
@@ -49,6 +53,8 @@ class SettingsContainer : public Component, protected ChangeListener
public:
SettingsContainer();
+
+ // Send change signal when destructed
~SettingsContainer();
void paint(Graphics& g) override;
diff --git a/Source/Settings/WheelsCalibrationComponent.h b/Source/Settings/WheelsCalibrationComponent.h
new file mode 100644
index 00000000..dc3e5e67
--- /dev/null
+++ b/Source/Settings/WheelsCalibrationComponent.h
@@ -0,0 +1,143 @@
+/*
+ ==============================================================================
+
+ WheelsCalibrationComponent.h
+ Created: 13 Jul 2021 7:45:55pm
+ Author: Vincenzo
+
+ ==============================================================================
+*/
+
+#pragma once
+
+#include
+#include "../LumatoneFirmwareDefinitions.h"
+
+//==============================================================================
+/*
+*/
+class WheelsCalibrationComponent : public juce::Component
+{
+public:
+
+ WheelsCalibrationComponent()
+ {
+ float defaultMinPoint = (0.5f - 0.15) / ADCSCALAR;
+ float defaultMaxPoint = (0.5f + 0.15) / ADCSCALAR;
+ auto initCalibration = WheelsCalibrationData();
+ initCalibration.minPitch = defaultMinPoint;
+ initCalibration.maxPitch = defaultMaxPoint;
+ initCalibration.minMod = defaultMinPoint;
+ initCalibration.maxMod= defaultMaxPoint;
+ }
+
+ void paint (juce::Graphics& g) override
+ {
+ // Fill gradient
+ g.setGradientFill(pitchGradient);
+ g.fillPath(pitchPath);
+
+ g.setGradientFill(modGradient);
+ g.fillPath(modPath);
+
+ // Draw outline
+ g.setColour(Colours::black);
+ g.strokePath(pitchPath, PathStrokeType(wheelBorderThickness));
+ g.strokePath(modPath, PathStrokeType(wheelBorderThickness));
+ }
+
+ void resized() override
+ {
+ wheelBounds.setSize(proportionOfHeight(widthToHeightRatio), getHeight());
+ int maxWidth = wheelBounds.getWidth() * 3;
+
+ if (maxWidth > getWidth())
+ {
+ wheelBounds.setSize(getHeight() * widthToHeightRatio, getHeight());
+ yMargin = roundToInt((getHeight() - wheelBounds.getHeight()) * 0.5 + 1.5f * wheelBorderThickness);
+ }
+
+ roundedCorner = proportionOfWidth(roundedCornerToWidth);
+ pitchPath = Path();
+ pitchPath.addRoundedRectangle(wheelBounds.withX(xMargin).reduced(0, yMargin), roundedCorner);
+
+ modPath = Path();
+ modPath.addRoundedRectangle(wheelBounds.withRightX(getWidth() - xMargin).reduced(0, yMargin), roundedCorner);
+
+ updateCalibrationData(calibrationData, false);
+ }
+
+ // TODO: improve this
+ void updateCalibrationData(WheelsCalibrationData calibrationDataIn, bool redraw = true)
+ {
+ calibrationData = calibrationDataIn;
+
+ float centerPitchNorm = calibrationData.getCentrePitchNorm();
+ float minPitchNorm = calibrationData.getMinPitchNorm();
+ float maxPitchNorm = calibrationData.getMaxPitchNorm();
+ float minModNorm = calibrationData.getMinModNorm();
+ float maxModNorm = calibrationData.getMaxModNorm();
+
+ // "Max" values are "up", so needs y-axis inversion
+ float minPitch = (1.0f - minPitchNorm);
+ float maxPitch = (1.0f - maxPitchNorm);
+
+ float minPitchMargin = jmin(1.0f, minPitch + gradientRange * 0.8f);
+ float maxPitchMargin = jmax(0.0f, maxPitch - gradientRange);
+
+ float minMod = (1.0f - minModNorm);
+ float maxMod = (1.0f - maxModNorm);
+
+ float minModMargin = jmin(1.0f, minMod + gradientRange * minMod * 2);
+ float maxModMargin = jmax(0.0f, maxMod - gradientRange * maxModNorm * 2);
+
+ Colour pitchMinColour = gradBackgroundBase.brighter((centerPitchNorm - minPitchNorm) * minPitch);
+ Colour pitchMaxColour = gradBackgroundBase.brighter((maxPitchNorm - centerPitchNorm) * maxPitchNorm);
+
+ Colour modMinColour = gradBackgroundBase.brighter(minMod * 0.3f);
+ Colour modMaxColour = gradBackgroundBase.brighter(maxModNorm * 0.3f);
+
+ pitchGradient = ColourGradient::vertical(gradBackgroundBase, gradBackgroundBase, wheelBounds);
+ pitchGradient.addColour(minPitchMargin, pitchMinColour);
+ pitchGradient.addColour(minPitch, gradCalibrate);
+ pitchGradient.addColour(maxPitch, gradCalibrate);
+ pitchGradient.addColour(maxPitchMargin, pitchMaxColour);
+
+ modGradient = ColourGradient::vertical(gradBackgroundBase, gradBackgroundBase, wheelBounds);
+ modGradient.addColour(minModMargin, modMinColour);
+ modGradient.addColour(minMod, gradCalibrate);
+ modGradient.addColour(maxMod, gradCalibrate);
+ modGradient.addColour(maxModMargin, modMaxColour);
+
+ if (redraw)
+ repaint();
+ }
+
+private:
+
+ WheelsCalibrationData calibrationData;
+
+ Rectangle wheelBounds;
+ Path pitchPath;
+ Path modPath;
+
+ float wheelBorderThickness = 1.0f;
+ int xMargin = roundToInt(wheelBorderThickness * 1.5f);
+ int yMargin = 0;
+ float roundedCorner = 12;
+
+ Colour gradBackgroundBase = Colour(0xff1a1b1c);
+ Colour gradCalibrate = Colour(0xffb1b1b1);
+ ColourGradient pitchGradient;
+ ColourGradient modGradient;
+
+ const float gradientRange = 0.1f;
+
+ Rectangle pitchCal;
+ Rectangle modCal;
+
+ const float widthToHeightRatio = 0.2381f;
+ const float roundedCornerToWidth = 0.1f;
+
+ JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WheelsCalibrationComponent)
+};
diff --git a/Source/SingleNoteAssign.cpp b/Source/SingleNoteAssign.cpp
index d7794876..67f2efdb 100644
--- a/Source/SingleNoteAssign.cpp
+++ b/Source/SingleNoteAssign.cpp
@@ -34,6 +34,7 @@ SingleNoteAssign::SingleNoteAssign ()
//[Constructor_pre] You can add your own custom stuff here..
//[/Constructor_pre]
+ setName ("SingleNoteAssign");
noteAutoIncrButton.reset (new juce::ToggleButton ("noteAutoIncrButton"));
addAndMakeVisible (noteAutoIncrButton.get());
noteAutoIncrButton->setButtonText (TRANS("Notes-Per-Click"));
@@ -147,15 +148,13 @@ SingleNoteAssign::SingleNoteAssign ()
keyTypeToggleButton->setButtonText(translate("KeyType"));
keyTypeToggleButton->setColour(ToggleButton::ColourIds::textColourId, toggleTextColour);
-
- ccFaderFlipBtn.reset(new juce::ImageButton());
- ccFaderFlipBtn->setClickingTogglesState(true);
- ccFaderFlipBtn->addListener(this);
- ccFaderFlipBtn->setTooltip(translate("Toggle CC polarity inversion. Default, arrow down: values increase with key press. Inverted, arrow up: values decrease with key press."));
- addAndMakeVisible(ccFaderFlipBtn.get());
- ccFaderFlipBtn->setColour(TextButton::ColourIds::buttonColourId, Colours::white.withAlpha(0.05f));
- ccFaderFlipBtn->setColour(TextButton::ColourIds::buttonOnColourId, Colours::grey.withAlpha(0.1f));
-
+
+ ccFaderIsDefault.reset(new juce::ImageButton());
+ ccFaderIsDefault->setClickingTogglesState(true);
+ ccFaderIsDefault->addListener(this);
+ ccFaderIsDefault->setTooltip(translate("Toggle CC polarity inversion. Default, arrow down: values increase with key press. Inverted, arrow up: values decrease with key press."));
+ addAndMakeVisible(ccFaderIsDefault.get());
+ ccFaderIsDefault->setToggleState(true, dontSendNotification);
faderUpArrow = getArrowPath(Point(0.5f, 1.0f), Point(0.5f, 0.0f), 0.5f, 0.25f);
faderDownArrow = getArrowPath(Point(0.5f, 0.0f), Point(0.5f, 1.0f), 0.5f, 0.75f);
@@ -184,7 +183,7 @@ SingleNoteAssign::SingleNoteAssign ()
// TODO: load last active colour?
colourTextEditor->addColourSelectionListener(this);
-
+
// Set up FlexBox rows
for (int i = 0; i <= SingleNoteFlexRows::channelIncrement; i++)
{
@@ -195,7 +194,7 @@ SingleNoteAssign::SingleNoteAssign ()
FlexBox::AlignItems::center,
FlexBox::JustifyContent::flexStart));
}
-
+
//[/UserPreSize]
setSize (320, 400);
@@ -207,7 +206,6 @@ SingleNoteAssign::SingleNoteAssign ()
setColourToggleButton->setToggleState(true, juce::NotificationType::sendNotification);
keyTypeToggleButton->setToggleState(true, juce::NotificationType::sendNotification);
keyTypeCombo->setSelectedId(LumatoneKeyType::noteOnNoteOff);
- juce::Timer::callAfterDelay(500, [&]{keyTypeCombo->setSelectedId(2, sendNotification);});
//[/Constructor]
}
@@ -263,7 +261,8 @@ void SingleNoteAssign::paint (juce::Graphics& g)
g.drawFittedText(translate("ManualAssignDirections"), instructionsBounds, Justification::centred, 2, 1.0f);
g.setColour(Colours::darkgrey);
- g.drawLine(controlsX, separatorY, getWidth() - controlsX, separatorY);
+ float thickness = getHeight() * separatorThicknessScalar;
+ g.drawLine(controlsX, separatorY, getWidth() - controlsX, separatorY, jmax(1.0f, thickness));
// // Debug FlexBox positioning
// Random r;
@@ -287,89 +286,90 @@ void SingleNoteAssign::resized()
float h = getHeight();
if (getParentComponent()) // This changes when the tab changes
- roundedCornerSize = round(getParentComponent()->getParentComponent()->getParentHeight() * ROUNDEDCORNERTOAPPHEIGHT);
+ roundedCornerSize = roundToInt(getParentComponent()->getParentComponent()->getParentHeight() * ROUNDEDCORNERTOAPPHEIGHT);
+
+ int controlAreaTop = roundToInt(h * controlAreaYScalar);
- int controlAreaTop = round(h * controlAreaYScalar);
-
int toggleHeight = roundToInt(h * toggleHeightScalar);
int toggleHeightScaled = roundToInt(toggleHeight * GLOBALFONTSCALAR);
-
+
int controlH = roundToInt(h * controlHeightScalar);
int controlHScaled = roundToInt(controlH * GLOBALFONTSCALAR);
-
+
int marginX = roundToInt(controlH * 0.6f);
int marginY = roundToInt(h * yMarginScalar);
int halfMarginX = roundToInt(marginX * 0.5f);
int halfMarginY = roundToInt(marginY * 0.5f);
-
+
int rowWidth = roundToInt(w - marginX * 2);
int toggleWidthMargin = toggleHeight + marginX * 1.25f; // Toggle icon + combined margins
-
+
//[/UserPreResize]
//[UserResized] Add your own custom resize handling here..
instructionsAreaBounds.setBounds(0, 0, w, controlAreaTop);
instructionsFont.setHeight(instructionsAreaBounds.getHeight() * fontHeightInBounds);
controlsX = roundToInt(w * controlsXScalar);
-
+
+
instructionsBounds.setBounds(marginX, 0, w - marginX * 2, controlAreaTop);
parametersFont.setHeight(toggleHeightScaled);
-
+
flexBounds.clear();
auto tglTemplateItem = FlexItem().withHeight(toggleHeightScaled).withAlignSelf(FlexItem::AlignSelf::center);
auto ctrlTemplateItem = FlexItem(FlexItem::autoValue, controlH).withFlex(1.0f);
-
+
auto getTglWidth = [=](ToggleButton* btn) {
return roundToInt(parametersFont.getStringWidthFloat(btn->getButtonText()) + toggleWidthMargin);
};
-
- for (int i = 0; i <= SingleNoteFlexRows::channelIncrement; i++)
+
+ for (int r = 0; r <= SingleNoteFlexRows::channelIncrement; r++)
{
- auto row = flexRows.getReference(i);
+ auto row = flexRows.getReference(r);
row.items.clear();
-
+
// Generalize row properties
auto toggleItem = FlexItem(tglTemplateItem);
auto setupToggleItem = [&] (ToggleButton* btn) {
toggleItem.width = getTglWidth(btn);
toggleItem.associatedComponent = btn;
};
-
- auto controlItem = FlexItem(ctrlTemplateItem);
-
+
+ auto controlItem = FlexItem(ctrlTemplateItem).withMargin(FlexItem::Margin(0, 0, 0, halfMarginX));
+
auto getAndPerformBounds = [&] () {
- auto bounds = Rectangle(controlsX, flexBounds[i - 1].getBottom() + halfMarginY, rowWidth, controlH);
+ auto bounds = Rectangle(controlsX, flexBounds[r - 1].getBottom() + halfMarginY, rowWidth, controlH);
flexBounds.add(bounds);
row.performLayout(bounds);
};
-
- switch (i)
+
+ switch (r)
{
case SingleNoteFlexRows::keyType:
{
setupToggleItem(keyTypeToggleButton.get());
row.items.add(toggleItem);
-
+
controlItem.associatedComponent = keyTypeCombo.get();
row.items.add(controlItem);
-
+
flexBounds.add(Rectangle(controlsX, controlAreaTop + halfMarginY, rowWidth, controlH));
- row.performLayout(flexBounds[i]);
+ row.performLayout(flexBounds[r]);
break;
}
case SingleNoteFlexRows::keyColour:
{
setupToggleItem(setColourToggleButton.get());
row.items.add(toggleItem);
-
+
auto colourItem = FlexItem(controlH * 1.5f, controlH, *colourSubwindow.get());
- colourItem.margin = FlexItem::Margin(0, halfMarginX, 0, 0);
+ colourItem.margin = FlexItem::Margin(0, halfMarginX, 0, halfMarginX);
row.items.add(colourItem);
-
+
controlItem.associatedComponent = colourTextEditor.get();
row.items.add(controlItem);
-
+
getAndPerformBounds();
break;
}
@@ -377,17 +377,18 @@ void SingleNoteAssign::resized()
{
setupToggleItem(setNoteToggleButton.get());
row.items.add(toggleItem);
-
+
if (keyTypeCombo->getSelectedId() == LumatoneKeyType::continuousController)
{
- auto ccInvertItem = FlexItem(controlH, controlH, *ccFaderFlipBtn.get());
+ auto ccInvertItem = FlexItem(controlH, controlH, *ccFaderIsDefault.get());
ccInvertItem.margin = FlexItem::Margin(0, halfMarginX, 0, 0);
row.items.add(ccInvertItem);
+ controlItem.margin = FlexItem::Margin();
}
-
+
controlItem.associatedComponent = noteInput.get();
row.items.add(controlItem);
-
+
getAndPerformBounds();
break;
}
@@ -395,29 +396,29 @@ void SingleNoteAssign::resized()
{
setupToggleItem(setChannelToggleButton.get());
row.items.add(toggleItem);
-
+
controlItem.associatedComponent = channelInput.get();
row.items.add(controlItem);
-
+
getAndPerformBounds();
-
+
// Position Auto-Increment section since channelIncrement is relative to it
separatorY = channelInput->getBottom() + halfMarginY;
autoIncrementLabel->setBounds(controlsX, separatorY + halfMarginY, w - controlsX, controlHScaled);
- noteAutoIncrButton->setTopLeftPosition(controlsX, autoIncrementLabel->getBottom() + halfMarginY);
- resizeToggleButtonWithHeight(noteAutoIncrButton.get(), parametersFont, toggleHeightScaled);
+ noteAutoIncrButton->setBounds(controlsX, autoIncrementLabel->getBottom() + halfMarginY,
+ rowWidth, toggleHeightScaled);
break;
}
case SingleNoteFlexRows::channelIncrement:
{
setupToggleItem(channelAutoIncrButton.get());
row.items.add(toggleItem);
-
+
controlItem.associatedComponent = channelAutoIncrNoteInput.get();
row.items.add(controlItem);
-
+
flexBounds.add(Rectangle(controlsX, noteAutoIncrButton->getBottom() + halfMarginY, rowWidth, controlH));
- row.performLayout(flexBounds[i]);
+ row.performLayout(flexBounds[r]);
break;
}
default:
@@ -429,11 +430,13 @@ void SingleNoteAssign::resized()
colourTextEditor->applyFontToAllText(TerpstraSysExApplication::getApp().getAppFont(LumatoneEditorFont::GothamNarrowMedium, controlHScaled * CONTROLBOXFONTHEIGHTSCALAR), true);
noteInput->setTextBoxStyle(Slider::TextEntryBoxPosition::TextBoxLeft, false, roundToInt(noteInput->getWidth() * incDecButtonTextBoxWidthScalar), noteInput->getHeight());
-
+
if (keyTypeCombo->getSelectedId() == LumatoneKeyType::continuousController)
redrawCCFlipBtn();
-
+
channelInput->setTextBoxStyle(Slider::TextEntryBoxPosition::TextBoxLeft, false, roundToInt(channelInput->getWidth() * incDecButtonTextBoxWidthScalar), channelInput->getHeight());
+
+ channelAutoIncrNoteInput->setTextBoxStyle(Slider::TextEntryBoxPosition::TextBoxLeft, false, roundToInt(channelAutoIncrNoteInput->getWidth() * incDecButtonTextBoxWidthScalar), channelAutoIncrNoteInput->getHeight());
//[/UserResized]
}
@@ -488,7 +491,7 @@ void SingleNoteAssign::buttonClicked (juce::Button* buttonThatWasClicked)
}
//[UserbuttonClicked_Post]
- else if (buttonThatWasClicked == ccFaderFlipBtn.get())
+ else if (buttonThatWasClicked == ccFaderIsDefault.get())
{
}
//[/UserbuttonClicked_Post]
@@ -507,12 +510,12 @@ void SingleNoteAssign::comboBoxChanged (juce::ComboBox* comboBoxThatHasChanged)
if (keyTypeCombo->getSelectedId() == LumatoneKeyType::continuousController)
{
setNoteToggleButton->setButtonText("CC #:");
- ccFaderFlipBtn->setVisible(true);
+ ccFaderIsDefault->setVisible(true);
}
else
{
setNoteToggleButton->setButtonText("Note # (0-127):");
- ccFaderFlipBtn->setVisible(false);
+ ccFaderIsDefault->setVisible(false);
}
resized();
@@ -563,8 +566,12 @@ void SingleNoteAssign::lookAndFeelChanged()
void SingleNoteAssign::colourChangedCallback(ColourSelectionBroadcaster* source, Colour newColour)
{
- if (source == colourTextEditor.get())
- colourSubwindow->setColour(colourTextEditor->getText());
+ if (source != colourTextEditor.get())
+ {
+ colourTextEditor->setText(newColour.toDisplayString(false), false);
+ }
+
+ colourSubwindow->setColour(colourTextEditor->getText());
}
/// Called from parent when one of the keys is clicked
@@ -573,16 +580,17 @@ UndoableAction* SingleNoteAssign::createEditAction(int setSelection, int keySele
{
int newNote = noteInput->getValue();
int newChannel = channelInput->getValue();
-
+
auto editAction = new Lumatone::SingleNoteAssignAction(
setSelection, keySelection,
keyTypeToggleButton->getToggleState(), setChannelToggleButton->getToggleState(),
setNoteToggleButton->getToggleState(), setColourToggleButton->getToggleState(),
+ (LumatoneKeyType)keyTypeCombo->getSelectedId() == LumatoneKeyType::continuousController,
(LumatoneKeyType)keyTypeCombo->getSelectedId(), newChannel,
- newNote, colourSubwindow->getColourAsObject());
+ newNote, colourSubwindow->getColourAsObject(), ccFaderIsDefault->getToggleState());
jassert(editAction != nullptr && editAction->isValid());
-
+
// Auto increment note
if (noteAutoIncrButton->getToggleState())
{
@@ -630,9 +638,6 @@ void SingleNoteAssign::restoreStateFromPropertiesFile(PropertiesFile* properties
keyTypeToggleButton->setToggleState(
propertiesFile->getBoolValue("SingleNoteKeyTypeSetActive", true),
juce::NotificationType::sendNotification);
- ccFaderFlipBtn->setToggleState(
- propertiesFile->getBoolValue("SingleNoteKeyCCFlipped", false),
- juce::NotificationType::sendNotification);
}
void SingleNoteAssign::saveStateToPropertiesFile(PropertiesFile* propertiesFile)
@@ -641,7 +646,6 @@ void SingleNoteAssign::saveStateToPropertiesFile(PropertiesFile* propertiesFile)
propertiesFile->setValue("SingleNoteChannelSetActive", setChannelToggleButton->getToggleState());
propertiesFile->setValue("SingleNoteColourSetActive", setColourToggleButton->getToggleState());
propertiesFile->setValue("SingleNoteKeyTypeSetActive", keyTypeToggleButton->getToggleState());
-
}
void SingleNoteAssign::redrawCCFlipBtn()
@@ -649,42 +653,42 @@ void SingleNoteAssign::redrawCCFlipBtn()
Path arrowPath, faderPath, arrowInvertedPath, faderInvertedPath;
getCCPolarityIconPath(false, arrowPath, faderPath);
getCCPolarityIconPath(true, arrowInvertedPath, faderInvertedPath);
- auto defaultImg = Image(Image::PixelFormat::ARGB, ccFaderFlipBtn->getWidth(), ccFaderFlipBtn->getHeight(), true);
+ auto defaultImg = Image(Image::PixelFormat::ARGB, ccFaderIsDefault->getWidth(), ccFaderIsDefault->getHeight(), true);
auto invertedImg = defaultImg.createCopy();
-
+
Graphics g(defaultImg);
Graphics gi(invertedImg);
-
+
auto transform = AffineTransform::scale(defaultImg.getWidth(), defaultImg.getHeight());
arrowPath.applyTransform(transform);
faderPath.applyTransform(transform);
arrowInvertedPath.applyTransform(transform);
faderInvertedPath.applyTransform(transform);
-
+
auto lookAndFeel = &TerpstraSysExApplication::getApp().getLookAndFeel();
auto background = lookAndFeel->findColour(TextButton::ColourIds::buttonColourId);
g.setColour(background);
- g.fillRoundedRectangle(ccFaderFlipBtn->getLocalBounds().toFloat(), 5.0f);
+ g.fillRoundedRectangle(ccFaderIsDefault->getLocalBounds().toFloat(), 5.0f);
gi.setColour(background);
- gi.fillRoundedRectangle(ccFaderFlipBtn->getLocalBounds().toFloat(), 5.0f);
-
+ gi.fillRoundedRectangle(ccFaderIsDefault->getLocalBounds().toFloat(), 5.0f);
+
auto arrowStroke = PathStrokeType(PHI, PathStrokeType::JointStyle::curved);
auto colour = lookAndFeel->findColour(LumatoneEditorColourIDs::ActiveText).brighter(0.1);
g.setColour(colour);
g.strokePath(arrowPath, arrowStroke);
g.fillPath(faderPath);
-
+
gi.setColour(colour);
gi.strokePath(arrowInvertedPath, arrowStroke);
gi.fillPath(faderInvertedPath);
-
+
// auto mouseOverImg = ccFaderFlipBtn->getToggleState() ? &invertedImg : &defaultImg;
auto highlight = Colours::white.withAlpha(0.0333f);
-
- ccFaderFlipBtn->setImages(false, false, true,
- defaultImg, 1.0f, Colour(),
- defaultImg, 1.0f, highlight,
- invertedImg, 1.0f, Colour());
+
+ ccFaderIsDefault->setImages(false, false, true,
+ invertedImg, 1.0f, Colour(),
+ invertedImg, 1.0f, highlight,
+ defaultImg, 1.0f, Colour());
}
//[/MiscUserCode]
@@ -698,7 +702,7 @@ void SingleNoteAssign::redrawCCFlipBtn()
BEGIN_JUCER_METADATA
- instructionsAreaBounds;
Rectangle instructionsBounds;
- int controlsX;
- int separatorY;
+ float controlsX;
+ float separatorY;
Font instructionsFont;
Font parametersFont;
-
+
Array flexRows;
Array> flexBounds; // Primarily for debugging
enum SingleNoteFlexRows
@@ -107,15 +107,17 @@ class SingleNoteAssign : public Component,
const float controlAreaYScalar = 0.183333f;
const float controlsXScalar = 0.06f;
const float separatorYScalar = 0.666667f;
- const float toggleHeightScalar = 0.042f;
+ const float toggleHeightScalar = 0.041f;
const float controlHeightScalar = 0.0647f;
const float controlBoxFontHeightScalar = 0.75f;
const float incDecButtonTextBoxWidthScalar = 0.4f;
+ const float separatorThicknessScalar = 0.005f;
+
const Colour toggleTextColour = Colour(0xffcbcbcb);
-
- std::unique_ptr ccFaderFlipBtn;
+
+ std::unique_ptr ccFaderIsDefault;
Path faderDownArrow;
Path faderUpArrow;
//[/UserVariables]
@@ -134,7 +136,7 @@ class SingleNoteAssign : public Component,
std::unique_ptr colourTextEditor;
std::unique_ptr channelInput;
std::unique_ptr channelAutoIncrNoteInput;
-
+
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SingleNoteAssign)
diff --git a/Source/TerpstraMidiDriver.cpp b/Source/TerpstraMidiDriver.cpp
index 29137b54..44786fa5 100644
--- a/Source/TerpstraMidiDriver.cpp
+++ b/Source/TerpstraMidiDriver.cpp
@@ -11,23 +11,72 @@
#include "TerpstraMidiDriver.h"
#include "Main.h"
+// There are different race-condition issues between macOS and Windows.
+// This Driver may need to be redesigned, but for now this define is
+// used for including a MessageManagerLock on Windows & Linux, but not on macOS.
+
+#define MIDI_DRIVER_USE_LOCK JUCE_WINDOWS //|| JUCE_LINUX
+
TerpstraMidiDriver::TerpstraMidiDriver() : HajuMidiDriver()
{
-
}
TerpstraMidiDriver::~TerpstraMidiDriver()
{
+ collectors.clear();
+}
+
+//============================================================================
+// TerpstaMidiDriver::Collector helpers
+
+void TerpstraMidiDriver::addMessageCollector(Collector* collectorToAdd)
+{
+ collectors.addIfNotAlreadyThere(collectorToAdd);
}
-void TerpstraMidiDriver::addListener(TerpstraMidiDriver::Listener* listener)
+void TerpstraMidiDriver::removeMessageCollector(Collector* collectorToRemove)
{
- listeners.add(listener);
+ collectors.removeFirstMatchingValue(collectorToRemove);
}
-void TerpstraMidiDriver::removeListener(TerpstraMidiDriver::Listener* listener)
+void TerpstraMidiDriver::notifyMessageReceived(MidiInput* source, const MidiMessage& midiMessage)
{
- listeners.remove(listener);
+#if MIDI_DRIVER_USE_LOCK
+ const MessageManagerLock lock;
+#endif
+ MessageManager::callAsync([this, source, midiMessage]{
+ for (auto collector : collectors) collector->midiMessageReceived(source, midiMessage);
+ });
+}
+
+void TerpstraMidiDriver::notifyMessageSent(MidiOutput* target, const MidiMessage& midiMessage)
+{
+ // Currently unused
+// #if MIDI_DRIVER_USE_LOCK
+// const MessageManagerLock lock;
+// #endif
+
+// MessageManager::callAsync([this, target, midiMessage]{
+// for (auto collector : collectors) collector->midiMessageSent(target, midiMessage);
+// });
+}
+
+void TerpstraMidiDriver::notifySendQueueSize()
+{
+ auto size = messageBuffer.size();
+ for (auto collector : collectors) collector->midiSendQueueSize(size);
+}
+
+void TerpstraMidiDriver::notifyLogMessage(String textMessage, HajuErrorVisualizer::ErrorLevel errorLevel)
+{
+ for (auto collector : collectors) collector->generalLogMessage(textMessage, errorLevel);
+}
+
+void TerpstraMidiDriver::notifyNoAnswerToMessage(MidiInput* expectedDevice, const MidiMessage& midiMessage)
+{
+ MessageManager::callAsync([this, expectedDevice, midiMessage]{
+ for (auto collector : collectors) collector->noAnswerToMessage(expectedDevice, midiMessage);
+ });
}
/*
@@ -169,9 +218,8 @@ void TerpstraMidiDriver::sendVelocityConfig(const uint8 velocityTable[])
reversedTable[x] = velocityTable[127 - x] & 0x7f;
}
- MidiMessage msg = sendTableSysEx(0, SET_VELOCITY_CONFIG, 128, reversedTable);
-
- sendMessageWithAcknowledge(msg);
+ auto msg = createTableSysEx(0, SET_VELOCITY_CONFIG, 128, reversedTable);
+ sendMessageWithAcknowledge(msg);
}
// CMD 09h: Save velocity config to EEPROM
@@ -189,7 +237,7 @@ void TerpstraMidiDriver::resetVelocityConfig()
// CMD 0Bh: Adjust the internal fader look-up table (128 7-bit values)
void TerpstraMidiDriver::sendFaderConfig(const uint8 faderTable[])
{
- MidiMessage msg = sendTableSysEx(0, SET_FADER_CONFIG, 128, faderTable);
+ MidiMessage msg = createTableSysEx(0, SET_FADER_CONFIG, 128, faderTable);
sendMessageWithAcknowledge(msg);
}
@@ -220,7 +268,7 @@ void TerpstraMidiDriver::sendCalibrateAfterTouch()
// CMD 10h: Adjust the internal aftertouch look-up table (size of 128)
void TerpstraMidiDriver::sendAftertouchConfig(const uint8 aftertouchTable[])
{
- MidiMessage msg = sendTableSysEx(0, SET_AFTERTOUCH_CONFIG, 128, aftertouchTable);
+ MidiMessage msg = createTableSysEx(0, SET_AFTERTOUCH_CONFIG, 128, aftertouchTable);
sendMessageWithAcknowledge(msg);
}
@@ -327,7 +375,7 @@ void TerpstraMidiDriver::sendVelocityIntervalConfig(const int velocityIntervalTa
formattedTable[1 + 2*i] = velocityIntervalTable[i] & 0x3f;
}
- MidiMessage msg = sendTableSysEx(0, SET_VELOCITY_INTERVALS, payloadSize, formattedTable);
+ MidiMessage msg = createTableSysEx(0, SET_VELOCITY_INTERVALS, payloadSize, formattedTable);
sendMessageWithAcknowledge(msg);
}
@@ -366,9 +414,12 @@ void TerpstraMidiDriver::startDemoMode(bool turnOn)
}
// CMD 26h: Initiate the pitch and mod wheel calibration routine, pass in false to stop
-void TerpstraMidiDriver::sendCalibratePitchModWheel(bool startCalibration)
+void TerpstraMidiDriver::sendCalibratePitchModWheel(bool startCalibration, int testOutputIndex)
{
- sendSysExToggle(0, CALIBRATE_PITCH_MOD_WHEEL, startCalibration);
+ if (testOutputIndex < 0)
+ sendSysExToggle(0, CALIBRATE_PITCH_MOD_WHEEL, startCalibration);
+ else
+ sendTestMessageNow(testOutputIndex, createTerpstraSysEx(0, CALIBRATE_PITCH_MOD_WHEEL, startCalibration, TEST_ECHO, '\0', '\0'));
}
// CMD 27h: Set the sensitivity value of the mod wheel, 0x01 to 0x07f
@@ -422,7 +473,7 @@ void TerpstraMidiDriver::setAftertouchKeySensitivity(uint8 boardIndex, uint8 sen
// CMD 2Dh: Adjust the Lumatouch table, a 128 byte array with value of 127 being a key fully pressed
void TerpstraMidiDriver::setLumatouchConfig(const uint8 lumatouchTable[])
{
- MidiMessage msg = sendTableSysEx(0, SET_LUMATOUCH_CONFIG, 128, lumatouchTable);
+ MidiMessage msg = createTableSysEx(0, SET_LUMATOUCH_CONFIG, 128, lumatouchTable);
sendMessageWithAcknowledge(msg);
}
@@ -453,7 +504,7 @@ void TerpstraMidiDriver::sendGetFirmwareRevisionRequest(int sendToTestDevice)
sendTestMessageNow(sendToTestDevice, createTerpstraSysEx(0, GET_FIRMWARE_REVISION, TEST_ECHO, '\0', '\0', '\0'));
}
-// CMD 32h: Set the thresold from key’s min value to trigger CA - 004 submodule CC events, ranging from 0x00 to 0xFE
+// CMD 32h: Set the thresold from key's min value to trigger CA - 004 submodule CC events, ranging from 0x00 to 0xFE
void TerpstraMidiDriver::setCCActiveThreshold(uint8 boardIndex, uint8 sensitivity)
{
if (sensitivity > 0xfe) sensitivity &= 0xfe;
@@ -471,7 +522,9 @@ void TerpstraMidiDriver::ping(uint8 value1, uint8 value2, uint8 value3, int send
if (sendToTestDevice < 0)
sendSysEx(0, LUMA_PING, TEST_ECHO, value1, value2, value3);
else if (sendToTestDevice < midiOutputs.size())
+ {
sendTestMessageNow(sendToTestDevice, createTerpstraSysEx(0, LUMA_PING, TEST_ECHO, value1, value2, value3));
+ }
}
// CMD 33h: Echo the payload, 0x00-0x7f, for use in connection monitoring
@@ -534,6 +587,11 @@ void TerpstraMidiDriver::getBoardSensitivityValues(uint8 boardIndex)
// CMD 3Ch: Set the MIDI channels for peripheral controllers
void TerpstraMidiDriver::setPeripheralChannels(uint8 pitchWheelChannel, uint8 modWheelChannel, uint8 expressionChannel, uint8 sustainChannel)
{
+ pitchWheelChannel -= 1;
+ modWheelChannel -= 1;
+ expressionChannel -= 1;
+ sustainChannel -= 1;
+
// For now set default channel to 1
if (pitchWheelChannel > 0xf) pitchWheelChannel &= 0x00;
if (modWheelChannel > 0xf) modWheelChannel &= 0x00;
@@ -611,11 +669,32 @@ void TerpstraMidiDriver::sendGetExpressionPedalADCThresholdRequest()
sendSysExRequest(0, GET_EXPRESSION_PEDAL_THRESHOLD);
}
+// CMD 45h: Configure the on/off settings of the sustain pedal
void TerpstraMidiDriver::sendInvertSustainPedal(bool setInverted)
{
sendSysExToggle(0, INVERT_SUSTAIN_PEDAL, setInverted);
}
+// CMD 46h: Replace current presets with factory presets
+void TerpstraMidiDriver::sendResetDefaultPresetsRequest(int presetIndex)
+{
+ presetIndex %= 10;
+ sendSysEx(0, RESET_DEFAULT_PRESETS, presetIndex, '\0', '\0', '\0');
+}
+
+// CMD 47h: Read back the currently configured preset flags of expression & sustain inversion,
+// plus light-on-keystroke and polyphonic aftertouch
+void TerpstraMidiDriver::sendGetPresetFlagsReset()
+{
+ sendSysExRequest(0, GET_PRESET_FLAGS);
+}
+
+// For CMD 48h response: get expression pedal sensitivity
+void TerpstraMidiDriver::sendGetExpressionPedalSensitivity()
+{
+ sendSysExRequest(0, GET_EXPRESSION_PEDAL_SENSITIVIY);
+}
+
/*
==============================================================================
Low-level SysEx calls
@@ -688,7 +767,7 @@ MidiMessage TerpstraMidiDriver::createExtendedMacroColourSysEx(uint8 cmd, int re
return createExtendedMacroColourSysEx(cmd, red >> 4, red & 0xf, green >> 4, green & 0xf, blue >> 4, blue & 0xf);
}
-MidiMessage TerpstraMidiDriver::sendTableSysEx(uint8 boardIndex, uint8 cmd, uint8 tableSize, const uint8 table[])
+MidiMessage TerpstraMidiDriver::createTableSysEx(uint8 boardIndex, uint8 cmd, uint8 tableSize, const uint8 table[])
{
size_t msgSize = tableSize + 5;
Array dataArray;
@@ -702,13 +781,12 @@ MidiMessage TerpstraMidiDriver::sendTableSysEx(uint8 boardIndex, uint8 cmd, uint
memmove(&sysExData[5], table, sizeof(uint8) * tableSize);
- //DEBUG
+#if JUCE_DEBUG
for (int i = 0; i < tableSize; i++)
jassert(table[i] <= 0x7f);
+#endif
auto msg = MidiMessage::createSysExMessage(sysExData, msgSize);
- sendMessageWithAcknowledge(msg);
-
return msg;
}
@@ -716,7 +794,7 @@ void TerpstraMidiDriver::sendSysEx(uint8 boardIndex, uint8 cmd, uint8 data1, uin
{
if (midiInput != nullptr)
{
- jassert(boardIndex < 0x6 && cmd <= 0x44 && data1 <= 0x7f && data2 <= 0x7f && data3 <= 0x7f && data4 <= 0x7f);
+ jassert(boardIndex < 0x6 && data1 <= 0x7f && data2 <= 0x7f && data3 <= 0x7f && data4 <= 0x7f);
MidiMessage msg = createTerpstraSysEx(boardIndex, cmd, data1, data2, data3, data4);
sendMessageWithAcknowledge(msg);
}
@@ -1155,18 +1233,37 @@ FirmwareSupport::Error TerpstraMidiDriver::unpackGetPeripheralChannelsResponse(c
if (status != FirmwareSupport::Error::noError)
return status;
- pitchWheelChannel = unpackedData[0];
- modWheelChannel = unpackedData[1];
- expressionChannel = unpackedData[2];
- sustainPedalChannel = unpackedData[3];
+ pitchWheelChannel = unpackedData[0] + 1;
+ modWheelChannel = unpackedData[1] + 1;
+ expressionChannel = unpackedData[2] + 1;
+ sustainPedalChannel = unpackedData[3] + 1;
return status;
}
+// For CMD 3Eh response: read back the calibration mode of the message
+FirmwareSupport::Error TerpstraMidiDriver::unpackPeripheralCalibrationMode(const MidiMessage& response, int& calibrationMode)
+{
+ // Other errors will be caught in corresponding mode unpacking
+ int msgSize = response.getSysExDataSize();
+ int expectedSize = PAYLOAD_INIT + 15;
+
+ if (msgSize < expectedSize)
+ return FirmwareSupport::Error::messageTooShort;
+
+ else if (msgSize > expectedSize)
+ return FirmwareSupport::Error::messageTooLong;
+
+ auto sysExData = response.getSysExData();
+ calibrationMode = sysExData[CALIB_MODE];
+
+ return FirmwareSupport::Error::noError;
+}
+
// For CMD 3Eh response: retrieve 12-bit expression pedal calibration status values in respective mode, automatically sent every 100ms
FirmwareSupport::Error TerpstraMidiDriver::unpackExpressionPedalCalibrationPayload(const MidiMessage& response, int& minBound, int& maxBound, bool& valid)
{
- const short NUM_UNPACKED = 5; // Actually two + boolean, but this message always returns 15-byte payload
+ const short NUM_UNPACKED = 15; // Actually two + boolean, but this message always returns 15-byte payload
int unpackedData[NUM_UNPACKED];
auto status = unpack12BitDataFrom4Bit(response, 15, unpackedData);
if (status != FirmwareSupport::Error::noError)
@@ -1182,7 +1279,7 @@ FirmwareSupport::Error TerpstraMidiDriver::unpackExpressionPedalCalibrationPaylo
// For CMD 3Eh response: retrieve 12-bit pitch & mod wheel calibration status values in respective mode, automatically sent every 100ms
FirmwareSupport::Error TerpstraMidiDriver::unpackWheelsCalibrationPayload(const MidiMessage& response, int& centerPitch, int& minPitch, int& maxPitch, int& minMod, int& maxMod)
{
- const short NUM_UNPACKED = 5;
+ const short NUM_UNPACKED = 15;
int unpackedData[NUM_UNPACKED];
auto status = unpack12BitDataFrom4Bit(response, NUM_UNPACKED, unpackedData);
if (status != FirmwareSupport::Error::noError)
@@ -1246,36 +1343,76 @@ FirmwareSupport::Error TerpstraMidiDriver::unpackGetExpressionPedalThresholdResp
return status;
}
+// For CMD 47h response: retrieve preset flags
+FirmwareSupport::Error TerpstraMidiDriver::unpackGetPresetFlagsResponse(const MidiMessage& response, bool& expressionInverted, bool& lightsOnKeystroke, bool& aftertouchOn, bool& sustainInverted)
+{
+ const short NUM_UNPACKED = 4;
+ int unpackedData[NUM_UNPACKED];
+ auto status = unpack7BitData(response, NUM_UNPACKED, unpackedData);
+ if (status != FirmwareSupport::Error::noError)
+ return status;
+
+ expressionInverted = unpackedData[0];
+ lightsOnKeystroke = unpackedData[1];
+ aftertouchOn = unpackedData[2];
+ sustainInverted = unpackedData[3];
+
+ return status;
+}
+
+// For CMD 48h response: get expression pedal sensitivity
+FirmwareSupport::Error TerpstraMidiDriver::unpackGetExpressionPedalSensitivityResponse(const MidiMessage& response, int& sensitivity)
+{
+ const short NUM_UNPACKED = 1;
+ int unpackedData[NUM_UNPACKED];
+ auto status = unpack7BitData(response, NUM_UNPACKED, unpackedData);
+ if (status != FirmwareSupport::Error::noError)
+ return status;
+
+ sensitivity = unpackedData[0];
+
+ return status;
+}
+
+
+
void TerpstraMidiDriver::sendMessageWithAcknowledge(const MidiMessage& message)
{
- if (sendTestMessagesOnly && message.isSysEx())
+ // Prevent certain messages from being sent
+ if (onlySendRequestMessages && message.isSysEx())
{
auto sysExData = message.getSysExData();
- if ( sysExData[CMD_ID] != GET_SERIAL_IDENTITY
- && sysExData[CMD_ID] != GET_FIRMWARE_REVISION
- && sysExData[CMD_ID] != LUMA_PING)
+ if ( sysExData[CMD_ID] == CHANGE_KEY_NOTE
+ || sysExData[CMD_ID] == SET_KEY_COLOUR
+ || sysExData[CMD_ID] == SET_VELOCITY_CONFIG
+ || sysExData[CMD_ID] == SET_FADER_CONFIG
+ || sysExData[CMD_ID] == SET_AFTERTOUCH_CONFIG
+ || sysExData[CMD_ID] == SET_VELOCITY_INTERVALS
+ || sysExData[CMD_ID] == SET_LUMATOUCH_CONFIG)
{
return;
}
}
- // If there is no MIDI input port active: just send, without expecting acknowledge
- // ToDo Or do nothing?
+// jassert(midiInput != nullptr);
if (midiInput == nullptr)
{
- sendMessageNow(message);
+ DBG("No MidiInput open to send message to.");
+// sendMessageNow(message);
// Notify listeners
- const MessageManagerLock mmLock;
- this->listeners.call(&Listener::midiMessageSent, message);
+ // const MessageManagerLock mmLock;
+ // this->listeners.call(&Listener::midiMessageSent, message);
+// notifyMessageSent(midiOutput, message);
}
else
{
// Add message to queue first. The oldest message in queue will be sent.
{
messageBuffer.add(message);
- const MessageManagerLock mmLock;
- this->listeners.call(&Listener::midiSendQueueSize, messageBuffer.size());
+ // const MessageManagerLock mmLock;
+ // this->listeners.call(&Listener::midiSendQueueSize, messageBuffer.size());
+ notifySendQueueSize();
}
// If there is no message waiting for acknowledge: send oldest message of queue
@@ -1296,10 +1433,10 @@ void TerpstraMidiDriver::sendOldestMessageInQueue()
currentMsgWaitingForAck = messageBuffer[0]; // oldest element in buffer
hasMsgWaitingForAck = true;
messageBuffer.remove(0); // remove from buffer
- {
- const MessageManagerLock mmLock;
- this->listeners.call(&Listener::midiSendQueueSize, messageBuffer.size());
- }
+
+ // const MessageManagerLock mmLock;
+ // this->listeners.call(&Listener::midiSendQueueSize, messageBuffer.size());
+ notifySendQueueSize();
sendCurrentMessage();
}
@@ -1322,11 +1459,10 @@ void TerpstraMidiDriver::sendCurrentMessage()
sendMessageNow(currentMsgWaitingForAck); // send it
// Notify listeners
- {
- DBG("SENT : " + currentMsgWaitingForAck.getDescription());
- const MessageManagerLock mmLock;
- this->listeners.call(&Listener::midiMessageSent, currentMsgWaitingForAck);
- }
+ DBG("SENT: " + currentMsgWaitingForAck.getDescription());
+ // const MessageManagerLock mmLock;
+ // this->listeners.call(&Listener::midiMessageSent, currentMsgWaitingForAck);
+ notifyMessageSent(midiOutput, currentMsgWaitingForAck);
timerType = waitForAnswer;
startTimer(receiveTimeoutInMilliseconds); // Start waiting for answer
@@ -1334,16 +1470,14 @@ void TerpstraMidiDriver::sendCurrentMessage()
void TerpstraMidiDriver::handleIncomingMidiMessage(MidiInput* source, const MidiMessage& message)
{
- // Notify listeners
- {
- const MessageManagerLock mmLock;
-
- // DEBUG
- if (message.isSysEx())
- DBG("RECEIVED: " + message.getDescription());
-
- this->listeners.call(&Listener::midiMessageReceived, source, message);
- }
+#if JUCE_DEBUG
+ if (message.isSysEx())
+ DBG("RCVD: " + message.getDescription());
+#endif
+
+ //const MessageManagerLock mmLock;
+ //this->listeners.call(&Listener::midiMessageReceived, source, message);
+ notifyMessageReceived(source, message);
// Check whether received message is an answer to the previously sent one
if (hasMsgWaitingForAck && messageIsResponseToMessage(message, currentMsgWaitingForAck))
@@ -1355,14 +1489,16 @@ void TerpstraMidiDriver::handleIncomingMidiMessage(MidiInput* source, const Midi
// Check answer state (error yes/no)
auto answerState = message.getSysExData()[5];
- // if answer state is "busy": resend message after a little delay
- if (answerState == TerpstraMIDIAnswerReturnCode::STATE)
- {
- // turn demo mode off
- startDemoMode(false);
- }
+
+ // This would be nice but we can't be sure the state is demo mode
+// if (answerState == TerpstraMIDIAnswerReturnCode::STATE)
+// {
+// // turn demo mode off
+// startDemoMode(false);
+// }
- if ( answerState == TerpstraMIDIAnswerReturnCode::BUSY)
+ // if answer state is "busy": resend message after a little delay
+ if (answerState == TerpstraMIDIAnswerReturnCode::BUSY)
{
// Start delay timer, after which message will be sent again
timerType = delayWhileDeviceBusy;
@@ -1392,12 +1528,14 @@ void TerpstraMidiDriver::timerCallback()
hasMsgWaitingForAck = false;
// No answer came from MIDI input
- {
- DBG("DRIVER: NO ANSWER");
- const MessageManagerLock mmLock;
- listeners.call(&Listener::generalLogMessage, "No answer from device", HajuErrorVisualizer::ErrorLevel::error);
- listeners.call(&Listener::noAnswerToMessage, currentMsgWaitingForAck);
- }
+
+ DBG("DRIVER: NO ANSWER");
+ //const MessageManagerLock mmLock;
+ // listeners.call(&Listener::generalLogMessage, "No answer from device", HajuErrorVisualizer::ErrorLevel::error);
+ // listeners.call(&Listener::noAnswerToMessage, currentMsgWaitingForAck);
+ notifyLogMessage("No answer from device", HajuErrorVisualizer::ErrorLevel::error);
+ notifyNoAnswerToMessage(midiInput, currentMsgWaitingForAck);
+
sendOldestMessageInQueue();
}
@@ -1414,5 +1552,7 @@ void TerpstraMidiDriver::clearMIDIMessageBuffer()
{
messageBuffer.clear();
hasMsgWaitingForAck = false;
- this->listeners.call(&Listener::midiSendQueueSize, 0);
+ stopTimer();
+ // this->listeners.call(&Listener::midiSendQueueSize, 0);
+ notifySendQueueSize();
}
diff --git a/Source/TerpstraMidiDriver.h b/Source/TerpstraMidiDriver.h
index f7a79725..d53fdfc3 100644
--- a/Source/TerpstraMidiDriver.h
+++ b/Source/TerpstraMidiDriver.h
@@ -22,23 +22,36 @@
Connection to midi, sending SysEx parameters to keyboard
==============================================================================
*/
-class TerpstraMidiDriver : public HajuMidiDriver, public MidiInputCallback, public Timer
+class TerpstraMidiDriver : public HajuMidiDriver, public Timer
{
// Types
public:
- // Listener class, to notify changes
- class Listener
+ // MidiMessageCollector version of previous TerpstraMidiDriver::Listener, as to keep the Midi thread lightweight
+ class Collector : protected MidiMessageCollector
{
+ MidiBuffer messagesSentQueue;
+
public:
- // Destructor
- virtual ~Listener() {}
+ virtual ~Collector() {}
+
+ virtual void midiMessageReceived(MidiInput* source, const MidiMessage& message) = 0;
+ virtual void midiMessageSent(MidiOutput* target, const MidiMessage& message) = 0;
+ virtual void midiSendQueueSize(int size) = 0;
+ virtual void generalLogMessage(String textMessage, HajuErrorVisualizer::ErrorLevel errorLevel) {}
+
+ // Realtime messages before a device is connected - not for heavy processing!
+ virtual void noAnswerToMessage(MidiInput* expectedDevice, const MidiMessage& message) = 0;
+// virtual void testMessageReceived(int testInputIndex, const MidiMessage& midiMessage) {};
+ };
- virtual void midiMessageReceived(MidiInput* source, const MidiMessage& midiMessage) = 0;
- virtual void midiMessageSent(const MidiMessage& midiMessage) = 0;
- virtual void midiSendQueueSize(int queueSize) = 0;
- virtual void generalLogMessage(String textMessage, HajuErrorVisualizer::ErrorLevel errorLevel) = 0;
- virtual void noAnswerToMessage(const MidiMessage& midiMessage) = 0;
- };
+private:
+ // Helper callbacks for notifying Collectors
+ void notifyMessageReceived(MidiInput* source, const MidiMessage& midiMessage);
+ void notifyMessageSent(MidiOutput* target, const MidiMessage& midiMessage);
+ void notifySendQueueSize();
+ void notifyLogMessage(String textMessage, HajuErrorVisualizer::ErrorLevel errorLevel);
+ void notifyNoAnswerToMessage(MidiInput* expectedDevice, const MidiMessage& midiMessage);
+// void notifyTestMessageReceived(int testInputIndex, const MidiMessage& midiMessage);
private:
typedef enum
@@ -51,10 +64,12 @@ class TerpstraMidiDriver : public HajuMidiDriver, public MidiInputCallback, publ
TerpstraMidiDriver();
~TerpstraMidiDriver();
- void addListener(Listener* listenerToAdd);
- void removeListener(Listener* listenerToRemove);
+// void addListener(Listener* listenerToAdd);
+// void removeListener(Listener* listenerToRemove);
+ void addMessageCollector(Collector* collectorToAdd);
+ void removeMessageCollector(Collector* collectorToRemove);
- void restrictToTestMessages(bool testMessagesOnly) { sendTestMessagesOnly = testMessagesOnly; }
+ void restrictToRequestMessages(bool testMessagesOnly) { onlySendRequestMessages = testMessagesOnly; }
//============================================================================
// Single (mid-level) commands, firmware specific
@@ -187,7 +202,7 @@ class TerpstraMidiDriver : public HajuMidiDriver, public MidiInputCallback, publ
void startDemoMode(bool turnOn);
// CMD 26h: Initiate the pitch and mod wheel calibration routine, pass in false to stop
- void sendCalibratePitchModWheel(bool startCalibration);
+ void sendCalibratePitchModWheel(bool startCalibration, int testOutputIndex = -1);
// CMD 27h: Set the sensitivity value of the mod wheel, 0x01 to 0x07f
void setModWheelSensitivity(uint8 sensitivity);
@@ -224,7 +239,7 @@ class TerpstraMidiDriver : public HajuMidiDriver, public MidiInputCallback, publ
// If the board has not been initialized, the Beaglebone will contain a firmware revision of 0.0.0 for the board
void sendGetFirmwareRevisionRequest(int sendToTestDevice = -1);
- // CMD 32h: Set the thresold from key’s min value to trigger CA - 004 submodule CC events, ranging from 0x00 to 0xFE
+ // CMD 32h: Set the thresold from key�s min value to trigger CA - 004 submodule CC events, ranging from 0x00 to 0xFE
void setCCActiveThreshold(uint8 boardIndex, uint8 sensitivity);
// CMD 33h: Echo the payload, 3 7-bit values, for use in connection monitoring
@@ -291,6 +306,18 @@ class TerpstraMidiDriver : public HajuMidiDriver, public MidiInputCallback, publ
// CMD 45h: Configure the on/off settings of the sustain pedal
void sendInvertSustainPedal(bool setInverted);
+ // CMD 46h: Replace a certain preset with the factory preset
+ void sendResetDefaultPresetsRequest(int presetIndex);
+
+ // CMD 47h: Read back the currently configured preset flags of expression & sustain inversion,
+ // plus light-on-keystroke and polyphonic aftertouch
+ void sendGetPresetFlagsReset();
+
+ // For CMD 48h response: get expression pedal sensitivity
+ void sendGetExpressionPedalSensitivity();
+
+ // TODO CMD 49h-4Eh
+
//============================================================================
// Implementation of bidirectional communication with acknowledge messages
@@ -379,6 +406,9 @@ class TerpstraMidiDriver : public HajuMidiDriver, public MidiInputCallback, publ
// For CMD 3Dh response: retrieve MIDI channels of which peripherals are configured
FirmwareSupport::Error unpackGetPeripheralChannelsResponse(const MidiMessage& response, int& pitchWheelChannel, int& modWheelChannel, int& expressionChannel, int& sustainPedalChannel);
+ // For CMD 3Eh response: read back the calibration mode of the message
+ FirmwareSupport::Error unpackPeripheralCalibrationMode(const MidiMessage& response, int& calibrationMode);
+
// For CMD 3Eh response: retrieve 12-bit expression pedal calibration status values in respective mode, automatically sent every 100ms
FirmwareSupport::Error unpackExpressionPedalCalibrationPayload(const MidiMessage& response, int& minBound, int& maxBound, bool& valid);
@@ -394,6 +424,11 @@ class TerpstraMidiDriver : public HajuMidiDriver, public MidiInputCallback, publ
// For CMD 44h response: retrieve 12-bit expression pedal adc threshold
FirmwareSupport::Error unpackGetExpressionPedalThresholdResponse(const MidiMessage& response, int& thresholdValue);
+ // For CMD 47h response: retrieve preset flags
+ FirmwareSupport::Error unpackGetPresetFlagsResponse(const MidiMessage& response, bool& expressionInverted, bool& lightsOnKeystroke, bool& aftertouchOn, bool& sustainInverted);
+
+ // For CMD 48h response: get expression pedal sensitivity
+ FirmwareSupport::Error unpackGetExpressionPedalSensitivityResponse(const MidiMessage& response, int& sensitivity);
private:
// Low-level SysEx message sending
@@ -420,7 +455,7 @@ class TerpstraMidiDriver : public HajuMidiDriver, public MidiInputCallback, publ
MidiMessage createExtendedMacroColourSysEx(uint8 cmd, int red, int green, int blue) const;
// Create a SysEx message encoding a table with a defined size
- MidiMessage sendTableSysEx(uint8 boardIndex, uint8 cmd, uint8 tableSize, const uint8 table[]);
+ MidiMessage createTableSysEx(uint8 boardIndex, uint8 cmd, uint8 tableSize, const uint8 table[]);
// Send a SysEx message with standardized length
void sendSysEx(uint8 boardIndex, uint8 cmd, uint8 data1, uint8 data2, uint8 data3, uint8 data4, bool overrideEditMode = false);
@@ -457,16 +492,18 @@ class TerpstraMidiDriver : public HajuMidiDriver, public MidiInputCallback, publ
// Attributes
protected:
- ListenerList listeners;
+ // ListenerList listeners;
+ Array collectors;
private:
MidiMessage currentMsgWaitingForAck; // std::optional would be the object of choice,once that is available...
bool hasMsgWaitingForAck = false; // will be obsolete when std::optional is available
- Array messageBuffer;
+ Array messageBuffer;
- bool sendTestMessagesOnly = false; // Only send GetSerialIdentity, GetFirmwareRevision, and Ping commands
+ // Used for device detection and "Offline" mode (no messages that mutate board data)
+ bool onlySendRequestMessages = false;
const int receiveTimeoutInMilliseconds = 2000;
const int busyTimeDelayInMilliseconds = 500;
diff --git a/Source/VelocityCurveComponents.h b/Source/VelocityCurveComponents.h
index 92df66ec..a7aba63e 100644
--- a/Source/VelocityCurveComponents.h
+++ b/Source/VelocityCurveComponents.h
@@ -28,10 +28,10 @@ class VelocityCurveBeam : public Component, public SettableTooltipClient
void setValueAtLeast(int newValue);
void setValueAtMost(int newValue);
- void paint(Graphics& g);
- void resized();
+ void paint(Graphics& g) override;
+ void resized() override;
- juce::Point getBottomMid();
+ juce::Point getBottomMid();
float getBeamHeightFromValue(int value);
float getBeamHeightFromValue() { return getBeamHeightFromValue(beamValue); }
diff --git a/Source/VelocityCurveDlgBase.cpp b/Source/VelocityCurveDlgBase.cpp
index 7e6a4421..81bc2ecf 100644
--- a/Source/VelocityCurveDlgBase.cpp
+++ b/Source/VelocityCurveDlgBase.cpp
@@ -7,7 +7,7 @@
the "//[xyz]" and "//[/xyz]" sections will be retained when the file is loaded
and re-saved.
- Created with Projucer version: 6.0.5
+ Created with Projucer version: 6.0.7
------------------------------------------------------------------------------
@@ -29,9 +29,9 @@
//==============================================================================
VelocityCurveDlgBase::VelocityCurveDlgBase (TerpstraVelocityCurveConfig::VelocityCurveType typeValue)
- : freeDrawingStrategy(beamTableFrame, velocityBeamTable),
- linearDrawingStrategy(beamTableFrame, velocityBeamTable),
- quadraticDrawingStrategy(beamTableFrame, velocityBeamTable)
+ : freeDrawingStrategy(velocityBeamTable),
+ linearDrawingStrategy(velocityBeamTable),
+ quadraticDrawingStrategy(velocityBeamTable)
{
//[Constructor_pre] You can add your own custom stuff here..
velocityCurveType = typeValue;
@@ -50,6 +50,7 @@ VelocityCurveDlgBase::VelocityCurveDlgBase (TerpstraVelocityCurveConfig::Velocit
cbEditMode->setBounds (8, 8, 296, 24);
+
//[UserPreSize]
cbEditMode->getProperties().set(LumatoneEditorStyleIDs::roundedDiagonalCorners, 0);
@@ -100,6 +101,8 @@ void VelocityCurveDlgBase::paint (juce::Graphics& g)
//[UserPrePaint] Add your own custom painting code here..
//[/UserPrePaint]
+ g.fillAll (juce::Colour (0xffbad0de));
+
//[UserPaint] Add your own custom painting code here..
g.fillAll(backgroundColour);
@@ -108,10 +111,10 @@ void VelocityCurveDlgBase::paint (juce::Graphics& g)
// Build contour path
beamTableContour.clear();
beamTableContour.startNewSubPath(0, h);
-
+
for (int x = 0; x < 128; x++)
beamTableContour.lineTo(velocityBeamTable[x]->getX(), h - velocityBeamTable[x]->getBeamHeightFromValue());
-
+
beamTableContour.lineTo(getWidth(), h);
beamTableContour.closeSubPath();
@@ -119,9 +122,6 @@ void VelocityCurveDlgBase::paint (juce::Graphics& g)
g.setGradientFill(beamColourGradient);
g.fillPath(beamTableContour);
- //g.setColour(findColour(VelocityCurveBeam::outlineColourId));
- //g.strokePath(beamTableFrame, PathStrokeType(1.000f));
-
auto currentDrawingStrategy = getCurrentDrawingStrategy();
if (currentDrawingStrategy != nullptr)
{
@@ -152,24 +152,12 @@ void VelocityCurveDlgBase::resized()
//[UserResized] Add your own custom resize handling here..
- //float graphicsXPadding = cbEditMode->getX();
- //float graphicsYPos = cbEditMode->getBottom() + BEAMTABLERIMABOVE;
- //float graphicsBottom = h - BEAMTABLERIMABOVE;
-
- //beamTableFrame.clear();
- //beamTableFrame.startNewSubPath(graphicsXPadding, graphicsYPos);
- //beamTableFrame.lineTo(graphicsXPadding, graphicsBottom);
- //beamTableFrame.lineTo(w - graphicsXPadding, graphicsBottom);
- //beamTableFrame.lineTo(w - graphicsXPadding, graphicsYPos);
- //beamTableFrame.closeSubPath();
-
cbEditMode->setBounds(0, 0, xUnit * 11, xUnit * 3);
auto currentDrawingStrategy = getCurrentDrawingStrategy();
if (currentDrawingStrategy != nullptr)
currentDrawingStrategy->resized();
- //float velocityGraphicsHeight = graphicsBottom - graphicsYPos;
float velocityBeamXPos = 0;
for (int x = 0; x < 128; x++)
{
@@ -230,27 +218,16 @@ void VelocityCurveDlgBase::comboBoxChanged (juce::ComboBox* comboBoxThatHasChang
//[/UsercomboBoxChanged_Post]
}
-
-
-//[MiscUserCode] You can add your own definitions of your custom methods or any other code here...
-
-void VelocityCurveDlgBase::paintOverChildren(juce::Graphics& g)
-{
- //int roundedCornerSize = getParentHeight() * ROUNDEDCORNERTOAPPHEIGHT;
- //Rectangle controlsBounds = getLocalBounds().toFloat().reduced(roundedCornerSize);
- //g.setColour(Colour(0xff272b2f));
- //g.drawRoundedRectangle(controlsBounds, roundedCornerSize, 4.0f);
-}
-
void VelocityCurveDlgBase::lookAndFeelChanged()
{
+ //[UserCode_lookAndFeelChanged] -- Add your code here...
auto lookAndFeel = dynamic_cast(&getLookAndFeel());
if (lookAndFeel)
{
beamColourGradient.clearColours();
beamColourGradient.addColour(0.0, lookAndFeel->findColour(LumatoneEditorColourIDs::CurveGradientMin));
beamColourGradient.addColour(1.0, lookAndFeel->findColour(LumatoneEditorColourIDs::CurveGradientMax));
-
+
backgroundColour = lookAndFeel->findColour(LumatoneEditorColourIDs::ControlBoxBackground);
gridColour = lookAndFeel->findColour(LumatoneEditorColourIDs::CurveGridColour);
@@ -289,6 +266,19 @@ void VelocityCurveDlgBase::loadFromMapping()
}
repaint();
+ //[/UserCode_lookAndFeelChanged]
+}
+
+
+
+//[MiscUserCode] You can add your own definitions of your custom methods or any other code here...
+
+void VelocityCurveDlgBase::paintOverChildren(juce::Graphics& g)
+{
+ //int roundedCornerSize = getParentHeight() * ROUNDEDCORNERTOAPPHEIGHT;
+ //Rectangle controlsBounds = getLocalBounds().toFloat().reduced(roundedCornerSize);
+ //g.setColour(Colour(0xff272b2f));
+ //g.drawRoundedRectangle(controlsBounds, roundedCornerSize, 4.0f);
}
void VelocityCurveDlgBase::sendVelocityTableToController()
@@ -300,7 +290,7 @@ void VelocityCurveDlgBase::sendVelocityTableToController()
velocityValues[x] = velocityBeamTable[x]->getValue();
}
- TerpstraSysExApplication::getApp().getLumatoneController().sendTableConfig(velocityCurveType, velocityValues);
+ TerpstraSysExApplication::getApp().getLumatoneController()->sendTableConfig(velocityCurveType, velocityValues);
}
void VelocityCurveDlgBase::mouseMove(const MouseEvent &event)
@@ -380,21 +370,7 @@ TerpstraVelocityCurveConfig* VelocityCurveDlgBase::getConfigInEdit()
if(mappingInEdit == nullptr)
return nullptr;
- switch(velocityCurveType)
- {
- case TerpstraVelocityCurveConfig::VelocityCurveType::noteOnNoteOff:
- return &mappingInEdit->noteOnOffVelocityCurveConfig;
-
- case TerpstraVelocityCurveConfig::VelocityCurveType::fader:
- return &mappingInEdit->faderConfig;
- case TerpstraVelocityCurveConfig::VelocityCurveType::afterTouch:
- return &mappingInEdit->afterTouchConfig;
- case TerpstraVelocityCurveConfig::VelocityCurveType::lumaTouch:
- return &mappingInEdit->lumaTouchConfig;
- default:
- jassertfalse;
- return nullptr;
- }
+ return mappingInEdit->getVelocityCurveConfig(velocityCurveType);
}
VelocityCurveEditStrategyBase* VelocityCurveDlgBase::getCurrentDrawingStrategy()
@@ -423,10 +399,13 @@ VelocityCurveEditStrategyBase* VelocityCurveDlgBase::getCurrentDrawingStrategy()
BEGIN_JUCER_METADATA
+
+
+
velocityBeamTable[128];
diff --git a/Source/VelocityCurveEditStrategy.cpp b/Source/VelocityCurveEditStrategy.cpp
index 3b2bc3c2..0d28c9f3 100644
--- a/Source/VelocityCurveEditStrategy.cpp
+++ b/Source/VelocityCurveEditStrategy.cpp
@@ -17,9 +17,9 @@ VelocityCurveEditStrategyBase class
*/
VelocityCurveEditStrategyBase::VelocityCurveEditStrategyBase(
- Path& beamTableFrameRef,
+ //Path& beamTableFrameRef,
std::unique_ptr* velocityBeamTablePtr)
- : beamTableFrame(beamTableFrameRef), velocityBeamTable(velocityBeamTablePtr)
+ : /*beamTableFrame(beamTableFrameRef), */velocityBeamTable(velocityBeamTablePtr)
{
}
@@ -30,9 +30,9 @@ VelocityCurveFreeDrawingStrategy class
*/
VelocityCurveFreeDrawingStrategy::VelocityCurveFreeDrawingStrategy(
- Path& beamTableFrameRef,
+ //Path& beamTableFrameRef,
std::unique_ptr* velocityBeamTablePtr)
- : VelocityCurveEditStrategyBase(beamTableFrameRef, velocityBeamTablePtr)
+ : VelocityCurveEditStrategyBase(/*beamTableFrameRef, */velocityBeamTablePtr)
{
}
@@ -132,9 +132,9 @@ VelocityCurveSegmentEditStrategyBase class
*/
VelocityCurveSegmentEditStrategyBase::VelocityCurveSegmentEditStrategyBase(
- Path& beamTableFrameRef,
+ //Path& beamTableFrameRef,
std::unique_ptr* velocityBeamTablePtr)
- : VelocityCurveEditStrategyBase(beamTableFrameRef, velocityBeamTablePtr)
+ : VelocityCurveEditStrategyBase(/*beamTableFrameRef, */velocityBeamTablePtr)
, mouseXPosition(-1), draggedOriginalXPosition(-1)
{
fixPointBeamHeights[0] = 0;
@@ -346,9 +346,9 @@ VelocityCurveLinearDrawingStrategy class
*/
VelocityCurveLinearDrawingStrategy::VelocityCurveLinearDrawingStrategy(
- Path& beamTableFrameRef,
+ //Path& beamTableFrameRef,
std::unique_ptr* velocityBeamTablePtr)
- : VelocityCurveSegmentEditStrategyBase(beamTableFrameRef, velocityBeamTablePtr)
+ : VelocityCurveSegmentEditStrategyBase(/*beamTableFrameRef, */velocityBeamTablePtr)
{
}
@@ -470,9 +470,9 @@ VelocityCurveQuadraticDrawingStrategy class
*/
VelocityCurveQuadraticDrawingStrategy::VelocityCurveQuadraticDrawingStrategy(
- Path& beamTableFrameRef,
+ //Path& beamTableFrameRef,
std::unique_ptr* velocityBeamTablePtr)
- : VelocityCurveSegmentEditStrategyBase(beamTableFrameRef, velocityBeamTablePtr)
+ : VelocityCurveSegmentEditStrategyBase(/*beamTableFrameRef, */velocityBeamTablePtr)
{
}
diff --git a/Source/VelocityCurveEditStrategy.h b/Source/VelocityCurveEditStrategy.h
index a96171c1..10bdbc1d 100644
--- a/Source/VelocityCurveEditStrategy.h
+++ b/Source/VelocityCurveEditStrategy.h
@@ -10,7 +10,7 @@
#pragma once
-#include "../JuceLibraryCode/JuceHeader.h"
+#include
#include "VelocityCurveComponents.h"
@@ -23,7 +23,8 @@ Base class for velocity curve editing
class VelocityCurveEditStrategyBase
{
public:
- VelocityCurveEditStrategyBase(Path& beamTableFrameRef, std::unique_ptr* velocityBeamTablePtr);
+ VelocityCurveEditStrategyBase(/*Path& beamTableFrameRef,*/ std::unique_ptr* velocityBeamTablePtr);
+ virtual ~VelocityCurveEditStrategyBase() {}
// Set value table (e. g. from LMT file)
virtual bool setEditConfig(int velocityTableValues[]) = 0;
@@ -47,7 +48,7 @@ class VelocityCurveEditStrategyBase
virtual void mouseUp(const MouseEvent &event, juce::Point localPoint) { };
protected:
- Path& beamTableFrame;
+ //Path& beamTableFrame;
std::unique_ptr* velocityBeamTable;
};
@@ -59,7 +60,7 @@ Velocity curve editing via free drawing
class VelocityCurveFreeDrawingStrategy : public VelocityCurveEditStrategyBase
{
public:
- VelocityCurveFreeDrawingStrategy(Path& beamTableFrameRef, std::unique_ptr* velocityBeamTablePtr);
+ VelocityCurveFreeDrawingStrategy(/*Path& beamTableFrameRef,*/ std::unique_ptr* velocityBeamTablePtr);
bool setEditConfig(int velocityTableValues[]) override;
bool exportEditConfig(int velocityTableValues[]) override;
@@ -83,7 +84,8 @@ Base class for velocity curve editing with segments
class VelocityCurveSegmentEditStrategyBase : public VelocityCurveEditStrategyBase
{
public:
- VelocityCurveSegmentEditStrategyBase(Path& beamTableFrameRef, std::unique_ptr* velocityBeamTablePtr);
+ VelocityCurveSegmentEditStrategyBase(/*Path& beamTableFrameRef, */std::unique_ptr* velocityBeamTablePtr);
+ virtual ~VelocityCurveSegmentEditStrategyBase() {}
bool setEditConfig(int velocityTableValues[]) override;
bool exportEditConfig(int velocityTableValues[]) override;
@@ -123,7 +125,7 @@ Velocity curve editing via line segments
class VelocityCurveLinearDrawingStrategy : public VelocityCurveSegmentEditStrategyBase
{
public:
- VelocityCurveLinearDrawingStrategy(Path& beamTableFrameRef, std::unique_ptr* velocityBeamTablePtr);
+ VelocityCurveLinearDrawingStrategy(/*Path& beamTableFrameRef, */std::unique_ptr* velocityBeamTablePtr);
bool setEditConfigFromVelocityTable() override;
void setVelocityTableValuesFromEditConfig() override;
@@ -145,7 +147,7 @@ Velocity curve editing via quadratic curves
class VelocityCurveQuadraticDrawingStrategy : public VelocityCurveSegmentEditStrategyBase
{
public:
- VelocityCurveQuadraticDrawingStrategy(Path& beamTableFrameRef, std::unique_ptr* velocityBeamTablePtr);
+ VelocityCurveQuadraticDrawingStrategy(/*Path& beamTableFrameRef,*/ std::unique_ptr* velocityBeamTablePtr);
bool setEditConfigFromVelocityTable() override;
void setVelocityTableValuesFromEditConfig() override;
diff --git a/Source/ViewComponents.cpp b/Source/ViewComponents.cpp
index a2354cfd..4021a97f 100644
--- a/Source/ViewComponents.cpp
+++ b/Source/ViewComponents.cpp
@@ -43,16 +43,27 @@ TerpstraKey TerpstraKeyEdit::getValue() const
newValue.channelNumber = midiChannelLabel->getText().getIntValue();
newValue.colour = keyColour;
newValue.keyType = keyType;
+ newValue.ccFaderDefault = ccFaderDefault;
return newValue;
}
void TerpstraKeyEdit::setValue(TerpstraKey newValue)
{
- midiNoteLabel->setText(String(newValue.noteNumber), juce::NotificationType::sendNotification);
- midiChannelLabel->setText(String(newValue.channelNumber), juce::NotificationType::sendNotification);
+ if (newValue.keyType == LumatoneKeyType::disabled || newValue.keyType == LumatoneKeyType::disabledDefault)
+ {
+ newValue.keyType = LumatoneKeyType::disabled;
+ midiNoteLabel->setText("x", juce::NotificationType::sendNotification);
+ midiChannelLabel->setText("x", juce::NotificationType::sendNotification);
+ }
+ else
+ {
+ midiNoteLabel->setText(String(newValue.noteNumber), juce::NotificationType::sendNotification);
+ midiChannelLabel->setText(String(newValue.channelNumber), juce::NotificationType::sendNotification);
+ }
keyColour = newValue.colour;
keyType = newValue.keyType;
+ ccFaderDefault = newValue.ccFaderDefault;
String newTooltip = translate("KeyType") + " ";
switch (keyType)
@@ -62,6 +73,11 @@ void TerpstraKeyEdit::setValue(TerpstraKey newValue)
break;
case LumatoneKeyType::continuousController:
newTooltip += translate("ContinuousController");
+ newTooltip += newLine;
+ if (ccFaderDefault)
+ newTooltip += "CC Default (0->127)";
+ else
+ newTooltip += "CC Inverted (127->0)";
break;
case LumatoneKeyType::lumaTouch:
newTooltip += translate("Lumatouch");
@@ -111,26 +127,28 @@ void TerpstraKeyEdit::paint(Graphics& g)
textColour = textColour.brighter();
}
- if (currentValue.keyType == LumatoneKeyType::disabled)
- {
- midiChannelLabel->setVisible(false);
- midiNoteLabel->setVisible(false);
- }
- else
- {
- midiChannelLabel->setVisible(true);
- midiChannelLabel->setColour(juce::Label::textColourId, textColour);
- midiNoteLabel->setVisible(true);
- midiNoteLabel->setColour(juce::Label::textColourId, textColour);
- }
- // Look depending on Key type
- if (currentValue.keyType == LumatoneKeyType::continuousController)
+ midiChannelLabel->setColour(juce::Label::textColourId, textColour);
+ midiNoteLabel->setColour(juce::Label::textColourId, textColour);
+
+ // Look depending on Key type
+ if (currentValue.keyType == LumatoneKeyType::continuousController)
{
// Key type is continuous controller. Set colour gradient.
float w = this->getWidth();
float h = this->getHeight();
+ Colour inside, outside;
+ if (currentValue.ccFaderDefault)
+ {
+ outside = bgColour.darker();
+ inside = bgColour.brighter();
+ }
+ else
+ {
+ outside = bgColour.brighter();
+ inside = bgColour.darker();
+ }
g.setGradientFill(
- ColourGradient(bgColour.darker(), w * 0.5f, h * 0.5f, bgColour.brighter(), w * 0.5f, 0.0f, true));
+ ColourGradient(inside, w * 0.5f, h * 0.5f, outside, w * 0.5f, 0.0f, true));
}
else
{
@@ -152,17 +170,26 @@ void TerpstraKeyEdit::paint(Graphics& g)
g.strokePath(hexOutline, PathStrokeType(lineWidth));
}
- if (currentValue.keyType == LumatoneKeyType::disabled)
- {
- float w = this->getWidth();
- float h = this->getHeight();
- float xProportion = 0.25f;
- // Draw X on key
- g.setColour(bgColour.contrasting(0.5f));
- g.drawLine(w * xProportion, h * xProportion, w * (1-xProportion), h * (1-xProportion), 2);
- g.drawLine(w * (1 - xProportion), h * xProportion, w * xProportion, h * (1 - xProportion), 2);
- }
-
+// if (currentValue.keyType == LumatoneKeyType::disabled)
+// {
+// float w = this->getWidth();
+// float h = this->getHeight();
+// float xProportion = 0.25f;
+// // Draw X on key
+// g.setColour(bgColour.contrasting(0.5f));
+// g.drawLine(w * xProportion, h * xProportion, w * (1-xProportion), h * (1-xProportion), 2);
+// g.drawLine(w * (1 - xProportion), h * xProportion, w * xProportion, h * (1 - xProportion), 2);
+// }
+
+// if (currentValue.keyType == LumatoneKeyType::disabled)
+// {
+// TerpstraSysExApplication::getApp().getLookAndFeel().getLabelFont(*midiNoteLabel);
+// g.setColour(textColour);
+// g.setFont(midiChannelLabel->getFont());
+// g.drawText("x", midiChannelLabel->getBounds(), midiChannelLabel->getJustificationType());
+// g.drawText("x", midiNoteLabel->getBounds(), midiChannelLabel->getJustificationType());
+// }
+
// Something parametrized or not?
if (currentValue.isEmpty())
{
@@ -184,7 +211,7 @@ void TerpstraKeyEdit::resized()
// Draw hexagon
hexPath.clear();
hexPath.addPolygon(centre.toFloat(), 6, radius, TERPSTRASINGLEKEYROTATIONANGLE);
- Rectangle hexBounds = hexPath.getBounds().reduced(1, 1);
+ //Rectangle hexBounds = hexPath.getBounds().reduced(1, 1);
float lblSize = radius * TERPSTRASINGLEKEYLABELSIZE;
float lblOffset = radius * 0.375f;
diff --git a/Source/ViewComponents.h b/Source/ViewComponents.h
index 898d6163..da2e285e 100644
--- a/Source/ViewComponents.h
+++ b/Source/ViewComponents.h
@@ -52,6 +52,7 @@ class TerpstraKeyEdit : public Component, public SettableTooltipClient
juce::Colour keyColour;
LumatoneKeyType keyType;
+ bool ccFaderDefault;
float keySize;
diff --git a/TerpstraSysEx.jucer b/TerpstraSysEx.jucer
index 4aa8dbba..ea6c9c3d 100644
--- a/TerpstraSysEx.jucer
+++ b/TerpstraSysEx.jucer
@@ -1,12 +1,13 @@
-
-
+ displaySplashScreen="0" companyWebsite="https://www.lumatone.io/"
+ companyEmail="support@lumatone.io">
+
@@ -84,7 +85,17 @@
+
+
+
+
+
+
+
+
@@ -181,6 +196,8 @@
file="Source/VelocityCurveComponents.cpp"/>
+
-
-
+ targetName="Lumatone Editor"/>
+
@@ -310,14 +324,14 @@
-
-
+
@@ -334,9 +348,9 @@
extraDefs="SERVERHOST="192.168.7.2"" externalLibraries="libssh2.dll.a">
+ libraryPath="$(ProjectDir)..\..\Libraries\win64\lib" targetName="Lumatone Editor"/>
+ headerPath="$(ProjectDir)..\..\Libraries\win64\include" targetName="Lumatone Editor"/>
@@ -353,9 +367,9 @@
smallIcon="fwSyLL" bigIcon="yjLw0u" extraDefs="SERVERHOST="192.168.7.2"">
+ libraryPath="$(ProjectDir)..\..\Libraries\win64\lib" targetName="Lumatone Editor"/>
+ headerPath="$(ProjectDir)..\..\Libraries\win64\include" targetName="Lumatone Editor"/>