commit
5989267615
|
@ -33,7 +33,6 @@
|
||||||
21AC354419419A4D80ADE43A /* include_juce_audio_plugin_client_AU_2.mm in Sources */ = {isa = PBXBuildFile; fileRef = 7E47C0A828016F7D0D63C0D6 /* include_juce_audio_plugin_client_AU_2.mm */; };
|
21AC354419419A4D80ADE43A /* include_juce_audio_plugin_client_AU_2.mm in Sources */ = {isa = PBXBuildFile; fileRef = 7E47C0A828016F7D0D63C0D6 /* include_juce_audio_plugin_client_AU_2.mm */; };
|
||||||
2918F46AFD2AB89F9FA847DC /* include_juce_events.mm in Sources */ = {isa = PBXBuildFile; fileRef = 373EF982A53046CE00BECE68 /* include_juce_events.mm */; };
|
2918F46AFD2AB89F9FA847DC /* include_juce_events.mm in Sources */ = {isa = PBXBuildFile; fileRef = 373EF982A53046CE00BECE68 /* include_juce_events.mm */; };
|
||||||
2E77C6FAF1BCDB9EB29D20B9 /* PluginProcessor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D53CAB963D5051C786D3A52D /* PluginProcessor.cpp */; };
|
2E77C6FAF1BCDB9EB29D20B9 /* PluginProcessor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = D53CAB963D5051C786D3A52D /* PluginProcessor.cpp */; };
|
||||||
305606C42BB0F2A12D382D34 /* SoundfontSynthVoice.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B5A057FEC371053E83A73E47 /* SoundfontSynthVoice.cpp */; };
|
|
||||||
358E458C22BEE5090087ED8D /* RecentFilesMenuTemplate.nib in Resources */ = {isa = PBXBuildFile; fileRef = 78CC5234CCFE3B170585DDAD /* RecentFilesMenuTemplate.nib */; };
|
358E458C22BEE5090087ED8D /* RecentFilesMenuTemplate.nib in Resources */ = {isa = PBXBuildFile; fileRef = 78CC5234CCFE3B170585DDAD /* RecentFilesMenuTemplate.nib */; };
|
||||||
358E458D22BEE5090087ED8D /* Accelerate.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C1616112041466F7324D7E19 /* Accelerate.framework */; };
|
358E458D22BEE5090087ED8D /* Accelerate.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C1616112041466F7324D7E19 /* Accelerate.framework */; };
|
||||||
358E458E22BEE5090087ED8D /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 28CA077CDD21D0FEC66FC290 /* AudioToolbox.framework */; };
|
358E458E22BEE5090087ED8D /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 28CA077CDD21D0FEC66FC290 /* AudioToolbox.framework */; };
|
||||||
|
@ -123,11 +122,9 @@
|
||||||
8502F736BECFB9CB752AC72F /* Pills.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2C62C3F0621604CDB65B55A6 /* Pills.cpp */; };
|
8502F736BECFB9CB752AC72F /* Pills.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2C62C3F0621604CDB65B55A6 /* Pills.cpp */; };
|
||||||
85E6C3826F86B1258C407725 /* FluidSynthModel.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DA25104C95E55D1822BFFBE2 /* FluidSynthModel.cpp */; };
|
85E6C3826F86B1258C407725 /* FluidSynthModel.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DA25104C95E55D1822BFFBE2 /* FluidSynthModel.cpp */; };
|
||||||
909EB835CB55BF0B86B4BD93 /* CoreMIDI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 26949DA45B5FE0F3A0355733 /* CoreMIDI.framework */; };
|
909EB835CB55BF0B86B4BD93 /* CoreMIDI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 26949DA45B5FE0F3A0355733 /* CoreMIDI.framework */; };
|
||||||
9AF2F3DE22C71A7F465B2EAD /* BankAndPreset.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DECFA95359BC1DDDD1CC86C3 /* BankAndPreset.cpp */; };
|
|
||||||
9C107CE4B586E4B097D9D04E /* SurjectiveMidiKeyboardComponent.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4119A8200AC54674C00EFE66 /* SurjectiveMidiKeyboardComponent.cpp */; };
|
9C107CE4B586E4B097D9D04E /* SurjectiveMidiKeyboardComponent.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4119A8200AC54674C00EFE66 /* SurjectiveMidiKeyboardComponent.cpp */; };
|
||||||
9C2580F953071AD611EB6166 /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 28CA077CDD21D0FEC66FC290 /* AudioToolbox.framework */; };
|
9C2580F953071AD611EB6166 /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 28CA077CDD21D0FEC66FC290 /* AudioToolbox.framework */; };
|
||||||
AC5E4EF988D864A298E3650D /* TablesComponent.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0DD5458189C039F5A4FAD62D /* TablesComponent.cpp */; };
|
AC5E4EF988D864A298E3650D /* TablesComponent.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0DD5458189C039F5A4FAD62D /* TablesComponent.cpp */; };
|
||||||
B66EBD76F6051D97D56C97AB /* SoundfontSynthSound.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7D7B71BE20CA213D2FCD7FEE /* SoundfontSynthSound.cpp */; };
|
|
||||||
B92F6EAB1D5ACC13AF0CD750 /* CoreAudioKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A67D09546C4E4831438F7DBD /* CoreAudioKit.framework */; };
|
B92F6EAB1D5ACC13AF0CD750 /* CoreAudioKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A67D09546C4E4831438F7DBD /* CoreAudioKit.framework */; };
|
||||||
BB7C2221DA61425A1AC65694 /* include_juce_gui_extra.mm in Sources */ = {isa = PBXBuildFile; fileRef = 44FB953DA425CBBA8AC21417 /* include_juce_gui_extra.mm */; };
|
BB7C2221DA61425A1AC65694 /* include_juce_gui_extra.mm in Sources */ = {isa = PBXBuildFile; fileRef = 44FB953DA425CBBA8AC21417 /* include_juce_gui_extra.mm */; };
|
||||||
BFD9EF2D67067FC1E5BA3546 /* MyColours.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 29F2CE1B40FAE1467C7876C5 /* MyColours.cpp */; };
|
BFD9EF2D67067FC1E5BA3546 /* MyColours.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 29F2CE1B40FAE1467C7876C5 /* MyColours.cpp */; };
|
||||||
|
@ -140,7 +137,6 @@
|
||||||
CB8F898ACB35575C1695E223 /* include_juce_gui_basics.mm in Sources */ = {isa = PBXBuildFile; fileRef = F5276945E14F83CA02C05B41 /* include_juce_gui_basics.mm */; };
|
CB8F898ACB35575C1695E223 /* include_juce_gui_basics.mm in Sources */ = {isa = PBXBuildFile; fileRef = F5276945E14F83CA02C05B41 /* include_juce_gui_basics.mm */; };
|
||||||
DB7F85571650636DB9ECE092 /* include_juce_audio_plugin_client_AU.r in Rez */ = {isa = PBXBuildFile; fileRef = 5704CA923F677280C02D97C6 /* include_juce_audio_plugin_client_AU.r */; };
|
DB7F85571650636DB9ECE092 /* include_juce_audio_plugin_client_AU.r in Rez */ = {isa = PBXBuildFile; fileRef = 5704CA923F677280C02D97C6 /* include_juce_audio_plugin_client_AU.r */; };
|
||||||
DDF28AD28F639A561292FE28 /* include_juce_core.mm in Sources */ = {isa = PBXBuildFile; fileRef = F69B741A63932433977CFCD8 /* include_juce_core.mm */; };
|
DDF28AD28F639A561292FE28 /* include_juce_core.mm in Sources */ = {isa = PBXBuildFile; fileRef = F69B741A63932433977CFCD8 /* include_juce_core.mm */; };
|
||||||
DF84F5E7E386AF7A38854939 /* Preset.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 59F9FEC807012C10B8A1FA07 /* Preset.cpp */; };
|
|
||||||
E08B3A2AF85F9FCF991F1CA2 /* include_juce_audio_basics.mm in Sources */ = {isa = PBXBuildFile; fileRef = 13E201F5B25AC078DB396A9C /* include_juce_audio_basics.mm */; };
|
E08B3A2AF85F9FCF991F1CA2 /* include_juce_audio_basics.mm in Sources */ = {isa = PBXBuildFile; fileRef = 13E201F5B25AC078DB396A9C /* include_juce_audio_basics.mm */; };
|
||||||
FDAB0F06D8758FF0407BB851 /* include_juce_data_structures.mm in Sources */ = {isa = PBXBuildFile; fileRef = 2C66D01D1DD9006E77E2E260 /* include_juce_data_structures.mm */; };
|
FDAB0F06D8758FF0407BB851 /* include_juce_data_structures.mm in Sources */ = {isa = PBXBuildFile; fileRef = 2C66D01D1DD9006E77E2E260 /* include_juce_data_structures.mm */; };
|
||||||
FE0869D2DF902682B6E4C925 /* WebKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C481A535CF8A4CBC3E594003 /* WebKit.framework */; };
|
FE0869D2DF902682B6E4C925 /* WebKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C481A535CF8A4CBC3E594003 /* WebKit.framework */; };
|
||||||
|
@ -285,6 +281,7 @@
|
||||||
2C66D01D1DD9006E77E2E260 /* include_juce_data_structures.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = include_juce_data_structures.mm; path = ../../JuceLibraryCode/include_juce_data_structures.mm; sourceTree = SOURCE_ROOT; };
|
2C66D01D1DD9006E77E2E260 /* include_juce_data_structures.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = include_juce_data_structures.mm; path = ../../JuceLibraryCode/include_juce_data_structures.mm; sourceTree = SOURCE_ROOT; };
|
||||||
307CB49DF900DE4A612FF98E /* FluidSynthModel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = FluidSynthModel.h; path = ../../Source/FluidSynthModel.h; sourceTree = SOURCE_ROOT; };
|
307CB49DF900DE4A612FF98E /* FluidSynthModel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = FluidSynthModel.h; path = ../../Source/FluidSynthModel.h; sourceTree = SOURCE_ROOT; };
|
||||||
35099D9022CA8EF500CD4523 /* Util.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = Util.h; path = ../../Source/Util.h; sourceTree = "<group>"; };
|
35099D9022CA8EF500CD4523 /* Util.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = Util.h; path = ../../Source/Util.h; sourceTree = "<group>"; };
|
||||||
|
35099D9422CAB0A400CD4523 /* GuiConstants.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = GuiConstants.h; path = ../../Source/GuiConstants.h; sourceTree = "<group>"; };
|
||||||
35880F58CB540AD30D1B0ED3 /* TablesComponent.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = TablesComponent.h; path = ../../Source/TablesComponent.h; sourceTree = SOURCE_ROOT; };
|
35880F58CB540AD30D1B0ED3 /* TablesComponent.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = TablesComponent.h; path = ../../Source/TablesComponent.h; sourceTree = SOURCE_ROOT; };
|
||||||
358E45B422BEE53A0087ED8D /* libpcre.1.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; path = libpcre.1.dylib; sourceTree = "<group>"; };
|
358E45B422BEE53A0087ED8D /* libpcre.1.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; path = libpcre.1.dylib; sourceTree = "<group>"; };
|
||||||
358E45B522BEE53A0087ED8D /* libvorbisenc.2.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; path = libvorbisenc.2.dylib; sourceTree = "<group>"; };
|
358E45B522BEE53A0087ED8D /* libvorbisenc.2.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; path = libvorbisenc.2.dylib; sourceTree = "<group>"; };
|
||||||
|
@ -312,12 +309,10 @@
|
||||||
5704CA923F677280C02D97C6 /* include_juce_audio_plugin_client_AU.r */ = {isa = PBXFileReference; lastKnownFileType = file.r; name = include_juce_audio_plugin_client_AU.r; path = ../../JuceLibraryCode/include_juce_audio_plugin_client_AU.r; sourceTree = SOURCE_ROOT; };
|
5704CA923F677280C02D97C6 /* include_juce_audio_plugin_client_AU.r */ = {isa = PBXFileReference; lastKnownFileType = file.r; name = include_juce_audio_plugin_client_AU.r; path = ../../JuceLibraryCode/include_juce_audio_plugin_client_AU.r; sourceTree = SOURCE_ROOT; };
|
||||||
576A01FC6A3620A39BD1BDEE /* MyColours.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = MyColours.h; path = ../../Source/MyColours.h; sourceTree = SOURCE_ROOT; };
|
576A01FC6A3620A39BD1BDEE /* MyColours.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = MyColours.h; path = ../../Source/MyColours.h; sourceTree = SOURCE_ROOT; };
|
||||||
5896415135C635B1EB2DC202 /* CoreAudio.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreAudio.framework; path = System/Library/Frameworks/CoreAudio.framework; sourceTree = SDKROOT; };
|
5896415135C635B1EB2DC202 /* CoreAudio.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreAudio.framework; path = System/Library/Frameworks/CoreAudio.framework; sourceTree = SDKROOT; };
|
||||||
59F9FEC807012C10B8A1FA07 /* Preset.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = Preset.cpp; path = ../../Source/Preset.cpp; sourceTree = SOURCE_ROOT; };
|
|
||||||
5A57BEB8628C7AE62ED1039F /* include_juce_audio_plugin_client_AU_1.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = include_juce_audio_plugin_client_AU_1.mm; path = ../../JuceLibraryCode/include_juce_audio_plugin_client_AU_1.mm; sourceTree = SOURCE_ROOT; };
|
5A57BEB8628C7AE62ED1039F /* include_juce_audio_plugin_client_AU_1.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = include_juce_audio_plugin_client_AU_1.mm; path = ../../JuceLibraryCode/include_juce_audio_plugin_client_AU_1.mm; sourceTree = SOURCE_ROOT; };
|
||||||
60ADEC8B20DC559737F84180 /* juce_gui_basics */ = {isa = PBXFileReference; lastKnownFileType = folder; name = juce_gui_basics; path = /Applications/JUCE/modules/juce_gui_basics; sourceTree = "<absolute>"; };
|
60ADEC8B20DC559737F84180 /* juce_gui_basics */ = {isa = PBXFileReference; lastKnownFileType = folder; name = juce_gui_basics; path = /Applications/JUCE/modules/juce_gui_basics; sourceTree = "<absolute>"; };
|
||||||
663ACFA11DCEC0D411B8497E /* juicysfplugin.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = juicysfplugin.entitlements; sourceTree = SOURCE_ROOT; };
|
663ACFA11DCEC0D411B8497E /* juicysfplugin.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = juicysfplugin.entitlements; sourceTree = SOURCE_ROOT; };
|
||||||
6714B050717A7500EE7AE867 /* include_juce_audio_plugin_client_VST2.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = include_juce_audio_plugin_client_VST2.cpp; path = ../../JuceLibraryCode/include_juce_audio_plugin_client_VST2.cpp; sourceTree = SOURCE_ROOT; };
|
6714B050717A7500EE7AE867 /* include_juce_audio_plugin_client_VST2.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = include_juce_audio_plugin_client_VST2.cpp; path = ../../JuceLibraryCode/include_juce_audio_plugin_client_VST2.cpp; sourceTree = SOURCE_ROOT; };
|
||||||
69DB3A0FB3D21F87D1E4B0C1 /* PresetsToBanks.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = PresetsToBanks.h; path = ../../Source/PresetsToBanks.h; sourceTree = SOURCE_ROOT; };
|
|
||||||
6A7F287E4159FA5167131D2B /* juce_audio_processors */ = {isa = PBXFileReference; lastKnownFileType = folder; name = juce_audio_processors; path = /Applications/JUCE/modules/juce_audio_processors; sourceTree = "<absolute>"; };
|
6A7F287E4159FA5167131D2B /* juce_audio_processors */ = {isa = PBXFileReference; lastKnownFileType = folder; name = juce_audio_processors; path = /Applications/JUCE/modules/juce_audio_processors; sourceTree = "<absolute>"; };
|
||||||
6C5DCE19B6DC0EF5BA12F99C /* juicysfplugin.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = juicysfplugin.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
6C5DCE19B6DC0EF5BA12F99C /* juicysfplugin.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = juicysfplugin.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
6D94DCB335360BDC7B3673BF /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; };
|
6D94DCB335360BDC7B3673BF /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; };
|
||||||
|
@ -325,13 +320,10 @@
|
||||||
6FA795817D2F3B3119FDD754 /* juce_core */ = {isa = PBXFileReference; lastKnownFileType = folder; name = juce_core; path = /Applications/JUCE/modules/juce_core; sourceTree = "<absolute>"; };
|
6FA795817D2F3B3119FDD754 /* juce_core */ = {isa = PBXFileReference; lastKnownFileType = folder; name = juce_core; path = /Applications/JUCE/modules/juce_core; sourceTree = "<absolute>"; };
|
||||||
6FEF19AE08ED1DC1E3D9DF43 /* AppConfig.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = AppConfig.h; path = ../../JuceLibraryCode/AppConfig.h; sourceTree = SOURCE_ROOT; };
|
6FEF19AE08ED1DC1E3D9DF43 /* AppConfig.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = AppConfig.h; path = ../../JuceLibraryCode/AppConfig.h; sourceTree = SOURCE_ROOT; };
|
||||||
706FF998202761F30811FA6B /* juce_audio_devices */ = {isa = PBXFileReference; lastKnownFileType = folder; name = juce_audio_devices; path = /Applications/JUCE/modules/juce_audio_devices; sourceTree = "<absolute>"; };
|
706FF998202761F30811FA6B /* juce_audio_devices */ = {isa = PBXFileReference; lastKnownFileType = folder; name = juce_audio_devices; path = /Applications/JUCE/modules/juce_audio_devices; sourceTree = "<absolute>"; };
|
||||||
76724E30D8976FC4C2EE56FF /* SoundfontSynthSound.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SoundfontSynthSound.h; path = ../../Source/SoundfontSynthSound.h; sourceTree = SOURCE_ROOT; };
|
|
||||||
78CC5234CCFE3B170585DDAD /* RecentFilesMenuTemplate.nib */ = {isa = PBXFileReference; lastKnownFileType = file.nib; path = RecentFilesMenuTemplate.nib; sourceTree = SOURCE_ROOT; };
|
78CC5234CCFE3B170585DDAD /* RecentFilesMenuTemplate.nib */ = {isa = PBXFileReference; lastKnownFileType = file.nib; path = RecentFilesMenuTemplate.nib; sourceTree = SOURCE_ROOT; };
|
||||||
7C699A8B65F3F9FB5004F22D /* juce_gui_extra */ = {isa = PBXFileReference; lastKnownFileType = folder; name = juce_gui_extra; path = /Applications/JUCE/modules/juce_gui_extra; sourceTree = "<absolute>"; };
|
7C699A8B65F3F9FB5004F22D /* juce_gui_extra */ = {isa = PBXFileReference; lastKnownFileType = folder; name = juce_gui_extra; path = /Applications/JUCE/modules/juce_gui_extra; sourceTree = "<absolute>"; };
|
||||||
7D2457AD994644752178FC82 /* include_juce_audio_plugin_client_VST_utils.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = include_juce_audio_plugin_client_VST_utils.mm; path = ../../JuceLibraryCode/include_juce_audio_plugin_client_VST_utils.mm; sourceTree = SOURCE_ROOT; };
|
7D2457AD994644752178FC82 /* include_juce_audio_plugin_client_VST_utils.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = include_juce_audio_plugin_client_VST_utils.mm; path = ../../JuceLibraryCode/include_juce_audio_plugin_client_VST_utils.mm; sourceTree = SOURCE_ROOT; };
|
||||||
7D7B71BE20CA213D2FCD7FEE /* SoundfontSynthSound.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = SoundfontSynthSound.cpp; path = ../../Source/SoundfontSynthSound.cpp; sourceTree = SOURCE_ROOT; };
|
|
||||||
7E47C0A828016F7D0D63C0D6 /* include_juce_audio_plugin_client_AU_2.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = include_juce_audio_plugin_client_AU_2.mm; path = ../../JuceLibraryCode/include_juce_audio_plugin_client_AU_2.mm; sourceTree = SOURCE_ROOT; };
|
7E47C0A828016F7D0D63C0D6 /* include_juce_audio_plugin_client_AU_2.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = include_juce_audio_plugin_client_AU_2.mm; path = ../../JuceLibraryCode/include_juce_audio_plugin_client_AU_2.mm; sourceTree = SOURCE_ROOT; };
|
||||||
88ADEBF51BD04FEA9422D276 /* FilePickerFragment.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = FilePickerFragment.h; path = ../../Source/FilePickerFragment.h; sourceTree = SOURCE_ROOT; };
|
|
||||||
8990F3EAFFBBD6A42247C663 /* PluginEditor.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = PluginEditor.h; path = ../../Source/PluginEditor.h; sourceTree = SOURCE_ROOT; };
|
8990F3EAFFBBD6A42247C663 /* PluginEditor.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = PluginEditor.h; path = ../../Source/PluginEditor.h; sourceTree = SOURCE_ROOT; };
|
||||||
910F2E433646EE260D61A91B /* AudioUnit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioUnit.framework; path = System/Library/Frameworks/AudioUnit.framework; sourceTree = SDKROOT; };
|
910F2E433646EE260D61A91B /* AudioUnit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioUnit.framework; path = System/Library/Frameworks/AudioUnit.framework; sourceTree = SDKROOT; };
|
||||||
91B7A726C6FDDEE3F364ED99 /* juce_events */ = {isa = PBXFileReference; lastKnownFileType = folder; name = juce_events; path = /Applications/JUCE/modules/juce_events; sourceTree = "<absolute>"; };
|
91B7A726C6FDDEE3F364ED99 /* juce_events */ = {isa = PBXFileReference; lastKnownFileType = folder; name = juce_events; path = /Applications/JUCE/modules/juce_events; sourceTree = "<absolute>"; };
|
||||||
|
@ -340,10 +332,6 @@
|
||||||
A67D09546C4E4831438F7DBD /* CoreAudioKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreAudioKit.framework; path = System/Library/Frameworks/CoreAudioKit.framework; sourceTree = SDKROOT; };
|
A67D09546C4E4831438F7DBD /* CoreAudioKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreAudioKit.framework; path = System/Library/Frameworks/CoreAudioKit.framework; sourceTree = SDKROOT; };
|
||||||
A6BC2528C1717DDC2B66215E /* include_juce_graphics.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = include_juce_graphics.mm; path = ../../JuceLibraryCode/include_juce_graphics.mm; sourceTree = SOURCE_ROOT; };
|
A6BC2528C1717DDC2B66215E /* include_juce_graphics.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = include_juce_graphics.mm; path = ../../JuceLibraryCode/include_juce_graphics.mm; sourceTree = SOURCE_ROOT; };
|
||||||
ADC93C26314F163B963461E2 /* include_juce_audio_utils.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = include_juce_audio_utils.mm; path = ../../JuceLibraryCode/include_juce_audio_utils.mm; sourceTree = SOURCE_ROOT; };
|
ADC93C26314F163B963461E2 /* include_juce_audio_utils.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = include_juce_audio_utils.mm; path = ../../JuceLibraryCode/include_juce_audio_utils.mm; sourceTree = SOURCE_ROOT; };
|
||||||
AE397302E7E3F3A14A0C5F3C /* Preset.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = Preset.h; path = ../../Source/Preset.h; sourceTree = SOURCE_ROOT; };
|
|
||||||
B000E7A360C0C86ADD3C911D /* BankAndPreset.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = BankAndPreset.h; path = ../../Source/BankAndPreset.h; sourceTree = SOURCE_ROOT; };
|
|
||||||
B40B7F24646CBA708718DE82 /* SoundfontSynthVoice.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SoundfontSynthVoice.h; path = ../../Source/SoundfontSynthVoice.h; sourceTree = SOURCE_ROOT; };
|
|
||||||
B5A057FEC371053E83A73E47 /* SoundfontSynthVoice.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = SoundfontSynthVoice.cpp; path = ../../Source/SoundfontSynthVoice.cpp; sourceTree = SOURCE_ROOT; };
|
|
||||||
B6D37AD919F9E83688578941 /* SurjectiveMidiKeyboardComponent.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SurjectiveMidiKeyboardComponent.h; path = ../../Source/SurjectiveMidiKeyboardComponent.h; sourceTree = SOURCE_ROOT; };
|
B6D37AD919F9E83688578941 /* SurjectiveMidiKeyboardComponent.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SurjectiveMidiKeyboardComponent.h; path = ../../Source/SurjectiveMidiKeyboardComponent.h; sourceTree = SOURCE_ROOT; };
|
||||||
BFB39134DE6876F9005CFA61 /* Pills.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = Pills.h; path = ../../Source/Pills.h; sourceTree = SOURCE_ROOT; };
|
BFB39134DE6876F9005CFA61 /* Pills.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = Pills.h; path = ../../Source/Pills.h; sourceTree = SOURCE_ROOT; };
|
||||||
BFF57868318157F12F087F07 /* Info-AU.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Info-AU.plist"; sourceTree = SOURCE_ROOT; };
|
BFF57868318157F12F087F07 /* Info-AU.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Info-AU.plist"; sourceTree = SOURCE_ROOT; };
|
||||||
|
@ -356,10 +344,8 @@
|
||||||
D11295BAED9825695A4DEAB8 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; };
|
D11295BAED9825695A4DEAB8 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; };
|
||||||
D53CAB963D5051C786D3A52D /* PluginProcessor.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = PluginProcessor.cpp; path = ../../Source/PluginProcessor.cpp; sourceTree = SOURCE_ROOT; };
|
D53CAB963D5051C786D3A52D /* PluginProcessor.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = PluginProcessor.cpp; path = ../../Source/PluginProcessor.cpp; sourceTree = SOURCE_ROOT; };
|
||||||
DA25104C95E55D1822BFFBE2 /* FluidSynthModel.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = FluidSynthModel.cpp; path = ../../Source/FluidSynthModel.cpp; sourceTree = SOURCE_ROOT; };
|
DA25104C95E55D1822BFFBE2 /* FluidSynthModel.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = FluidSynthModel.cpp; path = ../../Source/FluidSynthModel.cpp; sourceTree = SOURCE_ROOT; };
|
||||||
DECFA95359BC1DDDD1CC86C3 /* BankAndPreset.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = BankAndPreset.cpp; path = ../../Source/BankAndPreset.cpp; sourceTree = SOURCE_ROOT; };
|
|
||||||
E4F84AFD6C449D10FDB5DB14 /* include_juce_audio_plugin_client_VST3.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = include_juce_audio_plugin_client_VST3.cpp; path = ../../JuceLibraryCode/include_juce_audio_plugin_client_VST3.cpp; sourceTree = SOURCE_ROOT; };
|
E4F84AFD6C449D10FDB5DB14 /* include_juce_audio_plugin_client_VST3.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = include_juce_audio_plugin_client_VST3.cpp; path = ../../JuceLibraryCode/include_juce_audio_plugin_client_VST3.cpp; sourceTree = SOURCE_ROOT; };
|
||||||
E89ECA468FF133B4677F8327 /* juicysfplugin.vst */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = juicysfplugin.vst; sourceTree = BUILT_PRODUCTS_DIR; };
|
E89ECA468FF133B4677F8327 /* juicysfplugin.vst */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = juicysfplugin.vst; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
F1EB35E262DC717222E2F93D /* ExposesComponents.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = ExposesComponents.h; path = ../../Source/ExposesComponents.h; sourceTree = SOURCE_ROOT; };
|
|
||||||
F5276945E14F83CA02C05B41 /* include_juce_gui_basics.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = include_juce_gui_basics.mm; path = ../../JuceLibraryCode/include_juce_gui_basics.mm; sourceTree = SOURCE_ROOT; };
|
F5276945E14F83CA02C05B41 /* include_juce_gui_basics.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = include_juce_gui_basics.mm; path = ../../JuceLibraryCode/include_juce_gui_basics.mm; sourceTree = SOURCE_ROOT; };
|
||||||
F69B741A63932433977CFCD8 /* include_juce_core.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = include_juce_core.mm; path = ../../JuceLibraryCode/include_juce_core.mm; sourceTree = SOURCE_ROOT; };
|
F69B741A63932433977CFCD8 /* include_juce_core.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = include_juce_core.mm; path = ../../JuceLibraryCode/include_juce_core.mm; sourceTree = SOURCE_ROOT; };
|
||||||
FE960C7D2CFA204401860C13 /* juce_audio_formats */ = {isa = PBXFileReference; lastKnownFileType = folder; name = juce_audio_formats; path = /Applications/JUCE/modules/juce_audio_formats; sourceTree = "<absolute>"; };
|
FE960C7D2CFA204401860C13 /* juce_audio_formats */ = {isa = PBXFileReference; lastKnownFileType = folder; name = juce_audio_formats; path = /Applications/JUCE/modules/juce_audio_formats; sourceTree = "<absolute>"; };
|
||||||
|
@ -521,10 +507,6 @@
|
||||||
403EB0CF49CF1D62BF359002 /* Source */ = {
|
403EB0CF49CF1D62BF359002 /* Source */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
DECFA95359BC1DDDD1CC86C3 /* BankAndPreset.cpp */,
|
|
||||||
B000E7A360C0C86ADD3C911D /* BankAndPreset.h */,
|
|
||||||
F1EB35E262DC717222E2F93D /* ExposesComponents.h */,
|
|
||||||
88ADEBF51BD04FEA9422D276 /* FilePickerFragment.h */,
|
|
||||||
C13A2FEAA636713EC7A905AF /* FilePicker.cpp */,
|
C13A2FEAA636713EC7A905AF /* FilePicker.cpp */,
|
||||||
21828DE4341668D7E383F10A /* FilePicker.h */,
|
21828DE4341668D7E383F10A /* FilePicker.h */,
|
||||||
DA25104C95E55D1822BFFBE2 /* FluidSynthModel.cpp */,
|
DA25104C95E55D1822BFFBE2 /* FluidSynthModel.cpp */,
|
||||||
|
@ -533,13 +515,6 @@
|
||||||
576A01FC6A3620A39BD1BDEE /* MyColours.h */,
|
576A01FC6A3620A39BD1BDEE /* MyColours.h */,
|
||||||
2C62C3F0621604CDB65B55A6 /* Pills.cpp */,
|
2C62C3F0621604CDB65B55A6 /* Pills.cpp */,
|
||||||
BFB39134DE6876F9005CFA61 /* Pills.h */,
|
BFB39134DE6876F9005CFA61 /* Pills.h */,
|
||||||
59F9FEC807012C10B8A1FA07 /* Preset.cpp */,
|
|
||||||
AE397302E7E3F3A14A0C5F3C /* Preset.h */,
|
|
||||||
69DB3A0FB3D21F87D1E4B0C1 /* PresetsToBanks.h */,
|
|
||||||
7D7B71BE20CA213D2FCD7FEE /* SoundfontSynthSound.cpp */,
|
|
||||||
76724E30D8976FC4C2EE56FF /* SoundfontSynthSound.h */,
|
|
||||||
B5A057FEC371053E83A73E47 /* SoundfontSynthVoice.cpp */,
|
|
||||||
B40B7F24646CBA708718DE82 /* SoundfontSynthVoice.h */,
|
|
||||||
4119A8200AC54674C00EFE66 /* SurjectiveMidiKeyboardComponent.cpp */,
|
4119A8200AC54674C00EFE66 /* SurjectiveMidiKeyboardComponent.cpp */,
|
||||||
B6D37AD919F9E83688578941 /* SurjectiveMidiKeyboardComponent.h */,
|
B6D37AD919F9E83688578941 /* SurjectiveMidiKeyboardComponent.h */,
|
||||||
CE8C41308A31A71A1177D0D5 /* TableComponent.cpp */,
|
CE8C41308A31A71A1177D0D5 /* TableComponent.cpp */,
|
||||||
|
@ -554,6 +529,7 @@
|
||||||
358E45F922C80DCA0087ED8D /* SlidersComponent.cpp */,
|
358E45F922C80DCA0087ED8D /* SlidersComponent.cpp */,
|
||||||
358E45FA22C80DCA0087ED8D /* SlidersComponent.h */,
|
358E45FA22C80DCA0087ED8D /* SlidersComponent.h */,
|
||||||
35099D9022CA8EF500CD4523 /* Util.h */,
|
35099D9022CA8EF500CD4523 /* Util.h */,
|
||||||
|
35099D9422CAB0A400CD4523 /* GuiConstants.h */,
|
||||||
);
|
);
|
||||||
name = Source;
|
name = Source;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -936,14 +912,10 @@
|
||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
9AF2F3DE22C71A7F465B2EAD /* BankAndPreset.cpp in Sources */,
|
|
||||||
598516649859A6D6BB2856EF /* FilePicker.cpp in Sources */,
|
598516649859A6D6BB2856EF /* FilePicker.cpp in Sources */,
|
||||||
85E6C3826F86B1258C407725 /* FluidSynthModel.cpp in Sources */,
|
85E6C3826F86B1258C407725 /* FluidSynthModel.cpp in Sources */,
|
||||||
BFD9EF2D67067FC1E5BA3546 /* MyColours.cpp in Sources */,
|
BFD9EF2D67067FC1E5BA3546 /* MyColours.cpp in Sources */,
|
||||||
8502F736BECFB9CB752AC72F /* Pills.cpp in Sources */,
|
8502F736BECFB9CB752AC72F /* Pills.cpp in Sources */,
|
||||||
DF84F5E7E386AF7A38854939 /* Preset.cpp in Sources */,
|
|
||||||
B66EBD76F6051D97D56C97AB /* SoundfontSynthSound.cpp in Sources */,
|
|
||||||
305606C42BB0F2A12D382D34 /* SoundfontSynthVoice.cpp in Sources */,
|
|
||||||
9C107CE4B586E4B097D9D04E /* SurjectiveMidiKeyboardComponent.cpp in Sources */,
|
9C107CE4B586E4B097D9D04E /* SurjectiveMidiKeyboardComponent.cpp in Sources */,
|
||||||
4AE057561AEA78489D9E50F0 /* TableComponent.cpp in Sources */,
|
4AE057561AEA78489D9E50F0 /* TableComponent.cpp in Sources */,
|
||||||
AC5E4EF988D864A298E3650D /* TablesComponent.cpp in Sources */,
|
AC5E4EF988D864A298E3650D /* TablesComponent.cpp in Sources */,
|
||||||
|
|
|
@ -0,0 +1,84 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Scheme
|
||||||
|
LastUpgradeVersion = "1020"
|
||||||
|
version = "1.3">
|
||||||
|
<BuildAction
|
||||||
|
parallelizeBuildables = "YES"
|
||||||
|
buildImplicitDependencies = "YES">
|
||||||
|
<BuildActionEntries>
|
||||||
|
<BuildActionEntry
|
||||||
|
buildForTesting = "YES"
|
||||||
|
buildForRunning = "YES"
|
||||||
|
buildForProfiling = "YES"
|
||||||
|
buildForArchiving = "YES"
|
||||||
|
buildForAnalyzing = "YES">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "345302E3C02BBFCDACE98BE7"
|
||||||
|
BuildableName = "juicysfplugin.component"
|
||||||
|
BlueprintName = "juicysfplugin - AU"
|
||||||
|
ReferencedContainer = "container:juicysfplugin.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildActionEntry>
|
||||||
|
</BuildActionEntries>
|
||||||
|
</BuildAction>
|
||||||
|
<TestAction
|
||||||
|
buildConfiguration = "Debug"
|
||||||
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
|
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||||
|
<Testables>
|
||||||
|
</Testables>
|
||||||
|
<AdditionalOptions>
|
||||||
|
</AdditionalOptions>
|
||||||
|
</TestAction>
|
||||||
|
<LaunchAction
|
||||||
|
buildConfiguration = "Debug"
|
||||||
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
|
launchStyle = "0"
|
||||||
|
useCustomWorkingDirectory = "NO"
|
||||||
|
ignoresPersistentStateOnLaunch = "NO"
|
||||||
|
debugDocumentVersioning = "YES"
|
||||||
|
debugServiceExtension = "internal"
|
||||||
|
allowLocationSimulation = "YES">
|
||||||
|
<PathRunnable
|
||||||
|
runnableDebuggingMode = "0"
|
||||||
|
FilePath = "/Applications/JUCE/extras/AudioPluginHost/Builds/MacOSX/build/Debug/AudioPluginHost.app">
|
||||||
|
</PathRunnable>
|
||||||
|
<MacroExpansion>
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "345302E3C02BBFCDACE98BE7"
|
||||||
|
BuildableName = "juicysfplugin.component"
|
||||||
|
BlueprintName = "juicysfplugin - AU"
|
||||||
|
ReferencedContainer = "container:juicysfplugin.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</MacroExpansion>
|
||||||
|
<AdditionalOptions>
|
||||||
|
</AdditionalOptions>
|
||||||
|
</LaunchAction>
|
||||||
|
<ProfileAction
|
||||||
|
buildConfiguration = "Release"
|
||||||
|
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||||
|
savedToolIdentifier = ""
|
||||||
|
useCustomWorkingDirectory = "NO"
|
||||||
|
debugDocumentVersioning = "YES">
|
||||||
|
<MacroExpansion>
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "345302E3C02BBFCDACE98BE7"
|
||||||
|
BuildableName = "juicysfplugin.component"
|
||||||
|
BlueprintName = "juicysfplugin - AU"
|
||||||
|
ReferencedContainer = "container:juicysfplugin.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</MacroExpansion>
|
||||||
|
</ProfileAction>
|
||||||
|
<AnalyzeAction
|
||||||
|
buildConfiguration = "Debug">
|
||||||
|
</AnalyzeAction>
|
||||||
|
<ArchiveAction
|
||||||
|
buildConfiguration = "Release"
|
||||||
|
revealArchiveInOrganizer = "YES">
|
||||||
|
</ArchiveAction>
|
||||||
|
</Scheme>
|
|
@ -33,7 +33,7 @@
|
||||||
</AdditionalOptions>
|
</AdditionalOptions>
|
||||||
</TestAction>
|
</TestAction>
|
||||||
<LaunchAction
|
<LaunchAction
|
||||||
buildConfiguration = "Release"
|
buildConfiguration = "Debug"
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
launchStyle = "0"
|
launchStyle = "0"
|
||||||
|
|
|
@ -51,8 +51,11 @@
|
||||||
debugDocumentVersioning = "YES"
|
debugDocumentVersioning = "YES"
|
||||||
debugServiceExtension = "internal"
|
debugServiceExtension = "internal"
|
||||||
allowLocationSimulation = "YES">
|
allowLocationSimulation = "YES">
|
||||||
<BuildableProductRunnable
|
<PathRunnable
|
||||||
runnableDebuggingMode = "0">
|
runnableDebuggingMode = "0"
|
||||||
|
FilePath = "/Applications/JUCE/extras/AudioPluginHost/Builds/MacOSX/build/Debug/AudioPluginHost.app">
|
||||||
|
</PathRunnable>
|
||||||
|
<MacroExpansion>
|
||||||
<BuildableReference
|
<BuildableReference
|
||||||
BuildableIdentifier = "primary"
|
BuildableIdentifier = "primary"
|
||||||
BlueprintIdentifier = "24C399ED93EC47D5BEB26F76"
|
BlueprintIdentifier = "24C399ED93EC47D5BEB26F76"
|
||||||
|
@ -60,7 +63,7 @@
|
||||||
BlueprintName = "juicysfplugin - Standalone Plugin"
|
BlueprintName = "juicysfplugin - Standalone Plugin"
|
||||||
ReferencedContainer = "container:juicysfplugin.xcodeproj">
|
ReferencedContainer = "container:juicysfplugin.xcodeproj">
|
||||||
</BuildableReference>
|
</BuildableReference>
|
||||||
</BuildableProductRunnable>
|
</MacroExpansion>
|
||||||
<AdditionalOptions>
|
<AdditionalOptions>
|
||||||
</AdditionalOptions>
|
</AdditionalOptions>
|
||||||
</LaunchAction>
|
</LaunchAction>
|
||||||
|
|
|
@ -0,0 +1,84 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Scheme
|
||||||
|
LastUpgradeVersion = "1020"
|
||||||
|
version = "1.3">
|
||||||
|
<BuildAction
|
||||||
|
parallelizeBuildables = "YES"
|
||||||
|
buildImplicitDependencies = "YES">
|
||||||
|
<BuildActionEntries>
|
||||||
|
<BuildActionEntry
|
||||||
|
buildForTesting = "YES"
|
||||||
|
buildForRunning = "YES"
|
||||||
|
buildForProfiling = "YES"
|
||||||
|
buildForArchiving = "YES"
|
||||||
|
buildForAnalyzing = "YES">
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "48A570326AA09CE818BE1901"
|
||||||
|
BuildableName = "juicysfplugin.vst"
|
||||||
|
BlueprintName = "juicysfplugin - VST"
|
||||||
|
ReferencedContainer = "container:juicysfplugin.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</BuildActionEntry>
|
||||||
|
</BuildActionEntries>
|
||||||
|
</BuildAction>
|
||||||
|
<TestAction
|
||||||
|
buildConfiguration = "Debug"
|
||||||
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
|
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||||
|
<Testables>
|
||||||
|
</Testables>
|
||||||
|
<AdditionalOptions>
|
||||||
|
</AdditionalOptions>
|
||||||
|
</TestAction>
|
||||||
|
<LaunchAction
|
||||||
|
buildConfiguration = "Debug"
|
||||||
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
|
launchStyle = "0"
|
||||||
|
useCustomWorkingDirectory = "NO"
|
||||||
|
ignoresPersistentStateOnLaunch = "NO"
|
||||||
|
debugDocumentVersioning = "YES"
|
||||||
|
debugServiceExtension = "internal"
|
||||||
|
allowLocationSimulation = "YES">
|
||||||
|
<PathRunnable
|
||||||
|
runnableDebuggingMode = "0"
|
||||||
|
FilePath = "/Applications/JUCE/extras/AudioPluginHost/Builds/MacOSX/build/Debug/AudioPluginHost.app">
|
||||||
|
</PathRunnable>
|
||||||
|
<MacroExpansion>
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "48A570326AA09CE818BE1901"
|
||||||
|
BuildableName = "juicysfplugin.vst"
|
||||||
|
BlueprintName = "juicysfplugin - VST"
|
||||||
|
ReferencedContainer = "container:juicysfplugin.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</MacroExpansion>
|
||||||
|
<AdditionalOptions>
|
||||||
|
</AdditionalOptions>
|
||||||
|
</LaunchAction>
|
||||||
|
<ProfileAction
|
||||||
|
buildConfiguration = "Release"
|
||||||
|
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||||
|
savedToolIdentifier = ""
|
||||||
|
useCustomWorkingDirectory = "NO"
|
||||||
|
debugDocumentVersioning = "YES">
|
||||||
|
<MacroExpansion>
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "48A570326AA09CE818BE1901"
|
||||||
|
BuildableName = "juicysfplugin.vst"
|
||||||
|
BlueprintName = "juicysfplugin - VST"
|
||||||
|
ReferencedContainer = "container:juicysfplugin.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</MacroExpansion>
|
||||||
|
</ProfileAction>
|
||||||
|
<AnalyzeAction
|
||||||
|
buildConfiguration = "Debug">
|
||||||
|
</AnalyzeAction>
|
||||||
|
<ArchiveAction
|
||||||
|
buildConfiguration = "Release"
|
||||||
|
revealArchiveInOrganizer = "YES">
|
||||||
|
</ArchiveAction>
|
||||||
|
</Scheme>
|
|
@ -42,6 +42,11 @@
|
||||||
debugDocumentVersioning = "YES"
|
debugDocumentVersioning = "YES"
|
||||||
debugServiceExtension = "internal"
|
debugServiceExtension = "internal"
|
||||||
allowLocationSimulation = "YES">
|
allowLocationSimulation = "YES">
|
||||||
|
<PathRunnable
|
||||||
|
runnableDebuggingMode = "0"
|
||||||
|
BundleIdentifier = "com.roli.juce.pluginhost"
|
||||||
|
FilePath = "/Applications/JUCE/extras/AudioPluginHost/Builds/MacOSX/build/Debug/AudioPluginHost.app">
|
||||||
|
</PathRunnable>
|
||||||
<MacroExpansion>
|
<MacroExpansion>
|
||||||
<BuildableReference
|
<BuildableReference
|
||||||
BuildableIdentifier = "primary"
|
BuildableIdentifier = "primary"
|
||||||
|
|
|
@ -1,19 +0,0 @@
|
||||||
//
|
|
||||||
// Created by Alex Birch on 13/04/2018.
|
|
||||||
// Copyright (c) 2018 Birchlabs. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include "BankAndPreset.h"
|
|
||||||
|
|
||||||
BankAndPreset::BankAndPreset(int bank, int preset)
|
|
||||||
: bank(bank),
|
|
||||||
preset(preset)
|
|
||||||
{}
|
|
||||||
|
|
||||||
int BankAndPreset::getBank() {
|
|
||||||
return bank;
|
|
||||||
}
|
|
||||||
|
|
||||||
int BankAndPreset::getPreset() {
|
|
||||||
return preset;
|
|
||||||
}
|
|
|
@ -1,23 +0,0 @@
|
||||||
//
|
|
||||||
// Created by Alex Birch on 13/04/2018.
|
|
||||||
// Copyright (c) 2018 Birchlabs. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "../JuceLibraryCode/JuceHeader.h"
|
|
||||||
|
|
||||||
class BankAndPreset {
|
|
||||||
public:
|
|
||||||
BankAndPreset(int bank, int preset);
|
|
||||||
|
|
||||||
int getBank();
|
|
||||||
int getPreset();
|
|
||||||
|
|
||||||
private:
|
|
||||||
int bank;
|
|
||||||
int preset;
|
|
||||||
|
|
||||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BankAndPreset)
|
|
||||||
};
|
|
||||||
|
|
|
@ -1,16 +0,0 @@
|
||||||
//
|
|
||||||
// Created by Alex Birch on 11/04/2018.
|
|
||||||
// Copyright (c) 2018 Birchlabs. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "FilePickerFragment.h"
|
|
||||||
|
|
||||||
class ExposesComponents {
|
|
||||||
public:
|
|
||||||
virtual ~ExposesComponents() {}
|
|
||||||
|
|
||||||
virtual FilePickerFragment& getFilePicker() = 0;
|
|
||||||
|
|
||||||
};
|
|
|
@ -4,32 +4,39 @@
|
||||||
|
|
||||||
#include "FilePicker.h"
|
#include "FilePicker.h"
|
||||||
#include "MyColours.h"
|
#include "MyColours.h"
|
||||||
|
#include "Util.h"
|
||||||
|
|
||||||
FilePicker::FilePicker(
|
FilePicker::FilePicker(
|
||||||
FluidSynthModel* fluidSynthModel
|
AudioProcessorValueTreeState& valueTreeState
|
||||||
|
// FluidSynthModel& fluidSynthModel
|
||||||
)
|
)
|
||||||
: fileChooser(
|
: fileChooser{
|
||||||
"File",
|
"File",
|
||||||
File(),
|
File(),
|
||||||
true,
|
true,
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
"*.sf2;*.sf3",
|
"*.sf2;*.sf3",
|
||||||
String(),
|
String(),
|
||||||
"Choose a Soundfont file to load into the synthesizer"
|
"Choose a Soundfont file to load into the synthesizer"}
|
||||||
),
|
, valueTreeState{valueTreeState}
|
||||||
fluidSynthModel(fluidSynthModel),
|
// , fluidSynthModel{fluidSynthModel}
|
||||||
currentPath() {
|
// , currentPath{}
|
||||||
|
{
|
||||||
// faster (rounded edges introduce transparency)
|
// faster (rounded edges introduce transparency)
|
||||||
setOpaque (true);
|
setOpaque (true);
|
||||||
|
|
||||||
setDisplayedFilePath(fluidSynthModel->getCurrentSoundFontAbsPath());
|
// setDisplayedFilePath(fluidSynthModel.getCurrentSoundFontAbsPath());
|
||||||
|
setDisplayedFilePath(valueTreeState.state.getChildWithName("soundFont").getProperty("path", ""));
|
||||||
|
|
||||||
addAndMakeVisible (fileChooser);
|
addAndMakeVisible (fileChooser);
|
||||||
fileChooser.addListener (this);
|
fileChooser.addListener (this);
|
||||||
|
valueTreeState.state.addListener(this);
|
||||||
|
// valueTreeState.state.getChildWithName("soundFont").sendPropertyChangeMessage("path");
|
||||||
}
|
}
|
||||||
FilePicker::~FilePicker() {
|
FilePicker::~FilePicker() {
|
||||||
fileChooser.removeListener (this);
|
fileChooser.removeListener (this);
|
||||||
|
valueTreeState.state.removeListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FilePicker::resized() {
|
void FilePicker::resized() {
|
||||||
|
@ -46,15 +53,33 @@ void FilePicker::paint(Graphics& g)
|
||||||
}
|
}
|
||||||
|
|
||||||
void FilePicker::filenameComponentChanged (FilenameComponent*) {
|
void FilePicker::filenameComponentChanged (FilenameComponent*) {
|
||||||
currentPath = fileChooser.getCurrentFile().getFullPathName();
|
// currentPath = fileChooser.getCurrentFile().getFullPathName();
|
||||||
fluidSynthModel->onFileNameChanged(fileChooser.getCurrentFile().getFullPathName(), -1, -1);
|
// fluidSynthModel.onFileNameChanged(fileChooser.getCurrentFile().getFullPathName(), -1, -1);
|
||||||
|
Value value{valueTreeState.state.getChildWithName("soundFont").getPropertyAsValue("path", nullptr)};
|
||||||
|
value.setValue(fileChooser.getCurrentFile().getFullPathName());
|
||||||
|
// value = fileChooser.getCurrentFile().getFullPathName();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FilePicker::valueTreePropertyChanged(ValueTree& treeWhosePropertyHasChanged,
|
||||||
|
const Identifier& property) {
|
||||||
|
if (treeWhosePropertyHasChanged.getType() == StringRef("soundFont")) {
|
||||||
|
// if (&treeWhosePropertyHasChanged == &valueTree) {
|
||||||
|
if (property == StringRef("path")) {
|
||||||
|
String soundFontPath{treeWhosePropertyHasChanged.getProperty("path", "")};
|
||||||
|
DEBUG_PRINT(soundFontPath);
|
||||||
|
setDisplayedFilePath(soundFontPath);
|
||||||
|
// if (soundFontPath.isNotEmpty()) {
|
||||||
|
// loadFont(soundFontPath);
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FilePicker::setDisplayedFilePath(const String& path) {
|
void FilePicker::setDisplayedFilePath(const String& path) {
|
||||||
if (!shouldChangeDisplayedFilePath(path)) {
|
if (!shouldChangeDisplayedFilePath(path)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
currentPath = path;
|
// currentPath = path;
|
||||||
fileChooser.setCurrentFile(File(path), true, dontSendNotification);
|
fileChooser.setCurrentFile(File(path), true, dontSendNotification);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,4 +91,4 @@ bool FilePicker::shouldChangeDisplayedFilePath(const String &path) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,26 +6,40 @@
|
||||||
|
|
||||||
#include "../JuceLibraryCode/JuceHeader.h"
|
#include "../JuceLibraryCode/JuceHeader.h"
|
||||||
#include "FluidSynthModel.h"
|
#include "FluidSynthModel.h"
|
||||||
#include "FilePickerFragment.h"
|
|
||||||
|
|
||||||
class FilePicker: public Component,
|
class FilePicker: public Component,
|
||||||
public FilePickerFragment,
|
public ValueTree::Listener,
|
||||||
private FilenameComponentListener
|
private FilenameComponentListener
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
FilePicker(
|
FilePicker(
|
||||||
FluidSynthModel* fluidSynthModel
|
AudioProcessorValueTreeState& valueTreeState
|
||||||
|
// FluidSynthModel& fluidSynthModel
|
||||||
);
|
);
|
||||||
~FilePicker();
|
~FilePicker();
|
||||||
|
|
||||||
void resized() override;
|
void resized() override;
|
||||||
void paint (Graphics& g) override;
|
void paint (Graphics& g) override;
|
||||||
|
|
||||||
virtual void setDisplayedFilePath(const String&) override;
|
void setDisplayedFilePath(const String&);
|
||||||
|
|
||||||
|
|
||||||
|
virtual void valueTreePropertyChanged (ValueTree& treeWhosePropertyHasChanged,
|
||||||
|
const Identifier& property) override;
|
||||||
|
inline virtual void valueTreeChildAdded (ValueTree& parentTree,
|
||||||
|
ValueTree& childWhichHasBeenAdded) override {};
|
||||||
|
inline virtual void valueTreeChildRemoved (ValueTree& parentTree,
|
||||||
|
ValueTree& childWhichHasBeenRemoved,
|
||||||
|
int indexFromWhichChildWasRemoved) override {};
|
||||||
|
inline virtual void valueTreeChildOrderChanged (ValueTree& parentTreeWhoseChildrenHaveMoved,
|
||||||
|
int oldIndex, int newIndex) override {};
|
||||||
|
inline virtual void valueTreeParentChanged (ValueTree& treeWhoseParentHasChanged) override {};
|
||||||
|
inline virtual void valueTreeRedirected (ValueTree& treeWhichHasBeenChanged) override {};
|
||||||
private:
|
private:
|
||||||
FilenameComponent fileChooser;
|
FilenameComponent fileChooser;
|
||||||
|
|
||||||
FluidSynthModel* fluidSynthModel;
|
AudioProcessorValueTreeState& valueTreeState;
|
||||||
|
// FluidSynthModel& fluidSynthModel;
|
||||||
|
|
||||||
String currentPath;
|
String currentPath;
|
||||||
|
|
||||||
|
@ -34,4 +48,4 @@ private:
|
||||||
bool shouldChangeDisplayedFilePath(const String &path);
|
bool shouldChangeDisplayedFilePath(const String &path);
|
||||||
|
|
||||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FilePicker)
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FilePicker)
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,14 +0,0 @@
|
||||||
//
|
|
||||||
// Created by Alex Birch on 11/04/2018.
|
|
||||||
// Copyright (c) 2018 Birchlabs. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include "../JuceLibraryCode/JuceHeader.h"
|
|
||||||
|
|
||||||
class FilePickerFragment {
|
|
||||||
public:
|
|
||||||
virtual ~FilePickerFragment() {}
|
|
||||||
|
|
||||||
virtual void setDisplayedFilePath(const String&) = 0;
|
|
||||||
};
|
|
|
@ -3,329 +3,468 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <iterator>
|
||||||
|
#include <fluidsynth.h>
|
||||||
#include "FluidSynthModel.h"
|
#include "FluidSynthModel.h"
|
||||||
#include "MidiConstants.h"
|
#include "MidiConstants.h"
|
||||||
|
#include "Util.h"
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
FluidSynthModel::FluidSynthModel(SharesParams& p)
|
const map<fluid_midi_control_change, String> FluidSynthModel::controllerToParam{
|
||||||
: sharesParams(p),
|
{SOUND_CTRL2, "filterResonance"}, // MIDI CC 71 Timbre/Harmonic Intensity (filter resonance)
|
||||||
synth(nullptr),
|
{SOUND_CTRL3, "release"}, // MIDI CC 72 Release time
|
||||||
settings(nullptr),
|
{SOUND_CTRL4, "attack"}, // MIDI CC 73 Attack time
|
||||||
currentSoundFontAbsPath(),
|
{SOUND_CTRL5, "filterCutOff"}, // MIDI CC 74 Brightness (cutoff frequency, FILTERFC)
|
||||||
currentSampleRate(44100),
|
{SOUND_CTRL6, "decay"}, // MIDI CC 75 Decay Time
|
||||||
initialised(false),
|
{SOUND_CTRL10, "sustain"}}; // MIDI CC 79 undefined
|
||||||
sfont_id(0),
|
|
||||||
channel(0)/*,
|
|
||||||
mod(nullptr)*/
|
|
||||||
|
|
||||||
{}
|
const map<String, fluid_midi_control_change> FluidSynthModel::paramToController{[]{
|
||||||
|
map<String, fluid_midi_control_change> map;
|
||||||
|
transform(
|
||||||
|
controllerToParam.begin(),
|
||||||
|
controllerToParam.end(),
|
||||||
|
inserter(map, map.begin()),
|
||||||
|
[](const pair<fluid_midi_control_change, String>& pair) {
|
||||||
|
return make_pair(pair.second, pair.first);
|
||||||
|
});
|
||||||
|
return map;
|
||||||
|
}()};
|
||||||
|
|
||||||
|
FluidSynthModel::FluidSynthModel(
|
||||||
|
AudioProcessorValueTreeState& valueTreeState
|
||||||
|
)
|
||||||
|
: valueTreeState{valueTreeState}
|
||||||
|
, settings{nullptr, nullptr}
|
||||||
|
, synth{nullptr, nullptr}
|
||||||
|
, currentSampleRate{44100}
|
||||||
|
, sfont_id{-1}
|
||||||
|
, channel{0}
|
||||||
|
{
|
||||||
|
valueTreeState.addParameterListener("bank", this);
|
||||||
|
valueTreeState.addParameterListener("preset", this);
|
||||||
|
for (const auto &[param, controller]: paramToController) {
|
||||||
|
valueTreeState.addParameterListener(param, this);
|
||||||
|
}
|
||||||
|
valueTreeState.state.addListener(this);
|
||||||
|
}
|
||||||
|
|
||||||
FluidSynthModel::~FluidSynthModel() {
|
FluidSynthModel::~FluidSynthModel() {
|
||||||
if (initialised) {
|
for (const auto &[param, controller]: paramToController) {
|
||||||
// delete_fluid_audio_driver(driver);
|
valueTreeState.removeParameterListener(param, this);
|
||||||
delete_fluid_synth(synth);
|
|
||||||
delete_fluid_settings(settings);
|
|
||||||
// delete driver;
|
|
||||||
// delete settings;
|
|
||||||
// delete_fluid_mod(mod);
|
|
||||||
}
|
}
|
||||||
|
valueTreeState.removeParameterListener("bank", this);
|
||||||
|
valueTreeState.removeParameterListener("preset", this);
|
||||||
|
valueTreeState.state.removeListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FluidSynthModel::initialise() {
|
void FluidSynthModel::initialise() {
|
||||||
// if (initialised) {
|
settings = { new_fluid_settings(), delete_fluid_settings };
|
||||||
// delete_fluid_synth(synth);
|
|
||||||
// delete_fluid_settings(settings);
|
|
||||||
// }
|
|
||||||
// deactivate all audio drivers in fluidsynth to avoid FL Studio deadlock when initialising CoreAudio
|
// deactivate all audio drivers in fluidsynth to avoid FL Studio deadlock when initialising CoreAudio
|
||||||
// after all: we only use fluidsynth to render blocks of audio. it doesn't output to audio driver.
|
// after all: we only use fluidsynth to render blocks of audio. it doesn't output to audio driver.
|
||||||
const char *DRV[] = {NULL};
|
const char *DRV[] {NULL};
|
||||||
fluid_audio_driver_register(DRV);
|
fluid_audio_driver_register(DRV);
|
||||||
|
|
||||||
settings = new_fluid_settings();
|
|
||||||
// https://sourceforge.net/p/fluidsynth/wiki/FluidSettings/
|
// https://sourceforge.net/p/fluidsynth/wiki/FluidSettings/
|
||||||
#if JUCE_DEBUG
|
#if JUCE_DEBUG
|
||||||
fluid_settings_setint(settings, "synth.verbose", 1);
|
fluid_settings_setint(settings.get(), "synth.verbose", 1);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
synth = new_fluid_synth(settings);
|
synth = { new_fluid_synth(settings.get()), delete_fluid_synth };
|
||||||
fluid_synth_set_sample_rate(synth, currentSampleRate);
|
fluid_synth_set_sample_rate(synth.get(), currentSampleRate);
|
||||||
|
|
||||||
if (sharesParams.getSoundFontPath().isNotEmpty()) {
|
|
||||||
loadFont(sharesParams.getSoundFontPath());
|
|
||||||
changePreset(sharesParams.getBank(), sharesParams.getPreset());
|
|
||||||
}
|
|
||||||
|
|
||||||
fluid_synth_set_gain(synth, 2.0);
|
|
||||||
|
|
||||||
for(int i{SOUND_CTRL1}; i <= SOUND_CTRL10; i++)
|
ValueTree soundFont{valueTreeState.state.getChildWithName("soundFont")};
|
||||||
{
|
String path{soundFont.getProperty("path", "")};
|
||||||
setControllerValue(i, 0);
|
loadFont(path);
|
||||||
}
|
|
||||||
|
|
||||||
// fluid_synth_bank_select(synth, 0, 3);
|
// I can't hear a damned thing
|
||||||
|
fluid_synth_set_gain(synth.get(), 2.0);
|
||||||
// fluid_handle_inst
|
|
||||||
|
|
||||||
// driver = new_fluid_audio_driver(settings, synth);
|
|
||||||
|
|
||||||
// changePreset(128, 13);
|
|
||||||
|
|
||||||
// float env_amount(12000.0f);
|
|
||||||
|
|
||||||
// http://www.synthfont.com/SoundFont_NRPNs.PDF
|
|
||||||
float env_amount(20000.0f);
|
|
||||||
// float env_amount(24000.0f);
|
|
||||||
|
|
||||||
// note: fluid_chan.c#fluid_channel_init_ctrl()
|
// note: fluid_chan.c#fluid_channel_init_ctrl()
|
||||||
// all SOUND_CTRL are inited with value of 64, not zero.
|
// > Just like panning, a value of 64 indicates no change for sound ctrls
|
||||||
// "Just like panning, a value of 64 indicates no change for sound ctrls"
|
// --
|
||||||
|
// so, advice is to leave everything at 64
|
||||||
|
// and yet, I'm finding that default modulators start at MIN,
|
||||||
|
// i.e. we are forced to start at 0 and climb from there
|
||||||
|
// --
|
||||||
|
// let's loop through all audio params that we manage,
|
||||||
|
// restore them to whatever value we have stored for them
|
||||||
|
// (which by default would be 0)
|
||||||
|
// super likely to be 0 regardless, since JuicySFAudioProcessor::initialise()
|
||||||
|
// runs earlier than JuicySFAudioProcessor::setStateInformation()
|
||||||
|
for (const auto &[controller, parameterID]: controllerToParam) {
|
||||||
|
RangedAudioParameter *param{valueTreeState.getParameter(parameterID)};
|
||||||
|
jassert(dynamic_cast<AudioParameterInt*>(param) != nullptr);
|
||||||
|
AudioParameterInt* castParam{dynamic_cast<AudioParameterInt*>(param)};
|
||||||
|
int value{castParam->get()};
|
||||||
|
setControllerValue(static_cast<int>(controller), value);
|
||||||
|
}
|
||||||
|
|
||||||
fluid_mod_t *mod(new_fluid_mod());
|
// http://www.synthfont.com/SoundFont_NRPNs.PDF
|
||||||
//
|
float env_amount{20000.0f};
|
||||||
fluid_mod_set_source1(mod,
|
|
||||||
|
unique_ptr<fluid_mod_t, decltype(&delete_fluid_mod)> mod{new_fluid_mod(), delete_fluid_mod};
|
||||||
|
fluid_mod_set_source1(mod.get(),
|
||||||
static_cast<int>(SOUND_CTRL2), // MIDI CC 71 Timbre/Harmonic Intensity (filter resonance)
|
static_cast<int>(SOUND_CTRL2), // MIDI CC 71 Timbre/Harmonic Intensity (filter resonance)
|
||||||
FLUID_MOD_CC
|
FLUID_MOD_CC
|
||||||
| FLUID_MOD_UNIPOLAR
|
| FLUID_MOD_UNIPOLAR
|
||||||
| FLUID_MOD_CONCAVE
|
| FLUID_MOD_CONCAVE
|
||||||
| FLUID_MOD_POSITIVE);
|
| FLUID_MOD_POSITIVE);
|
||||||
fluid_mod_set_source2(mod, 0, 0);
|
fluid_mod_set_source2(mod.get(), 0, 0);
|
||||||
fluid_mod_set_dest(mod, GEN_FILTERQ);
|
fluid_mod_set_dest(mod.get(), GEN_FILTERQ);
|
||||||
fluid_mod_set_amount(mod, FLUID_PEAK_ATTENUATION);
|
fluid_mod_set_amount(mod.get(), FLUID_PEAK_ATTENUATION);
|
||||||
fluid_synth_add_default_mod(synth, mod, FLUID_SYNTH_ADD);
|
fluid_synth_add_default_mod(synth.get(), mod.get(), FLUID_SYNTH_ADD);
|
||||||
delete_fluid_mod(mod);
|
|
||||||
|
|
||||||
mod = new_fluid_mod();
|
mod = {new_fluid_mod(), delete_fluid_mod};
|
||||||
fluid_mod_set_source1(mod,
|
fluid_mod_set_source1(mod.get(),
|
||||||
static_cast<int>(SOUND_CTRL3), // MIDI CC 72 Release time
|
static_cast<int>(SOUND_CTRL3), // MIDI CC 72 Release time
|
||||||
FLUID_MOD_CC
|
FLUID_MOD_CC
|
||||||
| FLUID_MOD_UNIPOLAR
|
| FLUID_MOD_UNIPOLAR
|
||||||
| FLUID_MOD_LINEAR
|
| FLUID_MOD_LINEAR
|
||||||
| FLUID_MOD_POSITIVE);
|
| FLUID_MOD_POSITIVE);
|
||||||
fluid_mod_set_source2(mod, 0, 0);
|
fluid_mod_set_source2(mod.get(), 0, 0);
|
||||||
fluid_mod_set_dest(mod, GEN_VOLENVRELEASE);
|
fluid_mod_set_dest(mod.get(), GEN_VOLENVRELEASE);
|
||||||
// fluid_mod_set_amount(mod, 15200.0f);
|
fluid_mod_set_amount(mod.get(), env_amount);
|
||||||
fluid_mod_set_amount(mod, env_amount);
|
fluid_synth_add_default_mod(synth.get(), mod.get(), FLUID_SYNTH_ADD);
|
||||||
fluid_synth_add_default_mod(synth, mod, FLUID_SYNTH_ADD);
|
|
||||||
delete_fluid_mod(mod);
|
|
||||||
|
|
||||||
mod = new_fluid_mod();
|
mod = {new_fluid_mod(), delete_fluid_mod};
|
||||||
fluid_mod_set_source1(mod,
|
fluid_mod_set_source1(mod.get(),
|
||||||
static_cast<int>(SOUND_CTRL4), // MIDI CC 73 Attack time
|
static_cast<int>(SOUND_CTRL4), // MIDI CC 73 Attack time
|
||||||
FLUID_MOD_CC
|
FLUID_MOD_CC
|
||||||
| FLUID_MOD_UNIPOLAR
|
| FLUID_MOD_UNIPOLAR
|
||||||
| FLUID_MOD_LINEAR
|
| FLUID_MOD_LINEAR
|
||||||
| FLUID_MOD_POSITIVE);
|
| FLUID_MOD_POSITIVE);
|
||||||
fluid_mod_set_source2(mod, 0, 0);
|
fluid_mod_set_source2(mod.get(), 0, 0);
|
||||||
fluid_mod_set_dest(mod, GEN_VOLENVATTACK);
|
fluid_mod_set_dest(mod.get(), GEN_VOLENVATTACK);
|
||||||
fluid_mod_set_amount(mod, env_amount);
|
fluid_mod_set_amount(mod.get(), env_amount);
|
||||||
fluid_synth_add_default_mod(synth, mod, FLUID_SYNTH_ADD);
|
fluid_synth_add_default_mod(synth.get(), mod.get(), FLUID_SYNTH_ADD);
|
||||||
delete_fluid_mod(mod);
|
|
||||||
|
|
||||||
// soundfont spec says that if cutoff is >20kHz and resonance Q is 0, then no filtering occurs
|
// soundfont spec says that if cutoff is >20kHz and resonance Q is 0, then no filtering occurs
|
||||||
mod = new_fluid_mod();
|
mod = {new_fluid_mod(), delete_fluid_mod};
|
||||||
fluid_mod_set_source1(mod,
|
fluid_mod_set_source1(mod.get(),
|
||||||
static_cast<int>(SOUND_CTRL5), // MIDI CC 74 Brightness (cutoff frequency, FILTERFC)
|
static_cast<int>(SOUND_CTRL5), // MIDI CC 74 Brightness (cutoff frequency, FILTERFC)
|
||||||
FLUID_MOD_CC
|
FLUID_MOD_CC
|
||||||
| FLUID_MOD_LINEAR
|
| FLUID_MOD_LINEAR
|
||||||
| FLUID_MOD_UNIPOLAR
|
| FLUID_MOD_UNIPOLAR
|
||||||
| FLUID_MOD_POSITIVE);
|
| FLUID_MOD_POSITIVE);
|
||||||
fluid_mod_set_source2(mod, 0, 0);
|
fluid_mod_set_source2(mod.get(), 0, 0);
|
||||||
fluid_mod_set_dest(mod, GEN_FILTERFC);
|
fluid_mod_set_dest(mod.get(), GEN_FILTERFC);
|
||||||
fluid_mod_set_amount(mod, -2400.0f);
|
fluid_mod_set_amount(mod.get(), -2400.0f);
|
||||||
fluid_synth_add_default_mod(synth, mod, FLUID_SYNTH_ADD);
|
fluid_synth_add_default_mod(synth.get(), mod.get(), FLUID_SYNTH_ADD);
|
||||||
delete_fluid_mod(mod);
|
|
||||||
|
|
||||||
mod = new_fluid_mod();
|
mod = {new_fluid_mod(), delete_fluid_mod};
|
||||||
fluid_mod_set_source1(mod,
|
fluid_mod_set_source1(mod.get(),
|
||||||
static_cast<int>(SOUND_CTRL6), // MIDI CC 75 Decay Time
|
static_cast<int>(SOUND_CTRL6), // MIDI CC 75 Decay Time
|
||||||
FLUID_MOD_CC
|
FLUID_MOD_CC
|
||||||
| FLUID_MOD_UNIPOLAR
|
| FLUID_MOD_UNIPOLAR
|
||||||
| FLUID_MOD_LINEAR
|
| FLUID_MOD_LINEAR
|
||||||
| FLUID_MOD_POSITIVE);
|
| FLUID_MOD_POSITIVE);
|
||||||
fluid_mod_set_source2(mod, 0, 0);
|
fluid_mod_set_source2(mod.get(), 0, 0);
|
||||||
fluid_mod_set_dest(mod, GEN_VOLENVDECAY);
|
fluid_mod_set_dest(mod.get(), GEN_VOLENVDECAY);
|
||||||
fluid_mod_set_amount(mod, env_amount);
|
fluid_mod_set_amount(mod.get(), env_amount);
|
||||||
fluid_synth_add_default_mod(synth, mod, FLUID_SYNTH_ADD);
|
fluid_synth_add_default_mod(synth.get(), mod.get(), FLUID_SYNTH_ADD);
|
||||||
delete_fluid_mod(mod);
|
|
||||||
|
|
||||||
mod = new_fluid_mod();
|
mod = {new_fluid_mod(), delete_fluid_mod};
|
||||||
fluid_mod_set_source1(mod,
|
fluid_mod_set_source1(mod.get(),
|
||||||
static_cast<int>(SOUND_CTRL10), // MIDI CC 79 undefined
|
static_cast<int>(SOUND_CTRL10), // MIDI CC 79 undefined
|
||||||
FLUID_MOD_CC
|
FLUID_MOD_CC
|
||||||
| FLUID_MOD_UNIPOLAR
|
| FLUID_MOD_UNIPOLAR
|
||||||
| FLUID_MOD_CONCAVE
|
| FLUID_MOD_CONCAVE
|
||||||
| FLUID_MOD_POSITIVE);
|
| FLUID_MOD_POSITIVE);
|
||||||
fluid_mod_set_source2(mod, 0, 0);
|
fluid_mod_set_source2(mod.get(), 0, 0);
|
||||||
fluid_mod_set_dest(mod, GEN_VOLENVSUSTAIN);
|
fluid_mod_set_dest(mod.get(), GEN_VOLENVSUSTAIN);
|
||||||
// fluice_voice.c#fluid_voice_update_param()
|
// fluice_voice.c#fluid_voice_update_param()
|
||||||
// clamps the range to between 0 and 1000, so we'll copy that
|
// clamps the range to between 0 and 1000, so we'll copy that
|
||||||
fluid_mod_set_amount(mod, 1000.0f);
|
fluid_mod_set_amount(mod.get(), 1000.0f);
|
||||||
fluid_synth_add_default_mod(synth, mod, FLUID_SYNTH_ADD);
|
fluid_synth_add_default_mod(synth.get(), mod.get(), FLUID_SYNTH_ADD);
|
||||||
delete_fluid_mod(mod);
|
}
|
||||||
|
|
||||||
initialised = true;
|
const StringArray FluidSynthModel::programChangeParams{"bank", "preset"};
|
||||||
|
void FluidSynthModel::parameterChanged(const String& parameterID, float newValue) {
|
||||||
|
if (programChangeParams.contains(parameterID)) {
|
||||||
|
int bank, preset;
|
||||||
|
{
|
||||||
|
RangedAudioParameter *param{valueTreeState.getParameter("bank")};
|
||||||
|
jassert(dynamic_cast<AudioParameterInt*>(param) != nullptr);
|
||||||
|
AudioParameterInt* castParam{dynamic_cast<AudioParameterInt*>(param)};
|
||||||
|
bank = castParam->get();
|
||||||
|
}
|
||||||
|
{
|
||||||
|
RangedAudioParameter *param{valueTreeState.getParameter("preset")};
|
||||||
|
jassert(dynamic_cast<AudioParameterInt*>(param) != nullptr);
|
||||||
|
AudioParameterInt* castParam{dynamic_cast<AudioParameterInt*>(param)};
|
||||||
|
preset = castParam->get();
|
||||||
|
}
|
||||||
|
int bankOffset{fluid_synth_get_bank_offset(synth.get(), sfont_id)};
|
||||||
|
fluid_synth_program_select(
|
||||||
|
synth.get(),
|
||||||
|
channel,
|
||||||
|
sfont_id,
|
||||||
|
static_cast<unsigned int>(bankOffset + bank),
|
||||||
|
static_cast<unsigned int>(preset));
|
||||||
|
} else if (
|
||||||
|
// https://stackoverflow.com/a/55482091/5257399
|
||||||
|
auto it{paramToController.find(parameterID)};
|
||||||
|
it != end(paramToController)) {
|
||||||
|
RangedAudioParameter *param{valueTreeState.getParameter(parameterID)};
|
||||||
|
jassert(dynamic_cast<AudioParameterInt*>(param) != nullptr);
|
||||||
|
AudioParameterInt* castParam{dynamic_cast<AudioParameterInt*>(param)};
|
||||||
|
int value{castParam->get()};
|
||||||
|
int controllerNumber{static_cast<int>(it->second)};
|
||||||
|
|
||||||
|
fluid_synth_cc(
|
||||||
|
synth.get(),
|
||||||
|
channel,
|
||||||
|
controllerNumber,
|
||||||
|
value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FluidSynthModel::valueTreePropertyChanged(ValueTree& treeWhosePropertyHasChanged,
|
||||||
|
const Identifier& property) {
|
||||||
|
if (treeWhosePropertyHasChanged.getType() == StringRef("soundFont")) {
|
||||||
|
if (property == StringRef("path")) {
|
||||||
|
String soundFontPath{treeWhosePropertyHasChanged.getProperty("path", "")};
|
||||||
|
if (soundFontPath.isNotEmpty()) {
|
||||||
|
unloadAndLoadFont(soundFontPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FluidSynthModel::setControllerValue(int controller, int value) {
|
void FluidSynthModel::setControllerValue(int controller, int value) {
|
||||||
fluid_midi_event_t *midi_event(new_fluid_midi_event());
|
fluid_synth_cc(
|
||||||
fluid_midi_event_set_type(midi_event, static_cast<int>(CONTROL_CHANGE));
|
synth.get(),
|
||||||
fluid_midi_event_set_channel(midi_event, channel);
|
channel,
|
||||||
fluid_midi_event_set_control(midi_event, controller);
|
controller,
|
||||||
fluid_midi_event_set_value(midi_event, value);
|
value);
|
||||||
fluid_synth_handle_midi_event(synth, midi_event);
|
|
||||||
delete_fluid_midi_event(midi_event);
|
|
||||||
// fluid_channel_set_cc(channel, i, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int FluidSynthModel::getChannel() {
|
int FluidSynthModel::getChannel() {
|
||||||
return channel;
|
return channel;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FluidSynthModel::changePreset(int bank, int preset) {
|
|
||||||
if (bank == -1 || preset == -1) {
|
|
||||||
unique_ptr<BankAndPreset> bankAndPreset = getFirstBankAndPreset();
|
|
||||||
bank = bankAndPreset->getBank();
|
|
||||||
preset = bankAndPreset->getPreset();
|
|
||||||
}
|
|
||||||
changePresetImpl(bank, preset);
|
|
||||||
sharesParams.setPreset(preset);
|
|
||||||
sharesParams.setBank(bank);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FluidSynthModel::changePresetImpl(int bank, int preset) {
|
|
||||||
fluid_synth_program_select(synth, channel, sfont_id, static_cast<unsigned int>(bank), static_cast<unsigned int>(preset));
|
|
||||||
}
|
|
||||||
|
|
||||||
fluid_preset_t* FluidSynthModel::getFirstPreset() {
|
|
||||||
fluid_sfont_t* sfont = fluid_synth_get_sfont_by_id(synth, sfont_id);
|
|
||||||
|
|
||||||
jassert(sfont != nullptr);
|
|
||||||
fluid_sfont_iteration_start(sfont);
|
|
||||||
|
|
||||||
return fluid_sfont_iteration_next(sfont);
|
|
||||||
}
|
|
||||||
|
|
||||||
unique_ptr<BankAndPreset> FluidSynthModel::getFirstBankAndPreset() {
|
|
||||||
fluid_preset_t* preset = getFirstPreset();
|
|
||||||
|
|
||||||
int offset = fluid_synth_get_bank_offset(synth, sfont_id);
|
|
||||||
|
|
||||||
return make_unique<BankAndPreset>(fluid_preset_get_banknum(preset) + offset, fluid_preset_get_num(preset));
|
|
||||||
};
|
|
||||||
|
|
||||||
void FluidSynthModel::selectFirstPreset() {
|
|
||||||
fluid_preset_t* preset = getFirstPreset();
|
|
||||||
|
|
||||||
int offset = fluid_synth_get_bank_offset(synth, sfont_id);
|
|
||||||
|
|
||||||
changePreset(fluid_preset_get_banknum(preset) + offset, fluid_preset_get_num(preset));
|
|
||||||
}
|
|
||||||
|
|
||||||
BanksToPresets FluidSynthModel::getBanks() {
|
|
||||||
BanksToPresets banksToPresets;
|
|
||||||
|
|
||||||
int soundfontCount = fluid_synth_sfcount(synth);
|
|
||||||
|
|
||||||
if (soundfontCount == 0) {
|
|
||||||
// no soundfont selected
|
|
||||||
return banksToPresets;
|
|
||||||
}
|
|
||||||
|
|
||||||
fluid_sfont_t* sfont = fluid_synth_get_sfont_by_id(synth, sfont_id);
|
|
||||||
if(sfont == nullptr) {
|
|
||||||
// no soundfont found by that ID
|
|
||||||
// the above guard (soundfontCount) protects us for the
|
|
||||||
// main case we're expecting. this guard is just defensive programming.
|
|
||||||
return banksToPresets;
|
|
||||||
}
|
|
||||||
|
|
||||||
int offset = fluid_synth_get_bank_offset(synth, sfont_id);
|
|
||||||
|
|
||||||
fluid_sfont_iteration_start(sfont);
|
|
||||||
|
|
||||||
for(fluid_preset_t* preset = fluid_sfont_iteration_next(sfont);
|
|
||||||
preset != nullptr;
|
|
||||||
preset = fluid_sfont_iteration_next(sfont)) {
|
|
||||||
banksToPresets.insert(BanksToPresets::value_type(
|
|
||||||
fluid_preset_get_banknum(preset) + offset,
|
|
||||||
*new Preset(
|
|
||||||
fluid_preset_get_num(preset),
|
|
||||||
fluid_preset_get_name(preset)
|
|
||||||
)
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
return banksToPresets;
|
|
||||||
}
|
|
||||||
|
|
||||||
fluid_synth_t* FluidSynthModel::getSynth() {
|
|
||||||
// https://msdn.microsoft.com/en-us/library/hh279669.aspx
|
|
||||||
// You can pass a shared_ptr to another function in the following ways:
|
|
||||||
// Pass the shared_ptr by value. This invokes the copy constructor, increments the reference count, and makes the callee an owner.
|
|
||||||
return synth;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FluidSynthModel::onFileNameChanged(const String &absPath, int bank, int preset) {
|
|
||||||
if (!shouldLoadFont(absPath)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
unloadAndLoadFont(absPath);
|
|
||||||
changePreset(bank, preset);
|
|
||||||
sharesParams.setSoundFontPath(absPath);
|
|
||||||
eventListeners.call(&FluidSynthModel::Listener::fontChanged, this, absPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FluidSynthModel::unloadAndLoadFont(const String &absPath) {
|
void FluidSynthModel::unloadAndLoadFont(const String &absPath) {
|
||||||
// in the base case, there is no font loaded
|
// in the base case, there is no font loaded
|
||||||
if (fluid_synth_sfcount(synth) > 0) {
|
if (fluid_synth_sfcount(synth.get()) > 0) {
|
||||||
fluid_synth_sfunload(synth, sfont_id, 1);
|
// if -1 is returned, that indicates failure
|
||||||
|
// not really sure how to handle "fail to unload"
|
||||||
|
fluid_synth_sfunload(synth.get(), sfont_id, 1);
|
||||||
|
sfont_id = -1;
|
||||||
}
|
}
|
||||||
loadFont(absPath);
|
loadFont(absPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FluidSynthModel::loadFont(const String &absPath) {
|
void FluidSynthModel::loadFont(const String &absPath) {
|
||||||
currentSoundFontAbsPath = absPath;
|
if (!absPath.isEmpty()) {
|
||||||
sfont_id++;
|
sfont_id = fluid_synth_sfload(synth.get(), absPath.toStdString().c_str(), 1);
|
||||||
fluid_synth_sfload(synth, absPath.toStdString().c_str(), 1);
|
// if -1 is returned, that indicates failure
|
||||||
}
|
|
||||||
|
|
||||||
FluidSynthModel::Listener::~Listener() {
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FluidSynthModel::shouldLoadFont(const String &absPath) {
|
|
||||||
if (absPath.isEmpty()) {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
if (absPath == currentSoundFontAbsPath) {
|
// refresh regardless of success, if only to clear the table
|
||||||
return false;
|
refreshBanks();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FluidSynthModel::refreshBanks() {
|
||||||
|
ValueTree banks{"banks"};
|
||||||
|
fluid_sfont_t* sfont{
|
||||||
|
sfont_id == -1
|
||||||
|
? nullptr
|
||||||
|
: fluid_synth_get_sfont_by_id(synth.get(), sfont_id)
|
||||||
|
};
|
||||||
|
if (sfont) {
|
||||||
|
int greatestEncounteredBank{-1};
|
||||||
|
ValueTree bank;
|
||||||
|
|
||||||
|
fluid_sfont_iteration_start(sfont);
|
||||||
|
for(fluid_preset_t* preset {fluid_sfont_iteration_next(sfont)};
|
||||||
|
preset != nullptr;
|
||||||
|
preset = fluid_sfont_iteration_next(sfont)) {
|
||||||
|
int bankNum{fluid_preset_get_banknum(preset)};
|
||||||
|
if (bankNum > greatestEncounteredBank) {
|
||||||
|
if (greatestEncounteredBank > -1) {
|
||||||
|
banks.appendChild(bank, nullptr);
|
||||||
|
}
|
||||||
|
bank = { "bank", {
|
||||||
|
{ "num", bankNum }
|
||||||
|
} };
|
||||||
|
greatestEncounteredBank = bankNum;
|
||||||
|
}
|
||||||
|
bank.appendChild({ "preset", {
|
||||||
|
{ "num", fluid_preset_get_num(preset) },
|
||||||
|
{ "name", String{fluid_preset_get_name(preset)} }
|
||||||
|
}, {} }, nullptr);
|
||||||
|
}
|
||||||
|
if (greatestEncounteredBank > -1) {
|
||||||
|
banks.appendChild(bank, nullptr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return true;
|
valueTreeState.state.getChildWithName("banks").copyPropertiesAndChildrenFrom(banks, nullptr);
|
||||||
}
|
valueTreeState.state.getChildWithName("banks").sendPropertyChangeMessage("synthetic");
|
||||||
|
|
||||||
void FluidSynthModel::Listener::fontChanged(FluidSynthModel * model, const String &absPath) {
|
#if JUCE_DEBUG
|
||||||
}
|
// unique_ptr<XmlElement> xml{valueTreeState.state.createXml()};
|
||||||
|
// Logger::outputDebugString(xml->createDocument("",false,false));
|
||||||
const String& FluidSynthModel::getCurrentSoundFontAbsPath() {
|
#endif
|
||||||
return currentSoundFontAbsPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
//==============================================================================
|
|
||||||
void FluidSynthModel::addListener (FluidSynthModel::Listener* const newListener)
|
|
||||||
{
|
|
||||||
eventListeners.add(newListener);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FluidSynthModel::removeListener (FluidSynthModel::Listener* const listener)
|
|
||||||
{
|
|
||||||
eventListeners.remove(listener);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void FluidSynthModel::setSampleRate(float sampleRate) {
|
void FluidSynthModel::setSampleRate(float sampleRate) {
|
||||||
currentSampleRate = sampleRate;
|
currentSampleRate = sampleRate;
|
||||||
if (!initialised) {
|
// https://stackoverflow.com/a/40856043/5257399
|
||||||
|
// test if a smart pointer is null
|
||||||
|
if (!synth) {
|
||||||
// don't worry; we'll do this in initialise phase regardless
|
// don't worry; we'll do this in initialise phase regardless
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
fluid_synth_set_sample_rate(synth, sampleRate);
|
fluid_synth_set_sample_rate(synth.get(), sampleRate);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FluidSynthModel::processBlock(AudioBuffer<float>& buffer, MidiBuffer& midiMessages) {
|
||||||
|
MidiBuffer processedMidi;
|
||||||
|
int time;
|
||||||
|
MidiMessage m;
|
||||||
|
|
||||||
|
for (MidiBuffer::Iterator i{midiMessages}; i.getNextEvent(m, time);) {
|
||||||
|
DEBUG_PRINT(m.getDescription());
|
||||||
|
|
||||||
|
if (m.isNoteOn()) {
|
||||||
|
fluid_synth_noteon(
|
||||||
|
synth.get(),
|
||||||
|
channel,
|
||||||
|
m.getNoteNumber(),
|
||||||
|
m.getVelocity());
|
||||||
|
} else if (m.isNoteOff()) {
|
||||||
|
fluid_synth_noteoff(
|
||||||
|
synth.get(),
|
||||||
|
channel,
|
||||||
|
m.getNoteNumber());
|
||||||
|
} else if (m.isController()) {
|
||||||
|
fluid_synth_cc(
|
||||||
|
synth.get(),
|
||||||
|
channel,
|
||||||
|
m.getControllerNumber(),
|
||||||
|
m.getControllerValue());
|
||||||
|
|
||||||
|
fluid_midi_control_change controllerNum{static_cast<fluid_midi_control_change>(m.getControllerNumber())};
|
||||||
|
if (auto it{controllerToParam.find(controllerNum)};
|
||||||
|
it != end(controllerToParam)) {
|
||||||
|
String parameterID{it->second};
|
||||||
|
RangedAudioParameter *param{valueTreeState.getParameter(parameterID)};
|
||||||
|
jassert(dynamic_cast<AudioParameterInt*>(param) != nullptr);
|
||||||
|
AudioParameterInt* castParam{dynamic_cast<AudioParameterInt*>(param)};
|
||||||
|
*castParam = m.getControllerValue();
|
||||||
|
}
|
||||||
|
} else if (m.isProgramChange()) {
|
||||||
|
int result{fluid_synth_program_change(
|
||||||
|
synth.get(),
|
||||||
|
channel,
|
||||||
|
m.getProgramChangeNumber())};
|
||||||
|
if (result == FLUID_OK) {
|
||||||
|
RangedAudioParameter *param{valueTreeState.getParameter("preset")};
|
||||||
|
jassert(dynamic_cast<AudioParameterInt*>(param) != nullptr);
|
||||||
|
AudioParameterInt* castParam{dynamic_cast<AudioParameterInt*>(param)};
|
||||||
|
*castParam = m.getProgramChangeNumber();
|
||||||
|
}
|
||||||
|
} else if (m.isPitchWheel()) {
|
||||||
|
fluid_synth_pitch_bend(
|
||||||
|
synth.get(),
|
||||||
|
channel,
|
||||||
|
m.getPitchWheelValue());
|
||||||
|
} else if (m.isChannelPressure()) {
|
||||||
|
fluid_synth_channel_pressure(
|
||||||
|
synth.get(),
|
||||||
|
channel,
|
||||||
|
m.getChannelPressureValue());
|
||||||
|
} else if (m.isAftertouch()) {
|
||||||
|
fluid_synth_key_pressure(
|
||||||
|
synth.get(),
|
||||||
|
channel,
|
||||||
|
m.getNoteNumber(),
|
||||||
|
m.getAfterTouchValue());
|
||||||
|
// } else if (m.isMetaEvent()) {
|
||||||
|
// fluid_midi_event_t *midi_event{new_fluid_midi_event()};
|
||||||
|
// fluid_midi_event_set_type(midi_event, static_cast<int>(MIDI_SYSTEM_RESET));
|
||||||
|
// fluid_synth_handle_midi_event(synth.get(), midi_event);
|
||||||
|
// delete_fluid_midi_event(midi_event);
|
||||||
|
} else if (m.isSysEx()) {
|
||||||
|
fluid_synth_sysex(
|
||||||
|
synth.get(),
|
||||||
|
reinterpret_cast<const char*>(m.getSysExData()),
|
||||||
|
m.getSysExDataSize(),
|
||||||
|
nullptr, // no response pointer because we have no interest in handling response currently
|
||||||
|
nullptr, // no response_len pointer because we have no interest in handling response currently
|
||||||
|
nullptr, // no handled pointer because we have no interest in handling response currently
|
||||||
|
static_cast<int>(false));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fluid_synth_get_cc(fluidSynth, 0, 73, &pval);
|
||||||
|
// Logger::outputDebugString ( juce::String::formatted("hey: %d\n", pval) );
|
||||||
|
|
||||||
|
fluid_synth_process(
|
||||||
|
synth.get(),
|
||||||
|
buffer.getNumSamples(),
|
||||||
|
0,
|
||||||
|
nullptr,
|
||||||
|
buffer.getNumChannels(),
|
||||||
|
buffer.getArrayOfWritePointers());
|
||||||
|
}
|
||||||
|
|
||||||
|
int FluidSynthModel::getNumPrograms()
|
||||||
|
{
|
||||||
|
return 128; // NB: some hosts don't cope very well if you tell them there are 0 programs,
|
||||||
|
// so this should be at least 1, even if you're not really implementing programs.
|
||||||
|
}
|
||||||
|
|
||||||
|
int FluidSynthModel::getCurrentProgram()
|
||||||
|
{
|
||||||
|
RangedAudioParameter *param{valueTreeState.getParameter("preset")};
|
||||||
|
jassert(dynamic_cast<AudioParameterInt*>(param) != nullptr);
|
||||||
|
AudioParameterInt* castParam{dynamic_cast<AudioParameterInt*>(param)};
|
||||||
|
return castParam->get();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FluidSynthModel::setCurrentProgram(int index)
|
||||||
|
{
|
||||||
|
RangedAudioParameter *param{valueTreeState.getParameter("preset")};
|
||||||
|
jassert(dynamic_cast<AudioParameterInt*>(param) != nullptr);
|
||||||
|
AudioParameterInt* castParam{dynamic_cast<AudioParameterInt*>(param)};
|
||||||
|
// setCurrentProgram() gets invoked from non-message thread.
|
||||||
|
// AudioParameterInt#operator= will activate any listeners of audio parameter "preset".
|
||||||
|
// This includes TableComponent, who will update its UI.
|
||||||
|
// we need to lock the message thread whilst it does that UI update.
|
||||||
|
const MessageManagerLock mmLock;
|
||||||
|
*castParam = index;
|
||||||
|
}
|
||||||
|
|
||||||
|
const String FluidSynthModel::getProgramName(int index)
|
||||||
|
{
|
||||||
|
fluid_sfont_t* sfont{
|
||||||
|
sfont_id == -1
|
||||||
|
? nullptr
|
||||||
|
: fluid_synth_get_sfont_by_id(synth.get(), sfont_id)
|
||||||
|
};
|
||||||
|
if (!sfont) {
|
||||||
|
String presetName{"Preset "};
|
||||||
|
return presetName << index;
|
||||||
|
}
|
||||||
|
RangedAudioParameter *param{valueTreeState.getParameter("bank")};
|
||||||
|
jassert(dynamic_cast<AudioParameterInt*>(param) != nullptr);
|
||||||
|
AudioParameterInt* castParam{dynamic_cast<AudioParameterInt*>(param)};
|
||||||
|
int bank{castParam->get()};
|
||||||
|
|
||||||
|
fluid_preset_t *preset{fluid_sfont_get_preset(
|
||||||
|
sfont,
|
||||||
|
bank,
|
||||||
|
index)};
|
||||||
|
if (!preset) {
|
||||||
|
String presetName{"Preset "};
|
||||||
|
return presetName << index;
|
||||||
|
}
|
||||||
|
return {fluid_preset_get_name(preset)};
|
||||||
|
}
|
||||||
|
|
||||||
|
void FluidSynthModel::changeProgramName(int index, const String& newName)
|
||||||
|
{
|
||||||
|
// no-op; we don't support modifying the soundfont, so let's not support modification of preset names.
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,93 +5,80 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "../JuceLibraryCode/JuceHeader.h"
|
#include "../JuceLibraryCode/JuceHeader.h"
|
||||||
#include "SharesParams.h"
|
|
||||||
#include <fluidsynth.h>
|
#include <fluidsynth.h>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include "BankAndPreset.h"
|
#include <map>
|
||||||
#include "PresetsToBanks.h"
|
#include "MidiConstants.h"
|
||||||
|
|
||||||
|
|
||||||
// https://stackoverflow.com/a/13446565/5257399
|
|
||||||
//using std::shared_ptr;
|
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
class FluidSynthModel {
|
class FluidSynthModel
|
||||||
|
: public ValueTree::Listener
|
||||||
|
, public AudioProcessorValueTreeState::Listener {
|
||||||
public:
|
public:
|
||||||
FluidSynthModel(SharesParams& p);
|
FluidSynthModel(
|
||||||
~FluidSynthModel();
|
AudioProcessorValueTreeState& valueTreeState
|
||||||
|
);
|
||||||
|
~FluidSynthModel();
|
||||||
|
|
||||||
fluid_synth_t* getSynth();
|
|
||||||
void initialise();
|
void initialise();
|
||||||
|
|
||||||
BanksToPresets getBanks();
|
|
||||||
|
|
||||||
void changePreset(int bank, int preset);
|
|
||||||
int getChannel();
|
int getChannel();
|
||||||
|
|
||||||
void onFileNameChanged(const String &absPath, int bank, int preset);
|
|
||||||
void setControllerValue(int controller, int value);
|
void setControllerValue(int controller, int value);
|
||||||
|
|
||||||
//==============================================================================
|
void processBlock(AudioBuffer<float>& buffer, MidiBuffer& midiMessages);
|
||||||
/**
|
|
||||||
Used to receive callbacks when a button is clicked.
|
|
||||||
|
|
||||||
@see Button::addListener, Button::removeListener
|
|
||||||
*/
|
|
||||||
class Listener
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
/** Destructor. */
|
|
||||||
virtual ~Listener();
|
|
||||||
|
|
||||||
/** Called when the button is clicked. */
|
|
||||||
virtual void fontChanged (FluidSynthModel*, const String &absPath);
|
|
||||||
};
|
|
||||||
|
|
||||||
/** Registers a listener to receive events when this button's state changes.
|
|
||||||
If the listener is already registered, this will not register it again.
|
|
||||||
@see removeListener
|
|
||||||
*/
|
|
||||||
void addListener (Listener* newListener);
|
|
||||||
|
|
||||||
/** Removes a previously-registered button listener
|
|
||||||
@see addListener
|
|
||||||
*/
|
|
||||||
void removeListener (Listener* listener);
|
|
||||||
|
|
||||||
void setSampleRate(float sampleRate);
|
void setSampleRate(float sampleRate);
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
virtual void parameterChanged (const String& parameterID, float newValue) override;
|
||||||
|
|
||||||
|
virtual void valueTreePropertyChanged (ValueTree& treeWhosePropertyHasChanged,
|
||||||
|
const Identifier& property) override;
|
||||||
|
inline virtual void valueTreeChildAdded (ValueTree& parentTree,
|
||||||
|
ValueTree& childWhichHasBeenAdded) override {};
|
||||||
|
inline virtual void valueTreeChildRemoved (ValueTree& parentTree,
|
||||||
|
ValueTree& childWhichHasBeenRemoved,
|
||||||
|
int indexFromWhichChildWasRemoved) override {};
|
||||||
|
inline virtual void valueTreeChildOrderChanged (ValueTree& parentTreeWhoseChildrenHaveMoved,
|
||||||
|
int oldIndex, int newIndex) override {};
|
||||||
|
inline virtual void valueTreeParentChanged (ValueTree& treeWhoseParentHasChanged) override {};
|
||||||
|
inline virtual void valueTreeRedirected (ValueTree& treeWhichHasBeenChanged) override {};
|
||||||
|
|
||||||
const String& getCurrentSoundFontAbsPath();
|
//==============================================================================
|
||||||
|
int getNumPrograms();
|
||||||
|
int getCurrentProgram();
|
||||||
|
void setCurrentProgram(int index);
|
||||||
|
const String getProgramName(int index);
|
||||||
|
void changeProgramName(int index, const String& newName);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
SharesParams& sharesParams;
|
static const StringArray programChangeParams;
|
||||||
|
|
||||||
fluid_synth_t* synth;
|
// there's no bimap in the standard library!
|
||||||
fluid_settings_t* settings;
|
static const map<fluid_midi_control_change, String> controllerToParam;
|
||||||
// fluid_audio_driver_t* driver;
|
static const map<String, fluid_midi_control_change> paramToController;
|
||||||
|
|
||||||
String currentSoundFontAbsPath;
|
void refreshBanks();
|
||||||
|
|
||||||
|
AudioProcessorValueTreeState& valueTreeState;
|
||||||
|
|
||||||
|
// https://stackoverflow.com/questions/38980315/is-stdunique-ptr-deletion-order-guaranteed
|
||||||
|
// members are destroyed in reverse of the order they're declared
|
||||||
|
// http://www.fluidsynth.org/api/
|
||||||
|
// in their examples, they destroy the synth before destroying the settings
|
||||||
|
unique_ptr<fluid_settings_t, decltype(&delete_fluid_settings)> settings;
|
||||||
|
unique_ptr<fluid_synth_t, decltype(&delete_fluid_synth)> synth;
|
||||||
|
|
||||||
float currentSampleRate;
|
float currentSampleRate;
|
||||||
|
|
||||||
fluid_preset_t* getFirstPreset();
|
|
||||||
void selectFirstPreset();
|
|
||||||
unique_ptr<BankAndPreset> getFirstBankAndPreset();
|
|
||||||
|
|
||||||
void unloadAndLoadFont(const String &absPath);
|
void unloadAndLoadFont(const String &absPath);
|
||||||
void loadFont(const String &absPath);
|
void loadFont(const String &absPath);
|
||||||
bool shouldLoadFont(const String &absPath);
|
|
||||||
|
int sfont_id;
|
||||||
void changePresetImpl(int bank, int preset);
|
|
||||||
|
|
||||||
bool initialised;
|
|
||||||
unsigned int sfont_id;
|
|
||||||
unsigned int channel;
|
unsigned int channel;
|
||||||
|
|
||||||
// fluid_mod_t* mod;
|
|
||||||
|
|
||||||
ListenerList<Listener> eventListeners;
|
|
||||||
|
|
||||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FluidSynthModel)
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FluidSynthModel)
|
||||||
};
|
};
|
||||||
|
|
8
Source/GuiConstants.h
Normal file
8
Source/GuiConstants.h
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
struct GuiConstants {
|
||||||
|
inline static const int minWidth = 500;
|
||||||
|
inline static const int maxWidth = 1900;
|
||||||
|
inline static const int minHeight = 300;
|
||||||
|
inline static const int maxHeight = 1000;
|
||||||
|
};
|
|
@ -162,3 +162,8 @@ enum fluid_midi_control_change
|
||||||
- for case (2) or (3), FLUID_NOISE_FLOOR should be the noise floor for 16 bits (i.e -90 dB).
|
- for case (2) or (3), FLUID_NOISE_FLOOR should be the noise floor for 16 bits (i.e -90 dB).
|
||||||
*/
|
*/
|
||||||
#define FLUID_PEAK_ATTENUATION 960.0f
|
#define FLUID_PEAK_ATTENUATION 960.0f
|
||||||
|
|
||||||
|
struct MidiConstants {
|
||||||
|
inline static const int midiMinValue = 0;
|
||||||
|
inline static const int midiMaxValue = 127;
|
||||||
|
};
|
||||||
|
|
200
Source/Pills.cpp
200
Source/Pills.cpp
|
@ -4,88 +4,170 @@
|
||||||
|
|
||||||
#include "Pills.h"
|
#include "Pills.h"
|
||||||
#include "MyColours.h"
|
#include "MyColours.h"
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
|
Pill::Pill(
|
||||||
|
AudioProcessorValueTreeState& valueTreeState,
|
||||||
|
int bank,
|
||||||
|
bool isFirst,
|
||||||
|
bool isLast
|
||||||
|
)
|
||||||
|
: valueTreeState{valueTreeState}
|
||||||
|
, bank{bank}
|
||||||
|
, textButton{String(bank)}
|
||||||
|
{
|
||||||
|
setOpaque(true);
|
||||||
|
textButton.setConnectedEdges (
|
||||||
|
(isFirst ? 0 : Button::ConnectedOnLeft)
|
||||||
|
| (isLast ? 0 : Button::ConnectedOnRight)
|
||||||
|
);
|
||||||
|
textButton.setRadioGroupId(34567);
|
||||||
|
textButton.setClickingTogglesState(true);
|
||||||
|
|
||||||
|
addAndMakeVisible(textButton);
|
||||||
|
textButton.addListener(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Pill::paint (Graphics& g) {
|
||||||
|
// (Our component is opaque, so we must completely fill the background with a solid colour)
|
||||||
|
g.fillAll(getLookAndFeel().findColour(ResizableWindow::backgroundColourId));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Pill::resized() {
|
||||||
|
textButton.setBounds(getLocalBounds());
|
||||||
|
}
|
||||||
|
|
||||||
|
Pill::~Pill() {
|
||||||
|
textButton.removeListener(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Pill::bankChanged(int bank) {
|
||||||
|
textButton.setToggleState(this->bank == bank, dontSendNotification);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Pill::buttonClicked(Button* button) {
|
||||||
|
ValueTree banks{valueTreeState.state.getChildWithName("banks")};
|
||||||
|
int banksChildren{banks.getNumChildren()};
|
||||||
|
ValueTree bank;
|
||||||
|
for(int bankIx{0}; bankIx<banksChildren; bankIx++) {
|
||||||
|
ValueTree currentBank{banks.getChild(bankIx)};
|
||||||
|
int bankNum{currentBank.getProperty("num")};
|
||||||
|
if (bankNum == this->bank) {
|
||||||
|
bank = currentBank;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ValueTree preset{bank.getChild(0)};
|
||||||
|
int presetNum{preset.getProperty("num")};
|
||||||
|
|
||||||
|
{
|
||||||
|
RangedAudioParameter *param{valueTreeState.getParameter("bank")};
|
||||||
|
jassert(dynamic_cast<AudioParameterInt*>(param) != nullptr);
|
||||||
|
AudioParameterInt* castParam{dynamic_cast<AudioParameterInt*>(param)};
|
||||||
|
*castParam = this->bank;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
RangedAudioParameter *param{valueTreeState.getParameter("preset")};
|
||||||
|
jassert(dynamic_cast<AudioParameterInt*>(param) != nullptr);
|
||||||
|
AudioParameterInt* castParam{dynamic_cast<AudioParameterInt*>(param)};
|
||||||
|
*castParam = presetNum;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Pills::Pills(
|
Pills::Pills(
|
||||||
string label,
|
AudioProcessorValueTreeState& valueTreeState
|
||||||
const vector<string> &items,
|
)
|
||||||
const function<void (int)> &onItemSelected,
|
: valueTreeState{valueTreeState}
|
||||||
const function<int (const string&)> &itemToIDMapper,
|
|
||||||
int initiallySelectedItem
|
|
||||||
) : label(label),
|
|
||||||
items(items),
|
|
||||||
onItemSelected(onItemSelected),
|
|
||||||
itemToIDMapper(itemToIDMapper)
|
|
||||||
{
|
{
|
||||||
// faster (rounded edges introduce transparency)
|
// faster (rounded edges introduce transparency)
|
||||||
setOpaque (true);
|
setOpaque (true);
|
||||||
|
|
||||||
populate(initiallySelectedItem);
|
ValueTree banks{valueTreeState.state.getChildWithName("banks")};
|
||||||
|
loadModelFrom(banks);
|
||||||
|
|
||||||
|
valueTreeState.state.addListener(this);
|
||||||
|
valueTreeState.addParameterListener("bank", this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Pills::populate(int initiallySelectedItem) {
|
Pills::~Pills() {
|
||||||
int index = 0;
|
valueTreeState.removeParameterListener("bank", this);
|
||||||
for (string item : items) {
|
valueTreeState.state.removeListener(this);
|
||||||
TextButton* pill = addToList(new TextButton(
|
}
|
||||||
item
|
|
||||||
));
|
void Pills::parameterChanged(const String& parameterID, float newValue) {
|
||||||
// pill->setColour (TextButton::buttonOnColourId, Colours::blueviolet.brighter());
|
if (parameterID == "bank") {
|
||||||
// pill->setBounds(20 + index * 55, 260, 55, 24);
|
updatePillToggleStates();
|
||||||
pill->setConnectedEdges (
|
}
|
||||||
(index == 0 ? 0 : Button::ConnectedOnLeft)
|
}
|
||||||
| (index == (items.size()-1) ? 0 : Button::ConnectedOnRight)
|
|
||||||
);
|
void Pills::updatePillToggleStates() {
|
||||||
pill->setRadioGroupId(34567);
|
RangedAudioParameter *param {valueTreeState.getParameter("bank")};
|
||||||
if (index == initiallySelectedItem) {
|
jassert(dynamic_cast<AudioParameterInt*> (param) != nullptr);
|
||||||
pill->setToggleState(true, dontSendNotification);
|
AudioParameterInt* castParam {dynamic_cast<AudioParameterInt*> (param)};
|
||||||
selected = pill;
|
int bank{castParam->get()};
|
||||||
|
for (auto& pill: pills) {
|
||||||
|
pill->bankChanged(bank);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Pills::valueTreePropertyChanged(
|
||||||
|
ValueTree& treeWhosePropertyHasChanged,
|
||||||
|
const Identifier& property) {
|
||||||
|
if (treeWhosePropertyHasChanged.getType() == StringRef("banks")) {
|
||||||
|
if (property == StringRef("synthetic")) {
|
||||||
|
loadModelFrom(treeWhosePropertyHasChanged);
|
||||||
}
|
}
|
||||||
pill->setClickingTogglesState(true);
|
|
||||||
pill->addListener(this);
|
|
||||||
index++;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Pills::setItems(
|
void Pills::loadModelFrom(ValueTree& banks) {
|
||||||
const vector<string> &items,
|
pills.clear();
|
||||||
int initiallySelectedItem
|
int numChildren{banks.getNumChildren()};
|
||||||
) {
|
for(int i{0}; i < numChildren; i++) {
|
||||||
this->items = items;
|
ValueTree child{banks.getChild(i)};
|
||||||
for(TextButton* t : buttons) {
|
int num{child.getProperty("num")};
|
||||||
t->removeListener(this);
|
unique_ptr<Pill> pill{make_unique<Pill>(
|
||||||
|
valueTreeState,
|
||||||
|
num,
|
||||||
|
i == 0,
|
||||||
|
i == numChildren - 1)};
|
||||||
|
addAndMakeVisible(pill.get());
|
||||||
|
pills.push_back(move(pill));
|
||||||
}
|
}
|
||||||
buttons.clear(true);
|
updatePillToggleStates();
|
||||||
populate(initiallySelectedItem);
|
|
||||||
resized();
|
resized();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Pills::buttonClicked (Button* button) {
|
|
||||||
selected = button;
|
|
||||||
onItemSelected(itemToIDMapper(button->getName().toStdString()));
|
|
||||||
}
|
|
||||||
|
|
||||||
TextButton* Pills::addToList (TextButton* newButton) {
|
|
||||||
buttons.add (newButton);
|
|
||||||
addAndMakeVisible (newButton);
|
|
||||||
return newButton;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Pills::cycle(bool right) {
|
void Pills::cycle(bool right) {
|
||||||
int currentIx = static_cast<const int>(distance(buttons.begin(), find(buttons.begin(), buttons.end(), selected)));
|
RangedAudioParameter *param{valueTreeState.getParameter("bank")};
|
||||||
currentIx += right ? 1 : buttons.size()-1;
|
jassert(dynamic_cast<AudioParameterInt*>(param) != nullptr);
|
||||||
buttons[currentIx % buttons.size()]->triggerClick();
|
AudioParameterInt* castParam{dynamic_cast<AudioParameterInt*> (param)};
|
||||||
|
int bank{castParam->get()};
|
||||||
|
|
||||||
|
int currentIx{static_cast<const int>(
|
||||||
|
distance(
|
||||||
|
pills.begin(),
|
||||||
|
find_if(
|
||||||
|
pills.begin(),
|
||||||
|
pills.end(),
|
||||||
|
[bank](unique_ptr<Pill>& pill){
|
||||||
|
return pill->bank == bank;})))};
|
||||||
|
currentIx += right ? 1 : pills.size()-1;
|
||||||
|
pills[currentIx % pills.size()]->textButton.triggerClick();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Pills::resized() {
|
void Pills::resized() {
|
||||||
int index = 0;
|
int index{0};
|
||||||
Rectangle<int> r (getLocalBounds());
|
Rectangle<int> r{getLocalBounds()};
|
||||||
const int equalWidth = r.proportionOfWidth(buttons.size() <= 0 ? 1.0 : 1.0f/buttons.size());
|
const int equalWidth{r.proportionOfWidth(pills.size() <= 0 ? 1.0 : 1.0f/pills.size())};
|
||||||
for(TextButton* t : buttons) {
|
for(auto& pill : pills) {
|
||||||
Rectangle<int> r2 (getLocalBounds());
|
Rectangle<int> r2 (getLocalBounds());
|
||||||
r2.removeFromLeft(equalWidth * index);
|
r2.removeFromLeft(equalWidth * index);
|
||||||
r2.removeFromRight(equalWidth * (buttons.size()-index-1));
|
r2.removeFromRight(equalWidth * (static_cast<int>(pills.size())-index-1));
|
||||||
t->setBounds (r2);
|
pill->setBounds(r2);
|
||||||
index++;
|
index++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -96,4 +178,4 @@ void Pills::resized() {
|
||||||
void Pills::paint(Graphics& g)
|
void Pills::paint(Graphics& g)
|
||||||
{
|
{
|
||||||
g.fillAll(MyColours::getUIColourIfAvailable(LookAndFeel_V4::ColourScheme::UIColour::windowBackground, Colours::lightgrey));
|
g.fillAll(MyColours::getUIColourIfAvailable(LookAndFeel_V4::ColourScheme::UIColour::windowBackground, Colours::lightgrey));
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,39 +8,73 @@
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
class Pills : public Component,
|
class Pill
|
||||||
public Button::Listener {
|
: public Component
|
||||||
|
, public Button::Listener
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Pill(
|
||||||
|
AudioProcessorValueTreeState& valueTreeState,
|
||||||
|
int bank,
|
||||||
|
bool isFirst,
|
||||||
|
bool isLast
|
||||||
|
);
|
||||||
|
~Pill();
|
||||||
|
|
||||||
|
void buttonClicked(Button* button) override;
|
||||||
|
|
||||||
|
void resized() override;
|
||||||
|
void paint(Graphics& g) override;
|
||||||
|
|
||||||
|
void bankChanged(int bank);
|
||||||
|
private:
|
||||||
|
|
||||||
|
AudioProcessorValueTreeState& valueTreeState;
|
||||||
|
int bank;
|
||||||
|
TextButton textButton;
|
||||||
|
|
||||||
|
friend class Pills;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Pills
|
||||||
|
: public Component
|
||||||
|
, public ValueTree::Listener
|
||||||
|
, public AudioProcessorValueTreeState::Listener
|
||||||
|
{
|
||||||
public:
|
public:
|
||||||
Pills(
|
Pills(
|
||||||
string label,
|
AudioProcessorValueTreeState& valueTreeState
|
||||||
const vector<string> &items,
|
|
||||||
const function<void (int)> &onItemSelected,
|
|
||||||
const function<int (const string&)> &itemToIDMapper,
|
|
||||||
int initiallySelectedItem
|
|
||||||
);
|
);
|
||||||
|
~Pills();
|
||||||
void setItems(
|
|
||||||
const vector<string> &items,
|
|
||||||
int initiallySelectedItem
|
|
||||||
);
|
|
||||||
|
|
||||||
void buttonClicked (Button* button) override;
|
|
||||||
void cycle(bool right);
|
void cycle(bool right);
|
||||||
|
|
||||||
private:
|
virtual void parameterChanged (const String& parameterID, float newValue) override;
|
||||||
string label;
|
|
||||||
vector<string> items;
|
|
||||||
function<void (int)> onItemSelected;
|
|
||||||
function<int (const string&)> itemToIDMapper;
|
|
||||||
|
|
||||||
OwnedArray<TextButton> buttons;
|
virtual void valueTreePropertyChanged (ValueTree& treeWhosePropertyHasChanged,
|
||||||
|
const Identifier& property) override;
|
||||||
|
inline virtual void valueTreeChildAdded (ValueTree& parentTree,
|
||||||
|
ValueTree& childWhichHasBeenAdded) override {};
|
||||||
|
inline virtual void valueTreeChildRemoved (ValueTree& parentTree,
|
||||||
|
ValueTree& childWhichHasBeenRemoved,
|
||||||
|
int indexFromWhichChildWasRemoved) override {};
|
||||||
|
inline virtual void valueTreeChildOrderChanged (ValueTree& parentTreeWhoseChildrenHaveMoved,
|
||||||
|
int oldIndex, int newIndex) override {};
|
||||||
|
inline virtual void valueTreeParentChanged (ValueTree& treeWhoseParentHasChanged) override {};
|
||||||
|
inline virtual void valueTreeRedirected (ValueTree& treeWhichHasBeenChanged) override {};
|
||||||
|
private:
|
||||||
|
void loadModelFrom(ValueTree& banks);
|
||||||
|
|
||||||
|
AudioProcessorValueTreeState& valueTreeState;
|
||||||
|
|
||||||
|
vector<unique_ptr<Pill>> pills;
|
||||||
Button *selected;
|
Button *selected;
|
||||||
|
|
||||||
TextButton* addToList (TextButton* newButton);
|
void updatePillToggleStates();
|
||||||
|
|
||||||
void populate(int initiallySelectedItem);
|
void populate(int initiallySelectedItem);
|
||||||
void resized() override;
|
void resized() override;
|
||||||
void paint(Graphics& g) override;
|
void paint(Graphics& g) override;
|
||||||
|
|
||||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pills)
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pills)
|
||||||
};
|
};
|
||||||
|
|
|
@ -10,22 +10,35 @@
|
||||||
|
|
||||||
#include "PluginProcessor.h"
|
#include "PluginProcessor.h"
|
||||||
#include "PluginEditor.h"
|
#include "PluginEditor.h"
|
||||||
|
#include "GuiConstants.h"
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
JuicySFAudioProcessorEditor::JuicySFAudioProcessorEditor (JuicySFAudioProcessor& p)
|
JuicySFAudioProcessorEditor::JuicySFAudioProcessorEditor(
|
||||||
: AudioProcessorEditor (&p),
|
JuicySFAudioProcessor& p,
|
||||||
processor (p),
|
AudioProcessorValueTreeState& valueTreeState)
|
||||||
midiKeyboard (p.keyboardState, SurjectiveMidiKeyboardComponent::horizontalKeyboard),
|
: AudioProcessorEditor{&p}
|
||||||
tablesComponent(p.getFluidSynthModel()),
|
, processor{p}
|
||||||
filePicker(p.getFluidSynthModel()),
|
, valueTreeState{valueTreeState}
|
||||||
slidersComponent{p.getFluidSynthModel()}
|
, midiKeyboard{p.keyboardState, SurjectiveMidiKeyboardComponent::horizontalKeyboard}
|
||||||
|
, tablesComponent{valueTreeState}
|
||||||
|
, filePicker{valueTreeState}
|
||||||
|
, slidersComponent{valueTreeState, p.getFluidSynthModel()}
|
||||||
{
|
{
|
||||||
// set resize limits for this plug-in
|
// set resize limits for this plug-in
|
||||||
setResizeLimits (500, 300, 1900, 1000);
|
setResizeLimits(
|
||||||
|
GuiConstants::minWidth,
|
||||||
|
GuiConstants::minHeight,
|
||||||
|
GuiConstants::maxWidth,
|
||||||
|
GuiConstants::maxHeight);
|
||||||
|
|
||||||
setSize (p.lastUIWidth, p.lastUIHeight);
|
lastUIWidth.referTo(valueTreeState.state.getChildWithName("uiState").getPropertyAsValue("width", nullptr));
|
||||||
|
lastUIHeight.referTo(valueTreeState.state.getChildWithName("uiState").getPropertyAsValue("height", nullptr));
|
||||||
|
|
||||||
// processor.subscribeToStateChanges(this);
|
// set our component's initial size to be the last one that was stored in the filter's settings
|
||||||
|
setSize(lastUIWidth.getValue(), lastUIHeight.getValue());
|
||||||
|
|
||||||
|
lastUIWidth.addListener(this);
|
||||||
|
lastUIHeight.addListener(this);
|
||||||
|
|
||||||
midiKeyboard.setName ("MIDI Keyboard");
|
midiKeyboard.setName ("MIDI Keyboard");
|
||||||
|
|
||||||
|
@ -33,7 +46,7 @@ JuicySFAudioProcessorEditor::JuicySFAudioProcessorEditor (JuicySFAudioProcessor&
|
||||||
tablesComponent.setWantsKeyboardFocus(false);
|
tablesComponent.setWantsKeyboardFocus(false);
|
||||||
|
|
||||||
setWantsKeyboardFocus(true);
|
setWantsKeyboardFocus(true);
|
||||||
addAndMakeVisible (midiKeyboard);
|
addAndMakeVisible(midiKeyboard);
|
||||||
|
|
||||||
addAndMakeVisible(slidersComponent);
|
addAndMakeVisible(slidersComponent);
|
||||||
addAndMakeVisible(tablesComponent);
|
addAndMakeVisible(tablesComponent);
|
||||||
|
@ -41,22 +54,16 @@ JuicySFAudioProcessorEditor::JuicySFAudioProcessorEditor (JuicySFAudioProcessor&
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
JuicySFAudioProcessorEditor::~JuicySFAudioProcessorEditor()
|
// called when the stored window size changes
|
||||||
{
|
void JuicySFAudioProcessorEditor::valueChanged(Value&) {
|
||||||
// processor.unsubscribeFromStateChanges(this);
|
setSize(lastUIWidth.getValue(), lastUIHeight.getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
//void JuicySFAudioProcessorEditor::getStateInformation (XmlElement& xml) {
|
JuicySFAudioProcessorEditor::~JuicySFAudioProcessorEditor()
|
||||||
// // save
|
{
|
||||||
// xml.setAttribute ("uiWidth", getWidth());
|
lastUIWidth.removeListener(this);
|
||||||
// xml.setAttribute ("uiHeight", getHeight());
|
lastUIHeight.removeListener(this);
|
||||||
//}
|
}
|
||||||
//
|
|
||||||
//void JuicySFAudioProcessorEditor::setStateInformation (XmlElement* xmlState) {
|
|
||||||
// // load
|
|
||||||
// setSize (xmlState->getIntAttribute ("uiWidth", getWidth()),
|
|
||||||
// xmlState->getIntAttribute ("uiHeight", getHeight()));
|
|
||||||
//}
|
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
void JuicySFAudioProcessorEditor::paint (Graphics& g)
|
void JuicySFAudioProcessorEditor::paint (Graphics& g)
|
||||||
|
@ -64,10 +71,6 @@ void JuicySFAudioProcessorEditor::paint (Graphics& g)
|
||||||
// (Our component is opaque, so we must completely fill the background with a solid colour)
|
// (Our component is opaque, so we must completely fill the background with a solid colour)
|
||||||
g.fillAll (getLookAndFeel().findColour (ResizableWindow::backgroundColourId));
|
g.fillAll (getLookAndFeel().findColour (ResizableWindow::backgroundColourId));
|
||||||
|
|
||||||
// g.setColour (Colours::white);
|
|
||||||
// g.setFont (15.0f);
|
|
||||||
// g.drawFittedText ("Hello World!", getLocalBounds(), Justification::centred, 1);
|
|
||||||
|
|
||||||
if (!focusInitialized) {
|
if (!focusInitialized) {
|
||||||
if (!hasKeyboardFocus(false) && isVisible()) {
|
if (!hasKeyboardFocus(false) && isVisible()) {
|
||||||
grabKeyboardFocus();
|
grabKeyboardFocus();
|
||||||
|
@ -83,13 +86,9 @@ void JuicySFAudioProcessorEditor::resized()
|
||||||
const int padding{8};
|
const int padding{8};
|
||||||
const int pianoHeight{70};
|
const int pianoHeight{70};
|
||||||
const int filePickerHeight{25};
|
const int filePickerHeight{25};
|
||||||
// const int slidersHeight{150};
|
|
||||||
Rectangle<int> r{getLocalBounds()};
|
Rectangle<int> r{getLocalBounds()};
|
||||||
filePicker.setBounds(r.removeFromTop(filePickerHeight + padding).reduced(padding, 0).withTrimmedTop(padding));
|
filePicker.setBounds(r.removeFromTop(filePickerHeight + padding).reduced(padding, 0).withTrimmedTop(padding));
|
||||||
|
|
||||||
// Rectangle<int> r2 (getLocalBounds());
|
|
||||||
// slidersComponent.setBounds(r2.removeFromLeft(filePickerWidth + padding).reduced(padding, 0).withTrimmedLeft(padding));
|
|
||||||
|
|
||||||
midiKeyboard.setBounds (r.removeFromBottom (pianoHeight).reduced(padding, 0));
|
midiKeyboard.setBounds (r.removeFromBottom (pianoHeight).reduced(padding, 0));
|
||||||
|
|
||||||
Rectangle<int> rContent{r.reduced(0, padding)};
|
Rectangle<int> rContent{r.reduced(0, padding)};
|
||||||
|
@ -97,28 +96,11 @@ void JuicySFAudioProcessorEditor::resized()
|
||||||
|
|
||||||
tablesComponent.setBounds(rContent);
|
tablesComponent.setBounds(rContent);
|
||||||
|
|
||||||
|
lastUIWidth = getWidth();
|
||||||
processor.lastUIWidth = getWidth();
|
lastUIHeight = getHeight();
|
||||||
processor.lastUIHeight = getHeight();
|
|
||||||
|
|
||||||
// Rectangle<int> r2 (getLocalBounds());
|
|
||||||
// r2.reduce(0, padding);
|
|
||||||
// r2.removeFromBottom(pianoHeight);
|
|
||||||
// r2.removeFromTop(filePickerHeight);
|
|
||||||
// tablesComponent.setBounds (r2);
|
|
||||||
//
|
|
||||||
// Rectangle<int> r3 (getLocalBounds());
|
|
||||||
// r3.removeFromTop(filePickerHeight);
|
|
||||||
//
|
|
||||||
// filePicker.setBounds(r3);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool JuicySFAudioProcessorEditor::keyPressed(const KeyPress &key) {
|
bool JuicySFAudioProcessorEditor::keyPressed(const KeyPress &key) {
|
||||||
// if (!hasKeyboardFocus(false))
|
|
||||||
// return false;
|
|
||||||
// if (key.getKeyCode() == KeyPress::upKey){
|
|
||||||
// }
|
|
||||||
// cout << "hey\n";
|
|
||||||
const int cursorKeys[] = {
|
const int cursorKeys[] = {
|
||||||
KeyPress::leftKey,
|
KeyPress::leftKey,
|
||||||
KeyPress::rightKey,
|
KeyPress::rightKey,
|
||||||
|
@ -134,20 +116,9 @@ bool JuicySFAudioProcessorEditor::keyPressed(const KeyPress &key) {
|
||||||
} else {
|
} else {
|
||||||
return midiKeyboard.keyPressed(key);
|
return midiKeyboard.keyPressed(key);
|
||||||
}
|
}
|
||||||
// for(auto childComponent : getChildren()) {
|
|
||||||
// if (childComponent->keyPressed(key)) return true;
|
|
||||||
// }
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool JuicySFAudioProcessorEditor::keyStateChanged (bool isKeyDown) {
|
bool JuicySFAudioProcessorEditor::keyStateChanged (bool isKeyDown) {
|
||||||
return midiKeyboard.keyStateChanged(isKeyDown);
|
return midiKeyboard.keyStateChanged(isKeyDown);
|
||||||
// for(auto childComponent : getChildren()) {
|
|
||||||
// if (childComponent->keyStateChanged(isKeyDown)) return true;
|
|
||||||
// }
|
|
||||||
// return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FilePickerFragment& JuicySFAudioProcessorEditor::getFilePicker() {
|
|
||||||
return filePicker;
|
|
||||||
}
|
|
|
@ -14,21 +14,21 @@
|
||||||
#include "PluginProcessor.h"
|
#include "PluginProcessor.h"
|
||||||
#include "TablesComponent.h"
|
#include "TablesComponent.h"
|
||||||
#include "SurjectiveMidiKeyboardComponent.h"
|
#include "SurjectiveMidiKeyboardComponent.h"
|
||||||
#include "FilePickerFragment.h"
|
|
||||||
#include "ExposesComponents.h"
|
|
||||||
#include "FilePicker.h"
|
#include "FilePicker.h"
|
||||||
#include "StateChangeSubscriber.h"
|
|
||||||
#include "SlidersComponent.h"
|
#include "SlidersComponent.h"
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
class JuicySFAudioProcessorEditor : public AudioProcessorEditor,
|
class JuicySFAudioProcessorEditor
|
||||||
public ExposesComponents/*,
|
: public AudioProcessorEditor
|
||||||
public StateChangeSubscriber*/
|
, private Value::Listener
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
JuicySFAudioProcessorEditor (JuicySFAudioProcessor&);
|
JuicySFAudioProcessorEditor(
|
||||||
|
JuicySFAudioProcessor&,
|
||||||
|
AudioProcessorValueTreeState& valueTreeState
|
||||||
|
);
|
||||||
~JuicySFAudioProcessorEditor();
|
~JuicySFAudioProcessorEditor();
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
|
@ -38,16 +38,20 @@ public:
|
||||||
bool keyPressed(const KeyPress &key) override;
|
bool keyPressed(const KeyPress &key) override;
|
||||||
bool keyStateChanged (bool isKeyDown) override;
|
bool keyStateChanged (bool isKeyDown) override;
|
||||||
|
|
||||||
// void getStateInformation (XmlElement& xml) override;
|
|
||||||
// void setStateInformation (XmlElement* xmlState) override;
|
|
||||||
|
|
||||||
virtual FilePickerFragment& getFilePicker() override;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void valueChanged (Value&) override;
|
||||||
|
|
||||||
// This reference is provided as a quick way for your editor to
|
// This reference is provided as a quick way for your editor to
|
||||||
// access the processor object that created it.
|
// access the processor object that created it.
|
||||||
JuicySFAudioProcessor& processor;
|
JuicySFAudioProcessor& processor;
|
||||||
|
|
||||||
|
AudioProcessorValueTreeState& valueTreeState;
|
||||||
|
|
||||||
|
// these are used to persist the UI's size - the values are stored along with the
|
||||||
|
// filter's other parameters, and the UI component will update them when it gets
|
||||||
|
// resized.
|
||||||
|
Value lastUIWidth, lastUIHeight;
|
||||||
|
|
||||||
SurjectiveMidiKeyboardComponent midiKeyboard;
|
SurjectiveMidiKeyboardComponent midiKeyboard;
|
||||||
TablesComponent tablesComponent;
|
TablesComponent tablesComponent;
|
||||||
FilePicker filePicker;
|
FilePicker filePicker;
|
||||||
|
|
|
@ -10,47 +10,66 @@
|
||||||
|
|
||||||
#include "PluginProcessor.h"
|
#include "PluginProcessor.h"
|
||||||
#include "PluginEditor.h"
|
#include "PluginEditor.h"
|
||||||
#include "SoundfontSynthVoice.h"
|
|
||||||
#include "SoundfontSynthSound.h"
|
|
||||||
#include "ExposesComponents.h"
|
|
||||||
#include "MidiConstants.h"
|
#include "MidiConstants.h"
|
||||||
#include "Util.h"
|
#include "Util.h"
|
||||||
|
#include "GuiConstants.h"
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using Parameter = AudioProcessorValueTreeState::Parameter;
|
||||||
|
|
||||||
AudioProcessor* JUCE_CALLTYPE createPluginFilter();
|
AudioProcessor* JUCE_CALLTYPE createPluginFilter();
|
||||||
|
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
JuicySFAudioProcessor::JuicySFAudioProcessor()
|
JuicySFAudioProcessor::JuicySFAudioProcessor()
|
||||||
: AudioProcessor (getBusesProperties()),
|
: AudioProcessor{getBusesProperties()}
|
||||||
lastUIWidth(400),
|
, valueTreeState{
|
||||||
lastUIHeight(300),
|
*this,
|
||||||
soundFontPath(String()),
|
nullptr,
|
||||||
lastPreset(-1),
|
"MYPLUGINSETTINGS",
|
||||||
lastBank(-1),
|
createParameterLayout()}
|
||||||
fluidSynthModel(*this)/*,
|
, fluidSynthModel{valueTreeState}
|
||||||
pluginEditor(nullptr)*/
|
|
||||||
{
|
{
|
||||||
|
valueTreeState.state.appendChild({ "uiState", {
|
||||||
|
{ "width", GuiConstants::minWidth },
|
||||||
|
{ "height", GuiConstants::minHeight }
|
||||||
|
}, {} }, nullptr);
|
||||||
|
valueTreeState.state.appendChild({ "soundFont", {
|
||||||
|
{ "path", "" },
|
||||||
|
}, {} }, nullptr);
|
||||||
|
// no properties, no subtrees (yet)
|
||||||
|
valueTreeState.state.appendChild({ "banks", {}, {} }, nullptr);
|
||||||
|
|
||||||
initialiseSynth();
|
initialiseSynth();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AudioProcessorValueTreeState::ParameterLayout JuicySFAudioProcessor::createParameterLayout() {
|
||||||
|
// https://stackoverflow.com/a/8469002/5257399
|
||||||
|
unique_ptr<AudioParameterInt> params[] {
|
||||||
|
// SoundFont 2.4 spec section 7.2: zero through 127, or 128.
|
||||||
|
make_unique<AudioParameterInt>("bank", "which bank is selected in the soundfont", MidiConstants::midiMinValue, 128, MidiConstants::midiMinValue, "Bank" ),
|
||||||
|
// note: banks may be sparse, and lack a 0th preset. so defend against this.
|
||||||
|
make_unique<AudioParameterInt>("preset", "which patch (aka patch, program, instrument) is selected in the soundfont", MidiConstants::midiMinValue, MidiConstants::midiMaxValue, MidiConstants::midiMinValue, "Preset" ),
|
||||||
|
make_unique<AudioParameterInt>("attack", "volume envelope attack time", MidiConstants::midiMinValue, MidiConstants::midiMaxValue, MidiConstants::midiMinValue, "A" ),
|
||||||
|
make_unique<AudioParameterInt>("decay", "volume envelope sustain attentuation", MidiConstants::midiMinValue, MidiConstants::midiMaxValue, MidiConstants::midiMinValue, "D" ),
|
||||||
|
make_unique<AudioParameterInt>("sustain", "volume envelope decay time", MidiConstants::midiMinValue, MidiConstants::midiMaxValue, MidiConstants::midiMinValue, "S" ),
|
||||||
|
make_unique<AudioParameterInt>("release", "volume envelope release time", MidiConstants::midiMinValue, MidiConstants::midiMaxValue, MidiConstants::midiMinValue, "R" ),
|
||||||
|
make_unique<AudioParameterInt>("filterCutOff", "low-pass filter cut-off frequency", MidiConstants::midiMinValue, MidiConstants::midiMaxValue, MidiConstants::midiMinValue, "Cut" ),
|
||||||
|
make_unique<AudioParameterInt>("filterResonance", "low-pass filter resonance attentuation", MidiConstants::midiMinValue, MidiConstants::midiMaxValue, MidiConstants::midiMinValue, "Res" ),
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
make_move_iterator(begin(params)),
|
||||||
|
make_move_iterator(end(params))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
JuicySFAudioProcessor::~JuicySFAudioProcessor()
|
JuicySFAudioProcessor::~JuicySFAudioProcessor()
|
||||||
{
|
{
|
||||||
// delete fluidSynthModel;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void JuicySFAudioProcessor::initialiseSynth() {
|
void JuicySFAudioProcessor::initialiseSynth() {
|
||||||
fluidSynthModel.initialise();
|
fluidSynthModel.initialise();
|
||||||
|
|
||||||
fluidSynth = fluidSynthModel.getSynth();
|
|
||||||
|
|
||||||
const int numVoices = 8;
|
|
||||||
|
|
||||||
// Add some voices...
|
|
||||||
for (int i = numVoices; --i >= 0;)
|
|
||||||
synth.addVoice (new SoundfontSynthVoice(fluidSynthModel.getSynth()));
|
|
||||||
|
|
||||||
// ..and give the synth a sound to play
|
|
||||||
synth.addSound (new SoundfontSynthSound());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
|
@ -84,22 +103,23 @@ double JuicySFAudioProcessor::getTailLengthSeconds() const
|
||||||
|
|
||||||
int JuicySFAudioProcessor::getNumPrograms()
|
int JuicySFAudioProcessor::getNumPrograms()
|
||||||
{
|
{
|
||||||
return 1; // NB: some hosts don't cope very well if you tell them there are 0 programs,
|
return fluidSynthModel.getNumPrograms(); // NB: some hosts don't cope very well if you tell them there are 0 programs,
|
||||||
// so this should be at least 1, even if you're not really implementing programs.
|
// so this should be at least 1, even if you're not really implementing programs.
|
||||||
}
|
}
|
||||||
|
|
||||||
int JuicySFAudioProcessor::getCurrentProgram()
|
int JuicySFAudioProcessor::getCurrentProgram()
|
||||||
{
|
{
|
||||||
return 0;
|
return fluidSynthModel.getCurrentProgram();
|
||||||
}
|
}
|
||||||
|
|
||||||
void JuicySFAudioProcessor::setCurrentProgram (int index)
|
void JuicySFAudioProcessor::setCurrentProgram(int index)
|
||||||
{
|
{
|
||||||
|
fluidSynthModel.setCurrentProgram(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
const String JuicySFAudioProcessor::getProgramName (int index)
|
const String JuicySFAudioProcessor::getProgramName(int index)
|
||||||
{
|
{
|
||||||
return {};
|
return fluidSynthModel.getProgramName(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
void JuicySFAudioProcessor::changeProgramName (int index, const String& newName)
|
void JuicySFAudioProcessor::changeProgramName (int index, const String& newName)
|
||||||
|
@ -148,89 +168,17 @@ AudioProcessor::BusesProperties JuicySFAudioProcessor::getBusesProperties() {
|
||||||
.withOutput ("Output", AudioChannelSet::stereo(), true);
|
.withOutput ("Output", AudioChannelSet::stereo(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void JuicySFAudioProcessor::processBlock (AudioBuffer<float>& buffer, MidiBuffer& midiMessages) {
|
void JuicySFAudioProcessor::processBlock(AudioBuffer<float>& buffer, MidiBuffer& midiMessages) {
|
||||||
jassert (!isUsingDoublePrecision());
|
jassert (!isUsingDoublePrecision());
|
||||||
const int numSamples = buffer.getNumSamples();
|
|
||||||
|
|
||||||
// Now pass any incoming midi messages to our keyboard state object, and let it
|
// Now pass any incoming midi messages to our keyboard state object, and let it
|
||||||
// add messages to the buffer if the user is clicking on the on-screen keys
|
// add messages to the buffer if the user is clicking on the on-screen keys
|
||||||
keyboardState.processNextMidiBuffer (midiMessages, 0, numSamples, true);
|
keyboardState.processNextMidiBuffer(midiMessages, 0, buffer.getNumSamples(), true);
|
||||||
|
|
||||||
MidiBuffer processedMidi;
|
fluidSynthModel.processBlock(buffer, midiMessages);
|
||||||
int time;
|
|
||||||
MidiMessage m;
|
|
||||||
|
|
||||||
// TODO: factor into a MidiCollector
|
|
||||||
for (MidiBuffer::Iterator i (midiMessages); i.getNextEvent (m, time);) {
|
|
||||||
DEBUG_PRINT ( m.getDescription() );
|
|
||||||
|
|
||||||
// explicitly not handling note_on/off, or pitch_bend, because these are (for better or worse)
|
|
||||||
// responsibilities of SoundfontSynthVoice.
|
|
||||||
// well, by that logic maybe I should move program change onto Voice. but it doesn't feel like a per-voice concern.
|
|
||||||
if (m.isController()) {
|
|
||||||
fluid_midi_event_t *midi_event(new_fluid_midi_event());
|
|
||||||
fluid_midi_event_set_type(midi_event, static_cast<int>(CONTROL_CHANGE));
|
|
||||||
fluid_midi_event_set_channel(midi_event, fluidSynthModel.getChannel());
|
|
||||||
fluid_midi_event_set_control(midi_event, m.getControllerNumber());
|
|
||||||
fluid_midi_event_set_value(midi_event, m.getControllerValue());
|
|
||||||
fluid_synth_handle_midi_event(fluidSynth, midi_event);
|
|
||||||
delete_fluid_midi_event(midi_event);
|
|
||||||
} else if (m.isProgramChange()) {
|
|
||||||
fluid_midi_event_t *midi_event(new_fluid_midi_event());
|
|
||||||
fluid_midi_event_set_type(midi_event, static_cast<int>(PROGRAM_CHANGE));
|
|
||||||
fluid_midi_event_set_channel(midi_event, fluidSynthModel.getChannel());
|
|
||||||
fluid_midi_event_set_program(midi_event, m.getProgramChangeNumber());
|
|
||||||
fluid_synth_handle_midi_event(fluidSynth, midi_event);
|
|
||||||
delete_fluid_midi_event(midi_event);
|
|
||||||
} else if (m.isPitchWheel()) {
|
|
||||||
fluid_midi_event_t *midi_event(new_fluid_midi_event());
|
|
||||||
fluid_midi_event_set_type(midi_event, static_cast<int>(PITCH_BEND));
|
|
||||||
fluid_midi_event_set_channel(midi_event, fluidSynthModel.getChannel());
|
|
||||||
fluid_midi_event_set_pitch(midi_event, m.getPitchWheelValue());
|
|
||||||
fluid_synth_handle_midi_event(fluidSynth, midi_event);
|
|
||||||
delete_fluid_midi_event(midi_event);
|
|
||||||
} else if (m.isChannelPressure()) {
|
|
||||||
fluid_midi_event_t *midi_event(new_fluid_midi_event());
|
|
||||||
fluid_midi_event_set_type(midi_event, static_cast<int>(CHANNEL_PRESSURE));
|
|
||||||
fluid_midi_event_set_channel(midi_event, fluidSynthModel.getChannel());
|
|
||||||
fluid_midi_event_set_program(midi_event, m.getChannelPressureValue());
|
|
||||||
fluid_synth_handle_midi_event(fluidSynth, midi_event);
|
|
||||||
delete_fluid_midi_event(midi_event);
|
|
||||||
} else if (m.isAftertouch()) {
|
|
||||||
fluid_midi_event_t *midi_event(new_fluid_midi_event());
|
|
||||||
fluid_midi_event_set_type(midi_event, static_cast<int>(KEY_PRESSURE));
|
|
||||||
fluid_midi_event_set_channel(midi_event, fluidSynthModel.getChannel());
|
|
||||||
fluid_midi_event_set_key(midi_event, m.getNoteNumber());
|
|
||||||
fluid_midi_event_set_value(midi_event, m.getAfterTouchValue());
|
|
||||||
fluid_synth_handle_midi_event(fluidSynth, midi_event);
|
|
||||||
delete_fluid_midi_event(midi_event);
|
|
||||||
// } else if (m.isMetaEvent()) {
|
|
||||||
// fluid_midi_event_t *midi_event(new_fluid_midi_event());
|
|
||||||
// fluid_midi_event_set_type(midi_event, static_cast<int>(MIDI_SYSTEM_RESET));
|
|
||||||
// fluid_synth_handle_midi_event(fluidSynth, midi_event);
|
|
||||||
// delete_fluid_midi_event(midi_event);
|
|
||||||
} else if (m.isSysEx()) {
|
|
||||||
fluid_midi_event_t *midi_event(new_fluid_midi_event());
|
|
||||||
fluid_midi_event_set_type(midi_event, static_cast<int>(MIDI_SYSEX));
|
|
||||||
// I assume that the MidiMessage's sysex buffer would be freed anyway when MidiMessage is destroyed, so set dynamic=false
|
|
||||||
// to ensure that fluidsynth does not attempt to free the sysex buffer during delete_fluid_midi_event()
|
|
||||||
fluid_midi_event_set_sysex(midi_event, const_cast<juce::uint8*>(m.getSysExData()), m.getSysExDataSize(), static_cast<int>(false));
|
|
||||||
fluid_synth_handle_midi_event(fluidSynth, midi_event);
|
|
||||||
delete_fluid_midi_event(midi_event);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// int pval;
|
|
||||||
// 73: 64 attack
|
|
||||||
// 75: decay
|
|
||||||
// 79: sustain
|
|
||||||
// 72: 64 release
|
|
||||||
// fluid_synth_get_cc(fluidSynth, 0, 73, &pval);
|
|
||||||
// Logger::outputDebugString ( juce::String::formatted("hey: %d\n", pval) );
|
|
||||||
|
|
||||||
// and now get our synth to process these midi events and generate its output.
|
// and now get our synth to process these midi events and generate its output.
|
||||||
synth.renderNextBlock (buffer, midiMessages, 0, numSamples);
|
// synth.renderNextBlock(buffer, midiMessages, 0, numSamples);
|
||||||
fluid_synth_process(fluidSynth, numSamples, 0, nullptr, buffer.getNumChannels(), buffer.getArrayOfWritePointers());
|
|
||||||
|
|
||||||
// (see juce_VST3_Wrapper.cpp for the assertion this would trip otherwise)
|
// (see juce_VST3_Wrapper.cpp for the assertion this would trip otherwise)
|
||||||
// we are !JucePlugin_ProducesMidiOutput, so clear remaining MIDI messages from our buffer
|
// we are !JucePlugin_ProducesMidiOutput, so clear remaining MIDI messages from our buffer
|
||||||
|
@ -255,7 +203,7 @@ bool JuicySFAudioProcessor::hasEditor() const
|
||||||
AudioProcessorEditor* JuicySFAudioProcessor::createEditor()
|
AudioProcessorEditor* JuicySFAudioProcessor::createEditor()
|
||||||
{
|
{
|
||||||
// grab a raw pointer to it for our own use
|
// grab a raw pointer to it for our own use
|
||||||
return /*pluginEditor = */new JuicySFAudioProcessorEditor (*this);
|
return /*pluginEditor = */new JuicySFAudioProcessorEditor (*this, valueTreeState);
|
||||||
}
|
}
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
|
@ -266,27 +214,39 @@ void JuicySFAudioProcessor::getStateInformation (MemoryBlock& destData)
|
||||||
// as intermediaries to make it easy to save and load complex data.
|
// as intermediaries to make it easy to save and load complex data.
|
||||||
|
|
||||||
// Create an outer XML element..
|
// Create an outer XML element..
|
||||||
XmlElement xml ("MYPLUGINSETTINGS");
|
XmlElement xml{"MYPLUGINSETTINGS"};
|
||||||
|
|
||||||
// add some attributes to it..
|
|
||||||
xml.setAttribute ("uiWidth", lastUIWidth);
|
|
||||||
xml.setAttribute ("uiHeight", lastUIHeight);
|
|
||||||
xml.setAttribute ("soundFontPath", soundFontPath);
|
|
||||||
xml.setAttribute ("preset", lastPreset);
|
|
||||||
xml.setAttribute ("bank", lastBank);
|
|
||||||
|
|
||||||
// list<StateChangeSubscriber*>::iterator p;
|
|
||||||
// for(p = stateChangeSubscribers.begin(); p != stateChangeSubscribers.end(); p++) {
|
|
||||||
// (*p)->getStateInformation(xml);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Store the values of all our parameters, using their param ID as the XML attribute
|
// Store the values of all our parameters, using their param ID as the XML attribute
|
||||||
for (auto* param : getParameters())
|
XmlElement* params{xml.createNewChildElement("params")};
|
||||||
if (auto* p = dynamic_cast<AudioProcessorParameterWithID*> (param))
|
for (auto* param : getParameters()) {
|
||||||
xml.setAttribute (p->paramID, p->getValue());
|
if (auto* p = dynamic_cast<AudioProcessorParameterWithID*> (param)) {
|
||||||
|
params->setAttribute(p->paramID, p->getValue());
|
||||||
// then use this helper function to stuff it into the binary blob and return it..
|
}
|
||||||
copyXmlToBinary (xml, destData);
|
}
|
||||||
|
{
|
||||||
|
ValueTree tree{valueTreeState.state.getChildWithName("uiState")};
|
||||||
|
XmlElement* newElement{xml.createNewChildElement("uiState")};
|
||||||
|
{
|
||||||
|
double value{tree.getProperty("width", GuiConstants::minWidth)};
|
||||||
|
newElement->setAttribute("width", value);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
double value{tree.getProperty("height", GuiConstants::minHeight)};
|
||||||
|
newElement->setAttribute("height", value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
ValueTree tree{valueTreeState.state.getChildWithName("soundFont")};
|
||||||
|
XmlElement* newElement{xml.createNewChildElement("soundFont")};
|
||||||
|
{
|
||||||
|
String value{tree.getProperty("path", "")};
|
||||||
|
newElement->setAttribute("path", value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DEBUG_PRINT(xml.createDocument("",false,false));
|
||||||
|
|
||||||
|
copyXmlToBinary(xml, destData);
|
||||||
}
|
}
|
||||||
|
|
||||||
void JuicySFAudioProcessor::setStateInformation (const void* data, int sizeInBytes)
|
void JuicySFAudioProcessor::setStateInformation (const void* data, int sizeInBytes)
|
||||||
|
@ -294,84 +254,51 @@ void JuicySFAudioProcessor::setStateInformation (const void* data, int sizeInByt
|
||||||
// You should use this method to restore your parameters from this memory block,
|
// You should use this method to restore your parameters from this memory block,
|
||||||
// whose contents will have been created by the getStateInformation() call.
|
// whose contents will have been created by the getStateInformation() call.
|
||||||
// This getXmlFromBinary() helper function retrieves our XML from the binary blob..
|
// This getXmlFromBinary() helper function retrieves our XML from the binary blob..
|
||||||
ScopedPointer<XmlElement> xmlState (getXmlFromBinary (data, sizeInBytes));
|
shared_ptr<XmlElement> xmlState{getXmlFromBinary(data, sizeInBytes)};
|
||||||
|
DEBUG_PRINT(xmlState->createDocument("",false,false));
|
||||||
if (xmlState != nullptr)
|
|
||||||
{
|
if (xmlState.get() != nullptr) {
|
||||||
// make sure that it's actually our type of XML object..
|
// make sure that it's actually our type of XML object..
|
||||||
if (xmlState->hasTagName ("MYPLUGINSETTINGS"))
|
if (xmlState->hasTagName(valueTreeState.state.getType())) {
|
||||||
{
|
XmlElement* params{xmlState->getChildByName("params")};
|
||||||
// list<StateChangeSubscriber*>::iterator p;
|
if (params)
|
||||||
// for(p = stateChangeSubscribers.begin(); p != stateChangeSubscribers.end(); p++) {
|
for (auto* param : getParameters())
|
||||||
// (*p)->setStateInformation(xmlState);
|
if (auto* p = dynamic_cast<AudioProcessorParameterWithID*>(param))
|
||||||
// }
|
p->setValue(static_cast<float>(params->getDoubleAttribute(p->paramID, p->getValue())));
|
||||||
|
|
||||||
// ok, now pull out our last window size..
|
{
|
||||||
lastUIWidth = jmax (xmlState->getIntAttribute ("uiWidth", lastUIWidth), 400);
|
XmlElement* xmlElement{xmlState->getChildByName("soundFont")};
|
||||||
lastUIHeight = jmax (xmlState->getIntAttribute ("uiHeight", lastUIHeight), 300);
|
if (xmlElement) {
|
||||||
soundFontPath = xmlState->getStringAttribute ("soundFontPath", soundFontPath);
|
ValueTree tree{valueTreeState.state.getChildWithName("soundFont")};
|
||||||
lastPreset = xmlState->getIntAttribute ("preset", lastPreset);
|
Value value{tree.getPropertyAsValue("path", nullptr)};
|
||||||
lastBank = xmlState->getIntAttribute ("bank", lastBank);
|
value = xmlElement->getStringAttribute("path", value.getValue());
|
||||||
|
}
|
||||||
// Now reload our parameters..
|
}
|
||||||
for (auto* param : getParameters())
|
{
|
||||||
if (auto* p = dynamic_cast<AudioProcessorParameterWithID*> (param))
|
ValueTree tree{valueTreeState.state.getChildWithName("uiState")};
|
||||||
p->setValue ((float) xmlState->getDoubleAttribute (p->paramID, p->getValue()));
|
XmlElement* xmlElement{xmlState->getChildByName("uiState")};
|
||||||
|
if (xmlElement) {
|
||||||
fluidSynthModel.onFileNameChanged(soundFontPath, lastBank, lastPreset);
|
{
|
||||||
|
Value value{tree.getPropertyAsValue("width", nullptr)};
|
||||||
AudioProcessorEditor* editor = getActiveEditor();
|
value = xmlElement->getIntAttribute("width", value.getValue());
|
||||||
if (editor != nullptr) {
|
}
|
||||||
editor->setSize(lastUIWidth, lastUIHeight);
|
{
|
||||||
|
Value value{tree.getPropertyAsValue("height", nullptr)};
|
||||||
jassert(dynamic_cast<ExposesComponents*> (editor) != nullptr);
|
value = xmlElement->getIntAttribute("height", value.getValue());
|
||||||
ExposesComponents* exposesComponents = dynamic_cast<ExposesComponents*> (editor);
|
}
|
||||||
exposesComponents->getFilePicker().setDisplayedFilePath(soundFontPath);
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// const String& currentSoundFontAbsPath = fluidSynthModel->getCurrentSoundFontAbsPath();
|
|
||||||
// if (currentSoundFontAbsPath.isNotEmpty()) {
|
|
||||||
// fileChooser.setCurrentFile(File(currentSoundFontAbsPath), true, dontSendNotification);
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//void JuicySFAudioProcessor::subscribeToStateChanges(StateChangeSubscriber* subscriber) {
|
|
||||||
// stateChangeSubscribers.push_back(subscriber);
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
//void JuicySFAudioProcessor::unsubscribeFromStateChanges(StateChangeSubscriber* subscriber) {
|
|
||||||
// stateChangeSubscribers.remove(subscriber);
|
|
||||||
//}
|
|
||||||
|
|
||||||
// FluidSynth only supports float in its process function, so that's all we can support.
|
// FluidSynth only supports float in its process function, so that's all we can support.
|
||||||
bool JuicySFAudioProcessor::supportsDoublePrecisionProcessing() const {
|
bool JuicySFAudioProcessor::supportsDoublePrecisionProcessing() const {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
FluidSynthModel* JuicySFAudioProcessor::getFluidSynthModel() {
|
FluidSynthModel& JuicySFAudioProcessor::getFluidSynthModel() {
|
||||||
return &fluidSynthModel;
|
return fluidSynthModel;
|
||||||
}
|
|
||||||
|
|
||||||
void JuicySFAudioProcessor::setSoundFontPath(const String& value) {
|
|
||||||
soundFontPath = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
String& JuicySFAudioProcessor::getSoundFontPath() {
|
|
||||||
return soundFontPath;
|
|
||||||
}
|
|
||||||
int JuicySFAudioProcessor::getPreset() {
|
|
||||||
return lastPreset;
|
|
||||||
}
|
|
||||||
int JuicySFAudioProcessor::getBank() {
|
|
||||||
return lastBank;
|
|
||||||
}
|
|
||||||
void JuicySFAudioProcessor::setPreset(int preset) {
|
|
||||||
lastPreset = preset;
|
|
||||||
}
|
|
||||||
void JuicySFAudioProcessor::setBank(int bank) {
|
|
||||||
lastBank = bank;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
|
|
|
@ -12,8 +12,6 @@
|
||||||
|
|
||||||
#include "../JuceLibraryCode/JuceHeader.h"
|
#include "../JuceLibraryCode/JuceHeader.h"
|
||||||
#include "FluidSynthModel.h"
|
#include "FluidSynthModel.h"
|
||||||
#include "StateChangeSubscriber.h"
|
|
||||||
#include "SharesParams.h"
|
|
||||||
#include <list>
|
#include <list>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
@ -21,8 +19,7 @@ using namespace std;
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
class JuicySFAudioProcessor : public AudioProcessor,
|
class JuicySFAudioProcessor : public AudioProcessor
|
||||||
public SharesParams
|
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
|
@ -63,41 +60,22 @@ public:
|
||||||
|
|
||||||
bool supportsDoublePrecisionProcessing() const override;
|
bool supportsDoublePrecisionProcessing() const override;
|
||||||
|
|
||||||
FluidSynthModel* getFluidSynthModel();
|
FluidSynthModel& getFluidSynthModel();
|
||||||
|
|
||||||
MidiKeyboardState keyboardState;
|
MidiKeyboardState keyboardState;
|
||||||
|
|
||||||
virtual void setSoundFontPath(const String& value) override;
|
|
||||||
virtual String& getSoundFontPath() override;
|
|
||||||
virtual int getPreset() override;
|
|
||||||
virtual void setPreset(int preset) override;
|
|
||||||
virtual int getBank() override;
|
|
||||||
virtual void setBank(int bank) override;
|
|
||||||
|
|
||||||
// void subscribeToStateChanges(StateChangeSubscriber* subscriber);
|
|
||||||
// void unsubscribeFromStateChanges(StateChangeSubscriber* subscriber);
|
|
||||||
|
|
||||||
int lastUIWidth, lastUIHeight;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void initialiseSynth();
|
void initialiseSynth();
|
||||||
|
|
||||||
String soundFontPath;
|
AudioProcessorValueTreeState valueTreeState;
|
||||||
int lastPreset;
|
|
||||||
int lastBank;
|
|
||||||
|
|
||||||
FluidSynthModel fluidSynthModel;
|
FluidSynthModel fluidSynthModel;
|
||||||
fluid_synth_t* fluidSynth;
|
|
||||||
Synthesiser synth;
|
Synthesiser synth;
|
||||||
|
|
||||||
// // just a raw pointer; we do not own
|
AudioProcessorValueTreeState::ParameterLayout createParameterLayout();
|
||||||
// AudioProcessorEditor* pluginEditor;
|
|
||||||
|
|
||||||
// list<StateChangeSubscriber*> stateChangeSubscribers;
|
|
||||||
|
|
||||||
static BusesProperties getBusesProperties();
|
static BusesProperties getBusesProperties();
|
||||||
|
|
||||||
// Model* model;
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JuicySFAudioProcessor)
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JuicySFAudioProcessor)
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,21 +0,0 @@
|
||||||
//
|
|
||||||
// Created by Alex Birch on 17/09/2017.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include "Preset.h"
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
Preset::Preset(
|
|
||||||
int preset,
|
|
||||||
string name
|
|
||||||
) : preset(preset),
|
|
||||||
name(name) {}
|
|
||||||
|
|
||||||
int Preset::getPreset() {
|
|
||||||
return preset;
|
|
||||||
}
|
|
||||||
|
|
||||||
string Preset::getName() {
|
|
||||||
return name;
|
|
||||||
}
|
|
|
@ -1,24 +0,0 @@
|
||||||
//
|
|
||||||
// Created by Alex Birch on 17/09/2017.
|
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "../JuceLibraryCode/JuceHeader.h"
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
class Preset {
|
|
||||||
public:
|
|
||||||
Preset(
|
|
||||||
int preset,
|
|
||||||
string name
|
|
||||||
);
|
|
||||||
|
|
||||||
int getPreset();
|
|
||||||
string getName();
|
|
||||||
|
|
||||||
private:
|
|
||||||
int preset;
|
|
||||||
string name;
|
|
||||||
};
|
|
|
@ -1,10 +0,0 @@
|
||||||
//
|
|
||||||
// Created by Alex Birch on 17/09/2017.
|
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "Preset.h"
|
|
||||||
#include <map>
|
|
||||||
|
|
||||||
typedef std::multimap<int, Preset> BanksToPresets;
|
|
|
@ -1,23 +0,0 @@
|
||||||
//
|
|
||||||
// Created by Alex Birch on 10/04/2018.
|
|
||||||
// Copyright (c) 2018 Birchlabs. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "../JuceLibraryCode/JuceHeader.h"
|
|
||||||
|
|
||||||
class SharesParams {
|
|
||||||
public:
|
|
||||||
virtual ~SharesParams() {}
|
|
||||||
|
|
||||||
virtual void setSoundFontPath(const String& value) = 0;
|
|
||||||
virtual String& getSoundFontPath() = 0;
|
|
||||||
virtual int getPreset() = 0;
|
|
||||||
virtual void setPreset(int preset) = 0;
|
|
||||||
virtual int getBank() = 0;
|
|
||||||
virtual void setBank(int bank) = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -9,11 +9,21 @@
|
||||||
#include "SlidersComponent.h"
|
#include "SlidersComponent.h"
|
||||||
#include "FluidSynthModel.h"
|
#include "FluidSynthModel.h"
|
||||||
#include "MidiConstants.h"
|
#include "MidiConstants.h"
|
||||||
|
#include "Util.h"
|
||||||
|
using SliderAttachment = AudioProcessorValueTreeState::SliderAttachment;
|
||||||
|
|
||||||
std::function<void()> SlidersComponent::makeSliderListener(Slider& slider, int controller) {
|
std::function<void()> SlidersComponent::makeSliderListener(Slider& slider, int controller) {
|
||||||
return [this, controller, &slider]{
|
return [this, controller, &slider]{
|
||||||
|
// RangedAudioParameter *param{valueTreeState.getParameter("release")};
|
||||||
|
// jassert(dynamic_cast<AudioParameterInt*>(param) != nullptr);
|
||||||
|
// AudioParameterInt* castParam{dynamic_cast<AudioParameterInt*>(param)};
|
||||||
|
// int value{castParam->get()};
|
||||||
|
|
||||||
|
// String s{"slider "};
|
||||||
|
// s << slider.getComponentID() << ", controller " << controller << ", value " << slider.getValue() << ", xmlReleaseValue " << value;
|
||||||
|
// DEBUG_PRINT(s);
|
||||||
// slider.setValue(slider.getValue(), NotificationType::dontSendNotification);
|
// slider.setValue(slider.getValue(), NotificationType::dontSendNotification);
|
||||||
fluidSynthModel->setControllerValue(controller, slider.getValue());
|
fluidSynthModel.setControllerValue(controller, slider.getValue());
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,10 +68,38 @@ void SlidersComponent::resized() {
|
||||||
filterResonanceSlider.setBounds(rFilter.removeFromLeft(sliderWidth + sliderXMargin).withTrimmedTop(labelHeight).withTrimmedLeft(sliderXMargin));
|
filterResonanceSlider.setBounds(rFilter.removeFromLeft(sliderWidth + sliderXMargin).withTrimmedTop(labelHeight).withTrimmedLeft(sliderXMargin));
|
||||||
}
|
}
|
||||||
|
|
||||||
SlidersComponent::SlidersComponent(FluidSynthModel* fluidSynthModel) :
|
void SlidersComponent::acceptMidiControlEvent(int controller, int value) {
|
||||||
fluidSynthModel{fluidSynthModel},
|
switch(static_cast<fluid_midi_control_change>(controller)) {
|
||||||
envelopeGroup{"envelopeGroup", "Envelope"},
|
case SOUND_CTRL2: // MIDI CC 71 Timbre/Harmonic Intensity (filter resonance)
|
||||||
filterGroup{"filterGroup", "Filter"}
|
filterResonanceSlider.setValue(value, NotificationType::dontSendNotification);
|
||||||
|
break;
|
||||||
|
case SOUND_CTRL3: // MIDI CC 72 Release time
|
||||||
|
releaseSlider.setValue(value, NotificationType::dontSendNotification);
|
||||||
|
break;
|
||||||
|
case SOUND_CTRL4: // MIDI CC 73 Attack time
|
||||||
|
attackSlider.setValue(value, NotificationType::dontSendNotification);
|
||||||
|
break;
|
||||||
|
case SOUND_CTRL5: // MIDI CC 74 Brightness (cutoff frequency, FILTERFC)
|
||||||
|
filterCutOffSlider.setValue(value, NotificationType::dontSendNotification);
|
||||||
|
break;
|
||||||
|
case SOUND_CTRL6: // MIDI CC 75 Decay Time
|
||||||
|
decaySlider.setValue(value, NotificationType::dontSendNotification);
|
||||||
|
break;
|
||||||
|
case SOUND_CTRL10: // MIDI CC 79 undefined
|
||||||
|
sustainSlider.setValue(value, NotificationType::dontSendNotification);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SlidersComponent::SlidersComponent(
|
||||||
|
AudioProcessorValueTreeState& valueTreeState,
|
||||||
|
FluidSynthModel& fluidSynthModel)
|
||||||
|
: valueTreeState{valueTreeState}
|
||||||
|
, fluidSynthModel{fluidSynthModel}
|
||||||
|
, envelopeGroup{"envelopeGroup", "Envelope"}
|
||||||
|
, filterGroup{"filterGroup", "Filter"}
|
||||||
{
|
{
|
||||||
const Slider::SliderStyle style{Slider::SliderStyle::LinearVertical};
|
const Slider::SliderStyle style{Slider::SliderStyle::LinearVertical};
|
||||||
const double rangeMin(0);
|
const double rangeMin(0);
|
||||||
|
@ -70,33 +108,39 @@ filterGroup{"filterGroup", "Filter"}
|
||||||
|
|
||||||
attackSlider.setSliderStyle(style);
|
attackSlider.setSliderStyle(style);
|
||||||
attackSlider.setRange(rangeMin, rangeMax, rangeStep);
|
attackSlider.setRange(rangeMin, rangeMax, rangeStep);
|
||||||
attackSlider.onValueChange = makeSliderListener(attackSlider, static_cast<int>(SOUND_CTRL4));
|
attackSlider.onDragEnd = makeSliderListener(attackSlider, static_cast<int>(SOUND_CTRL4));
|
||||||
attackSlider.setTextBoxStyle(Slider::TextBoxBelow, true, attackSlider.getTextBoxWidth(), attackSlider.getTextBoxHeight());
|
attackSlider.setTextBoxStyle(Slider::TextBoxBelow, true, attackSlider.getTextBoxWidth(), attackSlider.getTextBoxHeight());
|
||||||
|
attackSliderAttachment = make_unique<SliderAttachment>(valueTreeState, "attack", attackSlider);
|
||||||
|
|
||||||
decaySlider.setSliderStyle(style);
|
decaySlider.setSliderStyle(style);
|
||||||
decaySlider.setRange(rangeMin, rangeMax, rangeStep);
|
decaySlider.setRange(rangeMin, rangeMax, rangeStep);
|
||||||
decaySlider.onValueChange = makeSliderListener(decaySlider, static_cast<int>(SOUND_CTRL6));
|
decaySlider.onDragEnd = makeSliderListener(decaySlider, static_cast<int>(SOUND_CTRL6));
|
||||||
decaySlider.setTextBoxStyle(Slider::TextBoxBelow, true, decaySlider.getTextBoxWidth(), decaySlider.getTextBoxHeight());
|
decaySlider.setTextBoxStyle(Slider::TextBoxBelow, true, decaySlider.getTextBoxWidth(), decaySlider.getTextBoxHeight());
|
||||||
|
decaySliderAttachment = make_unique<SliderAttachment>(valueTreeState, "decay", decaySlider);
|
||||||
|
|
||||||
sustainSlider.setSliderStyle(style);
|
sustainSlider.setSliderStyle(style);
|
||||||
sustainSlider.setRange(rangeMin, rangeMax, rangeStep);
|
sustainSlider.setRange(rangeMin, rangeMax, rangeStep);
|
||||||
sustainSlider.onValueChange = makeSliderListener(sustainSlider, static_cast<int>(SOUND_CTRL10));
|
sustainSlider.onDragEnd = makeSliderListener(sustainSlider, static_cast<int>(SOUND_CTRL10));
|
||||||
sustainSlider.setTextBoxStyle(Slider::TextBoxBelow, true, sustainSlider.getTextBoxWidth(), sustainSlider.getTextBoxHeight());
|
sustainSlider.setTextBoxStyle(Slider::TextBoxBelow, true, sustainSlider.getTextBoxWidth(), sustainSlider.getTextBoxHeight());
|
||||||
|
sustainSliderAttachment = make_unique<SliderAttachment>(valueTreeState, "sustain", sustainSlider);
|
||||||
|
|
||||||
releaseSlider.setSliderStyle(style);
|
releaseSlider.setSliderStyle(style);
|
||||||
releaseSlider.setRange(rangeMin, rangeMax, rangeStep);
|
releaseSlider.setRange(rangeMin, rangeMax, rangeStep);
|
||||||
releaseSlider.onValueChange = makeSliderListener(releaseSlider, static_cast<int>(SOUND_CTRL3));
|
releaseSlider.onDragEnd = makeSliderListener(releaseSlider, static_cast<int>(SOUND_CTRL3));
|
||||||
releaseSlider.setTextBoxStyle(Slider::TextBoxBelow, true, releaseSlider.getTextBoxWidth(), releaseSlider.getTextBoxHeight());
|
releaseSlider.setTextBoxStyle(Slider::TextBoxBelow, true, releaseSlider.getTextBoxWidth(), releaseSlider.getTextBoxHeight());
|
||||||
|
releaseSliderAttachment = make_unique<SliderAttachment>(valueTreeState, "release", releaseSlider);
|
||||||
|
|
||||||
filterCutOffSlider.setSliderStyle(style);
|
filterCutOffSlider.setSliderStyle(style);
|
||||||
filterCutOffSlider.setRange(rangeMin, rangeMax, rangeStep);
|
filterCutOffSlider.setRange(rangeMin, rangeMax, rangeStep);
|
||||||
filterCutOffSlider.onValueChange = makeSliderListener(filterCutOffSlider, static_cast<int>(SOUND_CTRL5));
|
filterCutOffSlider.onDragEnd = makeSliderListener(filterCutOffSlider, static_cast<int>(SOUND_CTRL5));
|
||||||
filterCutOffSlider.setTextBoxStyle(Slider::TextBoxBelow, true, filterCutOffSlider.getTextBoxWidth(), filterCutOffSlider.getTextBoxHeight());
|
filterCutOffSlider.setTextBoxStyle(Slider::TextBoxBelow, true, filterCutOffSlider.getTextBoxWidth(), filterCutOffSlider.getTextBoxHeight());
|
||||||
|
filterCutOffSliderAttachment = make_unique<SliderAttachment>(valueTreeState, "filterCutOff", filterCutOffSlider);
|
||||||
|
|
||||||
filterResonanceSlider.setSliderStyle(style);
|
filterResonanceSlider.setSliderStyle(style);
|
||||||
filterResonanceSlider.setRange(rangeMin, rangeMax, rangeStep);
|
filterResonanceSlider.setRange(rangeMin, rangeMax, rangeStep);
|
||||||
filterResonanceSlider.onValueChange = makeSliderListener(filterResonanceSlider, static_cast<int>(SOUND_CTRL2));
|
filterResonanceSlider.onDragEnd = makeSliderListener(filterResonanceSlider, static_cast<int>(SOUND_CTRL2));
|
||||||
filterResonanceSlider.setTextBoxStyle(Slider::TextBoxBelow, true, filterResonanceSlider.getTextBoxWidth(), filterResonanceSlider.getTextBoxHeight());
|
filterResonanceSlider.setTextBoxStyle(Slider::TextBoxBelow, true, filterResonanceSlider.getTextBoxWidth(), filterResonanceSlider.getTextBoxHeight());
|
||||||
|
filterResonanceSliderAttachment = make_unique<SliderAttachment>(valueTreeState, "filterResonance", filterResonanceSlider);
|
||||||
|
|
||||||
addAndMakeVisible(attackSlider);
|
addAndMakeVisible(attackSlider);
|
||||||
addAndMakeVisible(decaySlider);
|
addAndMakeVisible(decaySlider);
|
||||||
|
|
|
@ -4,43 +4,55 @@
|
||||||
#include "FluidSynthModel.h"
|
#include "FluidSynthModel.h"
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
using SliderAttachment = AudioProcessorValueTreeState::SliderAttachment;
|
||||||
|
|
||||||
class SlidersComponent : public Component
|
class SlidersComponent : public Component
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
SlidersComponent(FluidSynthModel* fluidSynthModel);
|
SlidersComponent(
|
||||||
|
AudioProcessorValueTreeState& valueTreeState,
|
||||||
|
FluidSynthModel& fluidSynthModel);
|
||||||
~SlidersComponent();
|
~SlidersComponent();
|
||||||
|
|
||||||
void resized() override;
|
void resized() override;
|
||||||
|
|
||||||
const int getDesiredWidth();
|
const int getDesiredWidth();
|
||||||
|
|
||||||
|
void acceptMidiControlEvent(int controller, int value);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::function<void()> makeSliderListener(Slider& slider, int controller);
|
std::function<void()> makeSliderListener(Slider& slider, int controller);
|
||||||
|
|
||||||
FluidSynthModel* fluidSynthModel;
|
AudioProcessorValueTreeState& valueTreeState;
|
||||||
|
FluidSynthModel& fluidSynthModel;
|
||||||
|
|
||||||
GroupComponent envelopeGroup;
|
GroupComponent envelopeGroup;
|
||||||
|
|
||||||
Slider attackSlider;
|
Slider attackSlider;
|
||||||
Label attackLabel;
|
Label attackLabel;
|
||||||
|
unique_ptr<SliderAttachment> attackSliderAttachment;
|
||||||
|
|
||||||
Slider decaySlider;
|
Slider decaySlider;
|
||||||
Label decayLabel;
|
Label decayLabel;
|
||||||
|
unique_ptr<SliderAttachment> decaySliderAttachment;
|
||||||
|
|
||||||
Slider sustainSlider;
|
Slider sustainSlider;
|
||||||
Label sustainLabel;
|
Label sustainLabel;
|
||||||
|
unique_ptr<SliderAttachment> sustainSliderAttachment;
|
||||||
|
|
||||||
Slider releaseSlider;
|
Slider releaseSlider;
|
||||||
Label releaseLabel;
|
Label releaseLabel;
|
||||||
|
unique_ptr<SliderAttachment> releaseSliderAttachment;
|
||||||
|
|
||||||
GroupComponent filterGroup;
|
GroupComponent filterGroup;
|
||||||
|
|
||||||
Slider filterCutOffSlider;
|
Slider filterCutOffSlider;
|
||||||
Label filterCutOffLabel;
|
Label filterCutOffLabel;
|
||||||
|
unique_ptr<SliderAttachment> filterCutOffSliderAttachment;
|
||||||
|
|
||||||
Slider filterResonanceSlider;
|
Slider filterResonanceSlider;
|
||||||
Label filterResonanceLabel;
|
Label filterResonanceLabel;
|
||||||
|
unique_ptr<SliderAttachment> filterResonanceSliderAttachment;
|
||||||
|
|
||||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SlidersComponent)
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SlidersComponent)
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,13 +0,0 @@
|
||||||
//
|
|
||||||
// Created by Alex Birch on 07/09/2017.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include "SoundfontSynthSound.h"
|
|
||||||
|
|
||||||
bool SoundfontSynthSound::appliesToChannel(int) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SoundfontSynthSound::appliesToNote(int) {
|
|
||||||
return true;
|
|
||||||
}
|
|
|
@ -1,13 +0,0 @@
|
||||||
//
|
|
||||||
// Created by Alex Birch on 07/09/2017.
|
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "../JuceLibraryCode/JuceHeader.h"
|
|
||||||
|
|
||||||
class SoundfontSynthSound : public SynthesiserSound {
|
|
||||||
public:
|
|
||||||
bool appliesToNote (int /*midiNoteNumber*/) override;
|
|
||||||
bool appliesToChannel (int /*midiChannel*/) override;
|
|
||||||
};
|
|
|
@ -1,109 +0,0 @@
|
||||||
//
|
|
||||||
// Created by Alex Birch on 07/09/2017.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include "SoundfontSynthVoice.h"
|
|
||||||
#include "SoundfontSynthSound.h"
|
|
||||||
#include "Util.h"
|
|
||||||
|
|
||||||
SoundfontSynthVoice::SoundfontSynthVoice(fluid_synth_t* synth)
|
|
||||||
: tailOff (0.0),
|
|
||||||
level(0.0),
|
|
||||||
currentAngle(0.0),
|
|
||||||
angleDelta(0.0),
|
|
||||||
midiNoteNumber(0),
|
|
||||||
synth(synth)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SoundfontSynthVoice::canPlaySound(SynthesiserSound* sound) {
|
|
||||||
return dynamic_cast<SoundfontSynthSound*> (sound) != nullptr;
|
|
||||||
}
|
|
||||||
void SoundfontSynthVoice::startNote(
|
|
||||||
int midiNoteNumber,
|
|
||||||
float velocity,
|
|
||||||
SynthesiserSound* sound,
|
|
||||||
int /*currentPitchWheelPosition*/) {
|
|
||||||
this->midiNoteNumber = midiNoteNumber;
|
|
||||||
DEBUG_PRINT ( juce::String::formatted("JUCE noteon: %d, %d\n", midiNoteNumber, velocity) );
|
|
||||||
fluid_synth_noteon(synth, 0, midiNoteNumber, static_cast<int>(velocity * 127));
|
|
||||||
|
|
||||||
// currentAngle = 0.0;
|
|
||||||
// level = velocity * 0.15;
|
|
||||||
// tailOff = 0.0;
|
|
||||||
//
|
|
||||||
// double cyclesPerSecond = MidiMessage::getMidiNoteInHertz (midiNoteNumber);
|
|
||||||
// double cyclesPerSample = cyclesPerSecond / getSampleRate();
|
|
||||||
//
|
|
||||||
// angleDelta = cyclesPerSample * 2.0 * double_Pi;
|
|
||||||
|
|
||||||
// jassert(dynamic_cast<SoundfontSynthSound*> (sound) != nullptr);
|
|
||||||
// SoundfontSynthSound* sfsynth = dynamic_cast<SoundfontSynthSound*> (sound);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SoundfontSynthVoice::stopNote (float /*velocity*/, bool allowTailOff) {
|
|
||||||
// if (allowTailOff) {
|
|
||||||
// // start a tail-off by setting this flag. The render callback will pick up on
|
|
||||||
// // this and do a fade out, calling clearCurrentNote() when it's finished.
|
|
||||||
//
|
|
||||||
// // we only need to begin a tail-off if it's not already doing so - the
|
|
||||||
// if (tailOff == 0.0) {
|
|
||||||
// // stopNote method could be called more than once.
|
|
||||||
// tailOff = 1.0;
|
|
||||||
// }
|
|
||||||
// } else {
|
|
||||||
// // we're being told to stop playing immediately, so reset everything..
|
|
||||||
//
|
|
||||||
// clearCurrentNote();
|
|
||||||
// angleDelta = 0.0;
|
|
||||||
// }
|
|
||||||
DEBUG_PRINT ( juce::String("JUCE noteoff\n") );
|
|
||||||
clearCurrentNote();
|
|
||||||
fluid_synth_noteoff(synth, 0, this->midiNoteNumber);
|
|
||||||
}
|
|
||||||
|
|
||||||
// receives input as MIDI 0 to 16383, with 8192 being center
|
|
||||||
// this is also exactly the input fluidsynth requires
|
|
||||||
void SoundfontSynthVoice::pitchWheelMoved (int newValue) {
|
|
||||||
// fluid_synth_pitch_bend(synth, 0, newValue);
|
|
||||||
// int ppitch_bend;
|
|
||||||
// fluid_synth_get_pitch_bend(synth, 0, &ppitch_bend);
|
|
||||||
// int ppitch_bend_sens;
|
|
||||||
// fluid_synth_get_pitch_wheel_sens(synth, 0, &ppitch_bend_sens);
|
|
||||||
// Logger::outputDebugString ( juce::String::formatted("Pitch wheel: %d %d %d\n", newValue, ppitch_bend, ppitch_bend_sens) );
|
|
||||||
}
|
|
||||||
|
|
||||||
void SoundfontSynthVoice::controllerMoved (int controllerNumber, int newValue) {
|
|
||||||
// this seems to be "program change" event
|
|
||||||
DEBUG_PRINT ( juce::String::formatted("Controller moved: %d, %d\n", controllerNumber, newValue) );
|
|
||||||
}
|
|
||||||
|
|
||||||
void SoundfontSynthVoice::renderNextBlock (AudioBuffer<float>& outputBuffer, int startSample, int numSamples) {
|
|
||||||
//fluid_synth_process(synth.get(), numSamples, 1, nullptr, outputBuffer.getNumChannels(), outputBuffer.getArrayOfWritePointers());
|
|
||||||
}
|
|
||||||
|
|
||||||
//void SoundfontSynthVoice::renderBlock (AudioBuffer<float>& outputBuffer, int startSample, int numSamples) {
|
|
||||||
// fluid_synth_process(synth.get(), numSamples, 1, nullptr, outputBuffer.getNumChannels(), outputBuffer.getArrayOfWritePointers());
|
|
||||||
// if (angleDelta == 0.0) {
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
// while (--numSamples >= 0) {
|
|
||||||
// double qualifiedTailOff = tailOff > 0 ? tailOff : 1.0;
|
|
||||||
// auto currentSample = static_cast<FloatType> (std::sin (currentAngle) * level * qualifiedTailOff);
|
|
||||||
// for (int i = outputBuffer.getNumChannels(); --i >= 0;)
|
|
||||||
// outputBuffer.addSample (i, startSample, currentSample);
|
|
||||||
//
|
|
||||||
// currentAngle += angleDelta;
|
|
||||||
// ++startSample;
|
|
||||||
//
|
|
||||||
// if (tailOff > 0) {
|
|
||||||
// tailOff *= 0.99;
|
|
||||||
//
|
|
||||||
// if (tailOff <= 0.005) {
|
|
||||||
// clearCurrentNote();
|
|
||||||
// angleDelta = 0.0;
|
|
||||||
// break;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//}
|
|
|
@ -1,39 +0,0 @@
|
||||||
//
|
|
||||||
// Created by Alex Birch on 07/09/2017.
|
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include<memory>
|
|
||||||
#include<fluidsynth.h>
|
|
||||||
#include "../JuceLibraryCode/JuceHeader.h"
|
|
||||||
|
|
||||||
using std::shared_ptr;
|
|
||||||
|
|
||||||
class SoundfontSynthVoice : public SynthesiserVoice {
|
|
||||||
public:
|
|
||||||
SoundfontSynthVoice(fluid_synth_t* synth);
|
|
||||||
|
|
||||||
bool canPlaySound (SynthesiserSound* sound) override;
|
|
||||||
void startNote (
|
|
||||||
int midiNoteNumber,
|
|
||||||
float velocity,
|
|
||||||
SynthesiserSound* /*sound*/,
|
|
||||||
int /*currentPitchWheelPosition*/) override;
|
|
||||||
|
|
||||||
void stopNote (float /*velocity*/, bool allowTailOff) override;
|
|
||||||
void pitchWheelMoved (int /*newValue*/) override;
|
|
||||||
|
|
||||||
void controllerMoved (int /*controllerNumber*/, int /*newValue*/) override;
|
|
||||||
|
|
||||||
void renderNextBlock (AudioBuffer<float>& outputBuffer, int startSample, int numSamples) override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
double tailOff;
|
|
||||||
double level;
|
|
||||||
double currentAngle;
|
|
||||||
double angleDelta;
|
|
||||||
int midiNoteNumber;
|
|
||||||
|
|
||||||
fluid_synth_t* synth;
|
|
||||||
};
|
|
|
@ -1,15 +0,0 @@
|
||||||
//
|
|
||||||
// Created by Alex Birch on 18/03/2018.
|
|
||||||
// Copyright (c) 2018 Birchlabs. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "../JuceLibraryCode/JuceHeader.h"
|
|
||||||
|
|
||||||
class StateChangeSubscriber {
|
|
||||||
public:
|
|
||||||
virtual ~StateChangeSubscriber() {} // pass pointer ownership to another party without exposing the concrete derived class
|
|
||||||
virtual void getStateInformation (XmlElement& xml) = 0;
|
|
||||||
virtual void setStateInformation (XmlElement* xmlState) = 0;
|
|
||||||
};
|
|
|
@ -7,25 +7,23 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
#include "TableComponent.h"
|
#include "TableComponent.h"
|
||||||
|
#include "Util.h"
|
||||||
|
#include <functional>
|
||||||
|
#include <iterator>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
using namespace Util;
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
/**
|
/**
|
||||||
This class shows how to implement a TableListBoxModel to show in a TableListBox.
|
This class shows how to implement a TableListBoxModel to show in a TableListBox.
|
||||||
*/
|
*/
|
||||||
TableComponent::TableComponent(
|
TableComponent::TableComponent(
|
||||||
const vector<string> &columns,
|
AudioProcessorValueTreeState& valueTreeState
|
||||||
const vector<vector<string>> &rows,
|
|
||||||
const function<void (int)> &onRowSelected,
|
|
||||||
const function<int (const vector<string>&)> &rowToIDMapper,
|
|
||||||
int initiallySelectedRow
|
|
||||||
)
|
)
|
||||||
: font (14.0f),
|
: valueTreeState{valueTreeState}
|
||||||
columns(columns),
|
, font{14.0f}
|
||||||
rows(rows),
|
|
||||||
onRowSelected(onRowSelected),
|
|
||||||
rowToIDMapper(rowToIDMapper)
|
|
||||||
{
|
{
|
||||||
// Create our table component and add it to this component..
|
// Create our table component and add it to this component..
|
||||||
addAndMakeVisible (table);
|
addAndMakeVisible (table);
|
||||||
|
@ -37,43 +35,104 @@ TableComponent::TableComponent(
|
||||||
|
|
||||||
int columnIx = 1;
|
int columnIx = 1;
|
||||||
|
|
||||||
// Add some columns to the table header, based on the column list in our database..
|
table.getHeader().addColumn (
|
||||||
for (auto &column : columns) // access by reference to avoid copying
|
String("#"),
|
||||||
{
|
columnIx++,
|
||||||
const int colWidth{ columnIx == 1 ? 30 : 200 };
|
30, // column width
|
||||||
table.getHeader().addColumn (
|
30, // min width
|
||||||
String(column),
|
400, // max width
|
||||||
columnIx++,
|
TableHeaderComponent::defaultFlags
|
||||||
colWidth, // column width
|
);
|
||||||
30, // min width
|
table.getHeader().addColumn (
|
||||||
400, // max width
|
String("Name"),
|
||||||
TableHeaderComponent::defaultFlags
|
columnIx++,
|
||||||
);
|
200, // column width
|
||||||
}
|
30, // min width
|
||||||
|
400, // max width
|
||||||
|
TableHeaderComponent::defaultFlags
|
||||||
|
);
|
||||||
|
|
||||||
table.setWantsKeyboardFocus(false);
|
table.setWantsKeyboardFocus(false);
|
||||||
|
|
||||||
table.selectRow(initiallySelectedRow);
|
ValueTree banks{valueTreeState.state.getChildWithName("banks")};
|
||||||
|
loadModelFrom(banks);
|
||||||
|
|
||||||
// we could now change some initial settings..
|
// we could now change some initial settings..
|
||||||
table.getHeader().setSortColumnId (1, false); // sort ascending by ID column
|
table.getHeader().setSortColumnId(1, false); // sort ascending by ID column
|
||||||
// table.getHeader().setColumnVisible (7, false); // hide the "length" column until the user shows it
|
valueTreeState.state.addListener(this);
|
||||||
|
valueTreeState.addParameterListener("bank", this);
|
||||||
// un-comment this line to have a go of stretch-to-fit mode
|
valueTreeState.addParameterListener("preset", this);
|
||||||
// table.getHeader().setStretchToFitActive (true);
|
|
||||||
|
|
||||||
// table.setMultipleSelectionEnabled (false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TableComponent::setRows(const vector<vector<string>>& rows, int initiallySelectedRow) {
|
TableComponent::~TableComponent() {
|
||||||
this->rows = rows;
|
valueTreeState.removeParameterListener("bank", this);
|
||||||
|
valueTreeState.removeParameterListener("preset", this);
|
||||||
|
valueTreeState.state.removeListener(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TableComponent::loadModelFrom(ValueTree& banks) {
|
||||||
|
banksToPresets.clear();
|
||||||
|
int banksChildren{banks.getNumChildren()};
|
||||||
|
for(int bankIx{0}; bankIx<banksChildren; bankIx++) {
|
||||||
|
ValueTree bank{banks.getChild(bankIx)};
|
||||||
|
int bankNum{bank.getProperty("num")};
|
||||||
|
int bankChildren{bank.getNumChildren()};
|
||||||
|
for(int presetIx{0}; presetIx<bankChildren; presetIx++) {
|
||||||
|
ValueTree preset{bank.getChild(presetIx)};
|
||||||
|
int presetNum{preset.getProperty("num")};
|
||||||
|
String presetName{preset.getProperty("name")};
|
||||||
|
TableRow row{presetNum, move(presetName)};
|
||||||
|
banksToPresets.emplace(bankNum, move(row));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
repopulateTable();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TableComponent::parameterChanged(const String& parameterID, float newValue) {
|
||||||
|
if (parameterID == "bank") {
|
||||||
|
repopulateTable();
|
||||||
|
} else if (parameterID == "preset") {
|
||||||
|
selectCurrentPreset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TableComponent::repopulateTable() {
|
||||||
|
rows.clear();
|
||||||
|
RangedAudioParameter *param{valueTreeState.getParameter("bank")};
|
||||||
|
jassert(dynamic_cast<AudioParameterInt*>(param) != nullptr);
|
||||||
|
AudioParameterInt* castParam{dynamic_cast<AudioParameterInt*>(param)};
|
||||||
|
int bank{castParam->get()};
|
||||||
|
|
||||||
|
BanksToPresets::iterator lowerBound{banksToPresets.lower_bound(bank)};
|
||||||
|
BanksToPresets::iterator upperBound{banksToPresets.upper_bound(bank)};
|
||||||
|
|
||||||
|
// basic syntaxes for a lambda which return's a pair's .second
|
||||||
|
// https://stackoverflow.com/questions/2568194/populate-a-vector-with-all-multimap-values-with-a-given-key
|
||||||
|
// shorter syntax with mem_fn()
|
||||||
|
// https://stackoverflow.com/a/36775400/5257399
|
||||||
|
transform(
|
||||||
|
lowerBound,
|
||||||
|
upperBound,
|
||||||
|
back_inserter(rows),
|
||||||
|
mem_fn(&BanksToPresets::value_type::second)
|
||||||
|
);
|
||||||
table.deselectAllRows();
|
table.deselectAllRows();
|
||||||
table.updateContent();
|
table.updateContent();
|
||||||
table.getHeader().setSortColumnId(0, true);
|
table.getHeader().setSortColumnId(0, true);
|
||||||
table.selectRow(initiallySelectedRow);
|
selectCurrentPreset();
|
||||||
table.repaint();
|
table.repaint();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TableComponent::valueTreePropertyChanged(
|
||||||
|
ValueTree& treeWhosePropertyHasChanged,
|
||||||
|
const Identifier& property) {
|
||||||
|
if (treeWhosePropertyHasChanged.getType() == StringRef("banks")) {
|
||||||
|
if (property == StringRef("synthetic")) {
|
||||||
|
loadModelFrom(treeWhosePropertyHasChanged);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// This is overloaded from TableListBoxModel, and must return the total number of rows in our table
|
// This is overloaded from TableListBoxModel, and must return the total number of rows in our table
|
||||||
int TableComponent::getNumRows()
|
int TableComponent::getNumRows()
|
||||||
{
|
{
|
||||||
|
@ -96,6 +155,13 @@ void TableComponent::paintRowBackground (
|
||||||
g.fillAll (alternateColour);
|
g.fillAll (alternateColour);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String TableRow::getStringContents(int columnId) {
|
||||||
|
if (columnId <= 1) {
|
||||||
|
return String(preset);
|
||||||
|
}
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
// This is overloaded from TableListBoxModel, and must paint any cells that aren't using custom
|
// This is overloaded from TableListBoxModel, and must paint any cells that aren't using custom
|
||||||
// components.
|
// components.
|
||||||
void TableComponent::paintCell (
|
void TableComponent::paintCell (
|
||||||
|
@ -109,7 +175,9 @@ void TableComponent::paintCell (
|
||||||
g.setColour (getLookAndFeel().findColour (ListBox::textColourId));
|
g.setColour (getLookAndFeel().findColour (ListBox::textColourId));
|
||||||
g.setFont (font);
|
g.setFont (font);
|
||||||
|
|
||||||
g.drawText (rows[rowNumber][columnId-1], 2, 0, width - 4, height, Justification::centredLeft, true);
|
TableRow& row{rows[rowNumber]};
|
||||||
|
String text{row.getStringContents(columnId)};
|
||||||
|
g.drawText (text, 2, 0, width - 4, height, Justification::centredLeft, true);
|
||||||
|
|
||||||
g.setColour (getLookAndFeel().findColour (ListBox::backgroundColourId));
|
g.setColour (getLookAndFeel().findColour (ListBox::backgroundColourId));
|
||||||
g.fillRect (width - 1, 0, 1, height);
|
g.fillRect (width - 1, 0, 1, height);
|
||||||
|
@ -122,25 +190,26 @@ void TableComponent::sortOrderChanged (
|
||||||
bool isForwards
|
bool isForwards
|
||||||
) {
|
) {
|
||||||
if (newSortColumnId != 0) {
|
if (newSortColumnId != 0) {
|
||||||
int selectedRowIx = table.getSelectedRow();
|
|
||||||
vector<string> selectedRow;
|
|
||||||
if (selectedRowIx >= 0) {
|
|
||||||
selectedRow = rows[selectedRowIx];
|
|
||||||
}
|
|
||||||
|
|
||||||
TableComponent::DataSorter sorter (newSortColumnId, isForwards);
|
TableComponent::DataSorter sorter (newSortColumnId, isForwards);
|
||||||
sort(rows.begin(), rows.end(), sorter);
|
sort(rows.begin(), rows.end(), sorter);
|
||||||
|
|
||||||
table.updateContent();
|
table.updateContent();
|
||||||
|
selectCurrentPreset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (selectedRowIx >= 0) {
|
void TableComponent::selectCurrentPreset() {
|
||||||
for (auto it = rows.begin(); it != rows.end(); ++it) {
|
table.deselectAllRows();
|
||||||
if(*it == selectedRow) {
|
RangedAudioParameter *param{valueTreeState.getParameter("preset")};
|
||||||
int index = static_cast<int>(std::distance(rows.begin(), it));
|
jassert(dynamic_cast<AudioParameterInt*>(param) != nullptr);
|
||||||
table.selectRow(index);
|
AudioParameterInt* castParam{dynamic_cast<AudioParameterInt*>(param)};
|
||||||
break;
|
int value{castParam->get()};
|
||||||
}
|
|
||||||
}
|
for (auto it{rows.begin()}; it != rows.end(); ++it) {
|
||||||
|
if(it->preset == value) {
|
||||||
|
int index{static_cast<int>(distance(rows.begin(), it))};
|
||||||
|
table.selectRow(index);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -148,17 +217,17 @@ void TableComponent::sortOrderChanged (
|
||||||
// This is overloaded from TableListBoxModel, and should choose the best width for the specified
|
// This is overloaded from TableListBoxModel, and should choose the best width for the specified
|
||||||
// column.
|
// column.
|
||||||
int TableComponent::getColumnAutoSizeWidth (int columnId) {
|
int TableComponent::getColumnAutoSizeWidth (int columnId) {
|
||||||
// if (columnId == 5)
|
|
||||||
// return 100; // (this is the ratings column, containing a custom combobox component)
|
|
||||||
if (columnId == 1)
|
if (columnId == 1)
|
||||||
return 30; // (this is the ratings column, containing a custom combobox component)
|
return 30;
|
||||||
|
|
||||||
|
|
||||||
int widest = 32;
|
int widest = 32;
|
||||||
|
|
||||||
// find the widest bit of text in this column..
|
// find the widest bit of text in this column..
|
||||||
for (int i = getNumRows(); --i >= 0;) {
|
for (int i{getNumRows()}; --i >= 0;) {
|
||||||
widest = jmax (widest, font.getStringWidth (rows[i][columnId-1]));
|
TableRow& row{rows[i]};
|
||||||
|
String text{row.getStringContents(columnId)};
|
||||||
|
widest = jmax (widest, font.getStringWidth (text));
|
||||||
}
|
}
|
||||||
|
|
||||||
return widest + 8;
|
return widest + 8;
|
||||||
|
@ -177,20 +246,24 @@ TableComponent::DataSorter::DataSorter (
|
||||||
int columnByWhichToSort,
|
int columnByWhichToSort,
|
||||||
bool forwards
|
bool forwards
|
||||||
)
|
)
|
||||||
: columnByWhichToSort (columnByWhichToSort),
|
: columnByWhichToSort (columnByWhichToSort)
|
||||||
direction (forwards ? 1 : -1)
|
, direction (forwards ? 1 : -1)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
bool TableComponent::DataSorter::operator ()(
|
bool TableComponent::DataSorter::operator ()(
|
||||||
vector<string> first,
|
TableRow first,
|
||||||
vector<string> second
|
TableRow second
|
||||||
) {
|
) {
|
||||||
int result = String(first[columnByWhichToSort-1])
|
int result;
|
||||||
.compareNatural (String(second[columnByWhichToSort-1]));
|
if (columnByWhichToSort <= 1) {
|
||||||
|
result = compare(first.preset, second.preset);
|
||||||
if (result == 0)
|
} else {
|
||||||
result = String(first[0])
|
result = first.name
|
||||||
.compareNatural (String(second[0]));
|
.compareNatural (second.name);
|
||||||
|
if (result == 0) {
|
||||||
|
result = compare(first.preset, second.preset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
result *= direction;
|
result *= direction;
|
||||||
|
|
||||||
|
@ -201,9 +274,21 @@ void TableComponent::selectedRowsChanged (int row) {
|
||||||
if (row < 0) {
|
if (row < 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
onRowSelected(rowToIDMapper(rows[row]));
|
int newPreset{rows[row].preset};
|
||||||
|
RangedAudioParameter *param{valueTreeState.getParameter("preset")};
|
||||||
|
jassert(dynamic_cast<AudioParameterInt*>(param) != nullptr);
|
||||||
|
AudioParameterInt* castParam{dynamic_cast<AudioParameterInt*>(param)};
|
||||||
|
*castParam = newPreset;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TableComponent::keyPressed(const KeyPress &key) {
|
bool TableComponent::keyPressed(const KeyPress &key) {
|
||||||
return table.keyPressed(key);
|
return table.keyPressed(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TableRow::TableRow(
|
||||||
|
int preset,
|
||||||
|
String name
|
||||||
|
)
|
||||||
|
: preset{preset}
|
||||||
|
, name{name}
|
||||||
|
{}
|
||||||
|
|
|
@ -9,22 +9,38 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "../JuceLibraryCode/JuceHeader.h"
|
#include "../JuceLibraryCode/JuceHeader.h"
|
||||||
#include "PresetsToBanks.h"
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
|
class TableRow {
|
||||||
|
public:
|
||||||
|
TableRow(
|
||||||
|
int preset,
|
||||||
|
String name
|
||||||
|
);
|
||||||
|
private:
|
||||||
|
/** 1-indexed */
|
||||||
|
String getStringContents(int columnId);
|
||||||
|
|
||||||
|
int preset;
|
||||||
|
String name;
|
||||||
|
|
||||||
|
friend class TableComponent;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
class TableComponent : public Component,
|
class TableComponent : public Component,
|
||||||
public TableListBoxModel {
|
public TableListBoxModel,
|
||||||
|
public ValueTree::Listener,
|
||||||
|
public AudioProcessorValueTreeState::Listener {
|
||||||
public:
|
public:
|
||||||
TableComponent(
|
TableComponent(
|
||||||
const vector<string> &columns,
|
AudioProcessorValueTreeState& valueTreeState
|
||||||
const vector<vector<string>> &rows,
|
|
||||||
const function<void (int)> &onRowSelected,
|
|
||||||
const function<int (const vector<string>&)> &rowToIDMapper,
|
|
||||||
int initiallySelectedRow
|
|
||||||
);
|
);
|
||||||
|
~TableComponent();
|
||||||
|
|
||||||
int getNumRows() override;
|
int getNumRows() override;
|
||||||
|
|
||||||
|
@ -52,19 +68,35 @@ public:
|
||||||
|
|
||||||
void resized() override;
|
void resized() override;
|
||||||
|
|
||||||
void setRows(const vector<vector<string>>& rows, int initiallySelectedRow);
|
|
||||||
|
|
||||||
bool keyPressed(const KeyPress &key) override;
|
bool keyPressed(const KeyPress &key) override;
|
||||||
|
|
||||||
|
virtual void parameterChanged (const String& parameterID, float newValue) override;
|
||||||
|
|
||||||
|
virtual void valueTreePropertyChanged (ValueTree& treeWhosePropertyHasChanged,
|
||||||
|
const Identifier& property) override;
|
||||||
|
inline virtual void valueTreeChildAdded (ValueTree& parentTree,
|
||||||
|
ValueTree& childWhichHasBeenAdded) override {};
|
||||||
|
inline virtual void valueTreeChildRemoved (ValueTree& parentTree,
|
||||||
|
ValueTree& childWhichHasBeenRemoved,
|
||||||
|
int indexFromWhichChildWasRemoved) override {};
|
||||||
|
inline virtual void valueTreeChildOrderChanged (ValueTree& parentTreeWhoseChildrenHaveMoved,
|
||||||
|
int oldIndex, int newIndex) override {};
|
||||||
|
inline virtual void valueTreeParentChanged (ValueTree& treeWhoseParentHasChanged) override {};
|
||||||
|
inline virtual void valueTreeRedirected (ValueTree& treeWhichHasBeenChanged) override {};
|
||||||
private:
|
private:
|
||||||
|
void loadModelFrom(ValueTree& banks);
|
||||||
|
void repopulateTable();
|
||||||
|
void selectCurrentPreset();
|
||||||
|
|
||||||
|
AudioProcessorValueTreeState& valueTreeState;
|
||||||
|
|
||||||
TableListBox table; // the table component itself
|
TableListBox table; // the table component itself
|
||||||
Font font;
|
Font font;
|
||||||
|
|
||||||
vector<string> columns;
|
typedef multimap<int, TableRow> BanksToPresets;
|
||||||
vector<vector<string>> rows;
|
BanksToPresets banksToPresets;
|
||||||
|
|
||||||
function<void (int)> onRowSelected;
|
vector<TableRow> rows;
|
||||||
function<int (const vector<string>&)> rowToIDMapper;
|
|
||||||
|
|
||||||
// A comparator used to sort our data when the user clicks a column header
|
// A comparator used to sort our data when the user clicks a column header
|
||||||
class DataSorter {
|
class DataSorter {
|
||||||
|
@ -75,8 +107,8 @@ private:
|
||||||
);
|
);
|
||||||
|
|
||||||
bool operator ()(
|
bool operator ()(
|
||||||
vector<string> first,
|
TableRow first,
|
||||||
vector<string> second
|
TableRow second
|
||||||
);
|
);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -85,4 +117,4 @@ private:
|
||||||
};
|
};
|
||||||
|
|
||||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TableComponent)
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TableComponent)
|
||||||
};
|
};
|
||||||
|
|
|
@ -8,180 +8,32 @@ using namespace std;
|
||||||
using namespace placeholders;
|
using namespace placeholders;
|
||||||
|
|
||||||
TablesComponent::TablesComponent(
|
TablesComponent::TablesComponent(
|
||||||
FluidSynthModel* fluidSynthModel
|
AudioProcessorValueTreeState& valueTreeState
|
||||||
) : fluidSynthModel(fluidSynthModel),
|
)
|
||||||
banksToPresets(fluidSynthModel->getBanks()),
|
: valueTreeState{valueTreeState}
|
||||||
initialised(false)
|
, banks{valueTreeState}
|
||||||
|
, presetTable{valueTreeState}
|
||||||
{
|
{
|
||||||
fluid_preset_t* currentPreset = getCurrentPreset();
|
|
||||||
selectedBank = -1;
|
|
||||||
int selectedPreset = -1;
|
|
||||||
|
|
||||||
if (currentPreset != nullptr) {
|
presetTable.setWantsKeyboardFocus(false);
|
||||||
selectedBank = fluid_preset_get_banknum(currentPreset);
|
|
||||||
selectedPreset = fluid_preset_get_num(currentPreset);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto rowToPresetMapper = [this](const vector<string> &row) {
|
|
||||||
return stoi(row[0]);
|
|
||||||
};
|
|
||||||
auto itemToBankMapper = [](const string &item) {
|
|
||||||
return stoi(item);
|
|
||||||
};
|
|
||||||
|
|
||||||
presetTable = new TableComponent(
|
|
||||||
{"#", "Name"},
|
|
||||||
mapPresets(
|
|
||||||
banksToPresets,
|
|
||||||
selectedBank
|
|
||||||
),
|
|
||||||
[this](int preset){
|
|
||||||
this->onPresetSelected(preset);
|
|
||||||
},
|
|
||||||
rowToPresetMapper,
|
|
||||||
presetToIndexMapper(selectedPreset)
|
|
||||||
);
|
|
||||||
banks = new Pills(
|
|
||||||
"Banks",
|
|
||||||
mapBanks(banksToPresets),
|
|
||||||
[this](int bank){
|
|
||||||
this->onBankSelected(bank);
|
|
||||||
},
|
|
||||||
itemToBankMapper,
|
|
||||||
selectedBank
|
|
||||||
);
|
|
||||||
|
|
||||||
presetTable->setWantsKeyboardFocus(false);
|
|
||||||
|
|
||||||
addAndMakeVisible(presetTable);
|
addAndMakeVisible(presetTable);
|
||||||
|
|
||||||
addAndMakeVisible(banks);
|
addAndMakeVisible(banks);
|
||||||
|
|
||||||
initialised = true;
|
|
||||||
|
|
||||||
fluidSynthModel->addListener(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
fluid_preset_t* TablesComponent::getCurrentPreset() {
|
|
||||||
fluid_synth_t* synth = fluidSynthModel->getSynth();
|
|
||||||
|
|
||||||
return fluid_synth_get_channel_preset(synth, fluidSynthModel->getChannel());
|
|
||||||
}
|
|
||||||
|
|
||||||
Preset TablesComponent::getFirstPresetInBank(int bank) {
|
|
||||||
pair<BanksToPresets::const_iterator, BanksToPresets::const_iterator> iterators = banksToPresets.equal_range(bank);
|
|
||||||
BanksToPresets::const_iterator it = iterators.first;
|
|
||||||
return it->second;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TablesComponent::onBankSelected(int bank) {
|
|
||||||
if (!initialised || bank == -1) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
cout << "Bank " << bank << endl;
|
|
||||||
selectedBank = bank;
|
|
||||||
Preset firstPresetInBank = getFirstPresetInBank(bank);
|
|
||||||
presetTable->setRows(
|
|
||||||
mapPresets(
|
|
||||||
banksToPresets,
|
|
||||||
bank
|
|
||||||
),
|
|
||||||
presetToIndexMapper(firstPresetInBank.getPreset())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
int TablesComponent::presetToIndexMapper(int preset) {
|
|
||||||
int ix = 0;
|
|
||||||
pair<BanksToPresets::const_iterator, BanksToPresets::const_iterator> iterators = this->banksToPresets.equal_range(this->selectedBank);
|
|
||||||
for (auto it = iterators.first; it != iterators.second; ++it, ix++) {
|
|
||||||
Preset b = it->second;
|
|
||||||
if (preset == b.getPreset()) {
|
|
||||||
return ix;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TablesComponent::onPresetSelected(int preset) {
|
|
||||||
if (!initialised || preset == -1) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
cout << "Preset " << preset << endl;
|
|
||||||
// selectedPreset = preset;
|
|
||||||
fluidSynthModel->changePreset(selectedBank, preset);
|
|
||||||
}
|
|
||||||
|
|
||||||
TablesComponent::~TablesComponent() {
|
|
||||||
delete presetTable;
|
|
||||||
delete banks;
|
|
||||||
fluidSynthModel->removeListener(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
vector<string> TablesComponent::mapBanks(const BanksToPresets &banksToPresets) {
|
|
||||||
vector<string> rows;
|
|
||||||
|
|
||||||
const auto compareKey = [](const BanksToPresets::value_type& lhs, const BanksToPresets::value_type& rhs) {
|
|
||||||
return lhs.first < rhs.first;
|
|
||||||
};
|
|
||||||
|
|
||||||
for(auto i = banksToPresets.begin(); i != banksToPresets.end(); i = std::upper_bound(i, banksToPresets.end(), *i, compareKey)) {
|
|
||||||
rows.push_back(to_string(i->first));
|
|
||||||
}
|
|
||||||
|
|
||||||
return rows;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
vector<vector<string>> TablesComponent::mapPresets(const BanksToPresets &banksToPresets, int bank) {
|
|
||||||
vector<vector<string>> rows;
|
|
||||||
|
|
||||||
pair<BanksToPresets::const_iterator, BanksToPresets::const_iterator> iterators = banksToPresets.equal_range(bank);
|
|
||||||
for (auto it = iterators.first; it != iterators.second; ++it) {
|
|
||||||
Preset b = it->second;
|
|
||||||
vector<string> row;
|
|
||||||
row.push_back(to_string(b.getPreset()));
|
|
||||||
row.push_back(b.getName());
|
|
||||||
|
|
||||||
rows.push_back(row);
|
|
||||||
}
|
|
||||||
|
|
||||||
return rows;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TablesComponent::resized() {
|
void TablesComponent::resized() {
|
||||||
Rectangle<int> r (getLocalBounds());
|
Rectangle<int> r (getLocalBounds());
|
||||||
banks->setBounds (r.removeFromTop(27).reduced(5,0));
|
banks.setBounds (r.removeFromTop(27).reduced(5,0));
|
||||||
|
|
||||||
presetTable->setBounds (r);
|
presetTable.setBounds (r);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TablesComponent::keyPressed(const KeyPress &key) {
|
bool TablesComponent::keyPressed(const KeyPress &key) {
|
||||||
if (key.getKeyCode() == KeyPress::leftKey
|
if (key.getKeyCode() == KeyPress::leftKey
|
||||||
|| key.getKeyCode() == KeyPress::rightKey) {
|
|| key.getKeyCode() == KeyPress::rightKey) {
|
||||||
banks->cycle(key.getKeyCode() == KeyPress::rightKey);
|
banks.cycle(key.getKeyCode() == KeyPress::rightKey);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return presetTable->keyPressed(key);
|
return presetTable.keyPressed(key);
|
||||||
}
|
|
||||||
|
|
||||||
void TablesComponent::fontChanged(FluidSynthModel *, const String &) {
|
|
||||||
banksToPresets = fluidSynthModel->getBanks();
|
|
||||||
|
|
||||||
fluid_preset_t* currentPreset = getCurrentPreset();
|
|
||||||
|
|
||||||
selectedBank = fluid_preset_get_banknum(currentPreset);
|
|
||||||
int selectedPreset = fluid_preset_get_num(currentPreset);
|
|
||||||
|
|
||||||
presetTable->setRows(
|
|
||||||
mapPresets(
|
|
||||||
banksToPresets,
|
|
||||||
selectedBank
|
|
||||||
),
|
|
||||||
presetToIndexMapper(selectedPreset)
|
|
||||||
);
|
|
||||||
|
|
||||||
banks->setItems(
|
|
||||||
mapBanks(banksToPresets),
|
|
||||||
selectedBank
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,48 +7,28 @@
|
||||||
#include "../JuceLibraryCode/JuceHeader.h"
|
#include "../JuceLibraryCode/JuceHeader.h"
|
||||||
#include "Pills.h"
|
#include "Pills.h"
|
||||||
#include "TableComponent.h"
|
#include "TableComponent.h"
|
||||||
#include "Preset.h"
|
|
||||||
#include "PresetsToBanks.h"
|
|
||||||
#include "FluidSynthModel.h"
|
#include "FluidSynthModel.h"
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <fluidsynth.h>
|
#include <fluidsynth.h>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
class TablesComponent : public Component,
|
class TablesComponent : public Component
|
||||||
public FluidSynthModel::Listener
|
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
TablesComponent(
|
TablesComponent(
|
||||||
FluidSynthModel* fluidSynthModel
|
AudioProcessorValueTreeState& valueTreeState
|
||||||
);
|
);
|
||||||
~TablesComponent();
|
|
||||||
|
|
||||||
void resized() override;
|
void resized() override;
|
||||||
|
|
||||||
bool keyPressed(const KeyPress &key) override;
|
bool keyPressed(const KeyPress &key) override;
|
||||||
void fontChanged(FluidSynthModel *, const String &) override;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
FluidSynthModel* fluidSynthModel;
|
AudioProcessorValueTreeState& valueTreeState;
|
||||||
int selectedBank;
|
|
||||||
|
|
||||||
Pills* banks;
|
Pills banks;
|
||||||
TableComponent* presetTable;
|
TableComponent presetTable;
|
||||||
|
|
||||||
BanksToPresets banksToPresets;
|
|
||||||
|
|
||||||
static vector<vector<string>> mapPresets(const BanksToPresets &banksToPresets, int bank);
|
|
||||||
static vector<string> mapBanks(const BanksToPresets &banksToPresets);
|
|
||||||
|
|
||||||
void onBankSelected(int bank);
|
|
||||||
void onPresetSelected(int preset);
|
|
||||||
int presetToIndexMapper(int preset);
|
|
||||||
|
|
||||||
fluid_preset_t* getCurrentPreset();
|
|
||||||
Preset getFirstPresetInBank(int bank);
|
|
||||||
|
|
||||||
bool initialised;
|
|
||||||
|
|
||||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TablesComponent)
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TablesComponent)
|
||||||
};
|
};
|
||||||
|
|
|
@ -9,3 +9,11 @@
|
||||||
#define DEBUG_PRINT(str)
|
#define DEBUG_PRINT(str)
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
namespace Util {
|
||||||
|
inline int compare(int a, int b) {
|
||||||
|
if (a > b) return 1;
|
||||||
|
if (a == b) return 0;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user