add lazarus source
This commit is contained in:
		@ -54,6 +54,31 @@ add_executable (STANDALONE_PLUGIN
 | 
				
			|||||||
set_source_files_properties ("../../../../../../Applications/JUCE/modules/juce_audio_plugin_client/juce_audio_plugin_client_Standalone.cpp" PROPERTIES HEADER_FILE_ONLY TRUE)
 | 
					set_source_files_properties ("../../../../../../Applications/JUCE/modules/juce_audio_plugin_client/juce_audio_plugin_client_Standalone.cpp" PROPERTIES HEADER_FILE_ONLY TRUE)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
add_library (SHARED_CODE STATIC
 | 
					add_library (SHARED_CODE STATIC
 | 
				
			||||||
 | 
					    "../../Source/FilePicker.cpp"
 | 
				
			||||||
 | 
					    "../../Source/FilePicker.h"
 | 
				
			||||||
 | 
					    "../../Source/FluidSynthModel.cpp"
 | 
				
			||||||
 | 
					    "../../Source/FluidSynthModel.h"
 | 
				
			||||||
 | 
					    "../../Source/MyColours.cpp"
 | 
				
			||||||
 | 
					    "../../Source/MyColours.h"
 | 
				
			||||||
 | 
					    "../../Source/Pill.cpp"
 | 
				
			||||||
 | 
					    "../../Source/Pill.h"
 | 
				
			||||||
 | 
					    "../../Source/Pills.cpp"
 | 
				
			||||||
 | 
					    "../../Source/Pills.h"
 | 
				
			||||||
 | 
					    "../../Source/Preset.cpp"
 | 
				
			||||||
 | 
					    "../../Source/Preset.h"
 | 
				
			||||||
 | 
					    "../../Source/PresetsToBanks.h"
 | 
				
			||||||
 | 
					    "../../Source/SoundfontSynthSound.cpp"
 | 
				
			||||||
 | 
					    "../../Source/SoundfontSynthSound.h"
 | 
				
			||||||
 | 
					    "../../Source/SoundfontSynthVoice.cpp"
 | 
				
			||||||
 | 
					    "../../Source/SoundfontSynthVoice.h"
 | 
				
			||||||
 | 
					    "../../Source/SurjectiveMidiKeyboardComponent.cpp"
 | 
				
			||||||
 | 
					    "../../Source/SurjectiveMidiKeyboardComponent.h"
 | 
				
			||||||
 | 
					    "../../Source/TableComponent.cpp"
 | 
				
			||||||
 | 
					    "../../Source/TableComponent.h"
 | 
				
			||||||
 | 
					    "../../Source/TableModel.cpp"
 | 
				
			||||||
 | 
					    "../../Source/TableModel.h"
 | 
				
			||||||
 | 
					    "../../Source/TablesComponent.cpp"
 | 
				
			||||||
 | 
					    "../../Source/TablesComponent.h"
 | 
				
			||||||
    "../../Source/PluginProcessor.cpp"
 | 
					    "../../Source/PluginProcessor.cpp"
 | 
				
			||||||
    "../../Source/PluginProcessor.h"
 | 
					    "../../Source/PluginProcessor.h"
 | 
				
			||||||
    "../../Source/PluginEditor.cpp"
 | 
					    "../../Source/PluginEditor.cpp"
 | 
				
			||||||
@ -1352,6 +1377,19 @@ add_library (SHARED_CODE STATIC
 | 
				
			|||||||
    "../../JuceLibraryCode/JuceHeader.h"
 | 
					    "../../JuceLibraryCode/JuceHeader.h"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					set_source_files_properties ("../../Source/FilePicker.h" PROPERTIES HEADER_FILE_ONLY TRUE)
 | 
				
			||||||
 | 
					set_source_files_properties ("../../Source/FluidSynthModel.h" PROPERTIES HEADER_FILE_ONLY TRUE)
 | 
				
			||||||
 | 
					set_source_files_properties ("../../Source/MyColours.h" PROPERTIES HEADER_FILE_ONLY TRUE)
 | 
				
			||||||
 | 
					set_source_files_properties ("../../Source/Pill.h" PROPERTIES HEADER_FILE_ONLY TRUE)
 | 
				
			||||||
 | 
					set_source_files_properties ("../../Source/Pills.h" PROPERTIES HEADER_FILE_ONLY TRUE)
 | 
				
			||||||
 | 
					set_source_files_properties ("../../Source/Preset.h" PROPERTIES HEADER_FILE_ONLY TRUE)
 | 
				
			||||||
 | 
					set_source_files_properties ("../../Source/PresetsToBanks.h" PROPERTIES HEADER_FILE_ONLY TRUE)
 | 
				
			||||||
 | 
					set_source_files_properties ("../../Source/SoundfontSynthSound.h" PROPERTIES HEADER_FILE_ONLY TRUE)
 | 
				
			||||||
 | 
					set_source_files_properties ("../../Source/SoundfontSynthVoice.h" PROPERTIES HEADER_FILE_ONLY TRUE)
 | 
				
			||||||
 | 
					set_source_files_properties ("../../Source/SurjectiveMidiKeyboardComponent.h" PROPERTIES HEADER_FILE_ONLY TRUE)
 | 
				
			||||||
 | 
					set_source_files_properties ("../../Source/TableComponent.h" PROPERTIES HEADER_FILE_ONLY TRUE)
 | 
				
			||||||
 | 
					set_source_files_properties ("../../Source/TableModel.h" PROPERTIES HEADER_FILE_ONLY TRUE)
 | 
				
			||||||
 | 
					set_source_files_properties ("../../Source/TablesComponent.h" PROPERTIES HEADER_FILE_ONLY TRUE)
 | 
				
			||||||
set_source_files_properties ("../../Source/PluginProcessor.h" PROPERTIES HEADER_FILE_ONLY TRUE)
 | 
					set_source_files_properties ("../../Source/PluginProcessor.h" PROPERTIES HEADER_FILE_ONLY TRUE)
 | 
				
			||||||
set_source_files_properties ("../../Source/PluginEditor.h" PROPERTIES HEADER_FILE_ONLY TRUE)
 | 
					set_source_files_properties ("../../Source/PluginEditor.h" PROPERTIES HEADER_FILE_ONLY TRUE)
 | 
				
			||||||
set_source_files_properties ("../../../../../../Applications/JUCE/modules/juce_audio_basics/audio_play_head/juce_AudioPlayHead.h" PROPERTIES HEADER_FILE_ONLY TRUE)
 | 
					set_source_files_properties ("../../../../../../Applications/JUCE/modules/juce_audio_basics/audio_play_head/juce_AudioPlayHead.h" PROPERTIES HEADER_FILE_ONLY TRUE)
 | 
				
			||||||
 | 
				
			|||||||
@ -29,6 +29,18 @@
 | 
				
			|||||||
		F6C481BB44ECD934C583F8DE = {isa = PBXBuildFile; fileRef = 2FB62D371AA3AB416FCB73CE; };
 | 
							F6C481BB44ECD934C583F8DE = {isa = PBXBuildFile; fileRef = 2FB62D371AA3AB416FCB73CE; };
 | 
				
			||||||
		4E15A26240490B6186AF5814 = {isa = PBXBuildFile; fileRef = 4FD107636B29A49B998F03CA; };
 | 
							4E15A26240490B6186AF5814 = {isa = PBXBuildFile; fileRef = 4FD107636B29A49B998F03CA; };
 | 
				
			||||||
		C00686DADD52BA7D05F5509C = {isa = PBXBuildFile; fileRef = 414563833DC377B6107201FD; };
 | 
							C00686DADD52BA7D05F5509C = {isa = PBXBuildFile; fileRef = 414563833DC377B6107201FD; };
 | 
				
			||||||
 | 
							D7EDE9D1D3EB839E23B30138 = {isa = PBXBuildFile; fileRef = 63942F8053F1E4E72C1BE98C; };
 | 
				
			||||||
 | 
							F74CC5452717B67C0B388588 = {isa = PBXBuildFile; fileRef = 86224165AC264294A5EC5D98; };
 | 
				
			||||||
 | 
							6CFEB62A3D350429A770FC6C = {isa = PBXBuildFile; fileRef = AF417398F43EA6221E80C5CD; };
 | 
				
			||||||
 | 
							7239ADCE2A4B2D0B6B532B4F = {isa = PBXBuildFile; fileRef = 16D350530F5AC08BBA372685; };
 | 
				
			||||||
 | 
							E886D809EB324E3A9DF877C4 = {isa = PBXBuildFile; fileRef = B98EB4DDD27035E23CB56C06; };
 | 
				
			||||||
 | 
							B0EEDCDF37720AE014344897 = {isa = PBXBuildFile; fileRef = 83CBB2BD8C28A055DF2200DA; };
 | 
				
			||||||
 | 
							340927BEB265E20CFA2FC43C = {isa = PBXBuildFile; fileRef = 9B0C59050B1829A5A118F652; };
 | 
				
			||||||
 | 
							FB8CA3A0A9B7C681F105556B = {isa = PBXBuildFile; fileRef = 9C0E9C73E7079FCAB66498C4; };
 | 
				
			||||||
 | 
							1CC9C1994887BB28C5841428 = {isa = PBXBuildFile; fileRef = 9B56ACC16CEF4E77D5F953F4; };
 | 
				
			||||||
 | 
							ED089881750EB9643689BA92 = {isa = PBXBuildFile; fileRef = 1630B10A5CCE5F19D6D9DED6; };
 | 
				
			||||||
 | 
							F8DF7CA78D33D9A7EC380CC7 = {isa = PBXBuildFile; fileRef = B69B5C0D575E1538E48DB0BC; };
 | 
				
			||||||
 | 
							BAC1351E9F5BE8EF42572A23 = {isa = PBXBuildFile; fileRef = CE9C9C10847F9625D99EA0C5; };
 | 
				
			||||||
		581AADF14FC0294CD5AE416C = {isa = PBXBuildFile; fileRef = E15CB0CA601DF22E4DA35DCF; };
 | 
							581AADF14FC0294CD5AE416C = {isa = PBXBuildFile; fileRef = E15CB0CA601DF22E4DA35DCF; };
 | 
				
			||||||
		8624BF275D1ECBB9C12BE9C8 = {isa = PBXBuildFile; fileRef = 457D4946B07CC4A74EB0FAE1; };
 | 
							8624BF275D1ECBB9C12BE9C8 = {isa = PBXBuildFile; fileRef = 457D4946B07CC4A74EB0FAE1; };
 | 
				
			||||||
		11EB2EB69F20083150F99E78 = {isa = PBXBuildFile; fileRef = ADA40386504651113647CE71; };
 | 
							11EB2EB69F20083150F99E78 = {isa = PBXBuildFile; fileRef = ADA40386504651113647CE71; };
 | 
				
			||||||
@ -55,16 +67,21 @@
 | 
				
			|||||||
		906232DF8DDD023678AB78A3 = {isa = PBXBuildFile; fileRef = 0C492ABD41089E16644BE612; };
 | 
							906232DF8DDD023678AB78A3 = {isa = PBXBuildFile; fileRef = 0C492ABD41089E16644BE612; };
 | 
				
			||||||
		B2B7F4D38157F527D17E0B44 = {isa = PBXBuildFile; fileRef = E097366DCF5B3FA61B3BD878; };
 | 
							B2B7F4D38157F527D17E0B44 = {isa = PBXBuildFile; fileRef = E097366DCF5B3FA61B3BD878; };
 | 
				
			||||||
		00595E54810E3F6E3F1774E1 = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = "include_juce_events.mm"; path = "../../JuceLibraryCode/include_juce_events.mm"; sourceTree = "SOURCE_ROOT"; };
 | 
							00595E54810E3F6E3F1774E1 = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = "include_juce_events.mm"; path = "../../JuceLibraryCode/include_juce_events.mm"; sourceTree = "SOURCE_ROOT"; };
 | 
				
			||||||
 | 
							015713B103D25BEC3F6BFDAF = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SurjectiveMidiKeyboardComponent.h; path = ../../Source/SurjectiveMidiKeyboardComponent.h; sourceTree = "SOURCE_ROOT"; };
 | 
				
			||||||
		065BC23C916D238CBB74C6EE = {isa = PBXFileReference; lastKnownFileType = file; name = "juce_graphics"; path = "/Applications/JUCE/modules/juce_graphics"; sourceTree = "<absolute>"; };
 | 
							065BC23C916D238CBB74C6EE = {isa = PBXFileReference; lastKnownFileType = file; name = "juce_graphics"; path = "/Applications/JUCE/modules/juce_graphics"; sourceTree = "<absolute>"; };
 | 
				
			||||||
		0C492ABD41089E16644BE612 = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = "include_juce_opengl.mm"; path = "../../JuceLibraryCode/include_juce_opengl.mm"; sourceTree = "SOURCE_ROOT"; };
 | 
							0C492ABD41089E16644BE612 = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = "include_juce_opengl.mm"; path = "../../JuceLibraryCode/include_juce_opengl.mm"; sourceTree = "SOURCE_ROOT"; };
 | 
				
			||||||
		0D8D06B7105FD68639DD4A2C = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = "include_juce_audio_plugin_client_AUv3.mm"; path = "../../JuceLibraryCode/include_juce_audio_plugin_client_AUv3.mm"; sourceTree = "SOURCE_ROOT"; };
 | 
							0D8D06B7105FD68639DD4A2C = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = "include_juce_audio_plugin_client_AUv3.mm"; path = "../../JuceLibraryCode/include_juce_audio_plugin_client_AUv3.mm"; sourceTree = "SOURCE_ROOT"; };
 | 
				
			||||||
		132B1211E0AF52A3265FFF59 = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "include_juce_audio_plugin_client_Standalone.cpp"; path = "../../JuceLibraryCode/include_juce_audio_plugin_client_Standalone.cpp"; sourceTree = "SOURCE_ROOT"; };
 | 
							132B1211E0AF52A3265FFF59 = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "include_juce_audio_plugin_client_Standalone.cpp"; path = "../../JuceLibraryCode/include_juce_audio_plugin_client_Standalone.cpp"; sourceTree = "SOURCE_ROOT"; };
 | 
				
			||||||
 | 
							1630B10A5CCE5F19D6D9DED6 = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = TableComponent.cpp; path = ../../Source/TableComponent.cpp; sourceTree = "SOURCE_ROOT"; };
 | 
				
			||||||
 | 
							16D350530F5AC08BBA372685 = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = Pill.cpp; path = ../../Source/Pill.cpp; sourceTree = "SOURCE_ROOT"; };
 | 
				
			||||||
		1987A2252A5209E2C84CA3AE = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = "Info-VST.plist"; path = "Info-VST.plist"; sourceTree = "SOURCE_ROOT"; };
 | 
							1987A2252A5209E2C84CA3AE = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = "Info-VST.plist"; path = "Info-VST.plist"; sourceTree = "SOURCE_ROOT"; };
 | 
				
			||||||
		1DE691CFDBE3AA935369A0D9 = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVKit.framework; path = System/Library/Frameworks/AVKit.framework; sourceTree = SDKROOT; };
 | 
							1DE691CFDBE3AA935369A0D9 = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVKit.framework; path = System/Library/Frameworks/AVKit.framework; sourceTree = SDKROOT; };
 | 
				
			||||||
 | 
							211587D8CDC501F1BACF8DD4 = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SoundfontSynthSound.h; path = ../../Source/SoundfontSynthSound.h; sourceTree = "SOURCE_ROOT"; };
 | 
				
			||||||
		2465FB46F4FBE888A5D785D5 = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "include_juce_audio_plugin_client_utils.cpp"; path = "../../JuceLibraryCode/include_juce_audio_plugin_client_utils.cpp"; sourceTree = "SOURCE_ROOT"; };
 | 
							2465FB46F4FBE888A5D785D5 = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "include_juce_audio_plugin_client_utils.cpp"; path = "../../JuceLibraryCode/include_juce_audio_plugin_client_utils.cpp"; sourceTree = "SOURCE_ROOT"; };
 | 
				
			||||||
		2FB62D371AA3AB416FCB73CE = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; };
 | 
							2FB62D371AA3AB416FCB73CE = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; };
 | 
				
			||||||
		379C84D5A55C1857A91CBC40 = {isa = PBXFileReference; lastKnownFileType = file; name = "juce_audio_plugin_client"; path = "/Applications/JUCE/modules/juce_audio_plugin_client"; sourceTree = "<absolute>"; };
 | 
							379C84D5A55C1857A91CBC40 = {isa = PBXFileReference; lastKnownFileType = file; name = "juce_audio_plugin_client"; path = "/Applications/JUCE/modules/juce_audio_plugin_client"; sourceTree = "<absolute>"; };
 | 
				
			||||||
		3B0BE48E8B34519259BA931D = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioUnit.framework; path = System/Library/Frameworks/AudioUnit.framework; sourceTree = SDKROOT; };
 | 
							3B0BE48E8B34519259BA931D = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioUnit.framework; path = System/Library/Frameworks/AudioUnit.framework; sourceTree = SDKROOT; };
 | 
				
			||||||
 | 
							3B1AE50061C46417C2C4E30D = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = FluidSynthModel.h; path = ../../Source/FluidSynthModel.h; sourceTree = "SOURCE_ROOT"; };
 | 
				
			||||||
		3BF01ACCE2847C5084FC6EE6 = {isa = PBXFileReference; lastKnownFileType = file; name = "juce_audio_formats"; path = "/Applications/JUCE/modules/juce_audio_formats"; sourceTree = "<absolute>"; };
 | 
							3BF01ACCE2847C5084FC6EE6 = {isa = PBXFileReference; lastKnownFileType = file; name = "juce_audio_formats"; path = "/Applications/JUCE/modules/juce_audio_formats"; sourceTree = "<absolute>"; };
 | 
				
			||||||
		3C86CF996DA9683D8BC69C07 = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = "Info-VST3.plist"; path = "Info-VST3.plist"; sourceTree = "SOURCE_ROOT"; };
 | 
							3C86CF996DA9683D8BC69C07 = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = "Info-VST3.plist"; path = "Info-VST3.plist"; sourceTree = "SOURCE_ROOT"; };
 | 
				
			||||||
		3F9038E23CFD1962440D79A0 = {isa = PBXFileReference; lastKnownFileType = file; name = "juce_audio_devices"; path = "/Applications/JUCE/modules/juce_audio_devices"; sourceTree = "<absolute>"; };
 | 
							3F9038E23CFD1962440D79A0 = {isa = PBXFileReference; lastKnownFileType = file; name = "juce_audio_devices"; path = "/Applications/JUCE/modules/juce_audio_devices"; sourceTree = "<absolute>"; };
 | 
				
			||||||
@ -75,21 +92,28 @@
 | 
				
			|||||||
		4C95500A577A3EB04B67B88A = {isa = PBXFileReference; lastKnownFileType = file; name = "juce_opengl"; path = "/Applications/JUCE/modules/juce_opengl"; sourceTree = "<absolute>"; };
 | 
							4C95500A577A3EB04B67B88A = {isa = PBXFileReference; lastKnownFileType = file; name = "juce_opengl"; path = "/Applications/JUCE/modules/juce_opengl"; sourceTree = "<absolute>"; };
 | 
				
			||||||
		4FD107636B29A49B998F03CA = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WebKit.framework; path = System/Library/Frameworks/WebKit.framework; sourceTree = SDKROOT; };
 | 
							4FD107636B29A49B998F03CA = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WebKit.framework; path = System/Library/Frameworks/WebKit.framework; sourceTree = SDKROOT; };
 | 
				
			||||||
		4FF86BF92DE08D8847D303E4 = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = "include_juce_core.mm"; path = "../../JuceLibraryCode/include_juce_core.mm"; sourceTree = "SOURCE_ROOT"; };
 | 
							4FF86BF92DE08D8847D303E4 = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = "include_juce_core.mm"; path = "../../JuceLibraryCode/include_juce_core.mm"; sourceTree = "SOURCE_ROOT"; };
 | 
				
			||||||
 | 
							526AF36E1188174AB300DA7B = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = Pills.h; path = ../../Source/Pills.h; sourceTree = "SOURCE_ROOT"; };
 | 
				
			||||||
 | 
							560D40E30164CA9D05C6AC3B = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = MyColours.h; path = ../../Source/MyColours.h; sourceTree = "SOURCE_ROOT"; };
 | 
				
			||||||
		571BC08FE42BABE3BAF364C8 = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = "Info-AUv3_AppExtension.plist"; path = "Info-AUv3_AppExtension.plist"; sourceTree = "SOURCE_ROOT"; };
 | 
							571BC08FE42BABE3BAF364C8 = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = "Info-AUv3_AppExtension.plist"; path = "Info-AUv3_AppExtension.plist"; sourceTree = "SOURCE_ROOT"; };
 | 
				
			||||||
		5B3CBC48DAB08EDF53CEE609 = {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"; };
 | 
							5B3CBC48DAB08EDF53CEE609 = {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"; };
 | 
				
			||||||
		6C0D1AFF0E1B107C54560D35 = {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"; };
 | 
					 | 
				
			||||||
		6FC6B8171FA703EB0D26D931 = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = juicysfplugin.vst; sourceTree = "BUILT_PRODUCTS_DIR"; };
 | 
							6FC6B8171FA703EB0D26D931 = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = juicysfplugin.vst; sourceTree = "BUILT_PRODUCTS_DIR"; };
 | 
				
			||||||
		4700F54A29B0F8757E9E3AC0 = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = juicysfplugin.vst3; sourceTree = "BUILT_PRODUCTS_DIR"; };
 | 
							4700F54A29B0F8757E9E3AC0 = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = juicysfplugin.vst3; sourceTree = "BUILT_PRODUCTS_DIR"; };
 | 
				
			||||||
 | 
							6C0D1AFF0E1B107C54560D35 = {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"; };
 | 
				
			||||||
 | 
							7525A1BA309F3F9A2C84B0D0 = {isa = PBXFileReference; lastKnownFileType = file; name = "juce_audio_processors"; path = "/Applications/JUCE/modules/juce_audio_processors"; sourceTree = "<absolute>"; };
 | 
				
			||||||
 | 
							75A66C3558F366D3A8477EFE = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = "include_juce_graphics.mm"; path = "../../JuceLibraryCode/include_juce_graphics.mm"; sourceTree = "SOURCE_ROOT"; };
 | 
				
			||||||
 | 
							7E486C828309A453850FE84B = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = TableComponent.h; path = ../../Source/TableComponent.h; sourceTree = "SOURCE_ROOT"; };
 | 
				
			||||||
 | 
							7EE782A780F66B4A55AFA7D6 = {isa = PBXFileReference; lastKnownFileType = file; name = "juce_gui_extra"; path = "/Applications/JUCE/modules/juce_gui_extra"; sourceTree = "<absolute>"; };
 | 
				
			||||||
		94060BC2E3F4083F53CDDDCA = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = juicysfplugin.component; sourceTree = "BUILT_PRODUCTS_DIR"; };
 | 
							94060BC2E3F4083F53CDDDCA = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = juicysfplugin.component; sourceTree = "BUILT_PRODUCTS_DIR"; };
 | 
				
			||||||
		33B56FA30952FB33928A557F = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = juicysfplugin.appex; sourceTree = "BUILT_PRODUCTS_DIR"; };
 | 
							33B56FA30952FB33928A557F = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = juicysfplugin.appex; sourceTree = "BUILT_PRODUCTS_DIR"; };
 | 
				
			||||||
		5BC90F629770BCF4193FABDD = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioToolbox.framework; path = System/Library/Frameworks/AudioToolbox.framework; sourceTree = SDKROOT; };
 | 
							5BC90F629770BCF4193FABDD = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioToolbox.framework; path = System/Library/Frameworks/AudioToolbox.framework; sourceTree = SDKROOT; };
 | 
				
			||||||
 | 
							63942F8053F1E4E72C1BE98C = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = FilePicker.cpp; path = ../../Source/FilePicker.cpp; sourceTree = "SOURCE_ROOT"; };
 | 
				
			||||||
		674E72377F8F5CAB9A844A6A = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; };
 | 
							674E72377F8F5CAB9A844A6A = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; };
 | 
				
			||||||
		710102EC39EC0F8E952234C6 = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = "Info-AU.plist"; path = "Info-AU.plist"; sourceTree = "SOURCE_ROOT"; };
 | 
							710102EC39EC0F8E952234C6 = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = "Info-AU.plist"; path = "Info-AU.plist"; sourceTree = "SOURCE_ROOT"; };
 | 
				
			||||||
		7525A1BA309F3F9A2C84B0D0 = {isa = PBXFileReference; lastKnownFileType = file; name = "juce_audio_processors"; path = "/Applications/JUCE/modules/juce_audio_processors"; sourceTree = "<absolute>"; };
 | 
					 | 
				
			||||||
		75A66C3558F366D3A8477EFE = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = "include_juce_graphics.mm"; path = "../../JuceLibraryCode/include_juce_graphics.mm"; sourceTree = "SOURCE_ROOT"; };
 | 
					 | 
				
			||||||
		7EE782A780F66B4A55AFA7D6 = {isa = PBXFileReference; lastKnownFileType = file; name = "juce_gui_extra"; path = "/Applications/JUCE/modules/juce_gui_extra"; sourceTree = "<absolute>"; };
 | 
					 | 
				
			||||||
		829055EE1D344045DA0CAA05 = {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"; };
 | 
							829055EE1D344045DA0CAA05 = {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"; };
 | 
				
			||||||
 | 
							83CBB2BD8C28A055DF2200DA = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = Preset.cpp; path = ../../Source/Preset.cpp; sourceTree = "SOURCE_ROOT"; };
 | 
				
			||||||
 | 
							847F052415DCD1CDC6B21CCF = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = PresetsToBanks.h; path = ../../Source/PresetsToBanks.h; sourceTree = "SOURCE_ROOT"; };
 | 
				
			||||||
		86183C1237DA7A5F2CF41CC7 = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreAudio.framework; path = System/Library/Frameworks/CoreAudio.framework; sourceTree = SDKROOT; };
 | 
							86183C1237DA7A5F2CF41CC7 = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreAudio.framework; path = System/Library/Frameworks/CoreAudio.framework; sourceTree = SDKROOT; };
 | 
				
			||||||
 | 
							86224165AC264294A5EC5D98 = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = FluidSynthModel.cpp; path = ../../Source/FluidSynthModel.cpp; sourceTree = "SOURCE_ROOT"; };
 | 
				
			||||||
		877D9F7DB97DBD969D0EF8CA = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Accelerate.framework; path = System/Library/Frameworks/Accelerate.framework; sourceTree = SDKROOT; };
 | 
							877D9F7DB97DBD969D0EF8CA = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Accelerate.framework; path = System/Library/Frameworks/Accelerate.framework; sourceTree = SDKROOT; };
 | 
				
			||||||
		8BB9990C94156A68C5E9752E = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = AppConfig.h; path = ../../JuceLibraryCode/AppConfig.h; sourceTree = "SOURCE_ROOT"; };
 | 
							8BB9990C94156A68C5E9752E = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = AppConfig.h; path = ../../JuceLibraryCode/AppConfig.h; sourceTree = "SOURCE_ROOT"; };
 | 
				
			||||||
		8D5359C5826FB54D34DD16E5 = {isa = PBXFileReference; lastKnownFileType = file; name = "juce_gui_basics"; path = "/Applications/JUCE/modules/juce_gui_basics"; sourceTree = "<absolute>"; };
 | 
							8D5359C5826FB54D34DD16E5 = {isa = PBXFileReference; lastKnownFileType = file; name = "juce_gui_basics"; path = "/Applications/JUCE/modules/juce_gui_basics"; sourceTree = "<absolute>"; };
 | 
				
			||||||
@ -101,21 +125,31 @@
 | 
				
			|||||||
		956F86CAE587B3ABC34C69A9 = {isa = PBXFileReference; lastKnownFileType = file; name = "juce_data_structures"; path = "/Applications/JUCE/modules/juce_data_structures"; sourceTree = "<absolute>"; };
 | 
							956F86CAE587B3ABC34C69A9 = {isa = PBXFileReference; lastKnownFileType = file; name = "juce_data_structures"; path = "/Applications/JUCE/modules/juce_data_structures"; sourceTree = "<absolute>"; };
 | 
				
			||||||
		965A4878123BD53E138EBAF4 = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = "include_juce_cryptography.mm"; path = "../../JuceLibraryCode/include_juce_cryptography.mm"; sourceTree = "SOURCE_ROOT"; };
 | 
							965A4878123BD53E138EBAF4 = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = "include_juce_cryptography.mm"; path = "../../JuceLibraryCode/include_juce_cryptography.mm"; sourceTree = "SOURCE_ROOT"; };
 | 
				
			||||||
		99900FF0BB5E405DDEB97C76 = {isa = PBXFileReference; lastKnownFileType = file; name = "juce_audio_basics"; path = "/Applications/JUCE/modules/juce_audio_basics"; sourceTree = "<absolute>"; };
 | 
							99900FF0BB5E405DDEB97C76 = {isa = PBXFileReference; lastKnownFileType = file; name = "juce_audio_basics"; path = "/Applications/JUCE/modules/juce_audio_basics"; sourceTree = "<absolute>"; };
 | 
				
			||||||
 | 
							9B0C59050B1829A5A118F652 = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = SoundfontSynthSound.cpp; path = ../../Source/SoundfontSynthSound.cpp; sourceTree = "SOURCE_ROOT"; };
 | 
				
			||||||
 | 
							9B56ACC16CEF4E77D5F953F4 = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = SurjectiveMidiKeyboardComponent.cpp; path = ../../Source/SurjectiveMidiKeyboardComponent.cpp; sourceTree = "SOURCE_ROOT"; };
 | 
				
			||||||
 | 
							9B832FDC7670EB6794D32F2E = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = TablesComponent.h; path = ../../Source/TablesComponent.h; sourceTree = "SOURCE_ROOT"; };
 | 
				
			||||||
 | 
							9C0E9C73E7079FCAB66498C4 = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = SoundfontSynthVoice.cpp; path = ../../Source/SoundfontSynthVoice.cpp; sourceTree = "SOURCE_ROOT"; };
 | 
				
			||||||
 | 
							9CDABFEBADFFA0A85C7C6321 = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SoundfontSynthVoice.h; path = ../../Source/SoundfontSynthVoice.h; sourceTree = "SOURCE_ROOT"; };
 | 
				
			||||||
		A147762BC87F26ED856D2F55 = {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"; };
 | 
							A147762BC87F26ED856D2F55 = {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"; };
 | 
				
			||||||
		A28FA9AB4E04B85000B5117D = {isa = PBXFileReference; lastKnownFileType = file; name = "juce_video"; path = "/Applications/JUCE/modules/juce_video"; sourceTree = "<absolute>"; };
 | 
							A28FA9AB4E04B85000B5117D = {isa = PBXFileReference; lastKnownFileType = file; name = "juce_video"; path = "/Applications/JUCE/modules/juce_video"; sourceTree = "<absolute>"; };
 | 
				
			||||||
		A6E1E5CF34685FECF77A432A = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = "include_juce_data_structures.mm"; path = "../../JuceLibraryCode/include_juce_data_structures.mm"; sourceTree = "SOURCE_ROOT"; };
 | 
							A6E1E5CF34685FECF77A432A = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = "include_juce_data_structures.mm"; path = "../../JuceLibraryCode/include_juce_data_structures.mm"; sourceTree = "SOURCE_ROOT"; };
 | 
				
			||||||
		ADA40386504651113647CE71 = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = "include_juce_audio_basics.mm"; path = "../../JuceLibraryCode/include_juce_audio_basics.mm"; sourceTree = "SOURCE_ROOT"; };
 | 
							ADA40386504651113647CE71 = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = "include_juce_audio_basics.mm"; path = "../../JuceLibraryCode/include_juce_audio_basics.mm"; sourceTree = "SOURCE_ROOT"; };
 | 
				
			||||||
 | 
							AF417398F43EA6221E80C5CD = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = MyColours.cpp; path = ../../Source/MyColours.cpp; sourceTree = "SOURCE_ROOT"; };
 | 
				
			||||||
		B05DD9A42AA47641F4DE2368 = {isa = PBXFileReference; lastKnownFileType = file; name = "juce_events"; path = "/Applications/JUCE/modules/juce_events"; sourceTree = "<absolute>"; };
 | 
							B05DD9A42AA47641F4DE2368 = {isa = PBXFileReference; lastKnownFileType = file; name = "juce_events"; path = "/Applications/JUCE/modules/juce_events"; sourceTree = "<absolute>"; };
 | 
				
			||||||
		B26B2A9A2233349B6579E60D = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = JuceHeader.h; path = ../../JuceLibraryCode/JuceHeader.h; sourceTree = "SOURCE_ROOT"; };
 | 
							B26B2A9A2233349B6579E60D = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = JuceHeader.h; path = ../../JuceLibraryCode/JuceHeader.h; sourceTree = "SOURCE_ROOT"; };
 | 
				
			||||||
		B3E8D1BE528BBA1B6004672E = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = PluginEditor.h; path = ../../Source/PluginEditor.h; sourceTree = "SOURCE_ROOT"; };
 | 
							B3E8D1BE528BBA1B6004672E = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = PluginEditor.h; path = ../../Source/PluginEditor.h; sourceTree = "SOURCE_ROOT"; };
 | 
				
			||||||
 | 
							B69B5C0D575E1538E48DB0BC = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = TableModel.cpp; path = ../../Source/TableModel.cpp; sourceTree = "SOURCE_ROOT"; };
 | 
				
			||||||
 | 
							B98EB4DDD27035E23CB56C06 = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = Pills.cpp; path = ../../Source/Pills.cpp; sourceTree = "SOURCE_ROOT"; };
 | 
				
			||||||
		C59CC905C8A1A22FD4B6D4F3 = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = "include_juce_audio_formats.mm"; path = "../../JuceLibraryCode/include_juce_audio_formats.mm"; sourceTree = "SOURCE_ROOT"; };
 | 
							C59CC905C8A1A22FD4B6D4F3 = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = "include_juce_audio_formats.mm"; path = "../../JuceLibraryCode/include_juce_audio_formats.mm"; sourceTree = "SOURCE_ROOT"; };
 | 
				
			||||||
		C80F88F3BE2DA4CE5296D346 = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = "Info-Standalone_Plugin.plist"; path = "Info-Standalone_Plugin.plist"; sourceTree = "SOURCE_ROOT"; };
 | 
							C80F88F3BE2DA4CE5296D346 = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = "Info-Standalone_Plugin.plist"; path = "Info-Standalone_Plugin.plist"; sourceTree = "SOURCE_ROOT"; };
 | 
				
			||||||
		CB5DC270A942A41EFC26AC64 = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = "include_juce_gui_extra.mm"; path = "../../JuceLibraryCode/include_juce_gui_extra.mm"; sourceTree = "SOURCE_ROOT"; };
 | 
					 | 
				
			||||||
		CBABA15F78A71EE58A0B721A = {isa = PBXFileReference; lastKnownFileType = file; name = "juce_audio_utils"; path = "/Applications/JUCE/modules/juce_audio_utils"; sourceTree = "<absolute>"; };
 | 
					 | 
				
			||||||
		CDEEF07090FA0F9AC13D71D4 = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = "include_juce_audio_devices.mm"; path = "../../JuceLibraryCode/include_juce_audio_devices.mm"; sourceTree = "SOURCE_ROOT"; };
 | 
					 | 
				
			||||||
		D7DD2AAFDD58DAB35733C236 = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = juicysfplugin.app; sourceTree = "BUILT_PRODUCTS_DIR"; };
 | 
							D7DD2AAFDD58DAB35733C236 = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = juicysfplugin.app; sourceTree = "BUILT_PRODUCTS_DIR"; };
 | 
				
			||||||
		C38E2C13EAAE0DF111831089 = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libjuicysfplugin.a; sourceTree = "BUILT_PRODUCTS_DIR"; };
 | 
							C38E2C13EAAE0DF111831089 = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libjuicysfplugin.a; sourceTree = "BUILT_PRODUCTS_DIR"; };
 | 
				
			||||||
 | 
							C9BF599C9715D12F9FAE7664 = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = Pill.h; path = ../../Source/Pill.h; sourceTree = "SOURCE_ROOT"; };
 | 
				
			||||||
 | 
							CB5DC270A942A41EFC26AC64 = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = "include_juce_gui_extra.mm"; path = "../../JuceLibraryCode/include_juce_gui_extra.mm"; sourceTree = "SOURCE_ROOT"; };
 | 
				
			||||||
 | 
							CBABA15F78A71EE58A0B721A = {isa = PBXFileReference; lastKnownFileType = file; name = "juce_audio_utils"; path = "/Applications/JUCE/modules/juce_audio_utils"; sourceTree = "<absolute>"; };
 | 
				
			||||||
		CC620AB9A3158E481982505A = {isa = PBXFileReference; lastKnownFileType = file; name = "juce_cryptography"; path = "/Applications/JUCE/modules/juce_cryptography"; sourceTree = "<absolute>"; };
 | 
							CC620AB9A3158E481982505A = {isa = PBXFileReference; lastKnownFileType = file; name = "juce_cryptography"; path = "/Applications/JUCE/modules/juce_cryptography"; sourceTree = "<absolute>"; };
 | 
				
			||||||
 | 
							CDEEF07090FA0F9AC13D71D4 = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = "include_juce_audio_devices.mm"; path = "../../JuceLibraryCode/include_juce_audio_devices.mm"; sourceTree = "SOURCE_ROOT"; };
 | 
				
			||||||
 | 
							CE9C9C10847F9625D99EA0C5 = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = TablesComponent.cpp; path = ../../Source/TablesComponent.cpp; sourceTree = "SOURCE_ROOT"; };
 | 
				
			||||||
		D000F312B9132388ED1431BF = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOKit.framework; path = System/Library/Frameworks/IOKit.framework; sourceTree = SDKROOT; };
 | 
							D000F312B9132388ED1431BF = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOKit.framework; path = System/Library/Frameworks/IOKit.framework; sourceTree = SDKROOT; };
 | 
				
			||||||
		D45370C4C04A476A5B316761 = {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"; };
 | 
							D45370C4C04A476A5B316761 = {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"; };
 | 
				
			||||||
		D83BE45225B0E49E077E5B87 = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = "include_juce_audio_processors.mm"; path = "../../JuceLibraryCode/include_juce_audio_processors.mm"; sourceTree = "SOURCE_ROOT"; };
 | 
							D83BE45225B0E49E077E5B87 = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = "include_juce_audio_processors.mm"; path = "../../JuceLibraryCode/include_juce_audio_processors.mm"; sourceTree = "SOURCE_ROOT"; };
 | 
				
			||||||
@ -124,10 +158,38 @@
 | 
				
			|||||||
		E15CB0CA601DF22E4DA35DCF = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = PluginProcessor.cpp; path = ../../Source/PluginProcessor.cpp; sourceTree = "SOURCE_ROOT"; };
 | 
							E15CB0CA601DF22E4DA35DCF = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = PluginProcessor.cpp; path = ../../Source/PluginProcessor.cpp; sourceTree = "SOURCE_ROOT"; };
 | 
				
			||||||
		E33003EEC78BECD6944E9755 = {isa = PBXFileReference; lastKnownFileType = file; name = "juce_core"; path = "/Applications/JUCE/modules/juce_core"; sourceTree = "<absolute>"; };
 | 
							E33003EEC78BECD6944E9755 = {isa = PBXFileReference; lastKnownFileType = file; name = "juce_core"; path = "/Applications/JUCE/modules/juce_core"; sourceTree = "<absolute>"; };
 | 
				
			||||||
		E36561722259E962C1EB89BD = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Carbon.framework; path = System/Library/Frameworks/Carbon.framework; sourceTree = SDKROOT; };
 | 
							E36561722259E962C1EB89BD = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Carbon.framework; path = System/Library/Frameworks/Carbon.framework; sourceTree = SDKROOT; };
 | 
				
			||||||
 | 
							E365B9925F815EB2C229831F = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = TableModel.h; path = ../../Source/TableModel.h; sourceTree = "SOURCE_ROOT"; };
 | 
				
			||||||
		F079F935113E4B832844A824 = {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"; };
 | 
							F079F935113E4B832844A824 = {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"; };
 | 
				
			||||||
 | 
							F376CFD96DB24F385F4278C5 = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = FilePicker.h; path = ../../Source/FilePicker.h; sourceTree = "SOURCE_ROOT"; };
 | 
				
			||||||
		F79C25232BB02BA6BAEB155A = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = System/Library/Frameworks/AVFoundation.framework; sourceTree = SDKROOT; };
 | 
							F79C25232BB02BA6BAEB155A = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = System/Library/Frameworks/AVFoundation.framework; sourceTree = SDKROOT; };
 | 
				
			||||||
		FB9DC0F378DBE6110579ABEB = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreAudioKit.framework; path = System/Library/Frameworks/CoreAudioKit.framework; sourceTree = SDKROOT; };
 | 
							FB9DC0F378DBE6110579ABEB = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreAudioKit.framework; path = System/Library/Frameworks/CoreAudioKit.framework; sourceTree = SDKROOT; };
 | 
				
			||||||
 | 
							FD8ABA8CD051ABB29C96EF0B = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = Preset.h; path = ../../Source/Preset.h; sourceTree = "SOURCE_ROOT"; };
 | 
				
			||||||
		D1F59E5B10A55A42B51DCF81 = {isa = PBXGroup; children = (
 | 
							D1F59E5B10A55A42B51DCF81 = {isa = PBXGroup; children = (
 | 
				
			||||||
 | 
										63942F8053F1E4E72C1BE98C,
 | 
				
			||||||
 | 
										F376CFD96DB24F385F4278C5,
 | 
				
			||||||
 | 
										86224165AC264294A5EC5D98,
 | 
				
			||||||
 | 
										3B1AE50061C46417C2C4E30D,
 | 
				
			||||||
 | 
										AF417398F43EA6221E80C5CD,
 | 
				
			||||||
 | 
										560D40E30164CA9D05C6AC3B,
 | 
				
			||||||
 | 
										16D350530F5AC08BBA372685,
 | 
				
			||||||
 | 
										C9BF599C9715D12F9FAE7664,
 | 
				
			||||||
 | 
										B98EB4DDD27035E23CB56C06,
 | 
				
			||||||
 | 
										526AF36E1188174AB300DA7B,
 | 
				
			||||||
 | 
										83CBB2BD8C28A055DF2200DA,
 | 
				
			||||||
 | 
										FD8ABA8CD051ABB29C96EF0B,
 | 
				
			||||||
 | 
										847F052415DCD1CDC6B21CCF,
 | 
				
			||||||
 | 
										9B0C59050B1829A5A118F652,
 | 
				
			||||||
 | 
										211587D8CDC501F1BACF8DD4,
 | 
				
			||||||
 | 
										9C0E9C73E7079FCAB66498C4,
 | 
				
			||||||
 | 
										9CDABFEBADFFA0A85C7C6321,
 | 
				
			||||||
 | 
										9B56ACC16CEF4E77D5F953F4,
 | 
				
			||||||
 | 
										015713B103D25BEC3F6BFDAF,
 | 
				
			||||||
 | 
										1630B10A5CCE5F19D6D9DED6,
 | 
				
			||||||
 | 
										7E486C828309A453850FE84B,
 | 
				
			||||||
 | 
										B69B5C0D575E1538E48DB0BC,
 | 
				
			||||||
 | 
										E365B9925F815EB2C229831F,
 | 
				
			||||||
 | 
										CE9C9C10847F9625D99EA0C5,
 | 
				
			||||||
 | 
										9B832FDC7670EB6794D32F2E,
 | 
				
			||||||
					E15CB0CA601DF22E4DA35DCF,
 | 
										E15CB0CA601DF22E4DA35DCF,
 | 
				
			||||||
					4C295CB748DFF39857513207,
 | 
										4C295CB748DFF39857513207,
 | 
				
			||||||
					457D4946B07CC4A74EB0FAE1,
 | 
										457D4946B07CC4A74EB0FAE1,
 | 
				
			||||||
@ -886,6 +948,18 @@
 | 
				
			|||||||
					1AAB501451B3A6B2188833CA,
 | 
										1AAB501451B3A6B2188833CA,
 | 
				
			||||||
					A20BA522ED3B37FC993BC005, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Debug; };
 | 
										A20BA522ED3B37FC993BC005, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Debug; };
 | 
				
			||||||
		6DE3DBD02F8BB43E5621DB38 = {isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = (
 | 
							6DE3DBD02F8BB43E5621DB38 = {isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = (
 | 
				
			||||||
 | 
										D7EDE9D1D3EB839E23B30138,
 | 
				
			||||||
 | 
										F74CC5452717B67C0B388588,
 | 
				
			||||||
 | 
										6CFEB62A3D350429A770FC6C,
 | 
				
			||||||
 | 
										7239ADCE2A4B2D0B6B532B4F,
 | 
				
			||||||
 | 
										E886D809EB324E3A9DF877C4,
 | 
				
			||||||
 | 
										B0EEDCDF37720AE014344897,
 | 
				
			||||||
 | 
										340927BEB265E20CFA2FC43C,
 | 
				
			||||||
 | 
										FB8CA3A0A9B7C681F105556B,
 | 
				
			||||||
 | 
										1CC9C1994887BB28C5841428,
 | 
				
			||||||
 | 
										ED089881750EB9643689BA92,
 | 
				
			||||||
 | 
										F8DF7CA78D33D9A7EC380CC7,
 | 
				
			||||||
 | 
										BAC1351E9F5BE8EF42572A23,
 | 
				
			||||||
					581AADF14FC0294CD5AE416C,
 | 
										581AADF14FC0294CD5AE416C,
 | 
				
			||||||
					8624BF275D1ECBB9C12BE9C8,
 | 
										8624BF275D1ECBB9C12BE9C8,
 | 
				
			||||||
					11EB2EB69F20083150F99E78,
 | 
										11EB2EB69F20083150F99E78,
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										47
									
								
								Source/FilePicker.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								Source/FilePicker.cpp
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,47 @@
 | 
				
			|||||||
 | 
					//
 | 
				
			||||||
 | 
					// Created by Alex Birch on 03/10/2017.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "FilePicker.h"
 | 
				
			||||||
 | 
					#include "MyColours.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					FilePicker::FilePicker(
 | 
				
			||||||
 | 
					        FluidSynthModel* fluidSynthModel
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					: fileChooser(
 | 
				
			||||||
 | 
					        "File",
 | 
				
			||||||
 | 
					        File(),
 | 
				
			||||||
 | 
					        true,
 | 
				
			||||||
 | 
					        false,
 | 
				
			||||||
 | 
					        false,
 | 
				
			||||||
 | 
					        "*.sf2;*.sf3",
 | 
				
			||||||
 | 
					        String(),
 | 
				
			||||||
 | 
					        "Choose a Soundfont file to load into the synthesizer"
 | 
				
			||||||
 | 
					),
 | 
				
			||||||
 | 
					  fluidSynthModel(fluidSynthModel) {
 | 
				
			||||||
 | 
					    // faster (rounded edges introduce transparency)
 | 
				
			||||||
 | 
					    setOpaque (true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    addAndMakeVisible (fileChooser);
 | 
				
			||||||
 | 
					    fileChooser.addListener (this);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					FilePicker::~FilePicker() {
 | 
				
			||||||
 | 
					    fileChooser.removeListener (this);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void FilePicker::resized() {
 | 
				
			||||||
 | 
					    Rectangle<int> r (getLocalBounds());
 | 
				
			||||||
 | 
					    fileChooser.setBounds (r);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * This is required to support setOpaque(true)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					void FilePicker::paint(Graphics& g)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    g.fillAll(MyColours::getUIColourIfAvailable(LookAndFeel_V4::ColourScheme::UIColour::windowBackground, Colours::lightgrey));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void FilePicker::filenameComponentChanged (FilenameComponent*) {
 | 
				
			||||||
 | 
					    fluidSynthModel->onFileNameChanged(fileChooser.getCurrentFile().getFullPathName().toStdString());
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										29
									
								
								Source/FilePicker.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								Source/FilePicker.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,29 @@
 | 
				
			|||||||
 | 
					//
 | 
				
			||||||
 | 
					// Created by Alex Birch on 03/10/2017.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "../JuceLibraryCode/JuceHeader.h"
 | 
				
			||||||
 | 
					#include "FluidSynthModel.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class FilePicker: public Component,
 | 
				
			||||||
 | 
					                  private FilenameComponentListener
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
					    FilePicker(
 | 
				
			||||||
 | 
					            FluidSynthModel* fluidSynthModel
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    ~FilePicker();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void resized() override;
 | 
				
			||||||
 | 
					    void paint (Graphics& g) override;
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
					    FilenameComponent fileChooser;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    FluidSynthModel* fluidSynthModel;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void filenameComponentChanged (FilenameComponent*) override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FilePicker)
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
							
								
								
									
										138
									
								
								Source/FluidSynthModel.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										138
									
								
								Source/FluidSynthModel.cpp
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,138 @@
 | 
				
			|||||||
 | 
					//
 | 
				
			||||||
 | 
					// Created by Alex Birch on 10/09/2017.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <iostream>
 | 
				
			||||||
 | 
					#include "FluidSynthModel.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					using namespace std;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					FluidSynthModel::FluidSynthModel() {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					FluidSynthModel::~FluidSynthModel() {
 | 
				
			||||||
 | 
					    if (initialised) {
 | 
				
			||||||
 | 
					//        delete_fluid_audio_driver(driver);
 | 
				
			||||||
 | 
					        delete_fluid_synth(synth);
 | 
				
			||||||
 | 
					        delete_fluid_settings(settings);
 | 
				
			||||||
 | 
					//        delete driver;
 | 
				
			||||||
 | 
					//        delete settings;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void FluidSynthModel::initialise() {
 | 
				
			||||||
 | 
					    settings = new_fluid_settings();
 | 
				
			||||||
 | 
					    // https://sourceforge.net/p/fluidsynth/wiki/FluidSettings/
 | 
				
			||||||
 | 
					    fluid_settings_setstr(settings, "synth.verbose", "yes");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    synth = new_fluid_synth(settings);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    sfont_id = 0;
 | 
				
			||||||
 | 
					    loadFont("/Users/birch/Documents/soundfont/EarthBound.sf2");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fluid_synth_set_gain(synth, 2.0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//    fluid_synth_bank_select(synth, 0, 3);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//    fluid_handle_inst
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//    driver = new_fluid_audio_driver(settings, synth);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//    changePreset(128, 13);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    initialised = true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int FluidSynthModel::getChannel() {
 | 
				
			||||||
 | 
					    return channel;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void FluidSynthModel::changePreset(int bank, int preset) {
 | 
				
			||||||
 | 
					    fluid_synth_program_select(synth, channel, sfont_id, static_cast<unsigned int>(bank), static_cast<unsigned int>(preset));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const fluid_preset_t FluidSynthModel::getFirstPreset() {
 | 
				
			||||||
 | 
					    fluid_sfont_t* sfont = fluid_synth_get_sfont_by_id(synth, sfont_id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    jassert(sfont != nullptr);
 | 
				
			||||||
 | 
					    sfont->iteration_start(sfont);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fluid_preset_t preset;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    sfont->iteration_next(sfont, &preset);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return preset;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void FluidSynthModel::selectFirstPreset() {
 | 
				
			||||||
 | 
					    fluid_preset_t preset = getFirstPreset();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    int offset = fluid_synth_get_bank_offset(synth, sfont_id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    changePreset(preset.get_banknum(&preset) + offset, preset.get_num(&preset));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					BanksToPresets FluidSynthModel::getBanks() {
 | 
				
			||||||
 | 
					    BanksToPresets banksToPresets;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fluid_sfont_t* sfont = fluid_synth_get_sfont_by_id(synth, sfont_id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    int offset = fluid_synth_get_bank_offset(synth, sfont_id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    jassert(sfont != nullptr);
 | 
				
			||||||
 | 
					    sfont->iteration_start(sfont);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fluid_preset_t preset;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    while(sfont->iteration_next(sfont, &preset)) {
 | 
				
			||||||
 | 
					        banksToPresets.insert(BanksToPresets::value_type(
 | 
				
			||||||
 | 
					                preset.get_banknum(&preset) + offset,
 | 
				
			||||||
 | 
					                *new Preset(
 | 
				
			||||||
 | 
					                        preset.get_num(&preset),
 | 
				
			||||||
 | 
					                        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) {
 | 
				
			||||||
 | 
					    unloadAndLoadFont(absPath);
 | 
				
			||||||
 | 
					    eventListeners.call(&FluidSynthModel::Listener::fontChanged, this);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void FluidSynthModel::unloadAndLoadFont(const string &absPath) {
 | 
				
			||||||
 | 
					    fluid_synth_sfunload(synth, sfont_id, 1);
 | 
				
			||||||
 | 
					    loadFont(absPath);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void FluidSynthModel::loadFont(const string &absPath) {
 | 
				
			||||||
 | 
					    sfont_id++;
 | 
				
			||||||
 | 
					    fluid_synth_sfload(synth, absPath.c_str(), 1);
 | 
				
			||||||
 | 
					    selectFirstPreset();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					FluidSynthModel::Listener::~Listener() {
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void FluidSynthModel::Listener::fontChanged(FluidSynthModel *) {
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//==============================================================================
 | 
				
			||||||
 | 
					void FluidSynthModel::addListener (FluidSynthModel::Listener* const newListener)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    eventListeners.add(newListener);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void FluidSynthModel::removeListener (FluidSynthModel::Listener* const listener)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    eventListeners.remove(listener);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										76
									
								
								Source/FluidSynthModel.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										76
									
								
								Source/FluidSynthModel.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,76 @@
 | 
				
			|||||||
 | 
					//
 | 
				
			||||||
 | 
					// Created by Alex Birch on 10/09/2017.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "../JuceLibraryCode/JuceHeader.h"
 | 
				
			||||||
 | 
					#include <fluidsynth.h>
 | 
				
			||||||
 | 
					#include <memory>
 | 
				
			||||||
 | 
					#include "PresetsToBanks.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// https://stackoverflow.com/a/13446565/5257399
 | 
				
			||||||
 | 
					using std::shared_ptr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class FluidSynthModel {
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
					    FluidSynthModel();
 | 
				
			||||||
 | 
					    ~FluidSynthModel();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fluid_synth_t* getSynth();
 | 
				
			||||||
 | 
					    void initialise();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    BanksToPresets getBanks();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void changePreset(int bank, int preset);
 | 
				
			||||||
 | 
					    int getChannel();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void onFileNameChanged(const string &absPath);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    //==============================================================================
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					        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*);
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** 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);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
					    fluid_synth_t* synth;
 | 
				
			||||||
 | 
					    fluid_settings_t* settings;
 | 
				
			||||||
 | 
					//    fluid_audio_driver_t* driver;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const fluid_preset_t getFirstPreset();
 | 
				
			||||||
 | 
					    void selectFirstPreset();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void unloadAndLoadFont(const string &absPath);
 | 
				
			||||||
 | 
					    void loadFont(const string &absPath);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bool initialised;
 | 
				
			||||||
 | 
					    unsigned int sfont_id;
 | 
				
			||||||
 | 
					    unsigned int channel;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ListenerList<Listener> eventListeners;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FluidSynthModel)
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
							
								
								
									
										12
									
								
								Source/MyColours.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								Source/MyColours.cpp
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,12 @@
 | 
				
			|||||||
 | 
					//
 | 
				
			||||||
 | 
					// Created by Alex Birch on 03/10/2017.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "MyColours.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Colour MyColours::getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour uiColour, Colour fallback = Colour (0xff4d4d4d)) {
 | 
				
			||||||
 | 
					    if (auto* v4 = dynamic_cast<LookAndFeel_V4*> (&LookAndFeel::getDefaultLookAndFeel()))
 | 
				
			||||||
 | 
					        return v4->getCurrentColourScheme().getUIColour (uiColour);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return fallback;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										13
									
								
								Source/MyColours.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								Source/MyColours.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,13 @@
 | 
				
			|||||||
 | 
					//
 | 
				
			||||||
 | 
					// Created by Alex Birch on 03/10/2017.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "../JuceLibraryCode/JuceHeader.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class MyColours {
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
					    static Colour getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour uiColour, Colour fallback);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
							
								
								
									
										17
									
								
								Source/Pill.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								Source/Pill.cpp
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,17 @@
 | 
				
			|||||||
 | 
					//
 | 
				
			||||||
 | 
					// Created by Alex Birch on 01/10/2017.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "Pill.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Pill::Pill(
 | 
				
			||||||
 | 
					        const String& buttonName,
 | 
				
			||||||
 | 
					        const int index,
 | 
				
			||||||
 | 
					        const int lastIx
 | 
				
			||||||
 | 
					) : TextButton(buttonName) {
 | 
				
			||||||
 | 
					    setBounds(20 + index * 55, 260, 55, 24);
 | 
				
			||||||
 | 
					    setConnectedEdges (
 | 
				
			||||||
 | 
					            (index == 0 ? 0 : Button::ConnectedOnLeft)
 | 
				
			||||||
 | 
					            | (index == lastIx ? 0 : Button::ConnectedOnRight)
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										19
									
								
								Source/Pill.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								Source/Pill.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,19 @@
 | 
				
			|||||||
 | 
					//
 | 
				
			||||||
 | 
					// Created by Alex Birch on 01/10/2017.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "../JuceLibraryCode/JuceHeader.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Pill : public TextButton {
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
					    explicit Pill(
 | 
				
			||||||
 | 
					            const String& buttonName,
 | 
				
			||||||
 | 
					            const int index,
 | 
				
			||||||
 | 
					            const int lastIx
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
					    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pill)
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
							
								
								
									
										99
									
								
								Source/Pills.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										99
									
								
								Source/Pills.cpp
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,99 @@
 | 
				
			|||||||
 | 
					//
 | 
				
			||||||
 | 
					// Created by Alex Birch on 01/10/2017.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "Pills.h"
 | 
				
			||||||
 | 
					#include "MyColours.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					using namespace std;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Pills::Pills(
 | 
				
			||||||
 | 
					        string label,
 | 
				
			||||||
 | 
					        const vector<string> &items,
 | 
				
			||||||
 | 
					        const function<void (int)> &onItemSelected,
 | 
				
			||||||
 | 
					        const function<int (const string&)> &itemToIDMapper,
 | 
				
			||||||
 | 
					        int initiallySelectedItem
 | 
				
			||||||
 | 
					) : label(label),
 | 
				
			||||||
 | 
					    items(items),
 | 
				
			||||||
 | 
					    onItemSelected(onItemSelected),
 | 
				
			||||||
 | 
					    itemToIDMapper(itemToIDMapper)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    // faster (rounded edges introduce transparency)
 | 
				
			||||||
 | 
					    setOpaque (true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    populate(initiallySelectedItem);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void Pills::populate(int initiallySelectedItem) {
 | 
				
			||||||
 | 
					    int index = 0;
 | 
				
			||||||
 | 
					    for (string item : items) {
 | 
				
			||||||
 | 
					        TextButton* pill = addToList(new TextButton(
 | 
				
			||||||
 | 
					                item
 | 
				
			||||||
 | 
					        ));
 | 
				
			||||||
 | 
					//        pill->setColour (TextButton::buttonOnColourId, Colours::blueviolet.brighter());
 | 
				
			||||||
 | 
					//        pill->setBounds(20 + index * 55, 260, 55, 24);
 | 
				
			||||||
 | 
					        pill->setConnectedEdges (
 | 
				
			||||||
 | 
					                (index == 0 ? 0 : Button::ConnectedOnLeft)
 | 
				
			||||||
 | 
					                        | (index == (items.size()-1) ? 0 : Button::ConnectedOnRight)
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        pill->setRadioGroupId(34567);
 | 
				
			||||||
 | 
					        if (index == initiallySelectedItem) {
 | 
				
			||||||
 | 
					            pill->setToggleState(true, dontSendNotification);
 | 
				
			||||||
 | 
					            selected = pill;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        pill->setClickingTogglesState(true);
 | 
				
			||||||
 | 
					        pill->addListener(this);
 | 
				
			||||||
 | 
					        index++;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void Pills::setItems(
 | 
				
			||||||
 | 
					        const vector<string> &items,
 | 
				
			||||||
 | 
					        int initiallySelectedItem
 | 
				
			||||||
 | 
					) {
 | 
				
			||||||
 | 
					    this->items = items;
 | 
				
			||||||
 | 
					    for(TextButton* t : buttons) {
 | 
				
			||||||
 | 
					        t->removeListener(this);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    buttons.clear(true);
 | 
				
			||||||
 | 
					    populate(initiallySelectedItem);
 | 
				
			||||||
 | 
					    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) {
 | 
				
			||||||
 | 
					    int currentIx = static_cast<const int>(distance(buttons.begin(), find(buttons.begin(), buttons.end(), selected)));
 | 
				
			||||||
 | 
					    currentIx += right ? 1 : buttons.size()-1;
 | 
				
			||||||
 | 
					    buttons[currentIx % buttons.size()]->triggerClick();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void Pills::resized() {
 | 
				
			||||||
 | 
					    int index = 0;
 | 
				
			||||||
 | 
					    Rectangle<int> r (getLocalBounds());
 | 
				
			||||||
 | 
					    const int equalWidth = r.proportionOfWidth(buttons.size() <= 0 ? 1.0 : 1.0f/buttons.size());
 | 
				
			||||||
 | 
					    for(TextButton* t : buttons) {
 | 
				
			||||||
 | 
					        Rectangle<int> r2 (getLocalBounds());
 | 
				
			||||||
 | 
					        r2.removeFromLeft(equalWidth * index);
 | 
				
			||||||
 | 
					        r2.removeFromRight(equalWidth * (buttons.size()-index-1));
 | 
				
			||||||
 | 
					        t->setBounds (r2);
 | 
				
			||||||
 | 
					        index++;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * This is required to support setOpaque(true)
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					void Pills::paint(Graphics& g)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    g.fillAll(MyColours::getUIColourIfAvailable(LookAndFeel_V4::ColourScheme::UIColour::windowBackground, Colours::lightgrey));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										46
									
								
								Source/Pills.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								Source/Pills.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,46 @@
 | 
				
			|||||||
 | 
					//
 | 
				
			||||||
 | 
					// Created by Alex Birch on 01/10/2017.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "../JuceLibraryCode/JuceHeader.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					using namespace std;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Pills : public Component,
 | 
				
			||||||
 | 
					              public Button::Listener {
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
					    Pills(
 | 
				
			||||||
 | 
					            string label,
 | 
				
			||||||
 | 
					            const vector<string> &items,
 | 
				
			||||||
 | 
					            const function<void (int)> &onItemSelected,
 | 
				
			||||||
 | 
					            const function<int (const string&)> &itemToIDMapper,
 | 
				
			||||||
 | 
					            int initiallySelectedItem
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void setItems(
 | 
				
			||||||
 | 
					            const vector<string> &items,
 | 
				
			||||||
 | 
					            int initiallySelectedItem
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void buttonClicked (Button* button) override;
 | 
				
			||||||
 | 
					    void cycle(bool right);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
					    string label;
 | 
				
			||||||
 | 
					    vector<string> items;
 | 
				
			||||||
 | 
					    function<void (int)> onItemSelected;
 | 
				
			||||||
 | 
					    function<int (const string&)> itemToIDMapper;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    OwnedArray<TextButton> buttons;
 | 
				
			||||||
 | 
					    Button *selected;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    TextButton* addToList (TextButton* newButton);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void populate(int initiallySelectedItem);
 | 
				
			||||||
 | 
					    void resized() override;
 | 
				
			||||||
 | 
					    void paint(Graphics& g) override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pills)
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
@ -11,33 +11,112 @@
 | 
				
			|||||||
#include "PluginProcessor.h"
 | 
					#include "PluginProcessor.h"
 | 
				
			||||||
#include "PluginEditor.h"
 | 
					#include "PluginEditor.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
//==============================================================================
 | 
					//==============================================================================
 | 
				
			||||||
JuicysfpluginAudioProcessorEditor::JuicysfpluginAudioProcessorEditor (JuicysfpluginAudioProcessor& p)
 | 
					LazarusAudioProcessorEditor::LazarusAudioProcessorEditor (LazarusAudioProcessor& p)
 | 
				
			||||||
    : AudioProcessorEditor (&p), processor (p)
 | 
					    : AudioProcessorEditor (&p),
 | 
				
			||||||
 | 
					      processor (p),
 | 
				
			||||||
 | 
					      midiKeyboard (p.keyboardState, SurjectiveMidiKeyboardComponent::horizontalKeyboard),
 | 
				
			||||||
 | 
					      tablesComponent(p.getFluidSynthModel()),
 | 
				
			||||||
 | 
					      filePicker(p.getFluidSynthModel())
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    // Make sure that before the constructor has finished, you've set the
 | 
					    // set resize limits for this plug-in
 | 
				
			||||||
    // editor's size to whatever you need it to be.
 | 
					    setResizeLimits (400, 300, 800, 600);
 | 
				
			||||||
    setSize (400, 300);
 | 
					
 | 
				
			||||||
 | 
					    setSize (p.getLastUIWidth(),
 | 
				
			||||||
 | 
					            p.getLastUIHeight());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    midiKeyboard.setName ("MIDI Keyboard");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    midiKeyboard.setWantsKeyboardFocus(false);
 | 
				
			||||||
 | 
					    tablesComponent.setWantsKeyboardFocus(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    setWantsKeyboardFocus(true);
 | 
				
			||||||
 | 
					    addAndMakeVisible (midiKeyboard);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    addAndMakeVisible(tablesComponent);
 | 
				
			||||||
 | 
					    addAndMakeVisible(filePicker);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
JuicysfpluginAudioProcessorEditor::~JuicysfpluginAudioProcessorEditor()
 | 
					LazarusAudioProcessorEditor::~LazarusAudioProcessorEditor()
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//==============================================================================
 | 
					//==============================================================================
 | 
				
			||||||
void JuicysfpluginAudioProcessorEditor::paint (Graphics& g)
 | 
					void LazarusAudioProcessorEditor::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.setColour (Colours::white);
 | 
				
			||||||
    g.setFont (15.0f);
 | 
					//    g.setFont (15.0f);
 | 
				
			||||||
    g.drawFittedText ("Hello World!", getLocalBounds(), Justification::centred, 1);
 | 
					//    g.drawFittedText ("Hello World!", getLocalBounds(), Justification::centred, 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!focusInitialized) {
 | 
				
			||||||
 | 
					        if (!hasKeyboardFocus(false) && isVisible()) {
 | 
				
			||||||
 | 
					            grabKeyboardFocus();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (getCurrentlyFocusedComponent() == this) {
 | 
				
			||||||
 | 
					            focusInitialized = true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void JuicysfpluginAudioProcessorEditor::resized()
 | 
					void LazarusAudioProcessorEditor::resized()
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    // This is generally where you'll want to lay out the positions of any
 | 
					    const int padding = 8;
 | 
				
			||||||
    // subcomponents in your editor..
 | 
					    const int pianoHeight = 70;
 | 
				
			||||||
 | 
					    const int filePickerHeight = 25;
 | 
				
			||||||
 | 
					    Rectangle<int> r (getLocalBounds());
 | 
				
			||||||
 | 
					    filePicker.setBounds(r.removeFromTop(filePickerHeight + padding).reduced(padding, 0).withTrimmedTop(padding));
 | 
				
			||||||
 | 
					    midiKeyboard.setBounds (r.removeFromBottom (pianoHeight).reduced(padding, 0));
 | 
				
			||||||
 | 
					    tablesComponent.setBounds(r.reduced(0, padding));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//    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);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    processor.setLastUIWidth(getWidth());
 | 
				
			||||||
 | 
					    processor.setLastUIHeight(getHeight());
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool LazarusAudioProcessorEditor::keyPressed(const KeyPress &key) {
 | 
				
			||||||
 | 
					//    if (!hasKeyboardFocus(false))
 | 
				
			||||||
 | 
					//        return false;
 | 
				
			||||||
 | 
					//    if (key.getKeyCode() == KeyPress::upKey){
 | 
				
			||||||
 | 
					//    }
 | 
				
			||||||
 | 
					//    cout << "hey\n";
 | 
				
			||||||
 | 
					    const int cursorKeys[] = {
 | 
				
			||||||
 | 
					            KeyPress::leftKey,
 | 
				
			||||||
 | 
					            KeyPress::rightKey,
 | 
				
			||||||
 | 
					            KeyPress::upKey,
 | 
				
			||||||
 | 
					            KeyPress::downKey
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    if (any_of(
 | 
				
			||||||
 | 
					            begin(cursorKeys),
 | 
				
			||||||
 | 
					            end(cursorKeys),
 | 
				
			||||||
 | 
					            [&](int i) { return i == key.getKeyCode(); }
 | 
				
			||||||
 | 
					    )) {
 | 
				
			||||||
 | 
					        return tablesComponent.keyPressed(key);
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        return midiKeyboard.keyPressed(key);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					//    for(auto childComponent : getChildren()) {
 | 
				
			||||||
 | 
					//        if (childComponent->keyPressed(key)) return true;
 | 
				
			||||||
 | 
					//    }
 | 
				
			||||||
 | 
					    return false;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool LazarusAudioProcessorEditor::keyStateChanged (bool isKeyDown) {
 | 
				
			||||||
 | 
					    return midiKeyboard.keyStateChanged(isKeyDown);
 | 
				
			||||||
 | 
					//    for(auto childComponent : getChildren()) {
 | 
				
			||||||
 | 
					//        if (childComponent->keyStateChanged(isKeyDown)) return true;
 | 
				
			||||||
 | 
					//    }
 | 
				
			||||||
 | 
					//    return false;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -12,25 +12,36 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#include "../JuceLibraryCode/JuceHeader.h"
 | 
					#include "../JuceLibraryCode/JuceHeader.h"
 | 
				
			||||||
#include "PluginProcessor.h"
 | 
					#include "PluginProcessor.h"
 | 
				
			||||||
 | 
					#include "TablesComponent.h"
 | 
				
			||||||
 | 
					#include "SurjectiveMidiKeyboardComponent.h"
 | 
				
			||||||
 | 
					#include "FilePicker.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//==============================================================================
 | 
					//==============================================================================
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
class JuicysfpluginAudioProcessorEditor  : public AudioProcessorEditor
 | 
					class LazarusAudioProcessorEditor  : public AudioProcessorEditor
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
public:
 | 
					public:
 | 
				
			||||||
    JuicysfpluginAudioProcessorEditor (JuicysfpluginAudioProcessor&);
 | 
					    LazarusAudioProcessorEditor (LazarusAudioProcessor&);
 | 
				
			||||||
    ~JuicysfpluginAudioProcessorEditor();
 | 
					    ~LazarusAudioProcessorEditor();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    //==============================================================================
 | 
					    //==============================================================================
 | 
				
			||||||
    void paint (Graphics&) override;
 | 
					    void paint (Graphics&) override;
 | 
				
			||||||
    void resized() override;
 | 
					    void resized() override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bool keyPressed(const KeyPress &key) override;
 | 
				
			||||||
 | 
					    bool keyStateChanged (bool isKeyDown) override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private:
 | 
					private:
 | 
				
			||||||
    // 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.
 | 
				
			||||||
    JuicysfpluginAudioProcessor& processor;
 | 
					    LazarusAudioProcessor& processor;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JuicysfpluginAudioProcessorEditor)
 | 
					    SurjectiveMidiKeyboardComponent midiKeyboard;
 | 
				
			||||||
 | 
					    TablesComponent tablesComponent;
 | 
				
			||||||
 | 
					    FilePicker filePicker;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bool focusInitialized;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LazarusAudioProcessorEditor)
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
				
			|||||||
@ -10,34 +10,49 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#include "PluginProcessor.h"
 | 
					#include "PluginProcessor.h"
 | 
				
			||||||
#include "PluginEditor.h"
 | 
					#include "PluginEditor.h"
 | 
				
			||||||
 | 
					#include "SoundfontSynthVoice.h"
 | 
				
			||||||
 | 
					#include "SoundfontSynthSound.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					AudioProcessor* JUCE_CALLTYPE createPluginFilter();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//==============================================================================
 | 
					//==============================================================================
 | 
				
			||||||
JuicysfpluginAudioProcessor::JuicysfpluginAudioProcessor()
 | 
					LazarusAudioProcessor::LazarusAudioProcessor()
 | 
				
			||||||
#ifndef JucePlugin_PreferredChannelConfigurations
 | 
					     : AudioProcessor (getBusesProperties()),
 | 
				
			||||||
     : AudioProcessor (BusesProperties()
 | 
					       fluidSynthModel(),
 | 
				
			||||||
                     #if ! JucePlugin_IsMidiEffect
 | 
					       lastUIWidth(400),
 | 
				
			||||||
                      #if ! JucePlugin_IsSynth
 | 
					       lastUIHeight(300)
 | 
				
			||||||
                       .withInput  ("Input",  AudioChannelSet::stereo(), true)
 | 
					 | 
				
			||||||
                      #endif
 | 
					 | 
				
			||||||
                       .withOutput ("Output", AudioChannelSet::stereo(), true)
 | 
					 | 
				
			||||||
                     #endif
 | 
					 | 
				
			||||||
                       )
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
					    initialiseSynth();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
JuicysfpluginAudioProcessor::~JuicysfpluginAudioProcessor()
 | 
					LazarusAudioProcessor::~LazarusAudioProcessor()
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
					//    delete fluidSynthModel;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void LazarusAudioProcessor::initialiseSynth() {
 | 
				
			||||||
 | 
					    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());
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//==============================================================================
 | 
					//==============================================================================
 | 
				
			||||||
const String JuicysfpluginAudioProcessor::getName() const
 | 
					const String LazarusAudioProcessor::getName() const
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    return JucePlugin_Name;
 | 
					    return JucePlugin_Name;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool JuicysfpluginAudioProcessor::acceptsMidi() const
 | 
					bool LazarusAudioProcessor::acceptsMidi() const
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
   #if JucePlugin_WantsMidiInput
 | 
					   #if JucePlugin_WantsMidiInput
 | 
				
			||||||
    return true;
 | 
					    return true;
 | 
				
			||||||
@ -46,7 +61,7 @@ bool JuicysfpluginAudioProcessor::acceptsMidi() const
 | 
				
			|||||||
   #endif
 | 
					   #endif
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool JuicysfpluginAudioProcessor::producesMidi() const
 | 
					bool LazarusAudioProcessor::producesMidi() const
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
   #if JucePlugin_ProducesMidiOutput
 | 
					   #if JucePlugin_ProducesMidiOutput
 | 
				
			||||||
    return true;
 | 
					    return true;
 | 
				
			||||||
@ -55,86 +70,87 @@ bool JuicysfpluginAudioProcessor::producesMidi() const
 | 
				
			|||||||
   #endif
 | 
					   #endif
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool JuicysfpluginAudioProcessor::isMidiEffect() const
 | 
					double LazarusAudioProcessor::getTailLengthSeconds() const
 | 
				
			||||||
{
 | 
					 | 
				
			||||||
   #if JucePlugin_IsMidiEffect
 | 
					 | 
				
			||||||
    return true;
 | 
					 | 
				
			||||||
   #else
 | 
					 | 
				
			||||||
    return false;
 | 
					 | 
				
			||||||
   #endif
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
double JuicysfpluginAudioProcessor::getTailLengthSeconds() const
 | 
					 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    return 0.0;
 | 
					    return 0.0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int JuicysfpluginAudioProcessor::getNumPrograms()
 | 
					int LazarusAudioProcessor::getNumPrograms()
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    return 1;   // NB: some hosts don't cope very well if you tell them there are 0 programs,
 | 
					    return 1;   // 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 JuicysfpluginAudioProcessor::getCurrentProgram()
 | 
					int LazarusAudioProcessor::getCurrentProgram()
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    return 0;
 | 
					    return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void JuicysfpluginAudioProcessor::setCurrentProgram (int index)
 | 
					void LazarusAudioProcessor::setCurrentProgram (int index)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const String JuicysfpluginAudioProcessor::getProgramName (int index)
 | 
					const String LazarusAudioProcessor::getProgramName (int index)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    return {};
 | 
					    return {};
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void JuicysfpluginAudioProcessor::changeProgramName (int index, const String& newName)
 | 
					void LazarusAudioProcessor::changeProgramName (int index, const String& newName)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//==============================================================================
 | 
					//==============================================================================
 | 
				
			||||||
void JuicysfpluginAudioProcessor::prepareToPlay (double sampleRate, int samplesPerBlock)
 | 
					void LazarusAudioProcessor::prepareToPlay (double sampleRate, int /*samplesPerBlock*/)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    // Use this method as the place to do any pre-playback
 | 
					    // Use this method as the place to do any pre-playback
 | 
				
			||||||
    // initialisation that you need..
 | 
					    // initialisation that you need..
 | 
				
			||||||
 | 
					    synth.setCurrentPlaybackSampleRate (sampleRate);
 | 
				
			||||||
 | 
					    keyboardState.reset();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    reset();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void JuicysfpluginAudioProcessor::releaseResources()
 | 
					void LazarusAudioProcessor::releaseResources()
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    // When playback stops, you can use this as an opportunity to free up any
 | 
					    // When playback stops, you can use this as an opportunity to free up any
 | 
				
			||||||
    // spare memory, etc.
 | 
					    // spare memory, etc.
 | 
				
			||||||
 | 
					    keyboardState.reset();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifndef JucePlugin_PreferredChannelConfigurations
 | 
					bool LazarusAudioProcessor::isBusesLayoutSupported (const BusesLayout& layouts) const
 | 
				
			||||||
bool JuicysfpluginAudioProcessor::isBusesLayoutSupported (const BusesLayout& layouts) const
 | 
					 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
  #if JucePlugin_IsMidiEffect
 | 
					    // Only mono/stereo and input/output must have same layout
 | 
				
			||||||
    ignoreUnused (layouts);
 | 
					    const AudioChannelSet& mainOutput = layouts.getMainOutputChannelSet();
 | 
				
			||||||
    return true;
 | 
					    const AudioChannelSet& mainInput  = layouts.getMainInputChannelSet();
 | 
				
			||||||
  #else
 | 
					
 | 
				
			||||||
    // This is the place where you check if the layout is supported.
 | 
					    // input and output layout must either be the same or the input must be disabled altogether
 | 
				
			||||||
    // In this template code we only support mono or stereo.
 | 
					    if (! mainInput.isDisabled() && mainInput != mainOutput)
 | 
				
			||||||
    if (layouts.getMainOutputChannelSet() != AudioChannelSet::mono()
 | 
					 | 
				
			||||||
     && layouts.getMainOutputChannelSet() != AudioChannelSet::stereo())
 | 
					 | 
				
			||||||
        return false;
 | 
					        return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // This checks if the input layout matches the output layout
 | 
					    // do not allow disabling the main buses
 | 
				
			||||||
   #if ! JucePlugin_IsSynth
 | 
					    if (mainOutput.isDisabled())
 | 
				
			||||||
    if (layouts.getMainOutputChannelSet() != layouts.getMainInputChannelSet())
 | 
					 | 
				
			||||||
        return false;
 | 
					        return false;
 | 
				
			||||||
   #endif
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return true;
 | 
					    // only allow stereo and mono
 | 
				
			||||||
  #endif
 | 
					    return mainOutput.size() <= 2;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
void JuicysfpluginAudioProcessor::processBlock (AudioBuffer<float>& buffer, MidiBuffer& midiMessages)
 | 
					AudioProcessor::BusesProperties LazarusAudioProcessor::getBusesProperties() {
 | 
				
			||||||
{
 | 
					    return BusesProperties().withInput  ("Input",  AudioChannelSet::stereo(), true)
 | 
				
			||||||
    ScopedNoDenormals noDenormals;
 | 
					            .withOutput ("Output", AudioChannelSet::stereo(), true);
 | 
				
			||||||
    auto totalNumInputChannels  = getTotalNumInputChannels();
 | 
					}
 | 
				
			||||||
    auto totalNumOutputChannels = getTotalNumOutputChannels();
 | 
					
 | 
				
			||||||
 | 
					void LazarusAudioProcessor::processBlock (AudioBuffer<float>& buffer, MidiBuffer& midiMessages) {
 | 
				
			||||||
 | 
					    jassert (!isUsingDoublePrecision());
 | 
				
			||||||
 | 
					    const int numSamples = buffer.getNumSamples();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 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
 | 
				
			||||||
 | 
					    keyboardState.processNextMidiBuffer (midiMessages, 0, numSamples, true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // and now get our synth to process these midi events and generate its output.
 | 
				
			||||||
 | 
					    synth.renderNextBlock (buffer, midiMessages, 0, numSamples);
 | 
				
			||||||
 | 
					    fluid_synth_process(fluidSynth, numSamples, 1, nullptr, buffer.getNumChannels(), buffer.getArrayOfWritePointers());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // In case we have more outputs than inputs, this code clears any output
 | 
					    // In case we have more outputs than inputs, this code clears any output
 | 
				
			||||||
    // channels that didn't contain input data, (because these aren't
 | 
					    // channels that didn't contain input data, (because these aren't
 | 
				
			||||||
@ -142,51 +158,94 @@ void JuicysfpluginAudioProcessor::processBlock (AudioBuffer<float>& buffer, Midi
 | 
				
			|||||||
    // This is here to avoid people getting screaming feedback
 | 
					    // This is here to avoid people getting screaming feedback
 | 
				
			||||||
    // when they first compile a plugin, but obviously you don't need to keep
 | 
					    // when they first compile a plugin, but obviously you don't need to keep
 | 
				
			||||||
    // this code if your algorithm always overwrites all the output channels.
 | 
					    // this code if your algorithm always overwrites all the output channels.
 | 
				
			||||||
    for (auto i = totalNumInputChannels; i < totalNumOutputChannels; ++i)
 | 
					//    for (int i = getTotalNumInputChannels(); i < getTotalNumOutputChannels(); ++i)
 | 
				
			||||||
        buffer.clear (i, 0, buffer.getNumSamples());
 | 
					//        buffer.clear (i, 0, numSamples);
 | 
				
			||||||
 | 
					 | 
				
			||||||
    // This is the place where you'd normally do the guts of your plugin's
 | 
					 | 
				
			||||||
    // audio processing...
 | 
					 | 
				
			||||||
    // Make sure to reset the state if your inner loop is processing
 | 
					 | 
				
			||||||
    // the samples and the outer loop is handling the channels.
 | 
					 | 
				
			||||||
    // Alternatively, you can process the samples with the channels
 | 
					 | 
				
			||||||
    // interleaved by keeping the same state.
 | 
					 | 
				
			||||||
    for (int channel = 0; channel < totalNumInputChannels; ++channel)
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        auto* channelData = buffer.getWritePointer (channel);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // ..do something to the data...
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//==============================================================================
 | 
					//==============================================================================
 | 
				
			||||||
bool JuicysfpluginAudioProcessor::hasEditor() const
 | 
					bool LazarusAudioProcessor::hasEditor() const
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    return true; // (change this to false if you choose to not supply an editor)
 | 
					    return true; // (change this to false if you choose to not supply an editor)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
AudioProcessorEditor* JuicysfpluginAudioProcessor::createEditor()
 | 
					AudioProcessorEditor* LazarusAudioProcessor::createEditor()
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    return new JuicysfpluginAudioProcessorEditor (*this);
 | 
					    return new LazarusAudioProcessorEditor (*this);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//==============================================================================
 | 
					//==============================================================================
 | 
				
			||||||
void JuicysfpluginAudioProcessor::getStateInformation (MemoryBlock& destData)
 | 
					void LazarusAudioProcessor::getStateInformation (MemoryBlock& destData)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    // You should use this method to store your parameters in the memory block.
 | 
					    // You should use this method to store your parameters in the memory block.
 | 
				
			||||||
    // You could do that either as raw data, or use the XML or ValueTree classes
 | 
					    // You could do that either as raw data, or use the XML or ValueTree classes
 | 
				
			||||||
    // 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..
 | 
				
			||||||
 | 
					    XmlElement xml ("MYPLUGINSETTINGS");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // add some attributes to it..
 | 
				
			||||||
 | 
					    xml.setAttribute ("uiWidth", lastUIWidth);
 | 
				
			||||||
 | 
					    xml.setAttribute ("uiHeight", lastUIHeight);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Store the values of all our parameters, using their param ID as the XML attribute
 | 
				
			||||||
 | 
					    for (auto* param : getParameters())
 | 
				
			||||||
 | 
					        if (auto* p = dynamic_cast<AudioProcessorParameterWithID*> (param))
 | 
				
			||||||
 | 
					            xml.setAttribute (p->paramID, p->getValue());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // then use this helper function to stuff it into the binary blob and return it..
 | 
				
			||||||
 | 
					    copyXmlToBinary (xml, destData);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void JuicysfpluginAudioProcessor::setStateInformation (const void* data, int sizeInBytes)
 | 
					void LazarusAudioProcessor::setStateInformation (const void* data, int sizeInBytes)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    // 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..
 | 
				
			||||||
 | 
					    ScopedPointer<XmlElement> xmlState (getXmlFromBinary (data, sizeInBytes));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (xmlState != nullptr)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        // make sure that it's actually our type of XML object..
 | 
				
			||||||
 | 
					        if (xmlState->hasTagName ("MYPLUGINSETTINGS"))
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            // ok, now pull out our last window size..
 | 
				
			||||||
 | 
					            lastUIWidth  = jmax (xmlState->getIntAttribute ("uiWidth", lastUIWidth), 400);
 | 
				
			||||||
 | 
					            lastUIHeight = jmax (xmlState->getIntAttribute ("uiHeight", lastUIHeight), 300);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Now reload our parameters..
 | 
				
			||||||
 | 
					            for (auto* param : getParameters())
 | 
				
			||||||
 | 
					                if (auto* p = dynamic_cast<AudioProcessorParameterWithID*> (param))
 | 
				
			||||||
 | 
					                    p->setValue ((float) xmlState->getDoubleAttribute (p->paramID, p->getValue()));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// FluidSynth only supports float in its process function, so that's all we can support.
 | 
				
			||||||
 | 
					bool LazarusAudioProcessor::supportsDoublePrecisionProcessing() const {
 | 
				
			||||||
 | 
					    return false;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					FluidSynthModel* LazarusAudioProcessor::getFluidSynthModel() {
 | 
				
			||||||
 | 
					    return &fluidSynthModel;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//==============================================================================
 | 
					//==============================================================================
 | 
				
			||||||
// This creates new instances of the plugin..
 | 
					// This creates new instances of the plugin..
 | 
				
			||||||
AudioProcessor* JUCE_CALLTYPE createPluginFilter()
 | 
					AudioProcessor* JUCE_CALLTYPE createPluginFilter()
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    return new JuicysfpluginAudioProcessor();
 | 
					    return new LazarusAudioProcessor();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void LazarusAudioProcessor::setLastUIWidth(int width) {
 | 
				
			||||||
 | 
					    this->lastUIWidth = width;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					void LazarusAudioProcessor::setLastUIHeight(int height) {
 | 
				
			||||||
 | 
					    this->lastUIHeight = height;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int LazarusAudioProcessor::getLastUIWidth() {
 | 
				
			||||||
 | 
					    return lastUIWidth;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					int LazarusAudioProcessor::getLastUIHeight() {
 | 
				
			||||||
 | 
					    return lastUIHeight;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -11,17 +11,17 @@
 | 
				
			|||||||
#pragma once
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "../JuceLibraryCode/JuceHeader.h"
 | 
					#include "../JuceLibraryCode/JuceHeader.h"
 | 
				
			||||||
 | 
					#include "FluidSynthModel.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//==============================================================================
 | 
					//==============================================================================
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
class JuicysfpluginAudioProcessor  : public AudioProcessor
 | 
					class LazarusAudioProcessor  : public AudioProcessor
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
public:
 | 
					public:
 | 
				
			||||||
    //==============================================================================
 | 
					    //==============================================================================
 | 
				
			||||||
    JuicysfpluginAudioProcessor();
 | 
					    LazarusAudioProcessor();
 | 
				
			||||||
    ~JuicysfpluginAudioProcessor();
 | 
					    ~LazarusAudioProcessor();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    //==============================================================================
 | 
					    //==============================================================================
 | 
				
			||||||
    void prepareToPlay (double sampleRate, int samplesPerBlock) override;
 | 
					    void prepareToPlay (double sampleRate, int samplesPerBlock) override;
 | 
				
			||||||
@ -31,7 +31,7 @@ public:
 | 
				
			|||||||
    bool isBusesLayoutSupported (const BusesLayout& layouts) const override;
 | 
					    bool isBusesLayoutSupported (const BusesLayout& layouts) const override;
 | 
				
			||||||
   #endif
 | 
					   #endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    void processBlock (AudioBuffer<float>&, MidiBuffer&) override;
 | 
					    void processBlock (AudioBuffer<float>& buffer, MidiBuffer& midiMessages) override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    //==============================================================================
 | 
					    //==============================================================================
 | 
				
			||||||
    AudioProcessorEditor* createEditor() override;
 | 
					    AudioProcessorEditor* createEditor() override;
 | 
				
			||||||
@ -42,7 +42,6 @@ public:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    bool acceptsMidi() const override;
 | 
					    bool acceptsMidi() const override;
 | 
				
			||||||
    bool producesMidi() const override;
 | 
					    bool producesMidi() const override;
 | 
				
			||||||
    bool isMidiEffect() const override;
 | 
					 | 
				
			||||||
    double getTailLengthSeconds() const override;
 | 
					    double getTailLengthSeconds() const override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    //==============================================================================
 | 
					    //==============================================================================
 | 
				
			||||||
@ -56,7 +55,30 @@ public:
 | 
				
			|||||||
    void getStateInformation (MemoryBlock& destData) override;
 | 
					    void getStateInformation (MemoryBlock& destData) override;
 | 
				
			||||||
    void setStateInformation (const void* data, int sizeInBytes) override;
 | 
					    void setStateInformation (const void* data, int sizeInBytes) override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bool supportsDoublePrecisionProcessing() const override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    FluidSynthModel* getFluidSynthModel();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    MidiKeyboardState keyboardState;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    int getLastUIWidth();
 | 
				
			||||||
 | 
					    int getLastUIHeight();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void setLastUIWidth(int width);
 | 
				
			||||||
 | 
					    void setLastUIHeight(int height);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private:
 | 
					private:
 | 
				
			||||||
 | 
					    void initialiseSynth();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    FluidSynthModel fluidSynthModel;
 | 
				
			||||||
 | 
					    fluid_synth_t* fluidSynth;
 | 
				
			||||||
 | 
					    Synthesiser synth;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    static BusesProperties getBusesProperties();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    int lastUIWidth, lastUIHeight;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//    Model* model;
 | 
				
			||||||
    //==============================================================================
 | 
					    //==============================================================================
 | 
				
			||||||
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JuicysfpluginAudioProcessor)
 | 
					    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LazarusAudioProcessor)
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										21
									
								
								Source/Preset.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								Source/Preset.cpp
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,21 @@
 | 
				
			|||||||
 | 
					//
 | 
				
			||||||
 | 
					// 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;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										24
									
								
								Source/Preset.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								Source/Preset.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,24 @@
 | 
				
			|||||||
 | 
					//
 | 
				
			||||||
 | 
					// 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;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
							
								
								
									
										10
									
								
								Source/PresetsToBanks.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								Source/PresetsToBanks.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,10 @@
 | 
				
			|||||||
 | 
					//
 | 
				
			||||||
 | 
					// Created by Alex Birch on 17/09/2017.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "Preset.h"
 | 
				
			||||||
 | 
					#include <map>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef std::multimap<int, Preset> BanksToPresets;
 | 
				
			||||||
							
								
								
									
										13
									
								
								Source/SoundfontSynthSound.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								Source/SoundfontSynthSound.cpp
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,13 @@
 | 
				
			|||||||
 | 
					//
 | 
				
			||||||
 | 
					// Created by Alex Birch on 07/09/2017.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "SoundfontSynthSound.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool SoundfontSynthSound::appliesToChannel(int) {
 | 
				
			||||||
 | 
					    return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool SoundfontSynthSound::appliesToNote(int) {
 | 
				
			||||||
 | 
					    return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										13
									
								
								Source/SoundfontSynthSound.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								Source/SoundfontSynthSound.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,13 @@
 | 
				
			|||||||
 | 
					//
 | 
				
			||||||
 | 
					// 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;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
							
								
								
									
										97
									
								
								Source/SoundfontSynthVoice.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										97
									
								
								Source/SoundfontSynthVoice.cpp
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,97 @@
 | 
				
			|||||||
 | 
					//
 | 
				
			||||||
 | 
					// Created by Alex Birch on 07/09/2017.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "SoundfontSynthVoice.h"
 | 
				
			||||||
 | 
					#include "SoundfontSynthSound.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;
 | 
				
			||||||
 | 
					    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;
 | 
				
			||||||
 | 
					//    }
 | 
				
			||||||
 | 
					    clearCurrentNote();
 | 
				
			||||||
 | 
					    fluid_synth_noteoff(synth, 0, this->midiNoteNumber);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					void SoundfontSynthVoice::pitchWheelMoved (int /*newValue*/) {
 | 
				
			||||||
 | 
					    // who cares?
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void SoundfontSynthVoice::controllerMoved (int /*controllerNumber*/, int /*newValue*/) {
 | 
				
			||||||
 | 
					    // what's a controller?
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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;
 | 
				
			||||||
 | 
					//            }
 | 
				
			||||||
 | 
					//        }
 | 
				
			||||||
 | 
					//    }
 | 
				
			||||||
 | 
					//}
 | 
				
			||||||
							
								
								
									
										39
									
								
								Source/SoundfontSynthVoice.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								Source/SoundfontSynthVoice.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,39 @@
 | 
				
			|||||||
 | 
					//
 | 
				
			||||||
 | 
					// 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;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
							
								
								
									
										984
									
								
								Source/SurjectiveMidiKeyboardComponent.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										984
									
								
								Source/SurjectiveMidiKeyboardComponent.cpp
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,984 @@
 | 
				
			|||||||
 | 
					//
 | 
				
			||||||
 | 
					// Created by Alex Birch on 27/09/2017.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "SurjectiveMidiKeyboardComponent.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Forked from JUCE/modules/juce_audio_utils/gui/juce_MidiKeyboardComponent.cpp,
 | 
				
			||||||
 | 
					 * which had the following license:
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					  ==============================================================================
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   This file is part of the JUCE library.
 | 
				
			||||||
 | 
					   Copyright (c) 2017 - ROLI Ltd.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   JUCE is an open source library subject to commercial or open-source
 | 
				
			||||||
 | 
					   licensing.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   By using JUCE, you agree to the terms of both the JUCE 5 End-User License
 | 
				
			||||||
 | 
					   Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
 | 
				
			||||||
 | 
					   27th April 2017).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   End User License Agreement: www.juce.com/juce-5-licence
 | 
				
			||||||
 | 
					   Privacy Policy: www.juce.com/juce-5-privacy-policy
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   Or: You may also use this code under the terms of the GPL v3 (see
 | 
				
			||||||
 | 
					   www.gnu.org/licenses).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 | 
				
			||||||
 | 
					   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 | 
				
			||||||
 | 
					   DISCLAIMED.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ==============================================================================
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					class SurjectiveMidiKeyboardUpDownButton  : public Button
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
					    SurjectiveMidiKeyboardUpDownButton (SurjectiveMidiKeyboardComponent& comp, const int d)
 | 
				
			||||||
 | 
					            : Button (String()), owner (comp), delta (d)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void clicked() override
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        int note = owner.getLowestVisibleKey();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (delta < 0)
 | 
				
			||||||
 | 
					            note = (note - 1) / 12;
 | 
				
			||||||
 | 
					        else
 | 
				
			||||||
 | 
					            note = note / 12 + 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        owner.setLowestVisibleKey (note * 12);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void paintButton (Graphics& g, bool isMouseOverButton, bool isButtonDown) override
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        owner.drawUpDownButton (g, getWidth(), getHeight(),
 | 
				
			||||||
 | 
					                isMouseOverButton, isButtonDown,
 | 
				
			||||||
 | 
					                delta > 0);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
					    SurjectiveMidiKeyboardComponent& owner;
 | 
				
			||||||
 | 
					    const int delta;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    JUCE_DECLARE_NON_COPYABLE (SurjectiveMidiKeyboardUpDownButton)
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//==============================================================================
 | 
				
			||||||
 | 
					SurjectiveMidiKeyboardComponent::SurjectiveMidiKeyboardComponent (MidiKeyboardState& s, Orientation o)
 | 
				
			||||||
 | 
					        : state (s),
 | 
				
			||||||
 | 
					          blackNoteLengthRatio (0.7f),
 | 
				
			||||||
 | 
					          xOffset (0),
 | 
				
			||||||
 | 
					          keyWidth (16.0f),
 | 
				
			||||||
 | 
					          orientation (o),
 | 
				
			||||||
 | 
					          midiChannel (1),
 | 
				
			||||||
 | 
					          midiInChannelMask (0xffff),
 | 
				
			||||||
 | 
					          velocity (1.0f),
 | 
				
			||||||
 | 
					          shouldCheckState (false),
 | 
				
			||||||
 | 
					          rangeStart (0),
 | 
				
			||||||
 | 
					          rangeEnd (127),
 | 
				
			||||||
 | 
					          firstKey (12 * 4.0f),
 | 
				
			||||||
 | 
					          canScroll (true),
 | 
				
			||||||
 | 
					          useMousePositionForVelocity (true),
 | 
				
			||||||
 | 
					          shouldCheckMousePos (false),
 | 
				
			||||||
 | 
					          keyMappingOctave (5),
 | 
				
			||||||
 | 
					          octaveNumForMiddleC (3)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    addChildComponent (scrollDown = new SurjectiveMidiKeyboardUpDownButton (*this, -1));
 | 
				
			||||||
 | 
					    addChildComponent (scrollUp   = new SurjectiveMidiKeyboardUpDownButton (*this, 1));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bindKeysToMidiKeyboard();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    mouseOverNotes.insertMultiple (0, -1, 32);
 | 
				
			||||||
 | 
					    mouseDownNotes.insertMultiple (0, -1, 32);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    colourChanged();
 | 
				
			||||||
 | 
					    setWantsKeyboardFocus (true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    state.addListener (this);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    startTimerHz (20);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					SurjectiveMidiKeyboardComponent::~SurjectiveMidiKeyboardComponent()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    state.removeListener (this);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void SurjectiveMidiKeyboardComponent::bindKeysToMidiKeyboard() {
 | 
				
			||||||
 | 
					    // FL studio key chart:
 | 
				
			||||||
 | 
					    // http://s3.amazonaws.com/fl_resource/flkeychart.png
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    int ix, degree;
 | 
				
			||||||
 | 
					    const int whiteJumps[] = {2,2,1,2,2,2,1};
 | 
				
			||||||
 | 
					    const int blackJumps[] = {2,3,2};
 | 
				
			||||||
 | 
					    const int whiteJumpsC = sizeof(whiteJumps)/sizeof(whiteJumps[0]);
 | 
				
			||||||
 | 
					    const int blackJumpsC = sizeof(blackJumps)/sizeof(blackJumps[0]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ix = degree = 0;
 | 
				
			||||||
 | 
					    for (const char keyCode : "ZXCVBNM,./") {
 | 
				
			||||||
 | 
					        setKeyPressForNote(KeyPress(keyCode), degree);
 | 
				
			||||||
 | 
					        degree += whiteJumps[ix++ % whiteJumpsC];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ix = 0;
 | 
				
			||||||
 | 
					    degree = 1;
 | 
				
			||||||
 | 
					    for (const char keyCode : "SDGHJL;") {
 | 
				
			||||||
 | 
					        setKeyPressForNote(KeyPress(keyCode), degree);
 | 
				
			||||||
 | 
					        degree += blackJumps[ix++ % blackJumpsC];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ix = 0;
 | 
				
			||||||
 | 
					    degree = 12;
 | 
				
			||||||
 | 
					    for (const char keyCode : "QWERTYUIOP") {
 | 
				
			||||||
 | 
					        setKeyPressForNote(KeyPress(keyCode), degree);
 | 
				
			||||||
 | 
					        degree += whiteJumps[ix++ % whiteJumpsC];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ix = 0;
 | 
				
			||||||
 | 
					    degree = 13;
 | 
				
			||||||
 | 
					    for (const char keyCode : "2356790") {
 | 
				
			||||||
 | 
					        setKeyPressForNote(KeyPress(keyCode), degree);
 | 
				
			||||||
 | 
					        degree += blackJumps[ix++ % blackJumpsC];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//==============================================================================
 | 
				
			||||||
 | 
					void SurjectiveMidiKeyboardComponent::setKeyWidth (const float widthInPixels)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    jassert (widthInPixels > 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (keyWidth != widthInPixels) // Prevent infinite recursion if the width is being computed in a 'resized()' call-back
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        keyWidth = widthInPixels;
 | 
				
			||||||
 | 
					        resized();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void SurjectiveMidiKeyboardComponent::setOrientation (const Orientation newOrientation)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    if (orientation != newOrientation)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        orientation = newOrientation;
 | 
				
			||||||
 | 
					        resized();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void SurjectiveMidiKeyboardComponent::setAvailableRange (const int lowestNote,
 | 
				
			||||||
 | 
					        const int highestNote)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    jassert (lowestNote >= 0 && lowestNote <= 127);
 | 
				
			||||||
 | 
					    jassert (highestNote >= 0 && highestNote <= 127);
 | 
				
			||||||
 | 
					    jassert (lowestNote <= highestNote);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (rangeStart != lowestNote || rangeEnd != highestNote)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        rangeStart = jlimit (0, 127, lowestNote);
 | 
				
			||||||
 | 
					        rangeEnd = jlimit (0, 127, highestNote);
 | 
				
			||||||
 | 
					        firstKey = jlimit ((float) rangeStart, (float) rangeEnd, firstKey);
 | 
				
			||||||
 | 
					        resized();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void SurjectiveMidiKeyboardComponent::setLowestVisibleKey (int noteNumber)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    setLowestVisibleKeyFloat ((float) noteNumber);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void SurjectiveMidiKeyboardComponent::setLowestVisibleKeyFloat (float noteNumber)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    noteNumber = jlimit ((float) rangeStart, (float) rangeEnd, noteNumber);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (noteNumber != firstKey)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        const bool hasMoved = (((int) firstKey) != (int) noteNumber);
 | 
				
			||||||
 | 
					        firstKey = noteNumber;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (hasMoved)
 | 
				
			||||||
 | 
					            sendChangeMessage();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        resized();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void SurjectiveMidiKeyboardComponent::setScrollButtonsVisible (const bool newCanScroll)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    if (canScroll != newCanScroll)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        canScroll = newCanScroll;
 | 
				
			||||||
 | 
					        resized();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void SurjectiveMidiKeyboardComponent::colourChanged()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    setOpaque (findColour (whiteNoteColourId).isOpaque());
 | 
				
			||||||
 | 
					    repaint();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//==============================================================================
 | 
				
			||||||
 | 
					void SurjectiveMidiKeyboardComponent::setMidiChannel (const int midiChannelNumber)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    jassert (midiChannelNumber > 0 && midiChannelNumber <= 16);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (midiChannel != midiChannelNumber)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        resetAnyKeysInUse();
 | 
				
			||||||
 | 
					        midiChannel = jlimit (1, 16, midiChannelNumber);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void SurjectiveMidiKeyboardComponent::setMidiChannelsToDisplay (const int midiChannelMask)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    midiInChannelMask = midiChannelMask;
 | 
				
			||||||
 | 
					    shouldCheckState = true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void SurjectiveMidiKeyboardComponent::setVelocity (const float v, const bool useMousePosition)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    velocity = jlimit (0.0f, 1.0f, v);
 | 
				
			||||||
 | 
					    useMousePositionForVelocity = useMousePosition;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//==============================================================================
 | 
				
			||||||
 | 
					void SurjectiveMidiKeyboardComponent::getKeyPosition (int midiNoteNumber, const float keyWidth_, int& x, int& w) const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    jassert (midiNoteNumber >= 0 && midiNoteNumber < 128);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    static const float blackNoteWidth = 0.7f;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    static const float notePos[] = { 0.0f, 1 - blackNoteWidth * 0.6f,
 | 
				
			||||||
 | 
					            1.0f, 2 - blackNoteWidth * 0.4f,
 | 
				
			||||||
 | 
					            2.0f,
 | 
				
			||||||
 | 
					            3.0f, 4 - blackNoteWidth * 0.7f,
 | 
				
			||||||
 | 
					            4.0f, 5 - blackNoteWidth * 0.5f,
 | 
				
			||||||
 | 
					            5.0f, 6 - blackNoteWidth * 0.3f,
 | 
				
			||||||
 | 
					            6.0f };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const int octave = midiNoteNumber / 12;
 | 
				
			||||||
 | 
					    const int note   = midiNoteNumber % 12;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    x = roundToInt (octave * 7.0f * keyWidth_ + notePos [note] * keyWidth_);
 | 
				
			||||||
 | 
					    w = roundToInt (MidiMessage::isMidiNoteBlack (note) ? blackNoteWidth * keyWidth_ : keyWidth_);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void SurjectiveMidiKeyboardComponent::getKeyPos (int midiNoteNumber, int& x, int& w) const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    getKeyPosition (midiNoteNumber, keyWidth, x, w);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    int rx, rw;
 | 
				
			||||||
 | 
					    getKeyPosition (rangeStart, keyWidth, rx, rw);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    x -= xOffset + rx;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Rectangle<int> SurjectiveMidiKeyboardComponent::getRectangleForKey (const int note) const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    jassert (note >= rangeStart && note <= rangeEnd);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    int x, w;
 | 
				
			||||||
 | 
					    getKeyPos (note, x, w);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (MidiMessage::isMidiNoteBlack (note))
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        const int blackNoteLength = getBlackNoteLength();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        switch (orientation)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            case horizontalKeyboard:            return Rectangle<int> (x, 0, w, blackNoteLength);
 | 
				
			||||||
 | 
					            case verticalKeyboardFacingLeft:    return Rectangle<int> (getWidth() - blackNoteLength, x, blackNoteLength, w);
 | 
				
			||||||
 | 
					            case verticalKeyboardFacingRight:   return Rectangle<int> (0, getHeight() - x - w, blackNoteLength, w);
 | 
				
			||||||
 | 
					            default:                            jassertfalse; break;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        switch (orientation)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            case horizontalKeyboard:            return Rectangle<int> (x, 0, w, getHeight());
 | 
				
			||||||
 | 
					            case verticalKeyboardFacingLeft:    return Rectangle<int> (0, x, getWidth(), w);
 | 
				
			||||||
 | 
					            case verticalKeyboardFacingRight:   return Rectangle<int> (0, getHeight() - x - w, getWidth(), w);
 | 
				
			||||||
 | 
					            default:                            jassertfalse; break;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return Rectangle<int>();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int SurjectiveMidiKeyboardComponent::getKeyStartPosition (const int midiNoteNumber) const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    int x, w;
 | 
				
			||||||
 | 
					    getKeyPos (midiNoteNumber, x, w);
 | 
				
			||||||
 | 
					    return x;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int SurjectiveMidiKeyboardComponent::getTotalKeyboardWidth() const noexcept
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    int x, w;
 | 
				
			||||||
 | 
					    getKeyPos (rangeEnd, x, w);
 | 
				
			||||||
 | 
					    return x + w;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int SurjectiveMidiKeyboardComponent::getNoteAtPosition (Point<int> p)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    float v;
 | 
				
			||||||
 | 
					    return xyToNote (p, v);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const uint8 SurjectiveMidiKeyboardComponent::whiteNotes[] = { 0, 2, 4, 5, 7, 9, 11 };
 | 
				
			||||||
 | 
					const uint8 SurjectiveMidiKeyboardComponent::blackNotes[] = { 1, 3, 6, 8, 10 };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int SurjectiveMidiKeyboardComponent::xyToNote (Point<int> pos, float& mousePositionVelocity)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    if (! reallyContains (pos, false))
 | 
				
			||||||
 | 
					        return -1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Point<int> p (pos);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (orientation != horizontalKeyboard)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        p = Point<int> (p.y, p.x);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (orientation == verticalKeyboardFacingLeft)
 | 
				
			||||||
 | 
					            p = Point<int> (p.x, getWidth() - p.y);
 | 
				
			||||||
 | 
					        else
 | 
				
			||||||
 | 
					            p = Point<int> (getHeight() - p.x, p.y);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return remappedXYToNote (p + Point<int> (xOffset, 0), mousePositionVelocity);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int SurjectiveMidiKeyboardComponent::remappedXYToNote (Point<int> pos, float& mousePositionVelocity) const
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    const int blackNoteLength = getBlackNoteLength();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (pos.getY() < blackNoteLength)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        for (int octaveStart = 12 * (rangeStart / 12); octaveStart <= rangeEnd; octaveStart += 12)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            for (int i = 0; i < 5; ++i)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                const int note = octaveStart + blackNotes [i];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (note >= rangeStart && note <= rangeEnd)
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    int kx, kw;
 | 
				
			||||||
 | 
					                    getKeyPos (note, kx, kw);
 | 
				
			||||||
 | 
					                    kx += xOffset;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    if (pos.x >= kx && pos.x < kx + kw)
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        mousePositionVelocity = pos.y / (float) blackNoteLength;
 | 
				
			||||||
 | 
					                        return note;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (int octaveStart = 12 * (rangeStart / 12); octaveStart <= rangeEnd; octaveStart += 12)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        for (int i = 0; i < 7; ++i)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            const int note = octaveStart + whiteNotes [i];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (note >= rangeStart && note <= rangeEnd)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                int kx, kw;
 | 
				
			||||||
 | 
					                getKeyPos (note, kx, kw);
 | 
				
			||||||
 | 
					                kx += xOffset;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (pos.x >= kx && pos.x < kx + kw)
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    const int whiteNoteLength = (orientation == horizontalKeyboard) ? getHeight() : getWidth();
 | 
				
			||||||
 | 
					                    mousePositionVelocity = pos.y / (float) whiteNoteLength;
 | 
				
			||||||
 | 
					                    return note;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    mousePositionVelocity = 0;
 | 
				
			||||||
 | 
					    return -1;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//==============================================================================
 | 
				
			||||||
 | 
					void SurjectiveMidiKeyboardComponent::repaintNote (const int noteNum)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    if (noteNum >= rangeStart && noteNum <= rangeEnd)
 | 
				
			||||||
 | 
					        repaint (getRectangleForKey (noteNum));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void SurjectiveMidiKeyboardComponent::paint (Graphics& g)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    g.fillAll (findColour (whiteNoteColourId));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const Colour lineColour (findColour (keySeparatorLineColourId));
 | 
				
			||||||
 | 
					    const Colour textColour (findColour (textLabelColourId));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (int octave = 0; octave < 128; octave += 12)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        for (int white = 0; white < 7; ++white)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            const int noteNum = octave + whiteNotes [white];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (noteNum >= rangeStart && noteNum <= rangeEnd)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                Rectangle<int> pos = getRectangleForKey (noteNum);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                drawWhiteNote (noteNum, g, pos.getX(), pos.getY(), pos.getWidth(), pos.getHeight(),
 | 
				
			||||||
 | 
					                        state.isNoteOnForChannels (midiInChannelMask, noteNum),
 | 
				
			||||||
 | 
					                        mouseOverNotes.contains (noteNum), lineColour, textColour);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    float x1 = 0.0f, y1 = 0.0f, x2 = 0.0f, y2 = 0.0f;
 | 
				
			||||||
 | 
					    const int width = getWidth();
 | 
				
			||||||
 | 
					    const int height = getHeight();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (orientation == verticalKeyboardFacingLeft)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        x1 = width - 1.0f;
 | 
				
			||||||
 | 
					        x2 = width - 5.0f;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    else if (orientation == verticalKeyboardFacingRight)
 | 
				
			||||||
 | 
					        x2 = 5.0f;
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					        y2 = 5.0f;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    int x, w;
 | 
				
			||||||
 | 
					    getKeyPos (rangeEnd, x, w);
 | 
				
			||||||
 | 
					    x += w;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const Colour shadowCol (findColour (shadowColourId));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (! shadowCol.isTransparent())
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        g.setGradientFill (ColourGradient (shadowCol, x1, y1, shadowCol.withAlpha (0.0f), x2, y2, false));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        switch (orientation)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            case horizontalKeyboard:            g.fillRect (0, 0, x, 5); break;
 | 
				
			||||||
 | 
					            case verticalKeyboardFacingLeft:    g.fillRect (width - 5, 0, 5, x); break;
 | 
				
			||||||
 | 
					            case verticalKeyboardFacingRight:   g.fillRect (0, 0, 5, x); break;
 | 
				
			||||||
 | 
					            default: break;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (! lineColour.isTransparent())
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        g.setColour (lineColour);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        switch (orientation)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            case horizontalKeyboard:            g.fillRect (0, height - 1, x, 1); break;
 | 
				
			||||||
 | 
					            case verticalKeyboardFacingLeft:    g.fillRect (0, 0, 1, x); break;
 | 
				
			||||||
 | 
					            case verticalKeyboardFacingRight:   g.fillRect (width - 1, 0, 1, x); break;
 | 
				
			||||||
 | 
					            default: break;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const Colour blackNoteColour (findColour (blackNoteColourId));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (int octave = 0; octave < 128; octave += 12)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        for (int black = 0; black < 5; ++black)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            const int noteNum = octave + blackNotes [black];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (noteNum >= rangeStart && noteNum <= rangeEnd)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                Rectangle<int> pos = getRectangleForKey (noteNum);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                drawBlackNote (noteNum, g, pos.getX(), pos.getY(), pos.getWidth(), pos.getHeight(),
 | 
				
			||||||
 | 
					                        state.isNoteOnForChannels (midiInChannelMask, noteNum),
 | 
				
			||||||
 | 
					                        mouseOverNotes.contains (noteNum), blackNoteColour);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void SurjectiveMidiKeyboardComponent::drawWhiteNote (int midiNoteNumber,
 | 
				
			||||||
 | 
					        Graphics& g, int x, int y, int w, int h,
 | 
				
			||||||
 | 
					        bool isDown, bool isOver,
 | 
				
			||||||
 | 
					        const Colour& lineColour,
 | 
				
			||||||
 | 
					        const Colour& textColour)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    Colour c (Colours::transparentWhite);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (isDown)  c = findColour (keyDownOverlayColourId);
 | 
				
			||||||
 | 
					    if (isOver)  c = c.overlaidWith (findColour (mouseOverKeyOverlayColourId));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    g.setColour (c);
 | 
				
			||||||
 | 
					    g.fillRect (x, y, w, h);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const String text (getWhiteNoteText (midiNoteNumber));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (text.isNotEmpty())
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        const float fontHeight = jmin (12.0f, keyWidth * 0.9f);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        g.setColour (textColour);
 | 
				
			||||||
 | 
					        g.setFont (Font (fontHeight).withHorizontalScale (0.8f));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        switch (orientation)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            case horizontalKeyboard:            g.drawText (text, x + 1, y,     w - 1, h - 2, Justification::centredBottom, false); break;
 | 
				
			||||||
 | 
					            case verticalKeyboardFacingLeft:    g.drawText (text, x + 2, y + 2, w - 4, h - 4, Justification::centredLeft,   false); break;
 | 
				
			||||||
 | 
					            case verticalKeyboardFacingRight:   g.drawText (text, x + 2, y + 2, w - 4, h - 4, Justification::centredRight,  false); break;
 | 
				
			||||||
 | 
					            default: break;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (! lineColour.isTransparent())
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        g.setColour (lineColour);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        switch (orientation)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            case horizontalKeyboard:            g.fillRect (x, y, 1, h); break;
 | 
				
			||||||
 | 
					            case verticalKeyboardFacingLeft:    g.fillRect (x, y, w, 1); break;
 | 
				
			||||||
 | 
					            case verticalKeyboardFacingRight:   g.fillRect (x, y + h - 1, w, 1); break;
 | 
				
			||||||
 | 
					            default: break;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (midiNoteNumber == rangeEnd)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            switch (orientation)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                case horizontalKeyboard:            g.fillRect (x + w, y, 1, h); break;
 | 
				
			||||||
 | 
					                case verticalKeyboardFacingLeft:    g.fillRect (x, y + h, w, 1); break;
 | 
				
			||||||
 | 
					                case verticalKeyboardFacingRight:   g.fillRect (x, y - 1, w, 1); break;
 | 
				
			||||||
 | 
					                default: break;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void SurjectiveMidiKeyboardComponent::drawBlackNote (int /*midiNoteNumber*/,
 | 
				
			||||||
 | 
					        Graphics& g, int x, int y, int w, int h,
 | 
				
			||||||
 | 
					        bool isDown, bool isOver,
 | 
				
			||||||
 | 
					        const Colour& noteFillColour)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    Colour c (noteFillColour);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (isDown)  c = c.overlaidWith (findColour (keyDownOverlayColourId));
 | 
				
			||||||
 | 
					    if (isOver)  c = c.overlaidWith (findColour (mouseOverKeyOverlayColourId));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    g.setColour (c);
 | 
				
			||||||
 | 
					    g.fillRect (x, y, w, h);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (isDown)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        g.setColour (noteFillColour);
 | 
				
			||||||
 | 
					        g.drawRect (x, y, w, h);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        g.setColour (c.brighter());
 | 
				
			||||||
 | 
					        const int xIndent = jmax (1, jmin (w, h) / 8);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        switch (orientation)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            case horizontalKeyboard:            g.fillRect (x + xIndent, y, w - xIndent * 2, 7 * h / 8); break;
 | 
				
			||||||
 | 
					            case verticalKeyboardFacingLeft:    g.fillRect (x + w / 8, y + xIndent, w - w / 8, h - xIndent * 2); break;
 | 
				
			||||||
 | 
					            case verticalKeyboardFacingRight:   g.fillRect (x, y + xIndent, 7 * w / 8, h - xIndent * 2); break;
 | 
				
			||||||
 | 
					            default: break;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void SurjectiveMidiKeyboardComponent::setOctaveForMiddleC (const int octaveNum)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    octaveNumForMiddleC = octaveNum;
 | 
				
			||||||
 | 
					    repaint();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					String SurjectiveMidiKeyboardComponent::getWhiteNoteText (const int midiNoteNumber)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    if (midiNoteNumber % 12 == 0)
 | 
				
			||||||
 | 
					        return MidiMessage::getMidiNoteName (midiNoteNumber, true, true, octaveNumForMiddleC);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return {};
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void SurjectiveMidiKeyboardComponent::drawUpDownButton (Graphics& g, int w, int h,
 | 
				
			||||||
 | 
					        const bool mouseOver,
 | 
				
			||||||
 | 
					        const bool buttonDown,
 | 
				
			||||||
 | 
					        const bool movesOctavesUp)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    g.fillAll (findColour (upDownButtonBackgroundColourId));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    float angle;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    switch (orientation)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        case horizontalKeyboard:            angle = movesOctavesUp ? 0.0f  : 0.5f;  break;
 | 
				
			||||||
 | 
					        case verticalKeyboardFacingLeft:    angle = movesOctavesUp ? 0.25f : 0.75f; break;
 | 
				
			||||||
 | 
					        case verticalKeyboardFacingRight:   angle = movesOctavesUp ? 0.75f : 0.25f; break;
 | 
				
			||||||
 | 
					        default:                            jassertfalse; angle = 0; break;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Path path;
 | 
				
			||||||
 | 
					    path.addTriangle (0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.5f);
 | 
				
			||||||
 | 
					    path.applyTransform (AffineTransform::rotation (float_Pi * 2.0f * angle, 0.5f, 0.5f));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    g.setColour (findColour (upDownButtonArrowColourId)
 | 
				
			||||||
 | 
					            .withAlpha (buttonDown ? 1.0f : (mouseOver ? 0.6f : 0.4f)));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    g.fillPath (path, path.getTransformToScaleToFit (1.0f, 1.0f, w - 2.0f, h - 2.0f, true));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void SurjectiveMidiKeyboardComponent::setBlackNoteLengthProportion (float ratio) noexcept
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    jassert (ratio >= 0.0f && ratio <= 1.0f);
 | 
				
			||||||
 | 
					    if (blackNoteLengthRatio != ratio)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        blackNoteLengthRatio = ratio;
 | 
				
			||||||
 | 
					        resized();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int SurjectiveMidiKeyboardComponent::getBlackNoteLength() const noexcept
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    const int whiteNoteLength = orientation == horizontalKeyboard ? getHeight() : getWidth();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return roundToInt (whiteNoteLength * blackNoteLengthRatio);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void SurjectiveMidiKeyboardComponent::resized()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    int w = getWidth();
 | 
				
			||||||
 | 
					    int h = getHeight();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (w > 0 && h > 0)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if (orientation != horizontalKeyboard)
 | 
				
			||||||
 | 
					            std::swap (w, h);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        int kx2, kw2;
 | 
				
			||||||
 | 
					        getKeyPos (rangeEnd, kx2, kw2);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        kx2 += kw2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if ((int) firstKey != rangeStart)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            int kx1, kw1;
 | 
				
			||||||
 | 
					            getKeyPos (rangeStart, kx1, kw1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (kx2 - kx1 <= w)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                firstKey = (float) rangeStart;
 | 
				
			||||||
 | 
					                sendChangeMessage();
 | 
				
			||||||
 | 
					                repaint();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        scrollDown->setVisible (canScroll && firstKey > (float) rangeStart);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        xOffset = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (canScroll)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            const int scrollButtonW = jmin (12, w / 2);
 | 
				
			||||||
 | 
					            Rectangle<int> r (getLocalBounds());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (orientation == horizontalKeyboard)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                scrollDown->setBounds (r.removeFromLeft  (scrollButtonW));
 | 
				
			||||||
 | 
					                scrollUp  ->setBounds (r.removeFromRight (scrollButtonW));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            else if (orientation == verticalKeyboardFacingLeft)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                scrollDown->setBounds (r.removeFromTop    (scrollButtonW));
 | 
				
			||||||
 | 
					                scrollUp  ->setBounds (r.removeFromBottom (scrollButtonW));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            else
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                scrollDown->setBounds (r.removeFromBottom (scrollButtonW));
 | 
				
			||||||
 | 
					                scrollUp  ->setBounds (r.removeFromTop    (scrollButtonW));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            int endOfLastKey, kw;
 | 
				
			||||||
 | 
					            getKeyPos (rangeEnd, endOfLastKey, kw);
 | 
				
			||||||
 | 
					            endOfLastKey += kw;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            float mousePositionVelocity;
 | 
				
			||||||
 | 
					            const int spaceAvailable = w;
 | 
				
			||||||
 | 
					            const int lastStartKey = remappedXYToNote (Point<int> (endOfLastKey - spaceAvailable, 0), mousePositionVelocity) + 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (lastStartKey >= 0 && ((int) firstKey) > lastStartKey)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                firstKey = (float) jlimit (rangeStart, rangeEnd, lastStartKey);
 | 
				
			||||||
 | 
					                sendChangeMessage();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            int newOffset = 0;
 | 
				
			||||||
 | 
					            getKeyPos ((int) firstKey, newOffset, kw);
 | 
				
			||||||
 | 
					            xOffset = newOffset;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            firstKey = (float) rangeStart;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        getKeyPos (rangeEnd, kx2, kw2);
 | 
				
			||||||
 | 
					        scrollUp->setVisible (canScroll && kx2 > w);
 | 
				
			||||||
 | 
					        repaint();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//==============================================================================
 | 
				
			||||||
 | 
					void SurjectiveMidiKeyboardComponent::handleNoteOn (MidiKeyboardState*, int /*midiChannel*/, int /*midiNoteNumber*/, float /*velocity*/)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    shouldCheckState = true; // (probably being called from the audio thread, so avoid blocking in here)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void SurjectiveMidiKeyboardComponent::handleNoteOff (MidiKeyboardState*, int /*midiChannel*/, int /*midiNoteNumber*/, float /*velocity*/)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    shouldCheckState = true; // (probably being called from the audio thread, so avoid blocking in here)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//==============================================================================
 | 
				
			||||||
 | 
					void SurjectiveMidiKeyboardComponent::resetAnyKeysInUse()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    if (! keysPressed.isZero())
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        for (int i = 128; --i >= 0;)
 | 
				
			||||||
 | 
					            if (keysPressed[i])
 | 
				
			||||||
 | 
					                state.noteOff (midiChannel, i, 0.0f);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        keysPressed.clear();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (int i = mouseDownNotes.size(); --i >= 0;)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        const int noteDown = mouseDownNotes.getUnchecked(i);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (noteDown >= 0)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            state.noteOff (midiChannel, noteDown, 0.0f);
 | 
				
			||||||
 | 
					            mouseDownNotes.set (i, -1);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        mouseOverNotes.set (i, -1);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void SurjectiveMidiKeyboardComponent::updateNoteUnderMouse (const MouseEvent& e, bool isDown)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    updateNoteUnderMouse (e.getEventRelativeTo (this).getPosition(), isDown, e.source.getIndex());
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void SurjectiveMidiKeyboardComponent::updateNoteUnderMouse (Point<int> pos, bool isDown, int fingerNum)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    float mousePositionVelocity = 0.0f;
 | 
				
			||||||
 | 
					    const int newNote = xyToNote (pos, mousePositionVelocity);
 | 
				
			||||||
 | 
					    const int oldNote = mouseOverNotes.getUnchecked (fingerNum);
 | 
				
			||||||
 | 
					    const int oldNoteDown = mouseDownNotes.getUnchecked (fingerNum);
 | 
				
			||||||
 | 
					    const float eventVelocity = useMousePositionForVelocity ? mousePositionVelocity * velocity : 1.0f;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (oldNote != newNote)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        repaintNote (oldNote);
 | 
				
			||||||
 | 
					        repaintNote (newNote);
 | 
				
			||||||
 | 
					        mouseOverNotes.set (fingerNum, newNote);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (isDown)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if (newNote != oldNoteDown)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            if (oldNoteDown >= 0)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                mouseDownNotes.set (fingerNum, -1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (! mouseDownNotes.contains (oldNoteDown))
 | 
				
			||||||
 | 
					                    state.noteOff (midiChannel, oldNoteDown, eventVelocity);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (newNote >= 0 && ! mouseDownNotes.contains (newNote))
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                state.noteOn (midiChannel, newNote, eventVelocity);
 | 
				
			||||||
 | 
					                mouseDownNotes.set (fingerNum, newNote);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    else if (oldNoteDown >= 0)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        mouseDownNotes.set (fingerNum, -1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (! mouseDownNotes.contains (oldNoteDown))
 | 
				
			||||||
 | 
					            state.noteOff (midiChannel, oldNoteDown, eventVelocity);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void SurjectiveMidiKeyboardComponent::mouseMove (const MouseEvent& e)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    updateNoteUnderMouse (e, false);
 | 
				
			||||||
 | 
					    shouldCheckMousePos = false;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void SurjectiveMidiKeyboardComponent::mouseDrag (const MouseEvent& e)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    float mousePositionVelocity;
 | 
				
			||||||
 | 
					    const int newNote = xyToNote (e.getPosition(), mousePositionVelocity);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (newNote >= 0)
 | 
				
			||||||
 | 
					        mouseDraggedToKey (newNote, e);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    updateNoteUnderMouse (e, true);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool SurjectiveMidiKeyboardComponent::mouseDownOnKey    (int, const MouseEvent&)  { return true; }
 | 
				
			||||||
 | 
					void SurjectiveMidiKeyboardComponent::mouseDraggedToKey (int, const MouseEvent&)  {}
 | 
				
			||||||
 | 
					void SurjectiveMidiKeyboardComponent::mouseUpOnKey      (int, const MouseEvent&)  {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void SurjectiveMidiKeyboardComponent::mouseDown (const MouseEvent& e)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    float mousePositionVelocity;
 | 
				
			||||||
 | 
					    const int newNote = xyToNote (e.getPosition(), mousePositionVelocity);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (newNote >= 0 && mouseDownOnKey (newNote, e))
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        updateNoteUnderMouse (e, true);
 | 
				
			||||||
 | 
					        shouldCheckMousePos = true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void SurjectiveMidiKeyboardComponent::mouseUp (const MouseEvent& e)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    updateNoteUnderMouse (e, false);
 | 
				
			||||||
 | 
					    shouldCheckMousePos = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    float mousePositionVelocity;
 | 
				
			||||||
 | 
					    const int note = xyToNote (e.getPosition(), mousePositionVelocity);
 | 
				
			||||||
 | 
					    if (note >= 0)
 | 
				
			||||||
 | 
					        mouseUpOnKey (note, e);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void SurjectiveMidiKeyboardComponent::mouseEnter (const MouseEvent& e)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    updateNoteUnderMouse (e, false);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void SurjectiveMidiKeyboardComponent::mouseExit (const MouseEvent& e)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    updateNoteUnderMouse (e, false);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void SurjectiveMidiKeyboardComponent::mouseWheelMove (const MouseEvent&, const MouseWheelDetails& wheel)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    const float amount = (orientation == horizontalKeyboard && wheel.deltaX != 0)
 | 
				
			||||||
 | 
					            ? wheel.deltaX : (orientation == verticalKeyboardFacingLeft ? wheel.deltaY
 | 
				
			||||||
 | 
					                    : -wheel.deltaY);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    setLowestVisibleKeyFloat (firstKey - amount * keyWidth);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void SurjectiveMidiKeyboardComponent::timerCallback()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    if (shouldCheckState)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        shouldCheckState = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for (int i = rangeStart; i <= rangeEnd; ++i)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            if (keysCurrentlyDrawnDown[i] != state.isNoteOnForChannels (midiInChannelMask, i))
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                keysCurrentlyDrawnDown.setBit (i, state.isNoteOnForChannels (midiInChannelMask, i));
 | 
				
			||||||
 | 
					                repaintNote (i);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (shouldCheckMousePos)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        for (auto& ms : Desktop::getInstance().getMouseSources())
 | 
				
			||||||
 | 
					            if (ms.getComponentUnderMouse() == this || isParentOf (ms.getComponentUnderMouse()))
 | 
				
			||||||
 | 
					                updateNoteUnderMouse (getLocalPoint (nullptr, ms.getScreenPosition()).roundToInt(), ms.isDragging(), ms.getIndex());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//==============================================================================
 | 
				
			||||||
 | 
					void SurjectiveMidiKeyboardComponent::clearKeyMappings()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    resetAnyKeysInUse();
 | 
				
			||||||
 | 
					    degreeToAsciis.clear();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void SurjectiveMidiKeyboardComponent::setKeyPressForNote (const KeyPress& key, int midiNoteOffsetFromC)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    degreeToAsciis.insert(DegreeToAscii::value_type(midiNoteOffsetFromC, key));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void SurjectiveMidiKeyboardComponent::removeKeyPressForNote (const int midiNoteOffsetFromC)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    degreeToAsciis.erase(midiNoteOffsetFromC);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void SurjectiveMidiKeyboardComponent::setKeyPressBaseOctave (const int newOctaveNumber)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    jassert (newOctaveNumber >= 0 && newOctaveNumber <= 10);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    keyMappingOctave = newOctaveNumber;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool SurjectiveMidiKeyboardComponent::keyStateChanged (const bool /*isKeyDown*/)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    bool keyPressUsed = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bool keyDepressedForCurrentNote = false;
 | 
				
			||||||
 | 
					    int currentNote = -1;
 | 
				
			||||||
 | 
					    for (auto it = degreeToAsciis.begin(); it != degreeToAsciis.end(); it++){
 | 
				
			||||||
 | 
					        const int proposedNote = 12 * keyMappingOctave + it->first;
 | 
				
			||||||
 | 
					        if (proposedNote != currentNote) {
 | 
				
			||||||
 | 
					            if (currentNote != -1
 | 
				
			||||||
 | 
					                    && !keyDepressedForCurrentNote
 | 
				
			||||||
 | 
					                    && keysPressed[currentNote]) {
 | 
				
			||||||
 | 
					                keysPressed.clearBit(currentNote);
 | 
				
			||||||
 | 
					                state.noteOff(midiChannel, currentNote, velocity);
 | 
				
			||||||
 | 
					                keyPressUsed = true;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            keyDepressedForCurrentNote = false;
 | 
				
			||||||
 | 
					            currentNote = proposedNote;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (!keyDepressedForCurrentNote
 | 
				
			||||||
 | 
					                && it->second.isCurrentlyDown()) {
 | 
				
			||||||
 | 
					            keyDepressedForCurrentNote = true;
 | 
				
			||||||
 | 
					            if (!keysPressed[currentNote]) {
 | 
				
			||||||
 | 
					                keysPressed.setBit(currentNote);
 | 
				
			||||||
 | 
					                state.noteOn(midiChannel, currentNote, velocity);
 | 
				
			||||||
 | 
					                keyPressUsed = true;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (currentNote != -1
 | 
				
			||||||
 | 
					            && !keyDepressedForCurrentNote
 | 
				
			||||||
 | 
					            && keysPressed[currentNote]) {
 | 
				
			||||||
 | 
					        keysPressed.clearBit(currentNote);
 | 
				
			||||||
 | 
					        state.noteOff(midiChannel, currentNote, velocity);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return keyPressUsed;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool SurjectiveMidiKeyboardComponent::keyPressed (const KeyPress& key)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    for (auto it = degreeToAsciis.begin(); it != degreeToAsciis.end(); it++){
 | 
				
			||||||
 | 
					        if (it->second == key) {
 | 
				
			||||||
 | 
					            return true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return false;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void SurjectiveMidiKeyboardComponent::focusLost (FocusChangeType)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    resetAnyKeysInUse();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										437
									
								
								Source/SurjectiveMidiKeyboardComponent.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										437
									
								
								Source/SurjectiveMidiKeyboardComponent.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,437 @@
 | 
				
			|||||||
 | 
					//
 | 
				
			||||||
 | 
					// Created by Alex Birch on 27/09/2017.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "../JuceLibraryCode/JuceHeader.h"
 | 
				
			||||||
 | 
					#include <map>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					using namespace std;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Forked from JUCE/modules/juce_audio_utils/gui/juce_SurjectiveMidiKeyboardComponent.h,
 | 
				
			||||||
 | 
					 * which had the following license:
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					  ==============================================================================
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   This file is part of the JUCE library.
 | 
				
			||||||
 | 
					   Copyright (c) 2017 - ROLI Ltd.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   JUCE is an open source library subject to commercial or open-source
 | 
				
			||||||
 | 
					   licensing.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   By using JUCE, you agree to the terms of both the JUCE 5 End-User License
 | 
				
			||||||
 | 
					   Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
 | 
				
			||||||
 | 
					   27th April 2017).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   End User License Agreement: www.juce.com/juce-5-licence
 | 
				
			||||||
 | 
					   Privacy Policy: www.juce.com/juce-5-privacy-policy
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   Or: You may also use this code under the terms of the GPL v3 (see
 | 
				
			||||||
 | 
					   www.gnu.org/licenses).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 | 
				
			||||||
 | 
					   EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 | 
				
			||||||
 | 
					   DISCLAIMED.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ==============================================================================
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					//==============================================================================
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * This fork aims to allow multiple QWERTY keys to map to the same MIDI keyboard key
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					    A component that displays a piano keyboard, whose notes can be clicked on.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    This component will mimic a physical midi keyboard, showing the current state of
 | 
				
			||||||
 | 
					    a MidiKeyboardState object. When the on-screen keys are clicked on, it will play these
 | 
				
			||||||
 | 
					    notes by calling the noteOn() and noteOff() methods of its MidiKeyboardState object.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Another feature is that the computer keyboard can also be used to play notes. By
 | 
				
			||||||
 | 
					    default it maps the top two rows of a standard qwerty keyboard to the notes, but
 | 
				
			||||||
 | 
					    these can be remapped if needed. It will only respond to keypresses when it has
 | 
				
			||||||
 | 
					    the keyboard focus, so to disable this feature you can call setWantsKeyboardFocus (false).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    The component is also a ChangeBroadcaster, so if you want to be informed when the
 | 
				
			||||||
 | 
					    keyboard is scrolled, you can register a ChangeListener for callbacks.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @see MidiKeyboardState
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					class SurjectiveMidiKeyboardComponent  : public Component,
 | 
				
			||||||
 | 
					                                         public MidiKeyboardStateListener,
 | 
				
			||||||
 | 
					                                         public ChangeBroadcaster,
 | 
				
			||||||
 | 
					                                         private Timer
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
					    //==============================================================================
 | 
				
			||||||
 | 
					    /** The direction of the keyboard.
 | 
				
			||||||
 | 
					        @see setOrientation
 | 
				
			||||||
 | 
					    */
 | 
				
			||||||
 | 
					    enum Orientation
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        horizontalKeyboard,
 | 
				
			||||||
 | 
					        verticalKeyboardFacingLeft,
 | 
				
			||||||
 | 
					        verticalKeyboardFacingRight,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** Creates a SurjectiveMidiKeyboardComponent.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        @param state        the midi keyboard model that this component will represent
 | 
				
			||||||
 | 
					        @param orientation  whether the keyboard is horizonal or vertical
 | 
				
			||||||
 | 
					    */
 | 
				
			||||||
 | 
					    SurjectiveMidiKeyboardComponent (MidiKeyboardState& state,
 | 
				
			||||||
 | 
					            Orientation orientation);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** Destructor. */
 | 
				
			||||||
 | 
					    ~SurjectiveMidiKeyboardComponent();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    //==============================================================================
 | 
				
			||||||
 | 
					    /** Changes the velocity used in midi note-on messages that are triggered by clicking
 | 
				
			||||||
 | 
					        on the component.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Values are 0 to 1.0, where 1.0 is the heaviest.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        @see setMidiChannel
 | 
				
			||||||
 | 
					    */
 | 
				
			||||||
 | 
					    void setVelocity (float velocity, bool useMousePositionForVelocity);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** Changes the midi channel number that will be used for events triggered by clicking
 | 
				
			||||||
 | 
					        on the component.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        The channel must be between 1 and 16 (inclusive). This is the channel that will be
 | 
				
			||||||
 | 
					        passed on to the MidiKeyboardState::noteOn() method when the user clicks the component.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Although this is the channel used for outgoing events, the component can display
 | 
				
			||||||
 | 
					        incoming events from more than one channel - see setMidiChannelsToDisplay()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        @see setVelocity
 | 
				
			||||||
 | 
					    */
 | 
				
			||||||
 | 
					    void setMidiChannel (int midiChannelNumber);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** Returns the midi channel that the keyboard is using for midi messages.
 | 
				
			||||||
 | 
					        @see setMidiChannel
 | 
				
			||||||
 | 
					    */
 | 
				
			||||||
 | 
					    int getMidiChannel() const noexcept                             { return midiChannel; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** Sets a mask to indicate which incoming midi channels should be represented by
 | 
				
			||||||
 | 
					        key movements.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        The mask is a set of bits, where bit 0 = midi channel 1, bit 1 = midi channel 2, etc.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        If the MidiKeyboardState has a key down for any of the channels whose bits are set
 | 
				
			||||||
 | 
					        in this mask, the on-screen keys will also go down.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        By default, this mask is set to 0xffff (all channels displayed).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        @see setMidiChannel
 | 
				
			||||||
 | 
					    */
 | 
				
			||||||
 | 
					    void setMidiChannelsToDisplay (int midiChannelMask);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** Returns the current set of midi channels represented by the component.
 | 
				
			||||||
 | 
					        This is the value that was set with setMidiChannelsToDisplay().
 | 
				
			||||||
 | 
					    */
 | 
				
			||||||
 | 
					    int getMidiChannelsToDisplay() const noexcept                   { return midiInChannelMask; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    //==============================================================================
 | 
				
			||||||
 | 
					    /** Changes the width used to draw the white keys. */
 | 
				
			||||||
 | 
					    void setKeyWidth (float widthInPixels);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** Returns the width that was set by setKeyWidth(). */
 | 
				
			||||||
 | 
					    float getKeyWidth() const noexcept                              { return keyWidth; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** Changes the keyboard's current direction. */
 | 
				
			||||||
 | 
					    void setOrientation (Orientation newOrientation);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** Returns the keyboard's current direction. */
 | 
				
			||||||
 | 
					    Orientation getOrientation() const noexcept                     { return orientation; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** Sets the range of midi notes that the keyboard will be limited to.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        By default the range is 0 to 127 (inclusive), but you can limit this if you
 | 
				
			||||||
 | 
					        only want a restricted set of the keys to be shown.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Note that the values here are inclusive and must be between 0 and 127.
 | 
				
			||||||
 | 
					    */
 | 
				
			||||||
 | 
					    void setAvailableRange (int lowestNote,
 | 
				
			||||||
 | 
					            int highestNote);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** Returns the first note in the available range.
 | 
				
			||||||
 | 
					        @see setAvailableRange
 | 
				
			||||||
 | 
					    */
 | 
				
			||||||
 | 
					    int getRangeStart() const noexcept                              { return rangeStart; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** Returns the last note in the available range.
 | 
				
			||||||
 | 
					        @see setAvailableRange
 | 
				
			||||||
 | 
					    */
 | 
				
			||||||
 | 
					    int getRangeEnd() const noexcept                                { return rangeEnd; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** If the keyboard extends beyond the size of the component, this will scroll
 | 
				
			||||||
 | 
					        it to show the given key at the start.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Whenever the keyboard's position is changed, this will use the ChangeBroadcaster
 | 
				
			||||||
 | 
					        base class to send a callback to any ChangeListeners that have been registered.
 | 
				
			||||||
 | 
					    */
 | 
				
			||||||
 | 
					    void setLowestVisibleKey (int noteNumber);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** Returns the number of the first key shown in the component.
 | 
				
			||||||
 | 
					        @see setLowestVisibleKey
 | 
				
			||||||
 | 
					    */
 | 
				
			||||||
 | 
					    int getLowestVisibleKey() const noexcept                        { return (int) firstKey; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** Sets the length of the black notes as a proportion of the white note length. */
 | 
				
			||||||
 | 
					    void setBlackNoteLengthProportion (float ratio) noexcept;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** Returns the length of the black notes as a proportion of the white note length. */
 | 
				
			||||||
 | 
					    float getBlackNoteLengthProportion() const noexcept             { return blackNoteLengthRatio; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** Returns the absolute length of the black notes.
 | 
				
			||||||
 | 
					        This will be their vertical or horizontal length, depending on the keyboard's orientation.
 | 
				
			||||||
 | 
					    */
 | 
				
			||||||
 | 
					    int getBlackNoteLength() const noexcept;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** If set to true, then scroll buttons will appear at either end of the keyboard
 | 
				
			||||||
 | 
					        if there are too many notes to fit them all in the component at once.
 | 
				
			||||||
 | 
					    */
 | 
				
			||||||
 | 
					    void setScrollButtonsVisible (bool canScroll);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    //==============================================================================
 | 
				
			||||||
 | 
					    /** A set of colour IDs to use to change the colour of various aspects of the keyboard.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        These constants can be used either via the Component::setColour(), or LookAndFeel::setColour()
 | 
				
			||||||
 | 
					        methods.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        @see Component::setColour, Component::findColour, LookAndFeel::setColour, LookAndFeel::findColour
 | 
				
			||||||
 | 
					    */
 | 
				
			||||||
 | 
					    enum ColourIds
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        whiteNoteColourId               = 0x1005000,
 | 
				
			||||||
 | 
					        blackNoteColourId               = 0x1005001,
 | 
				
			||||||
 | 
					        keySeparatorLineColourId        = 0x1005002,
 | 
				
			||||||
 | 
					        mouseOverKeyOverlayColourId     = 0x1005003,  /**< This colour will be overlaid on the normal note colour. */
 | 
				
			||||||
 | 
					        keyDownOverlayColourId          = 0x1005004,  /**< This colour will be overlaid on the normal note colour. */
 | 
				
			||||||
 | 
					        textLabelColourId               = 0x1005005,
 | 
				
			||||||
 | 
					        upDownButtonBackgroundColourId  = 0x1005006,
 | 
				
			||||||
 | 
					        upDownButtonArrowColourId       = 0x1005007,
 | 
				
			||||||
 | 
					        shadowColourId                  = 0x1005008
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** Returns the position within the component of the left-hand edge of a key.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Depending on the keyboard's orientation, this may be a horizontal or vertical
 | 
				
			||||||
 | 
					        distance, in either direction.
 | 
				
			||||||
 | 
					    */
 | 
				
			||||||
 | 
					    int getKeyStartPosition (int midiNoteNumber) const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** Returns the total width needed to fit all the keys in the available range. */
 | 
				
			||||||
 | 
					    int getTotalKeyboardWidth() const noexcept;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** Returns the key at a given coordinate. */
 | 
				
			||||||
 | 
					    int getNoteAtPosition (Point<int> position);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    //==============================================================================
 | 
				
			||||||
 | 
					    /** Deletes all key-mappings.
 | 
				
			||||||
 | 
					        @see setKeyPressForNote
 | 
				
			||||||
 | 
					    */
 | 
				
			||||||
 | 
					    void clearKeyMappings();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** Maps a key-press to a given note.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        @param key                  the key that should trigger the note
 | 
				
			||||||
 | 
					        @param midiNoteOffsetFromC  how many semitones above C the triggered note should
 | 
				
			||||||
 | 
					                                    be. The actual midi note that gets played will be
 | 
				
			||||||
 | 
					                                    this value + (12 * the current base octave). To change
 | 
				
			||||||
 | 
					                                    the base octave, see setKeyPressBaseOctave()
 | 
				
			||||||
 | 
					    */
 | 
				
			||||||
 | 
					    void setKeyPressForNote (const KeyPress& key,
 | 
				
			||||||
 | 
					            int midiNoteOffsetFromC);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** Removes any key-mappings for a given note.
 | 
				
			||||||
 | 
					        For a description of what the note number means, see setKeyPressForNote().
 | 
				
			||||||
 | 
					    */
 | 
				
			||||||
 | 
					    void removeKeyPressForNote (int midiNoteOffsetFromC);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** Changes the base note above which key-press-triggered notes are played.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        The set of key-mappings that trigger notes can be moved up and down to cover
 | 
				
			||||||
 | 
					        the entire scale using this method.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        The value passed in is an octave number between 0 and 10 (inclusive), and
 | 
				
			||||||
 | 
					        indicates which C is the base note to which the key-mapped notes are
 | 
				
			||||||
 | 
					        relative.
 | 
				
			||||||
 | 
					    */
 | 
				
			||||||
 | 
					    void setKeyPressBaseOctave (int newOctaveNumber);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** This sets the octave number which is shown as the octave number for middle C.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        This affects only the default implementation of getWhiteNoteText(), which
 | 
				
			||||||
 | 
					        passes this octave number to MidiMessage::getMidiNoteName() in order to
 | 
				
			||||||
 | 
					        get the note text. See MidiMessage::getMidiNoteName() for more info about
 | 
				
			||||||
 | 
					        the parameter.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        By default this value is set to 3.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        @see getOctaveForMiddleC
 | 
				
			||||||
 | 
					    */
 | 
				
			||||||
 | 
					    void setOctaveForMiddleC (int octaveNumForMiddleC);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** This returns the value set by setOctaveForMiddleC().
 | 
				
			||||||
 | 
					        @see setOctaveForMiddleC
 | 
				
			||||||
 | 
					    */
 | 
				
			||||||
 | 
					    int getOctaveForMiddleC() const noexcept            { return octaveNumForMiddleC; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    //==============================================================================
 | 
				
			||||||
 | 
					    /** @internal */
 | 
				
			||||||
 | 
					    void paint (Graphics&) override;
 | 
				
			||||||
 | 
					    /** @internal */
 | 
				
			||||||
 | 
					    void resized() override;
 | 
				
			||||||
 | 
					    /** @internal */
 | 
				
			||||||
 | 
					    void mouseMove (const MouseEvent&) override;
 | 
				
			||||||
 | 
					    /** @internal */
 | 
				
			||||||
 | 
					    void mouseDrag (const MouseEvent&) override;
 | 
				
			||||||
 | 
					    /** @internal */
 | 
				
			||||||
 | 
					    void mouseDown (const MouseEvent&) override;
 | 
				
			||||||
 | 
					    /** @internal */
 | 
				
			||||||
 | 
					    void mouseUp (const MouseEvent&) override;
 | 
				
			||||||
 | 
					    /** @internal */
 | 
				
			||||||
 | 
					    void mouseEnter (const MouseEvent&) override;
 | 
				
			||||||
 | 
					    /** @internal */
 | 
				
			||||||
 | 
					    void mouseExit (const MouseEvent&) override;
 | 
				
			||||||
 | 
					    /** @internal */
 | 
				
			||||||
 | 
					    void mouseWheelMove (const MouseEvent&, const MouseWheelDetails&) override;
 | 
				
			||||||
 | 
					    /** @internal */
 | 
				
			||||||
 | 
					    void timerCallback() override;
 | 
				
			||||||
 | 
					    /** @internal */
 | 
				
			||||||
 | 
					    bool keyStateChanged (bool isKeyDown) override;
 | 
				
			||||||
 | 
					    /** @internal */
 | 
				
			||||||
 | 
					    bool keyPressed (const KeyPress&) override;
 | 
				
			||||||
 | 
					    /** @internal */
 | 
				
			||||||
 | 
					    void focusLost (FocusChangeType) override;
 | 
				
			||||||
 | 
					    /** @internal */
 | 
				
			||||||
 | 
					    void handleNoteOn (MidiKeyboardState*, int midiChannel, int midiNoteNumber, float velocity) override;
 | 
				
			||||||
 | 
					    /** @internal */
 | 
				
			||||||
 | 
					    void handleNoteOff (MidiKeyboardState*, int midiChannel, int midiNoteNumber, float velocity) override;
 | 
				
			||||||
 | 
					    /** @internal */
 | 
				
			||||||
 | 
					    void colourChanged() override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					protected:
 | 
				
			||||||
 | 
					    //==============================================================================
 | 
				
			||||||
 | 
					    /** Draws a white note in the given rectangle.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        isOver indicates whether the mouse is over the key, isDown indicates whether the key is
 | 
				
			||||||
 | 
					        currently pressed down.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        When doing this, be sure to note the keyboard's orientation.
 | 
				
			||||||
 | 
					    */
 | 
				
			||||||
 | 
					    virtual void drawWhiteNote (int midiNoteNumber,
 | 
				
			||||||
 | 
					            Graphics& g,
 | 
				
			||||||
 | 
					            int x, int y, int w, int h,
 | 
				
			||||||
 | 
					            bool isDown, bool isOver,
 | 
				
			||||||
 | 
					            const Colour& lineColour,
 | 
				
			||||||
 | 
					            const Colour& textColour);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** Draws a black note in the given rectangle.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        isOver indicates whether the mouse is over the key, isDown indicates whether the key is
 | 
				
			||||||
 | 
					        currently pressed down.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        When doing this, be sure to note the keyboard's orientation.
 | 
				
			||||||
 | 
					    */
 | 
				
			||||||
 | 
					    virtual void drawBlackNote (int midiNoteNumber,
 | 
				
			||||||
 | 
					            Graphics& g,
 | 
				
			||||||
 | 
					            int x, int y, int w, int h,
 | 
				
			||||||
 | 
					            bool isDown, bool isOver,
 | 
				
			||||||
 | 
					            const Colour& noteFillColour);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** Allows text to be drawn on the white notes.
 | 
				
			||||||
 | 
					        By default this is used to label the C in each octave, but could be used for other things.
 | 
				
			||||||
 | 
					        @see setOctaveForMiddleC
 | 
				
			||||||
 | 
					    */
 | 
				
			||||||
 | 
					    virtual String getWhiteNoteText (const int midiNoteNumber);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** Draws the up and down buttons that change the base note. */
 | 
				
			||||||
 | 
					    virtual void drawUpDownButton (Graphics& g, int w, int h,
 | 
				
			||||||
 | 
					            const bool isMouseOver,
 | 
				
			||||||
 | 
					            const bool isButtonPressed,
 | 
				
			||||||
 | 
					            const bool movesOctavesUp);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** Callback when the mouse is clicked on a key.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        You could use this to do things like handle right-clicks on keys, etc.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Return true if you want the click to trigger the note, or false if you
 | 
				
			||||||
 | 
					        want to handle it yourself and not have the note played.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        @see mouseDraggedToKey
 | 
				
			||||||
 | 
					    */
 | 
				
			||||||
 | 
					    virtual bool mouseDownOnKey (int midiNoteNumber, const MouseEvent& e);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** Callback when the mouse is dragged from one key onto another.
 | 
				
			||||||
 | 
					        @see mouseDownOnKey
 | 
				
			||||||
 | 
					    */
 | 
				
			||||||
 | 
					    virtual void mouseDraggedToKey (int midiNoteNumber, const MouseEvent& e);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** Callback when the mouse is released from a key.
 | 
				
			||||||
 | 
					        @see mouseDownOnKey
 | 
				
			||||||
 | 
					    */
 | 
				
			||||||
 | 
					    virtual void mouseUpOnKey (int midiNoteNumber, const MouseEvent& e);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** Calculates the position of a given midi-note.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        This can be overridden to create layouts with custom key-widths.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        @param midiNoteNumber   the note to find
 | 
				
			||||||
 | 
					        @param keyWidth         the desired width in pixels of one key - see setKeyWidth()
 | 
				
			||||||
 | 
					        @param x                the x position of the left-hand edge of the key (this method
 | 
				
			||||||
 | 
					                                always works in terms of a horizontal keyboard)
 | 
				
			||||||
 | 
					        @param w                the width of the key
 | 
				
			||||||
 | 
					    */
 | 
				
			||||||
 | 
					    virtual void getKeyPosition (int midiNoteNumber, float keyWidth,
 | 
				
			||||||
 | 
					            int& x, int& w) const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** Returns the rectangle for a given key if within the displayable range */
 | 
				
			||||||
 | 
					    Rectangle<int> getRectangleForKey (int midiNoteNumber) const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
					    //==============================================================================
 | 
				
			||||||
 | 
					    friend class SurjectiveMidiKeyboardUpDownButton;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    MidiKeyboardState& state;
 | 
				
			||||||
 | 
					    float blackNoteLengthRatio;
 | 
				
			||||||
 | 
					    int xOffset;
 | 
				
			||||||
 | 
					    float keyWidth;
 | 
				
			||||||
 | 
					    Orientation orientation;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    int midiChannel, midiInChannelMask;
 | 
				
			||||||
 | 
					    float velocity;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Array<int> mouseOverNotes, mouseDownNotes;
 | 
				
			||||||
 | 
					    BigInteger keysPressed, keysCurrentlyDrawnDown;
 | 
				
			||||||
 | 
					    bool shouldCheckState;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    int rangeStart, rangeEnd;
 | 
				
			||||||
 | 
					    float firstKey;
 | 
				
			||||||
 | 
					    bool canScroll, useMousePositionForVelocity, shouldCheckMousePos;
 | 
				
			||||||
 | 
					    ScopedPointer<Button> scrollDown, scrollUp;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    typedef multimap<int, KeyPress> DegreeToAscii;
 | 
				
			||||||
 | 
					    DegreeToAscii degreeToAsciis;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    int keyMappingOctave, octaveNumForMiddleC;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    static const uint8 whiteNotes[];
 | 
				
			||||||
 | 
					    static const uint8 blackNotes[];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void getKeyPos (int midiNoteNumber, int& x, int& w) const;
 | 
				
			||||||
 | 
					    int xyToNote (Point<int>, float& mousePositionVelocity);
 | 
				
			||||||
 | 
					    int remappedXYToNote (Point<int>, float& mousePositionVelocity) const;
 | 
				
			||||||
 | 
					    void resetAnyKeysInUse();
 | 
				
			||||||
 | 
					    void updateNoteUnderMouse (Point<int>, bool isDown, int fingerNum);
 | 
				
			||||||
 | 
					    void updateNoteUnderMouse (const MouseEvent&, bool isDown);
 | 
				
			||||||
 | 
					    void repaintNote (int midiNoteNumber);
 | 
				
			||||||
 | 
					    void setLowestVisibleKeyFloat (float noteNumber);
 | 
				
			||||||
 | 
					    void bindKeysToMidiKeyboard();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SurjectiveMidiKeyboardComponent)
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
							
								
								
									
										204
									
								
								Source/TableComponent.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										204
									
								
								Source/TableComponent.cpp
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,204 @@
 | 
				
			|||||||
 | 
					//
 | 
				
			||||||
 | 
					//  Model.cpp
 | 
				
			||||||
 | 
					//  Lazarus
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//  Created by Alex Birch on 01/09/2017.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "TableComponent.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					using namespace std;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//==============================================================================
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					    This class shows how to implement a TableListBoxModel to show in a TableListBox.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					TableComponent::TableComponent(
 | 
				
			||||||
 | 
					        const vector<string> &columns,
 | 
				
			||||||
 | 
					        const vector<vector<string>> &rows,
 | 
				
			||||||
 | 
					        const function<void (int)> &onRowSelected,
 | 
				
			||||||
 | 
					        const function<int (const vector<string>&)> &rowToIDMapper,
 | 
				
			||||||
 | 
					        int initiallySelectedRow
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					        : font (14.0f),
 | 
				
			||||||
 | 
					          columns(columns),
 | 
				
			||||||
 | 
					          rows(rows),
 | 
				
			||||||
 | 
					          onRowSelected(onRowSelected),
 | 
				
			||||||
 | 
					          rowToIDMapper(rowToIDMapper)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    // Create our table component and add it to this component..
 | 
				
			||||||
 | 
					    addAndMakeVisible (table);
 | 
				
			||||||
 | 
					    table.setModel (this);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // give it a border
 | 
				
			||||||
 | 
					    table.setColour (ListBox::outlineColourId, Colours::grey);
 | 
				
			||||||
 | 
					    table.setOutlineThickness (1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    int columnIx = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Add some columns to the table header, based on the column list in our database..
 | 
				
			||||||
 | 
					    for (auto &column : columns) // access by reference to avoid copying
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        table.getHeader().addColumn (
 | 
				
			||||||
 | 
					                String(column),
 | 
				
			||||||
 | 
					                columnIx++,
 | 
				
			||||||
 | 
					                100, // column width
 | 
				
			||||||
 | 
					                50, // min width
 | 
				
			||||||
 | 
					                400, // max width
 | 
				
			||||||
 | 
					                TableHeaderComponent::defaultFlags
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    table.setWantsKeyboardFocus(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    table.selectRow(initiallySelectedRow);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // we could now change some initial settings..
 | 
				
			||||||
 | 
					    table.getHeader().setSortColumnId (1, false); // sort ascending by ID column
 | 
				
			||||||
 | 
					//    table.getHeader().setColumnVisible (7, false); // hide the "length" column until the user shows it
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // un-comment this line to have a go of stretch-to-fit mode
 | 
				
			||||||
 | 
					    // table.getHeader().setStretchToFitActive (true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//    table.setMultipleSelectionEnabled (false);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void TableComponent::setRows(const vector<vector<string>>& rows, int initiallySelectedRow) {
 | 
				
			||||||
 | 
					    this->rows = rows;
 | 
				
			||||||
 | 
					    table.deselectAllRows();
 | 
				
			||||||
 | 
					    table.updateContent();
 | 
				
			||||||
 | 
					    table.getHeader().setSortColumnId(0, true);
 | 
				
			||||||
 | 
					    table.selectRow(initiallySelectedRow);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// This is overloaded from TableListBoxModel, and must return the total number of rows in our table
 | 
				
			||||||
 | 
					int TableComponent::getNumRows()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    return static_cast<int>(rows.size());
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// This is overloaded from TableListBoxModel, and should fill in the background of the whole row
 | 
				
			||||||
 | 
					void TableComponent::paintRowBackground (
 | 
				
			||||||
 | 
					        Graphics& g,
 | 
				
			||||||
 | 
					        int rowNumber,
 | 
				
			||||||
 | 
					        int /*width*/,
 | 
				
			||||||
 | 
					        int /*height*/,
 | 
				
			||||||
 | 
					        bool rowIsSelected
 | 
				
			||||||
 | 
					) {
 | 
				
			||||||
 | 
					    const Colour alternateColour (getLookAndFeel().findColour (ListBox::backgroundColourId)
 | 
				
			||||||
 | 
					            .interpolatedWith (getLookAndFeel().findColour (ListBox::textColourId), 0.03f));
 | 
				
			||||||
 | 
					    if (rowIsSelected)
 | 
				
			||||||
 | 
					        g.fillAll (Colours::lightblue);
 | 
				
			||||||
 | 
					    else if (rowNumber % 2)
 | 
				
			||||||
 | 
					        g.fillAll (alternateColour);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// This is overloaded from TableListBoxModel, and must paint any cells that aren't using custom
 | 
				
			||||||
 | 
					// components.
 | 
				
			||||||
 | 
					void TableComponent::paintCell (
 | 
				
			||||||
 | 
					        Graphics& g,
 | 
				
			||||||
 | 
					        int rowNumber,
 | 
				
			||||||
 | 
					        int columnId,
 | 
				
			||||||
 | 
					        int width,
 | 
				
			||||||
 | 
					        int height,
 | 
				
			||||||
 | 
					        bool /*rowIsSelected*/
 | 
				
			||||||
 | 
					) {
 | 
				
			||||||
 | 
					    g.setColour (getLookAndFeel().findColour (ListBox::textColourId));
 | 
				
			||||||
 | 
					    g.setFont (font);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    g.drawText (rows[rowNumber][columnId-1], 2, 0, width - 4, height, Justification::centredLeft, true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    g.setColour (getLookAndFeel().findColour (ListBox::backgroundColourId));
 | 
				
			||||||
 | 
					    g.fillRect (width - 1, 0, 1, height);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// This is overloaded from TableListBoxModel, and tells us that the user has clicked a table header
 | 
				
			||||||
 | 
					// to change the sort order.
 | 
				
			||||||
 | 
					void TableComponent::sortOrderChanged (
 | 
				
			||||||
 | 
					        int newSortColumnId,
 | 
				
			||||||
 | 
					        bool isForwards
 | 
				
			||||||
 | 
					) {
 | 
				
			||||||
 | 
					    if (newSortColumnId != 0) {
 | 
				
			||||||
 | 
					        int selectedRowIx = table.getSelectedRow();
 | 
				
			||||||
 | 
					        vector<string> selectedRow;
 | 
				
			||||||
 | 
					        if (selectedRowIx >= 0) {
 | 
				
			||||||
 | 
					            selectedRow = rows[selectedRowIx];
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        TableComponent::DataSorter sorter (newSortColumnId, isForwards);
 | 
				
			||||||
 | 
					        sort(rows.begin(), rows.end(), sorter);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        table.updateContent();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (selectedRowIx >= 0) {
 | 
				
			||||||
 | 
					            for (auto it = rows.begin(); it != rows.end(); ++it) {
 | 
				
			||||||
 | 
					                if(*it == selectedRow) {
 | 
				
			||||||
 | 
					                    int index = static_cast<int>(std::distance(rows.begin(), it));
 | 
				
			||||||
 | 
					                    table.selectRow(index);
 | 
				
			||||||
 | 
					                    break;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// This is overloaded from TableListBoxModel, and should choose the best width for the specified
 | 
				
			||||||
 | 
					// column.
 | 
				
			||||||
 | 
					int TableComponent::getColumnAutoSizeWidth (int columnId) {
 | 
				
			||||||
 | 
					    if (columnId == 5)
 | 
				
			||||||
 | 
					        return 100; // (this is the ratings column, containing a custom combobox component)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    int widest = 32;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // find the widest bit of text in this column..
 | 
				
			||||||
 | 
					    for (int i = getNumRows(); --i >= 0;) {
 | 
				
			||||||
 | 
					        widest = jmax (widest, font.getStringWidth (rows[i][columnId-1]));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return widest + 8;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//==============================================================================
 | 
				
			||||||
 | 
					void TableComponent::resized() {
 | 
				
			||||||
 | 
					    // position our table with a gap around its edge
 | 
				
			||||||
 | 
					    table.setBoundsInset (BorderSize<int> (7));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//==============================================================================
 | 
				
			||||||
 | 
					// A comparator used to sort our data when the user clicks a column header
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					TableComponent::DataSorter::DataSorter (
 | 
				
			||||||
 | 
					        int columnByWhichToSort,
 | 
				
			||||||
 | 
					        bool forwards
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					        : columnByWhichToSort (columnByWhichToSort),
 | 
				
			||||||
 | 
					          direction (forwards ? 1 : -1)
 | 
				
			||||||
 | 
					{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool TableComponent::DataSorter::operator ()(
 | 
				
			||||||
 | 
					        vector<string> first,
 | 
				
			||||||
 | 
					        vector<string> second
 | 
				
			||||||
 | 
					) {
 | 
				
			||||||
 | 
					    int result = String(first[columnByWhichToSort-1])
 | 
				
			||||||
 | 
					            .compareNatural (String(second[columnByWhichToSort-1]));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (result == 0)
 | 
				
			||||||
 | 
					        result = String(first[0])
 | 
				
			||||||
 | 
					                .compareNatural (String(second[0]));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    result *= direction;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return result > 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void TableComponent::selectedRowsChanged (int row) {
 | 
				
			||||||
 | 
					    if (row < 0) {
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    onRowSelected(rowToIDMapper(rows[row]));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool TableComponent::keyPressed(const KeyPress &key) {
 | 
				
			||||||
 | 
					    return table.keyPressed(key);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										88
									
								
								Source/TableComponent.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								Source/TableComponent.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,88 @@
 | 
				
			|||||||
 | 
					//
 | 
				
			||||||
 | 
					//  Model.hpp
 | 
				
			||||||
 | 
					//  Lazarus
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//  Created by Alex Birch on 01/09/2017.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "../JuceLibraryCode/JuceHeader.h"
 | 
				
			||||||
 | 
					#include "PresetsToBanks.h"
 | 
				
			||||||
 | 
					#include <memory>
 | 
				
			||||||
 | 
					#include <string>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					using namespace std;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TableComponent    : public Component,
 | 
				
			||||||
 | 
					                          public TableListBoxModel {
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
					    TableComponent(
 | 
				
			||||||
 | 
					            const vector<string> &columns,
 | 
				
			||||||
 | 
					            const vector<vector<string>> &rows,
 | 
				
			||||||
 | 
					            const function<void (int)> &onRowSelected,
 | 
				
			||||||
 | 
					            const function<int (const vector<string>&)> &rowToIDMapper,
 | 
				
			||||||
 | 
					            int initiallySelectedRow
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    int getNumRows() override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void paintRowBackground (
 | 
				
			||||||
 | 
					            Graphics& g,
 | 
				
			||||||
 | 
					            int rowNumber,
 | 
				
			||||||
 | 
					            int width,
 | 
				
			||||||
 | 
					            int height,
 | 
				
			||||||
 | 
					            bool rowIsSelected
 | 
				
			||||||
 | 
					    ) override;
 | 
				
			||||||
 | 
					    void paintCell (
 | 
				
			||||||
 | 
					            Graphics& g,
 | 
				
			||||||
 | 
					            int rowNumber,
 | 
				
			||||||
 | 
					            int columnId,
 | 
				
			||||||
 | 
					            int width,
 | 
				
			||||||
 | 
					            int height,
 | 
				
			||||||
 | 
					            bool rowIsSelected
 | 
				
			||||||
 | 
					    ) override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void sortOrderChanged (int newSortColumnId, bool isForwards) override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    int getColumnAutoSizeWidth (int columnId) override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void selectedRowsChanged (int row) override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void resized() override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void setRows(const vector<vector<string>>& rows, int initiallySelectedRow);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bool keyPressed(const KeyPress &key) override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
					    TableListBox table;     // the table component itself
 | 
				
			||||||
 | 
					    Font font;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    vector<string> columns;
 | 
				
			||||||
 | 
					    vector<vector<string>> rows;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    function<void (int)> onRowSelected;
 | 
				
			||||||
 | 
					    function<int (const vector<string>&)> rowToIDMapper;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // A comparator used to sort our data when the user clicks a column header
 | 
				
			||||||
 | 
					    class DataSorter {
 | 
				
			||||||
 | 
					    public:
 | 
				
			||||||
 | 
					        DataSorter (
 | 
				
			||||||
 | 
					                int columnByWhichToSort,
 | 
				
			||||||
 | 
					                bool forwards
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        bool operator ()(
 | 
				
			||||||
 | 
					                vector<string> first,
 | 
				
			||||||
 | 
					                vector<string> second
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private:
 | 
				
			||||||
 | 
					        int columnByWhichToSort;
 | 
				
			||||||
 | 
					        int direction;
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TableComponent)
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
							
								
								
									
										88
									
								
								Source/TableModel.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								Source/TableModel.cpp
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,88 @@
 | 
				
			|||||||
 | 
					//
 | 
				
			||||||
 | 
					// Created by Alex Birch on 17/09/2017.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "TableModel.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// This is overloaded from TableListBoxModel, and must return the total number of rows in our table
 | 
				
			||||||
 | 
					int TableModel::getNumRows()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    return static_cast<int>(dataList.size());
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// This is overloaded from TableListBoxModel, and should fill in the background of the whole row
 | 
				
			||||||
 | 
					void TableModel::paintRowBackground (
 | 
				
			||||||
 | 
					        Graphics& g,
 | 
				
			||||||
 | 
					        int rowNumber,
 | 
				
			||||||
 | 
					        int /*width*/,
 | 
				
			||||||
 | 
					        int /*height*/,
 | 
				
			||||||
 | 
					        bool rowIsSelected
 | 
				
			||||||
 | 
					) {
 | 
				
			||||||
 | 
					    const Colour alternateColour (getLookAndFeel().findColour (ListBox::backgroundColourId)
 | 
				
			||||||
 | 
					            .interpolatedWith (getLookAndFeel().findColour (ListBox::textColourId), 0.03f));
 | 
				
			||||||
 | 
					    if (rowIsSelected)
 | 
				
			||||||
 | 
					        g.fillAll (Colours::lightblue);
 | 
				
			||||||
 | 
					    else if (rowNumber % 2)
 | 
				
			||||||
 | 
					        g.fillAll (alternateColour);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// This is overloaded from TableListBoxModel, and must paint any cells that aren't using custom
 | 
				
			||||||
 | 
					// components.
 | 
				
			||||||
 | 
					void TableModel::paintCell (
 | 
				
			||||||
 | 
					        Graphics& g,
 | 
				
			||||||
 | 
					        int rowNumber,
 | 
				
			||||||
 | 
					        int columnId,
 | 
				
			||||||
 | 
					        int width,
 | 
				
			||||||
 | 
					        int height,
 | 
				
			||||||
 | 
					        bool /*rowIsSelected*/
 | 
				
			||||||
 | 
					) {
 | 
				
			||||||
 | 
					    g.setColour (getLookAndFeel().findColour (ListBox::textColourId));
 | 
				
			||||||
 | 
					    g.setFont (font);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (const XmlElement* rowElement = dataList->getChildElement (rowNumber))
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        const String text (rowElement->getStringAttribute (getAttributeNameForColumnId (columnId)));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        g.drawText (text, 2, 0, width - 4, height, Justification::centredLeft, true);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    g.setColour (getLookAndFeel().findColour (ListBox::backgroundColourId));
 | 
				
			||||||
 | 
					    g.fillRect (width - 1, 0, 1, height);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// This is overloaded from TableListBoxModel, and tells us that the user has clicked a table header
 | 
				
			||||||
 | 
					// to change the sort order.
 | 
				
			||||||
 | 
					void TableModel::sortOrderChanged (
 | 
				
			||||||
 | 
					        int newSortColumnId,
 | 
				
			||||||
 | 
					        bool isForwards
 | 
				
			||||||
 | 
					) {
 | 
				
			||||||
 | 
					    if (newSortColumnId != 0)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        TableModel::DataSorter sorter (getAttributeNameForColumnId (newSortColumnId), isForwards);
 | 
				
			||||||
 | 
					        dataList->sortChildElements (sorter);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        table.updateContent();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// This is overloaded from TableListBoxModel, and should choose the best width for the specified
 | 
				
			||||||
 | 
					// column.
 | 
				
			||||||
 | 
					int TableModel::getColumnAutoSizeWidth (int columnId) {
 | 
				
			||||||
 | 
					    if (columnId == 5)
 | 
				
			||||||
 | 
					        return 100; // (this is the ratings column, containing a custom combobox component)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    int widest = 32;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // find the widest bit of text in this column..
 | 
				
			||||||
 | 
					    for (int i = getNumRows(); --i >= 0;)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if (const XmlElement* rowElement = dataList->getChildElement (i))
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            const String text (rowElement->getStringAttribute (getAttributeNameForColumnId (columnId)));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            widest = jmax (widest, font.getStringWidth (text));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return widest + 8;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										61
									
								
								Source/TableModel.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								Source/TableModel.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,61 @@
 | 
				
			|||||||
 | 
					//
 | 
				
			||||||
 | 
					// Created by Alex Birch on 17/09/2017.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "../JuceLibraryCode/JuceHeader.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TableModel: public TableListBoxModel {
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
					    int getNumRows() override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void paintRowBackground (
 | 
				
			||||||
 | 
					            Graphics& g,
 | 
				
			||||||
 | 
					            int rowNumber,
 | 
				
			||||||
 | 
					            int width,
 | 
				
			||||||
 | 
					            int height,
 | 
				
			||||||
 | 
					            bool rowIsSelected
 | 
				
			||||||
 | 
					    ) override;
 | 
				
			||||||
 | 
					    void paintCell (
 | 
				
			||||||
 | 
					            Graphics& g,
 | 
				
			||||||
 | 
					            int rowNumber,
 | 
				
			||||||
 | 
					            int columnId,
 | 
				
			||||||
 | 
					            int width,
 | 
				
			||||||
 | 
					            int height,
 | 
				
			||||||
 | 
					            bool rowIsSelected
 | 
				
			||||||
 | 
					    ) override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void sortOrderChanged (int newSortColumnId, bool isForwards) override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    int getColumnAutoSizeWidth (int columnId) override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
					    ScopedPointer<XmlElement> demoData;   // This is the XML document loaded from the embedded file "demo table data.xml"
 | 
				
			||||||
 | 
					    std::vector<String> columnList;
 | 
				
			||||||
 | 
					    std::vector<std::vector<String>> dataList;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void loadData();
 | 
				
			||||||
 | 
					    String getAttributeNameForColumnId (const int columnId) const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // A comparator used to sort our data when the user clicks a column header
 | 
				
			||||||
 | 
					    class DataSorter {
 | 
				
			||||||
 | 
					    public:
 | 
				
			||||||
 | 
					        DataSorter (
 | 
				
			||||||
 | 
					                const String& attributeToSortBy,
 | 
				
			||||||
 | 
					                bool forwards
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        int compareElements (
 | 
				
			||||||
 | 
					                XmlElement* first,
 | 
				
			||||||
 | 
					                XmlElement* second
 | 
				
			||||||
 | 
					        ) const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private:
 | 
				
			||||||
 | 
					        String attributeToSort;
 | 
				
			||||||
 | 
					        int direction;
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TableModel)
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
							
								
								
									
										183
									
								
								Source/TablesComponent.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										183
									
								
								Source/TablesComponent.cpp
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,183 @@
 | 
				
			|||||||
 | 
					//
 | 
				
			||||||
 | 
					// Created by Alex Birch on 17/09/2017.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "TablesComponent.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					using namespace std;
 | 
				
			||||||
 | 
					using namespace placeholders;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					TablesComponent::TablesComponent(
 | 
				
			||||||
 | 
					        FluidSynthModel* fluidSynthModel
 | 
				
			||||||
 | 
					) : fluidSynthModel(fluidSynthModel),
 | 
				
			||||||
 | 
					    banksToPresets(fluidSynthModel->getBanks()),
 | 
				
			||||||
 | 
					    initialised(false)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    fluid_preset_t* currentPreset = getCurrentPreset();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    selectedBank = currentPreset->get_banknum(currentPreset);
 | 
				
			||||||
 | 
					    int selectedPreset = currentPreset->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(
 | 
				
			||||||
 | 
					            {"Preset", "Name"},
 | 
				
			||||||
 | 
					            mapPresets(
 | 
				
			||||||
 | 
					                    banksToPresets,
 | 
				
			||||||
 | 
					                    currentPreset->get_banknum(currentPreset)
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					            [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(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) {
 | 
				
			||||||
 | 
					        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) {
 | 
				
			||||||
 | 
					        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() {
 | 
				
			||||||
 | 
					    Rectangle<int> r (getLocalBounds());
 | 
				
			||||||
 | 
					    banks->setBounds (r.removeFromTop(27).reduced(5,0));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    presetTable->setBounds (r);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool TablesComponent::keyPressed(const KeyPress &key) {
 | 
				
			||||||
 | 
					    if (key.getKeyCode() == KeyPress::leftKey
 | 
				
			||||||
 | 
					            || key.getKeyCode() == KeyPress::rightKey) {
 | 
				
			||||||
 | 
					        banks->cycle(key.getKeyCode() == KeyPress::rightKey);
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return presetTable->keyPressed(key);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void TablesComponent::fontChanged(FluidSynthModel *) {
 | 
				
			||||||
 | 
					    banksToPresets = fluidSynthModel->getBanks();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fluid_preset_t* currentPreset = getCurrentPreset();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    selectedBank = currentPreset->get_banknum(currentPreset);
 | 
				
			||||||
 | 
					    int selectedPreset = currentPreset->get_num(currentPreset);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    presetTable->setRows(
 | 
				
			||||||
 | 
					            mapPresets(
 | 
				
			||||||
 | 
					                    banksToPresets,
 | 
				
			||||||
 | 
					                    selectedBank
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					            presetToIndexMapper(selectedPreset)
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    banks->setItems(
 | 
				
			||||||
 | 
					            mapBanks(banksToPresets),
 | 
				
			||||||
 | 
					            selectedBank
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										54
									
								
								Source/TablesComponent.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								Source/TablesComponent.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,54 @@
 | 
				
			|||||||
 | 
					//
 | 
				
			||||||
 | 
					// Created by Alex Birch on 17/09/2017.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "../JuceLibraryCode/JuceHeader.h"
 | 
				
			||||||
 | 
					#include "Pills.h"
 | 
				
			||||||
 | 
					#include "TableComponent.h"
 | 
				
			||||||
 | 
					#include "Preset.h"
 | 
				
			||||||
 | 
					#include "PresetsToBanks.h"
 | 
				
			||||||
 | 
					#include "FluidSynthModel.h"
 | 
				
			||||||
 | 
					#include <memory>
 | 
				
			||||||
 | 
					#include <fluidsynth.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					using namespace std;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TablesComponent : public Component,
 | 
				
			||||||
 | 
					                        public FluidSynthModel::Listener
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
					    TablesComponent(
 | 
				
			||||||
 | 
					            FluidSynthModel* fluidSynthModel
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    ~TablesComponent();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void resized() override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bool keyPressed(const KeyPress &key) override;
 | 
				
			||||||
 | 
					    void fontChanged(FluidSynthModel *) override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
					    FluidSynthModel* fluidSynthModel;
 | 
				
			||||||
 | 
					    int selectedBank;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Pills* banks;
 | 
				
			||||||
 | 
					    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)
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
@ -8,6 +8,44 @@
 | 
				
			|||||||
              pluginCode="Jspf" pluginManufacturerCode="Blbs">
 | 
					              pluginCode="Jspf" pluginManufacturerCode="Blbs">
 | 
				
			||||||
  <MAINGROUP id="rCqBG3" name="juicysfplugin">
 | 
					  <MAINGROUP id="rCqBG3" name="juicysfplugin">
 | 
				
			||||||
    <GROUP id="{ED7E27E3-B67D-383C-F819-F05289E08A33}" name="Source">
 | 
					    <GROUP id="{ED7E27E3-B67D-383C-F819-F05289E08A33}" name="Source">
 | 
				
			||||||
 | 
					      <FILE id="jhSsxG" name="FilePicker.cpp" compile="1" resource="0" file="Source/FilePicker.cpp"/>
 | 
				
			||||||
 | 
					      <FILE id="kDg5Qi" name="FilePicker.h" compile="0" resource="0" file="Source/FilePicker.h"/>
 | 
				
			||||||
 | 
					      <FILE id="xg2XPM" name="FluidSynthModel.cpp" compile="1" resource="0"
 | 
				
			||||||
 | 
					            file="Source/FluidSynthModel.cpp"/>
 | 
				
			||||||
 | 
					      <FILE id="kOSlkW" name="FluidSynthModel.h" compile="0" resource="0"
 | 
				
			||||||
 | 
					            file="Source/FluidSynthModel.h"/>
 | 
				
			||||||
 | 
					      <FILE id="olazZO" name="MyColours.cpp" compile="1" resource="0" file="Source/MyColours.cpp"/>
 | 
				
			||||||
 | 
					      <FILE id="RbaYg9" name="MyColours.h" compile="0" resource="0" file="Source/MyColours.h"/>
 | 
				
			||||||
 | 
					      <FILE id="ZFYfhD" name="Pill.cpp" compile="1" resource="0" file="Source/Pill.cpp"/>
 | 
				
			||||||
 | 
					      <FILE id="CBKk98" name="Pill.h" compile="0" resource="0" file="Source/Pill.h"/>
 | 
				
			||||||
 | 
					      <FILE id="esyv7j" name="Pills.cpp" compile="1" resource="0" file="Source/Pills.cpp"/>
 | 
				
			||||||
 | 
					      <FILE id="qlJw1C" name="Pills.h" compile="0" resource="0" file="Source/Pills.h"/>
 | 
				
			||||||
 | 
					      <FILE id="Q7V7pC" name="Preset.cpp" compile="1" resource="0" file="Source/Preset.cpp"/>
 | 
				
			||||||
 | 
					      <FILE id="bPOtSV" name="Preset.h" compile="0" resource="0" file="Source/Preset.h"/>
 | 
				
			||||||
 | 
					      <FILE id="tlzfXV" name="PresetsToBanks.h" compile="0" resource="0"
 | 
				
			||||||
 | 
					            file="Source/PresetsToBanks.h"/>
 | 
				
			||||||
 | 
					      <FILE id="SEcFsz" name="SoundfontSynthSound.cpp" compile="1" resource="0"
 | 
				
			||||||
 | 
					            file="Source/SoundfontSynthSound.cpp"/>
 | 
				
			||||||
 | 
					      <FILE id="kCI8aL" name="SoundfontSynthSound.h" compile="0" resource="0"
 | 
				
			||||||
 | 
					            file="Source/SoundfontSynthSound.h"/>
 | 
				
			||||||
 | 
					      <FILE id="FKMDFe" name="SoundfontSynthVoice.cpp" compile="1" resource="0"
 | 
				
			||||||
 | 
					            file="Source/SoundfontSynthVoice.cpp"/>
 | 
				
			||||||
 | 
					      <FILE id="t75qEY" name="SoundfontSynthVoice.h" compile="0" resource="0"
 | 
				
			||||||
 | 
					            file="Source/SoundfontSynthVoice.h"/>
 | 
				
			||||||
 | 
					      <FILE id="uAVIjl" name="SurjectiveMidiKeyboardComponent.cpp" compile="1"
 | 
				
			||||||
 | 
					            resource="0" file="Source/SurjectiveMidiKeyboardComponent.cpp"/>
 | 
				
			||||||
 | 
					      <FILE id="m3NMU5" name="SurjectiveMidiKeyboardComponent.h" compile="0"
 | 
				
			||||||
 | 
					            resource="0" file="Source/SurjectiveMidiKeyboardComponent.h"/>
 | 
				
			||||||
 | 
					      <FILE id="lKz1Zz" name="TableComponent.cpp" compile="1" resource="0"
 | 
				
			||||||
 | 
					            file="Source/TableComponent.cpp"/>
 | 
				
			||||||
 | 
					      <FILE id="D2v6G2" name="TableComponent.h" compile="0" resource="0"
 | 
				
			||||||
 | 
					            file="Source/TableComponent.h"/>
 | 
				
			||||||
 | 
					      <FILE id="DFpwRJ" name="TableModel.cpp" compile="1" resource="0" file="Source/TableModel.cpp"/>
 | 
				
			||||||
 | 
					      <FILE id="iaUXIc" name="TableModel.h" compile="0" resource="0" file="Source/TableModel.h"/>
 | 
				
			||||||
 | 
					      <FILE id="XSCMOI" name="TablesComponent.cpp" compile="1" resource="0"
 | 
				
			||||||
 | 
					            file="Source/TablesComponent.cpp"/>
 | 
				
			||||||
 | 
					      <FILE id="m0tqZa" name="TablesComponent.h" compile="0" resource="0"
 | 
				
			||||||
 | 
					            file="Source/TablesComponent.h"/>
 | 
				
			||||||
      <FILE id="wm4EZl" name="PluginProcessor.cpp" compile="1" resource="0"
 | 
					      <FILE id="wm4EZl" name="PluginProcessor.cpp" compile="1" resource="0"
 | 
				
			||||||
            file="Source/PluginProcessor.cpp"/>
 | 
					            file="Source/PluginProcessor.cpp"/>
 | 
				
			||||||
      <FILE id="ABJlXP" name="PluginProcessor.h" compile="0" resource="0"
 | 
					      <FILE id="ABJlXP" name="PluginProcessor.h" compile="0" resource="0"
 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user