From a9e726681c5a41b1b2c950d7a7210cdd04105d40 Mon Sep 17 00:00:00 2001 From: jules Date: Thu, 3 Jan 2008 21:35:24 +0000 Subject: [PATCH] --- build/linux/JUCE.make | 24 + build/linux/juce_premake.lua | 4 + build/macosx/Juce.xcodeproj/project.pbxproj | 120 +- build/macosx/juce.xcconfig | 21 +- build/win32/vc8/JUCE.vcproj | 116 +- docs/JUCE changelist.txt | 7 + .../build/win32/PluginHost.vcproj | 72 - extras/audio plugin host/src/HostStartup.cpp | 1 + .../src/host/FilterGraph.cpp | 736 +- .../audio plugin host/src/host/FilterGraph.h | 48 +- .../src/host/GraphEditorPanel.cpp | 192 +- .../src/host/GraphEditorPanel.h | 32 +- .../src/host/InternalFilters.cpp | 315 +- .../src/host/InternalFilters.h | 2 +- .../src/host/MainHostWindow.cpp | 16 +- .../src/host/MainHostWindow.h | 1 - .../formats/juce_AudioUnitPluginFormat.cpp | 2550 +++---- .../plugins/formats/juce_VSTPluginFormat.cpp | 6006 +++++++++-------- .../src/plugins/juce_AudioPluginFormat.cpp | 24 + .../src/plugins/juce_AudioPluginFormat.h | 13 + .../src/plugins/juce_KnownPluginList.cpp | 11 +- .../src/plugins/juce_KnownPluginList.h | 7 + .../src/plugins/juce_PluginDescription.cpp | 41 +- .../src/plugins/juce_PluginDescription.h | 23 +- .../audio plugins/demo/src/DemoJuceFilter.cpp | 10 + .../audio plugins/demo/src/DemoJuceFilter.h | 3 + .../Standalone/juce_AudioFilterStreamer.cpp | 7 +- .../Standalone/juce_AudioFilterStreamer.h | 2 +- .../macosx/jucedemo.xcodeproj/project.pbxproj | 12 +- extras/juce demo/src/BinaryData.cpp | 114 +- extras/juce demo/src/BinaryData.h | 2 +- extras/juce demo/src/binarydata/AudioDemo.cpp | 8 +- .../src/binarydata/DragAndDropDemo.cpp | 3 +- extras/juce demo/src/demos/AudioDemo.cpp | 8 +- .../juce demo/src/demos/DragAndDropDemo.cpp | 3 +- juce.h | 1 + juce_Config.h | 10 + src/juce_app_includes.h | 39 + .../formats/juce_AudioUnitPluginFormat.cpp | 1284 ++++ .../formats/juce_AudioUnitPluginFormat.h | 67 + .../formats/juce_DirectXPluginFormat.h | 71 + .../plugins/formats/juce_LADSPAPluginFormat.h | 71 + .../plugins/formats/juce_VSTPluginFormat.cpp | 3017 +++++++++ .../plugins/formats/juce_VSTPluginFormat.h | 68 + .../audio/plugins/juce_AudioPluginFormat.cpp | 49 + .../audio/plugins/juce_AudioPluginFormat.h | 107 + .../plugins/juce_AudioPluginFormatManager.cpp | 138 + .../plugins/juce_AudioPluginFormatManager.h | 107 + .../plugins/juce_AudioPluginInstance.cpp | 51 + .../audio/plugins/juce_AudioPluginInstance.h | 76 + .../audio/plugins/juce_KnownPluginList.cpp | 431 ++ .../audio/plugins/juce_KnownPluginList.h | 176 + .../audio/plugins/juce_PluginDescription.cpp | 139 + .../audio/plugins/juce_PluginDescription.h | 140 + .../plugins/juce_PluginDirectoryScanner.cpp | 164 + .../plugins/juce_PluginDirectoryScanner.h | 129 + .../plugins/juce_PluginListComponent.cpp | 308 + .../audio/plugins/juce_PluginListComponent.h | 107 + .../audio/processors/juce_AudioProcessor.h | 14 + .../processors/juce_AudioProcessorGraph.cpp | 1340 ++++ .../processors/juce_AudioProcessorGraph.h | 440 ++ .../processors/juce_AudioProcessorPlayer.cpp | 207 + .../processors/juce_AudioProcessorPlayer.h | 121 + 63 files changed, 13943 insertions(+), 5483 deletions(-) create mode 100644 src/juce_appframework/audio/plugins/formats/juce_AudioUnitPluginFormat.cpp create mode 100644 src/juce_appframework/audio/plugins/formats/juce_AudioUnitPluginFormat.h create mode 100644 src/juce_appframework/audio/plugins/formats/juce_DirectXPluginFormat.h create mode 100644 src/juce_appframework/audio/plugins/formats/juce_LADSPAPluginFormat.h create mode 100644 src/juce_appframework/audio/plugins/formats/juce_VSTPluginFormat.cpp create mode 100644 src/juce_appframework/audio/plugins/formats/juce_VSTPluginFormat.h create mode 100644 src/juce_appframework/audio/plugins/juce_AudioPluginFormat.cpp create mode 100644 src/juce_appframework/audio/plugins/juce_AudioPluginFormat.h create mode 100644 src/juce_appframework/audio/plugins/juce_AudioPluginFormatManager.cpp create mode 100644 src/juce_appframework/audio/plugins/juce_AudioPluginFormatManager.h create mode 100644 src/juce_appframework/audio/plugins/juce_AudioPluginInstance.cpp create mode 100644 src/juce_appframework/audio/plugins/juce_AudioPluginInstance.h create mode 100644 src/juce_appframework/audio/plugins/juce_KnownPluginList.cpp create mode 100644 src/juce_appframework/audio/plugins/juce_KnownPluginList.h create mode 100644 src/juce_appframework/audio/plugins/juce_PluginDescription.cpp create mode 100644 src/juce_appframework/audio/plugins/juce_PluginDescription.h create mode 100644 src/juce_appframework/audio/plugins/juce_PluginDirectoryScanner.cpp create mode 100644 src/juce_appframework/audio/plugins/juce_PluginDirectoryScanner.h create mode 100644 src/juce_appframework/audio/plugins/juce_PluginListComponent.cpp create mode 100644 src/juce_appframework/audio/plugins/juce_PluginListComponent.h create mode 100644 src/juce_appframework/audio/processors/juce_AudioProcessorGraph.cpp create mode 100644 src/juce_appframework/audio/processors/juce_AudioProcessorGraph.h create mode 100644 src/juce_appframework/audio/processors/juce_AudioProcessorPlayer.cpp create mode 100644 src/juce_appframework/audio/processors/juce_AudioProcessorPlayer.h diff --git a/build/linux/JUCE.make b/build/linux/JUCE.make index 65bbfad8e6..072bb72a63 100644 --- a/build/linux/JUCE.make +++ b/build/linux/JUCE.make @@ -109,6 +109,8 @@ OBJECTS := \ $(OBJDIR)/juce_MidiMessageSequence.o \ $(OBJDIR)/juce_AudioProcessor.o \ $(OBJDIR)/juce_AudioProcessorEditor.o \ + $(OBJDIR)/juce_AudioProcessorGraph.o \ + $(OBJDIR)/juce_AudioProcessorPlayer.o \ $(OBJDIR)/juce_GenericAudioProcessorEditor.o \ $(OBJDIR)/juce_AiffAudioFormat.o \ $(OBJDIR)/juce_AudioCDReader.o \ @@ -120,6 +122,8 @@ OBJECTS := \ $(OBJDIR)/juce_WavAudioFormat.o \ $(OBJDIR)/bitbuffer.o \ $(OBJDIR)/bitmath.o \ + $(OBJDIR)/bitreader.o \ + $(OBJDIR)/bitwriter.o \ $(OBJDIR)/cpu.o \ $(OBJDIR)/crc.o \ $(OBJDIR)/fixed.o \ @@ -801,6 +805,16 @@ $(OBJDIR)/juce_AudioProcessorEditor.o: ../../src/juce_appframework/audio/process @echo $(notdir $<) @$(CXX) $(CXXFLAGS) -o $@ -c $< +$(OBJDIR)/juce_AudioProcessorGraph.o: ../../src/juce_appframework/audio/processors/juce_AudioProcessorGraph.cpp + -@$(CMD_MKOBJDIR) + @echo $(notdir $<) + @$(CXX) $(CXXFLAGS) -o $@ -c $< + +$(OBJDIR)/juce_AudioProcessorPlayer.o: ../../src/juce_appframework/audio/processors/juce_AudioProcessorPlayer.cpp + -@$(CMD_MKOBJDIR) + @echo $(notdir $<) + @$(CXX) $(CXXFLAGS) -o $@ -c $< + $(OBJDIR)/juce_GenericAudioProcessorEditor.o: ../../src/juce_appframework/audio/processors/juce_GenericAudioProcessorEditor.cpp -@$(CMD_MKOBJDIR) @echo $(notdir $<) @@ -856,6 +870,16 @@ $(OBJDIR)/bitmath.o: ../../src/juce_appframework/audio/audio_file_formats/flac/l @echo $(notdir $<) @$(CC) $(CFLAGS) -o $@ -c $< +$(OBJDIR)/bitreader.o: ../../src/juce_appframework/audio/audio_file_formats/flac/libFLAC/bitreader.c + -@$(CMD_MKOBJDIR) + @echo $(notdir $<) + @$(CC) $(CFLAGS) -o $@ -c $< + +$(OBJDIR)/bitwriter.o: ../../src/juce_appframework/audio/audio_file_formats/flac/libFLAC/bitwriter.c + -@$(CMD_MKOBJDIR) + @echo $(notdir $<) + @$(CC) $(CFLAGS) -o $@ -c $< + $(OBJDIR)/cpu.o: ../../src/juce_appframework/audio/audio_file_formats/flac/libFLAC/cpu.c -@$(CMD_MKOBJDIR) @echo $(notdir $<) diff --git a/build/linux/juce_premake.lua b/build/linux/juce_premake.lua index 2e8c5c79f7..8225519919 100644 --- a/build/linux/juce_premake.lua +++ b/build/linux/juce_premake.lua @@ -68,6 +68,10 @@ package.files = { matchfiles ( "../../src/juce_appframework/audio/midi/*.h", "../../src/juce_appframework/audio/processors/*.cpp", "../../src/juce_appframework/audio/processors/*.h", + "../../src/juce_appframework/audio/plugins/*.cpp", + "../../src/juce_appframework/audio/plugins/*.h", + "../../src/juce_appframework/audio/plugins/formats/*.cpp", + "../../src/juce_appframework/audio/plugins/formats/*.h", "../../src/juce_appframework/audio/audio_file_formats/*.cpp", "../../src/juce_appframework/audio/audio_file_formats/*.h", "../../src/juce_appframework/audio/audio_file_formats/flac/libFLAC/*.c", diff --git a/build/macosx/Juce.xcodeproj/project.pbxproj b/build/macosx/Juce.xcodeproj/project.pbxproj index 238d856062..9c0613314c 100644 --- a/build/macosx/Juce.xcodeproj/project.pbxproj +++ b/build/macosx/Juce.xcodeproj/project.pbxproj @@ -20,6 +20,22 @@ 84099CA10AE52BD000B2A05D /* juce_Synthesiser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84099C9D0AE52BD000B2A05D /* juce_Synthesiser.cpp */; }; 84099CA20AE52BD000B2A05D /* juce_Synthesiser.h in Headers */ = {isa = PBXBuildFile; fileRef = 84099C9E0AE52BD000B2A05D /* juce_Synthesiser.h */; }; 840F80BC092B399D005E7B4E /* juce.h in Headers */ = {isa = PBXBuildFile; fileRef = 840F80BB092B399D005E7B4E /* juce.h */; }; + 8416EB750D1999620094A433 /* bitbuffer.c in Sources */ = {isa = PBXBuildFile; fileRef = 8416EB650D1999610094A433 /* bitbuffer.c */; }; + 8416EB760D1999620094A433 /* bitmath.c in Sources */ = {isa = PBXBuildFile; fileRef = 8416EB660D1999610094A433 /* bitmath.c */; }; + 8416EB770D1999620094A433 /* bitreader.c in Sources */ = {isa = PBXBuildFile; fileRef = 8416EB670D1999610094A433 /* bitreader.c */; }; + 8416EB780D1999620094A433 /* bitwriter.c in Sources */ = {isa = PBXBuildFile; fileRef = 8416EB680D1999610094A433 /* bitwriter.c */; }; + 8416EB790D1999620094A433 /* cpu.c in Sources */ = {isa = PBXBuildFile; fileRef = 8416EB690D1999610094A433 /* cpu.c */; }; + 8416EB7A0D1999620094A433 /* crc.c in Sources */ = {isa = PBXBuildFile; fileRef = 8416EB6A0D1999610094A433 /* crc.c */; }; + 8416EB7B0D1999620094A433 /* fixed.c in Sources */ = {isa = PBXBuildFile; fileRef = 8416EB6B0D1999610094A433 /* fixed.c */; }; + 8416EB7C0D1999620094A433 /* float.c in Sources */ = {isa = PBXBuildFile; fileRef = 8416EB6C0D1999610094A433 /* float.c */; }; + 8416EB7D0D1999620094A433 /* format.c in Sources */ = {isa = PBXBuildFile; fileRef = 8416EB6D0D1999610094A433 /* format.c */; }; + 8416EB7E0D1999620094A433 /* lpc_flac.c in Sources */ = {isa = PBXBuildFile; fileRef = 8416EB6E0D1999610094A433 /* lpc_flac.c */; }; + 8416EB7F0D1999620094A433 /* md5.c in Sources */ = {isa = PBXBuildFile; fileRef = 8416EB6F0D1999620094A433 /* md5.c */; }; + 8416EB800D1999620094A433 /* memory.c in Sources */ = {isa = PBXBuildFile; fileRef = 8416EB700D1999620094A433 /* memory.c */; }; + 8416EB810D1999620094A433 /* stream_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 8416EB710D1999620094A433 /* stream_decoder.c */; }; + 8416EB820D1999620094A433 /* stream_encoder_framing.c in Sources */ = {isa = PBXBuildFile; fileRef = 8416EB720D1999620094A433 /* stream_encoder_framing.c */; }; + 8416EB830D1999620094A433 /* stream_encoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 8416EB730D1999620094A433 /* stream_encoder.c */; }; + 8416EB840D1999620094A433 /* window_flac.c in Sources */ = {isa = PBXBuildFile; fileRef = 8416EB740D1999620094A433 /* window_flac.c */; }; 8417EE530A6E7A280058E04E /* juce_TableHeaderComponent.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8417EE4F0A6E7A270058E04E /* juce_TableHeaderComponent.cpp */; }; 8417EE540A6E7A280058E04E /* juce_TableHeaderComponent.h in Headers */ = {isa = PBXBuildFile; fileRef = 8417EE500A6E7A270058E04E /* juce_TableHeaderComponent.h */; }; 8417EE550A6E7A280058E04E /* juce_TableListBox.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8417EE510A6E7A280058E04E /* juce_TableListBox.cpp */; }; @@ -571,20 +587,6 @@ 84F593AE09855679008153BA /* juce_DocumentWindow.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84F593AC09855679008153BA /* juce_DocumentWindow.cpp */; }; 84F593AF09855679008153BA /* juce_DocumentWindow.h in Headers */ = {isa = PBXBuildFile; fileRef = 84F593AD09855679008153BA /* juce_DocumentWindow.h */; }; 84F593B109855693008153BA /* juce_LassoComponent.h in Headers */ = {isa = PBXBuildFile; fileRef = 84F593B009855693008153BA /* juce_LassoComponent.h */; }; - 84F690EF0B3444E500ABAE1C /* bitbuffer.c in Sources */ = {isa = PBXBuildFile; fileRef = 84F690E10B3444E500ABAE1C /* bitbuffer.c */; }; - 84F690F00B3444E500ABAE1C /* bitmath.c in Sources */ = {isa = PBXBuildFile; fileRef = 84F690E20B3444E500ABAE1C /* bitmath.c */; }; - 84F690F10B3444E500ABAE1C /* cpu.c in Sources */ = {isa = PBXBuildFile; fileRef = 84F690E30B3444E500ABAE1C /* cpu.c */; }; - 84F690F20B3444E500ABAE1C /* crc.c in Sources */ = {isa = PBXBuildFile; fileRef = 84F690E40B3444E500ABAE1C /* crc.c */; }; - 84F690F30B3444E500ABAE1C /* fixed.c in Sources */ = {isa = PBXBuildFile; fileRef = 84F690E50B3444E500ABAE1C /* fixed.c */; }; - 84F690F40B3444E500ABAE1C /* float.c in Sources */ = {isa = PBXBuildFile; fileRef = 84F690E60B3444E500ABAE1C /* float.c */; }; - 84F690F50B3444E500ABAE1C /* format.c in Sources */ = {isa = PBXBuildFile; fileRef = 84F690E70B3444E500ABAE1C /* format.c */; }; - 84F690F60B3444E500ABAE1C /* lpc_flac.c in Sources */ = {isa = PBXBuildFile; fileRef = 84F690E80B3444E500ABAE1C /* lpc_flac.c */; }; - 84F690F70B3444E500ABAE1C /* md5.c in Sources */ = {isa = PBXBuildFile; fileRef = 84F690E90B3444E500ABAE1C /* md5.c */; }; - 84F690F80B3444E500ABAE1C /* memory.c in Sources */ = {isa = PBXBuildFile; fileRef = 84F690EA0B3444E500ABAE1C /* memory.c */; }; - 84F690F90B3444E500ABAE1C /* stream_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 84F690EB0B3444E500ABAE1C /* stream_decoder.c */; }; - 84F690FA0B3444E500ABAE1C /* stream_encoder_framing.c in Sources */ = {isa = PBXBuildFile; fileRef = 84F690EC0B3444E500ABAE1C /* stream_encoder_framing.c */; }; - 84F690FB0B3444E500ABAE1C /* stream_encoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 84F690ED0B3444E500ABAE1C /* stream_encoder.c */; }; - 84F690FC0B3444E500ABAE1C /* window_flac.c in Sources */ = {isa = PBXBuildFile; fileRef = 84F690EE0B3444E500ABAE1C /* window_flac.c */; }; 84F690FF0B3444F800ABAE1C /* bitwise.c in Sources */ = {isa = PBXBuildFile; fileRef = 84F690FD0B3444F800ABAE1C /* bitwise.c */; }; 84F691000B3444F800ABAE1C /* framing.c in Sources */ = {isa = PBXBuildFile; fileRef = 84F690FE0B3444F800ABAE1C /* framing.c */; }; 84F691170B34453600ABAE1C /* analysis.c in Sources */ = {isa = PBXBuildFile; fileRef = 84F691010B34453600ABAE1C /* analysis.c */; }; @@ -672,6 +674,22 @@ 84099C9D0AE52BD000B2A05D /* juce_Synthesiser.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = juce_Synthesiser.cpp; sourceTree = ""; }; 84099C9E0AE52BD000B2A05D /* juce_Synthesiser.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = juce_Synthesiser.h; sourceTree = ""; }; 840F80BB092B399D005E7B4E /* juce.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = juce.h; path = ../../juce.h; sourceTree = SOURCE_ROOT; }; + 8416EB650D1999610094A433 /* bitbuffer.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = bitbuffer.c; path = flac/libFLAC/bitbuffer.c; sourceTree = ""; }; + 8416EB660D1999610094A433 /* bitmath.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = bitmath.c; path = flac/libFLAC/bitmath.c; sourceTree = ""; }; + 8416EB670D1999610094A433 /* bitreader.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = bitreader.c; path = flac/libFLAC/bitreader.c; sourceTree = ""; }; + 8416EB680D1999610094A433 /* bitwriter.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = bitwriter.c; path = flac/libFLAC/bitwriter.c; sourceTree = ""; }; + 8416EB690D1999610094A433 /* cpu.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = cpu.c; path = flac/libFLAC/cpu.c; sourceTree = ""; }; + 8416EB6A0D1999610094A433 /* crc.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = crc.c; path = flac/libFLAC/crc.c; sourceTree = ""; }; + 8416EB6B0D1999610094A433 /* fixed.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = fixed.c; path = flac/libFLAC/fixed.c; sourceTree = ""; }; + 8416EB6C0D1999610094A433 /* float.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = float.c; path = flac/libFLAC/float.c; sourceTree = ""; }; + 8416EB6D0D1999610094A433 /* format.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = format.c; path = flac/libFLAC/format.c; sourceTree = ""; }; + 8416EB6E0D1999610094A433 /* lpc_flac.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = lpc_flac.c; path = flac/libFLAC/lpc_flac.c; sourceTree = ""; }; + 8416EB6F0D1999620094A433 /* md5.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = md5.c; path = flac/libFLAC/md5.c; sourceTree = ""; }; + 8416EB700D1999620094A433 /* memory.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = memory.c; path = flac/libFLAC/memory.c; sourceTree = ""; }; + 8416EB710D1999620094A433 /* stream_decoder.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = stream_decoder.c; path = flac/libFLAC/stream_decoder.c; sourceTree = ""; }; + 8416EB720D1999620094A433 /* stream_encoder_framing.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = stream_encoder_framing.c; path = flac/libFLAC/stream_encoder_framing.c; sourceTree = ""; }; + 8416EB730D1999620094A433 /* stream_encoder.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = stream_encoder.c; path = flac/libFLAC/stream_encoder.c; sourceTree = ""; }; + 8416EB740D1999620094A433 /* window_flac.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = window_flac.c; path = flac/libFLAC/window_flac.c; sourceTree = ""; }; 8417EE4F0A6E7A270058E04E /* juce_TableHeaderComponent.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = juce_TableHeaderComponent.cpp; sourceTree = ""; }; 8417EE500A6E7A270058E04E /* juce_TableHeaderComponent.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = juce_TableHeaderComponent.h; sourceTree = ""; }; 8417EE510A6E7A280058E04E /* juce_TableListBox.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = juce_TableListBox.cpp; sourceTree = ""; }; @@ -1226,20 +1244,6 @@ 84F593AC09855679008153BA /* juce_DocumentWindow.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = juce_DocumentWindow.cpp; sourceTree = ""; }; 84F593AD09855679008153BA /* juce_DocumentWindow.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = juce_DocumentWindow.h; sourceTree = ""; }; 84F593B009855693008153BA /* juce_LassoComponent.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = juce_LassoComponent.h; sourceTree = ""; }; - 84F690E10B3444E500ABAE1C /* bitbuffer.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = bitbuffer.c; path = flac/libFLAC/bitbuffer.c; sourceTree = ""; }; - 84F690E20B3444E500ABAE1C /* bitmath.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = bitmath.c; path = flac/libFLAC/bitmath.c; sourceTree = ""; }; - 84F690E30B3444E500ABAE1C /* cpu.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = cpu.c; path = flac/libFLAC/cpu.c; sourceTree = ""; }; - 84F690E40B3444E500ABAE1C /* crc.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = crc.c; path = flac/libFLAC/crc.c; sourceTree = ""; }; - 84F690E50B3444E500ABAE1C /* fixed.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = fixed.c; path = flac/libFLAC/fixed.c; sourceTree = ""; }; - 84F690E60B3444E500ABAE1C /* float.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = float.c; path = flac/libFLAC/float.c; sourceTree = ""; }; - 84F690E70B3444E500ABAE1C /* format.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = format.c; path = flac/libFLAC/format.c; sourceTree = ""; }; - 84F690E80B3444E500ABAE1C /* lpc_flac.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = lpc_flac.c; path = flac/libFLAC/lpc_flac.c; sourceTree = ""; }; - 84F690E90B3444E500ABAE1C /* md5.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = md5.c; path = flac/libFLAC/md5.c; sourceTree = ""; }; - 84F690EA0B3444E500ABAE1C /* memory.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = memory.c; path = flac/libFLAC/memory.c; sourceTree = ""; }; - 84F690EB0B3444E500ABAE1C /* stream_decoder.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = stream_decoder.c; path = flac/libFLAC/stream_decoder.c; sourceTree = ""; }; - 84F690EC0B3444E500ABAE1C /* stream_encoder_framing.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = stream_encoder_framing.c; path = flac/libFLAC/stream_encoder_framing.c; sourceTree = ""; }; - 84F690ED0B3444E500ABAE1C /* stream_encoder.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = stream_encoder.c; path = flac/libFLAC/stream_encoder.c; sourceTree = ""; }; - 84F690EE0B3444E500ABAE1C /* window_flac.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = window_flac.c; path = flac/libFLAC/window_flac.c; sourceTree = ""; }; 84F690FD0B3444F800ABAE1C /* bitwise.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = bitwise.c; path = oggvorbis/bitwise.c; sourceTree = ""; }; 84F690FE0B3444F800ABAE1C /* framing.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = framing.c; path = oggvorbis/framing.c; sourceTree = ""; }; 84F691010B34453600ABAE1C /* analysis.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = analysis.c; path = "oggvorbis/libvorbis-1.1.2/lib/analysis.c"; sourceTree = ""; }; @@ -2364,20 +2368,22 @@ 84F690DC0B3444A500ABAE1C /* flac */ = { isa = PBXGroup; children = ( - 84F690E10B3444E500ABAE1C /* bitbuffer.c */, - 84F690E20B3444E500ABAE1C /* bitmath.c */, - 84F690E30B3444E500ABAE1C /* cpu.c */, - 84F690E40B3444E500ABAE1C /* crc.c */, - 84F690E50B3444E500ABAE1C /* fixed.c */, - 84F690E60B3444E500ABAE1C /* float.c */, - 84F690E70B3444E500ABAE1C /* format.c */, - 84F690E80B3444E500ABAE1C /* lpc_flac.c */, - 84F690E90B3444E500ABAE1C /* md5.c */, - 84F690EA0B3444E500ABAE1C /* memory.c */, - 84F690EB0B3444E500ABAE1C /* stream_decoder.c */, - 84F690EC0B3444E500ABAE1C /* stream_encoder_framing.c */, - 84F690ED0B3444E500ABAE1C /* stream_encoder.c */, - 84F690EE0B3444E500ABAE1C /* window_flac.c */, + 8416EB650D1999610094A433 /* bitbuffer.c */, + 8416EB660D1999610094A433 /* bitmath.c */, + 8416EB670D1999610094A433 /* bitreader.c */, + 8416EB680D1999610094A433 /* bitwriter.c */, + 8416EB690D1999610094A433 /* cpu.c */, + 8416EB6A0D1999610094A433 /* crc.c */, + 8416EB6B0D1999610094A433 /* fixed.c */, + 8416EB6C0D1999610094A433 /* float.c */, + 8416EB6D0D1999610094A433 /* format.c */, + 8416EB6E0D1999610094A433 /* lpc_flac.c */, + 8416EB6F0D1999620094A433 /* md5.c */, + 8416EB700D1999620094A433 /* memory.c */, + 8416EB710D1999620094A433 /* stream_decoder.c */, + 8416EB720D1999620094A433 /* stream_encoder_framing.c */, + 8416EB730D1999620094A433 /* stream_encoder.c */, + 8416EB740D1999620094A433 /* window_flac.c */, ); name = flac; sourceTree = ""; @@ -3119,20 +3125,6 @@ 841E06370AF8CE75005E6FCC /* juce_AudioSampleBuffer.cpp in Sources */, 841E06390AF8CE75005E6FCC /* juce_IIRFilter.cpp in Sources */, 84F525000B32A8D500597B73 /* juce_ApplicationProperties.cpp in Sources */, - 84F690EF0B3444E500ABAE1C /* bitbuffer.c in Sources */, - 84F690F00B3444E500ABAE1C /* bitmath.c in Sources */, - 84F690F10B3444E500ABAE1C /* cpu.c in Sources */, - 84F690F20B3444E500ABAE1C /* crc.c in Sources */, - 84F690F30B3444E500ABAE1C /* fixed.c in Sources */, - 84F690F40B3444E500ABAE1C /* float.c in Sources */, - 84F690F50B3444E500ABAE1C /* format.c in Sources */, - 84F690F60B3444E500ABAE1C /* lpc_flac.c in Sources */, - 84F690F70B3444E500ABAE1C /* md5.c in Sources */, - 84F690F80B3444E500ABAE1C /* memory.c in Sources */, - 84F690F90B3444E500ABAE1C /* stream_decoder.c in Sources */, - 84F690FA0B3444E500ABAE1C /* stream_encoder_framing.c in Sources */, - 84F690FB0B3444E500ABAE1C /* stream_encoder.c in Sources */, - 84F690FC0B3444E500ABAE1C /* window_flac.c in Sources */, 84F690FF0B3444F800ABAE1C /* bitwise.c in Sources */, 84F691000B3444F800ABAE1C /* framing.c in Sources */, 84F691170B34453600ABAE1C /* analysis.c in Sources */, @@ -3166,6 +3158,22 @@ 84BC4E2A0C8DD38C00FA249B /* juce_AudioProcessor.cpp in Sources */, 84BC4E2C0C8DD38C00FA249B /* juce_AudioProcessorEditor.cpp in Sources */, 84BC4E2F0C8DD38C00FA249B /* juce_GenericAudioProcessorEditor.cpp in Sources */, + 8416EB750D1999620094A433 /* bitbuffer.c in Sources */, + 8416EB760D1999620094A433 /* bitmath.c in Sources */, + 8416EB770D1999620094A433 /* bitreader.c in Sources */, + 8416EB780D1999620094A433 /* bitwriter.c in Sources */, + 8416EB790D1999620094A433 /* cpu.c in Sources */, + 8416EB7A0D1999620094A433 /* crc.c in Sources */, + 8416EB7B0D1999620094A433 /* fixed.c in Sources */, + 8416EB7C0D1999620094A433 /* float.c in Sources */, + 8416EB7D0D1999620094A433 /* format.c in Sources */, + 8416EB7E0D1999620094A433 /* lpc_flac.c in Sources */, + 8416EB7F0D1999620094A433 /* md5.c in Sources */, + 8416EB800D1999620094A433 /* memory.c in Sources */, + 8416EB810D1999620094A433 /* stream_decoder.c in Sources */, + 8416EB820D1999620094A433 /* stream_encoder_framing.c in Sources */, + 8416EB830D1999620094A433 /* stream_encoder.c in Sources */, + 8416EB840D1999620094A433 /* window_flac.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/build/macosx/juce.xcconfig b/build/macosx/juce.xcconfig index 0822260502..ecfcfdb1ae 100644 --- a/build/macosx/juce.xcconfig +++ b/build/macosx/juce.xcconfig @@ -1,13 +1,22 @@ ARCHS = ppc i386 -// For 10.2 compatibility, use these values: +// For 10.2 (and later) compatibility, use these values: GCC_VERSION_ppc = 3.3 +MACOSX_DEPLOYMENT_TARGET = 10.4 MACOSX_DEPLOYMENT_TARGET_ppc = 10.2 -OTHER_LD_FLAGS_ppc = /Developer/SDKs/MacOSX10.2.8.sdk/usr/lib/gcc/darwin/3.3/libstdc++.a -SDKROOT_ppc = /Developer/SDKs/MacOSX10.2.8.sdk +OTHER_LD_FLAGS_ppc = $(DEVELOPER_SDK_DIR)/MacOSX10.2.8.sdk/usr/lib/gcc/darwin/3.3/libstdc++.a +SDKROOT_ppc = $(DEVELOPER_SDK_DIR)/MacOSX10.2.8.sdk -// For 10.3 compatibility, use these: +// For 10.3 (and later) compatibility, use these instead: +// MACOSX_DEPLOYMENT_TARGET = 10.4 // MACOSX_DEPLOYMENT_TARGET_ppc = 10.3 -// SDKROOT = /Developer/SDKs/MacOSX10.4u.sdk -// SDKROOT_ppc = /Developer/SDKs/MacOSX10.3.9.sdk +// SDKROOT = $(DEVELOPER_SDK_DIR)/MacOSX10.4u.sdk +// SDKROOT_ppc = $(DEVELOPER_SDK_DIR)/MacOSX10.3.9.sdk +// For 10.4 (and later) compatibility, use these instead: +// MACOSX_DEPLOYMENT_TARGET = 10.4 +// SDKROOT = $(DEVELOPER_SDK_DIR)/MacOSX10.4u.sdk + +// For 10.5 (and later) compatibility, use these instead: +// MACOSX_DEPLOYMENT_TARGET = 10.5 +// SDKROOT = $(DEVELOPER_SDK_DIR)/MacOSX10.5.sdk diff --git a/build/win32/vc8/JUCE.vcproj b/build/win32/vc8/JUCE.vcproj index f74513feeb..95c6d3bc4e 100644 --- a/build/win32/vc8/JUCE.vcproj +++ b/build/win32/vc8/JUCE.vcproj @@ -705,11 +705,15 @@ Name="flac" > + + - - @@ -1189,10 +1189,26 @@ RelativePath="..\..\..\src\juce_appframework\audio\processors\juce_AudioProcessorEditor.h" > + + + + + + + + @@ -1202,6 +1218,94 @@ > + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - addFormat (new InternalPluginFormat()); mainWindow = new MainHostWindow(); + //mainWindow->setUsingNativeTitleBar (true); commandManager->registerAllCommandsForTarget (this); commandManager->registerAllCommandsForTarget (mainWindow); diff --git a/extras/audio plugin host/src/host/FilterGraph.cpp b/extras/audio plugin host/src/host/FilterGraph.cpp index d40ec41486..a9b119419e 100644 --- a/extras/audio plugin host/src/host/FilterGraph.cpp +++ b/extras/audio plugin host/src/host/FilterGraph.cpp @@ -32,6 +32,7 @@ #include "../../../../juce.h" #include "FilterGraph.h" #include "InternalFilters.h" +#include "GraphEditorPanel.h" //============================================================================== @@ -55,264 +56,185 @@ FilterConnection::~FilterConnection() //============================================================================== -class PluginWindow : public DocumentWindow -{ -public: - PluginWindow (Component* const uiComp, - FilterInGraph& owner_) - : DocumentWindow (uiComp->getName(), Colours::lightblue, - DocumentWindow::minimiseButton | DocumentWindow::closeButton), - owner (owner_) - { - setSize (400, 300); - - setContentComponent (uiComp, true, true); - - setTopLeftPosition (owner.lastX, owner.lastY); - setVisible (true); - } - - ~PluginWindow() - { - if (owner.activeGenericUI == this) - owner.activeGenericUI = 0; - else - owner.activeUI = 0; +const int FilterGraph::midiChannelNumber = 0x1000; - setContentComponent (0); - } +FilterGraph::FilterGraph() + : FileBasedDocument (filenameSuffix, + filenameWildcard, + "Load a filter graph", + "Save a filter graph"), + lastUID (0) +{ + InternalPluginFormat internalFormat; - void moved() - { - owner.lastX = getX(); - owner.lastY = getY(); - } + addFilter (internalFormat.getDescriptionFor (InternalPluginFormat::audioInputFilter), + 0.5f, 0.1f); - void closeButtonPressed() - { - delete this; - } + addFilter (internalFormat.getDescriptionFor (InternalPluginFormat::midiInputFilter), + 0.25f, 0.1f); -private: - FilterInGraph& owner; -}; + addFilter (internalFormat.getDescriptionFor (InternalPluginFormat::audioOutputFilter), + 0.5f, 0.9f); + setChangedFlag (false); +} -//============================================================================== -FilterInGraph::FilterInGraph (FilterGraph& owner_, AudioPluginInstance* const filter_) - : owner (owner_), - filter (filter_), - uid (0), - processedAudio (1, 1), - activeUI (0), - activeGenericUI (0) +FilterGraph::~FilterGraph() { - lastX = 100 + Random::getSystemRandom().nextInt (400); - lastY = 100 + Random::getSystemRandom().nextInt (400); + graph.clear(); } -FilterInGraph::~FilterInGraph() +uint32 FilterGraph::getNextUID() throw() { - delete activeGenericUI; - delete activeUI; - delete filter; + return ++lastUID; } -void FilterInGraph::setPosition (double newX, double newY) throw() +//============================================================================== +int FilterGraph::getNumFilters() const throw() { - x = jlimit (0.0, 1.0, newX); - y = jlimit (0.0, 1.0, newY); + return graph.getNumNodes(); } -void FilterInGraph::showUI (bool useGenericUI) +const AudioProcessorGraph::Node::Ptr FilterGraph::getNode (const int index) const throw() { - if (! useGenericUI) - { - if (activeUI == 0) - { - Component* ui = filter->createEditorIfNeeded(); - - if (ui == 0) - return showUI (true); - - ui->setName (filter->getName()); - activeUI = new PluginWindow (ui, *this); - } - - if (activeUI != 0) - activeUI->toFront (true); - } - else - { - if (activeGenericUI == 0) - { - Component* ui = new GenericAudioProcessorEditor (filter); - - ui->setName (filter->getName()); - activeGenericUI = new PluginWindow (ui, *this); - } - - if (activeGenericUI != 0) - activeGenericUI->toFront (true); - } + return graph.getNode (index); } -void FilterInGraph::prepareBuffers (int blockSize) +const AudioProcessorGraph::Node::Ptr FilterGraph::getNodeForId (const uint32 uid) const throw() { - processedAudio.setSize (jmax (1, filter->getNumInputChannels(), filter->getNumOutputChannels()), blockSize); - processedAudio.clear(); - - processedMidi.clear(); + return graph.getNodeForId (uid); } -void FilterInGraph::renderBlock (int numSamples, - const ReferenceCountedArray & filters, - const OwnedArray & connections) +void FilterGraph::addFilter (const PluginDescription* desc, double x, double y) { - processedAudio.setSize (jmax (1, filter->getNumInputChannels(), filter->getNumOutputChannels()), numSamples); + if (desc != 0) + { + String errorMessage; - // this isn't particularly efficient - could do with some optimising here - processedAudio.clear(); - processedMidi.clear(); + AudioPluginInstance* instance + = AudioPluginFormatManager::getInstance()->createPluginInstance (*desc, errorMessage); - for (int i = connections.size(); --i >= 0;) - { - const FilterConnection* const fc = connections.getUnchecked(i); + AudioProcessorGraph::Node* node = 0; + + if (instance != 0) + node = graph.addNode (instance); - if (fc->destFilterID == uid) + if (node != 0) + { + node->properties.setValue ("x", x); + node->properties.setValue ("y", y); + changed(); + } + else { - for (int j = filters.size(); --j >= 0;) - { - const FilterInGraph* const input = filters.getUnchecked(j); - - if (filters.getUnchecked(j)->uid == fc->sourceFilterID) - { - if (fc->sourceChannel == FilterGraph::midiChannelNumber) - { - processedMidi.addEvents (input->processedMidi, 0, numSamples, 0); - } - else - { - if (fc->destChannel < filter->getNumInputChannels() - && fc->sourceChannel < input->filter->getNumOutputChannels()) - { - processedAudio.addFrom (fc->destChannel, 0, input->processedAudio, - fc->sourceChannel, 0, numSamples); - } - } - - break; - } - } + AlertWindow::showMessageBox (AlertWindow::WarningIcon, + TRANS("Couldn't create filter"), + errorMessage); } } - - filter->processBlock (processedAudio, processedMidi); } -XmlElement* FilterInGraph::createXml() const +void FilterGraph::removeFilter (const uint32 id) { - XmlElement* e = new XmlElement ("FILTER"); - e->setAttribute (T("uid"), (int) uid); - e->setAttribute (T("x"), x); - e->setAttribute (T("y"), y); - e->setAttribute (T("uiLastX"), lastX); - e->setAttribute (T("uiLastY"), lastY); - - PluginDescription pd; - pd.fillInFromInstance (*filter); - e->addChildElement (pd.createXml()); - - XmlElement* state = new XmlElement ("STATE"); - - MemoryBlock m; - filter->getStateInformation (m); - state->addTextElement (m.toBase64Encoding()); - e->addChildElement (state); + PluginWindow::closeCurrentlyOpenWindowsFor (id); - return e; + if (graph.removeNode (id)) + changed(); } -FilterInGraph* FilterInGraph::createForDescription (FilterGraph& owner, - const PluginDescription& desc, - String& errorMessage) +void FilterGraph::disconnectFilter (const uint32 id) { - AudioPluginInstance* instance = desc.createInstance (errorMessage); - - if (instance != 0) - return new FilterInGraph (owner, instance); + if (graph.disconnectNode (id)) + changed(); +} - return 0; +void FilterGraph::removeIllegalConnections() +{ + if (graph.removeIllegalConnections()) + changed(); } -FilterInGraph* FilterInGraph::createFromXml (FilterGraph& owner, const XmlElement& xml) +void FilterGraph::setNodePosition (const int nodeId, double x, double y) { - PluginDescription pd; + const AudioProcessorGraph::Node::Ptr n (graph.getNodeForId (nodeId)); - forEachXmlChildElement (xml, e) + if (n != 0) { - if (pd.loadFromXml (*e)) - break; + n->properties.setValue ("x", jlimit (0.0, 1.0, x)); + n->properties.setValue ("y", jlimit (0.0, 1.0, y)); } +} - String errorMessage; - FilterInGraph* const c = createForDescription (owner, pd, errorMessage); - - if (c == 0) - return 0; +void FilterGraph::getNodePosition (const int nodeId, double& x, double& y) const +{ + x = y = 0; - const XmlElement* const state = xml.getChildByName (T("STATE")); + const AudioProcessorGraph::Node::Ptr n (graph.getNodeForId (nodeId)); - if (state != 0) + if (n != 0) { - MemoryBlock m; - m.fromBase64Encoding (state->getAllSubText()); - - c->filter->setStateInformation (m.getData(), m.getSize()); + x = n->properties.getDoubleValue ("x"); + y = n->properties.getDoubleValue ("y"); } +} - c->uid = xml.getIntAttribute (T("uid")); - c->x = xml.getDoubleAttribute (T("x")); - c->y = xml.getDoubleAttribute (T("y")); - c->lastX = xml.getIntAttribute (T("uiLastX"), c->lastX); - c->lastY = xml.getIntAttribute (T("uiLastY"), c->lastY); - - return c; +//============================================================================== +int FilterGraph::getNumConnections() const throw() +{ + return graph.getNumConnections(); } +const AudioProcessorGraph::Connection* FilterGraph::getConnection (const int index) const throw() +{ + return graph.getConnection (index); +} -//============================================================================== -const int FilterGraph::midiChannelNumber = 0x1000; +const AudioProcessorGraph::Connection* FilterGraph::getConnectionBetween (uint32 sourceFilterUID, int sourceFilterChannel, + uint32 destFilterUID, int destFilterChannel) const throw() +{ + return graph.getConnectionBetween (sourceFilterUID, sourceFilterChannel, + destFilterUID, destFilterChannel); +} -FilterGraph::FilterGraph() - : FileBasedDocument (filenameSuffix, - filenameWildcard, - "Load a filter graph", - "Save a filter graph"), - lastUID (0) +bool FilterGraph::canConnect (uint32 sourceFilterUID, int sourceFilterChannel, + uint32 destFilterUID, int destFilterChannel) const throw() { - InternalPluginFormat internalFormat; + return graph.canConnect (sourceFilterUID, sourceFilterChannel, + destFilterUID, destFilterChannel); +} - addFilter (internalFormat.getDescriptionFor (InternalPluginFormat::audioInputFilter), - 0.5f, 0.1f); +bool FilterGraph::addConnection (uint32 sourceFilterUID, int sourceFilterChannel, + uint32 destFilterUID, int destFilterChannel) +{ + const bool result = graph.addConnection (sourceFilterUID, sourceFilterChannel, + destFilterUID, destFilterChannel); - addFilter (internalFormat.getDescriptionFor (InternalPluginFormat::midiInputFilter), - 0.25f, 0.1f); + if (result) + changed(); - addFilter (internalFormat.getDescriptionFor (InternalPluginFormat::audioOutputFilter), - 0.5f, 0.9f); + return result; +} - setChangedFlag (false); +void FilterGraph::removeConnection (const int index) +{ + graph.removeConnection (index); + changed(); } -FilterGraph::~FilterGraph() +void FilterGraph::removeConnection (uint32 sourceFilterUID, int sourceFilterChannel, + uint32 destFilterUID, int destFilterChannel) { - clear(); + if (graph.removeConnection (sourceFilterUID, sourceFilterChannel, + destFilterUID, destFilterChannel)) + changed(); } -uint32 FilterGraph::getNextUID() throw() +void FilterGraph::clear() { - return ++lastUID; + PluginWindow::closeAllCurrentlyOpenWindows(); + + graph.clear(); + changed(); } //============================================================================== @@ -376,224 +298,78 @@ void FilterGraph::setLastDocumentOpened (const File& file) } //============================================================================== -FilterInGraph* FilterGraph::getFilterForUID (const uint32 uid) const throw() -{ - for (int i = filters.size(); --i >= 0;) - if (filters.getUnchecked(i)->uid == uid) - return filters.getUnchecked(i); - - return 0; -} - -void FilterGraph::addFilter (const FilterInGraph::Ptr& newFilter) +static XmlElement* createNodeXml (AudioProcessorGraph::Node* const node) throw() { - if (newFilter->uid == 0) - newFilter->uid = getNextUID(); - else if (newFilter->uid > lastUID) - lastUID = newFilter->uid; + AudioPluginInstance* plugin = dynamic_cast (node->processor); - filters.add (newFilter); - - changed(); -} - -void FilterGraph::addFilter (const PluginDescription* desc, double x, double y) -{ - if (desc != 0) + if (plugin == 0) { - String errorMessage; - FilterInGraph* cf = FilterInGraph::createForDescription (*this, *desc, errorMessage); - - if (cf != 0) - { - cf->setPosition (x, y); - addFilter (cf); - } - else - { - AlertWindow::showMessageBox (AlertWindow::WarningIcon, - TRANS("Couldn't create filter"), - errorMessage); - } - } -} - -void FilterGraph::removeFilter (const uint32 uid) -{ - disconnectFilter (uid); - - for (int i = filters.size(); --i >= 0;) - { - if (filters.getUnchecked(i)->uid == uid) - { - filters.remove (i); - changed(); - } + jassertfalse + return 0; } -} - -void FilterGraph::disconnectFilter (const uint32 uid) -{ - for (int i = connections.size(); --i >= 0;) - { - const FilterConnection* const fc = connections.getUnchecked(i); - if (fc->sourceFilterID == uid - || fc->destFilterID == uid) - { - removeConnection (i); - } - } -} + XmlElement* e = new XmlElement ("FILTER"); + e->setAttribute (T("uid"), (int) node->id); + e->setAttribute (T("x"), node->properties.getDoubleValue("x")); + e->setAttribute (T("y"), node->properties.getDoubleValue("y")); + e->setAttribute (T("uiLastX"), node->properties.getIntValue("uiLastX")); + e->setAttribute (T("uiLastY"), node->properties.getIntValue("uiLastY")); -bool FilterGraph::isAnInputTo (const uint32 possibleInput, const uint32 possibleDestination, int recursionCheck) const throw() -{ - if (recursionCheck > 0) - { - for (int i = 0; i < connections.size(); ++i) - { - if (connections.getUnchecked(i)->destFilterID == possibleDestination - && (connections.getUnchecked(i)->sourceFilterID == possibleInput - || isAnInputTo (possibleInput, connections.getUnchecked(i)->sourceFilterID, recursionCheck - 1))) - return true; - } - } + PluginDescription pd; - return false; -} + plugin->fillInPluginDescription (pd); -bool FilterGraph::isAnInputTo (const uint32 possibleInput, const uint32 possibleDestination) const throw() -{ - return isAnInputTo (possibleInput, possibleDestination, filters.size() + 1); -} + e->addChildElement (pd.createXml()); -const FilterConnection* FilterGraph::getConnectionBetween (uint32 sourceFilterUID, int sourceFilterChannel, - uint32 destFilterUID, int destFilterChannel) const throw() -{ - for (int i = connections.size(); --i >= 0;) - { - FilterConnection* const fc = connections.getUnchecked(i); + XmlElement* state = new XmlElement ("STATE"); - if (fc->sourceFilterID == sourceFilterUID - && fc->sourceChannel == sourceFilterChannel - && fc->destFilterID == destFilterUID - && fc->destChannel == destFilterChannel) - { - return fc; - } - } + MemoryBlock m; + node->processor->getStateInformation (m); + state->addTextElement (m.toBase64Encoding()); + e->addChildElement (state); - return 0; + return e; } -bool FilterGraph::canConnect (uint32 sourceFilterUID, int sourceFilterChannel, - uint32 destFilterUID, int destFilterChannel) const throw() +void FilterGraph::createNodeFromXml (const XmlElement& xml) { - if (sourceFilterChannel < 0 - || destFilterChannel < 0 - || sourceFilterUID == destFilterUID - || (destFilterChannel == midiChannelNumber) != (sourceFilterChannel == midiChannelNumber)) - return false; - - const FilterInGraph* const source = getFilterForUID (sourceFilterUID); - - if (source == 0 - || (sourceFilterChannel != midiChannelNumber && (sourceFilterChannel < 0 || sourceFilterChannel >= source->filter->getNumOutputChannels())) - || (sourceFilterChannel == midiChannelNumber && ! source->filter->producesMidi())) - return false; - - const FilterInGraph* const dest = getFilterForUID (destFilterUID); - - if (dest == 0 - || (destFilterChannel != midiChannelNumber && (destFilterChannel < 0 || destFilterChannel >= dest->filter->getNumInputChannels())) - || (destFilterChannel == midiChannelNumber && ! dest->filter->acceptsMidi())) - return false; - - if (getConnectionBetween (sourceFilterUID, sourceFilterChannel, destFilterUID, destFilterChannel) != 0) - return false; - - return true; -} + PluginDescription pd; -bool FilterGraph::addConnection (uint32 sourceFilterUID, int sourceChannel, - uint32 destFilterUID, int destChannel) -{ - if (canConnect (sourceFilterUID, sourceChannel, destFilterUID, destChannel)) + forEachXmlChildElement (xml, e) { - FilterConnection* const conn = new FilterConnection (*this); - - conn->sourceFilterID = sourceFilterUID; - conn->sourceChannel = sourceChannel; - conn->destFilterID = destFilterUID; - conn->destChannel = destChannel; - - connections.add (conn); - changed(); - - return true; + if (pd.loadFromXml (*e)) + break; } - return false; -} + String errorMessage; -void FilterGraph::removeConnection (const int index) -{ - if (connections [index] != 0) - { - connections.remove (index); - changed(); - } -} + AudioPluginInstance* instance + = AudioPluginFormatManager::getInstance()->createPluginInstance (pd, errorMessage); -void FilterGraph::removeIllegalConnections() -{ - for (int i = connections.size(); --i >= 0;) + if (instance == 0) { - const FilterConnection* const fc = connections.getUnchecked(i); - - bool ok = true; - const FilterInGraph* const source = getFilterForUID (fc->sourceFilterID); - - if (source == 0 - || (fc->sourceChannel != midiChannelNumber && (fc->sourceChannel < 0 || fc->sourceChannel >= source->filter->getNumOutputChannels())) - || (fc->sourceChannel == midiChannelNumber && ! source->filter->producesMidi())) - ok = false; - - const FilterInGraph* const dest = getFilterForUID (fc->destFilterID); + // xxx handle ins + outs + } - if (dest == 0 - || (fc->destChannel != midiChannelNumber && (fc->destChannel < 0 || fc->destChannel >= dest->filter->getNumInputChannels())) - || (fc->destChannel == midiChannelNumber && ! dest->filter->acceptsMidi())) - ok = false; + if (instance == 0) + return; + AudioProcessorGraph::Node::Ptr node (graph.addNode (instance, xml.getIntAttribute (T("uid")))); - if (! ok) - removeConnection (i); - } -} + const XmlElement* const state = xml.getChildByName (T("STATE")); -void FilterGraph::removeConnection (uint32 sourceFilterUID, int sourceFilterChannel, - uint32 destFilterUID, int destFilterChannel) -{ - for (int i = connections.size(); --i >= 0;) + if (state != 0) { - const FilterConnection* const fc = connections.getUnchecked(i); + MemoryBlock m; + m.fromBase64Encoding (state->getAllSubText()); - if (fc->sourceFilterID == sourceFilterUID - && fc->sourceChannel == sourceFilterChannel - && fc->destFilterID == destFilterUID - && fc->destChannel == destFilterChannel) - { - removeConnection (i); - } + node->processor->setStateInformation (m.getData(), m.getSize()); } -} -void FilterGraph::clear() -{ - connections.clear(); - filters.clear(); - changed(); + node->properties.setValue ("x", xml.getDoubleAttribute (T("x"))); + node->properties.setValue ("y", xml.getDoubleAttribute (T("y"))); + node->properties.setValue ("uiLastX", xml.getIntAttribute (T("uiLastX"))); + node->properties.setValue ("uiLastY", xml.getIntAttribute (T("uiLastY"))); } XmlElement* FilterGraph::createXml() const @@ -601,19 +377,21 @@ XmlElement* FilterGraph::createXml() const XmlElement* xml = new XmlElement ("FILTERGRAPH"); int i; - for (i = 0; i < filters.size(); ++i) - xml->addChildElement (filters.getUnchecked(i)->createXml()); + for (i = 0; i < graph.getNumNodes(); ++i) + { + xml->addChildElement (createNodeXml (graph.getNode (i))); + } - for (i = 0; i < connections.size(); ++i) + for (i = 0; i < graph.getNumConnections(); ++i) { - const FilterConnection* const fc = connections.getUnchecked(i); + const AudioProcessorGraph::Connection* const fc = graph.getConnection(i); XmlElement* e = new XmlElement ("CONNECTION"); - e->setAttribute (T("srcFilter"), (int) fc->sourceFilterID); - e->setAttribute (T("srcChannel"), fc->sourceChannel); - e->setAttribute (T("dstFilter"), (int) fc->destFilterID); - e->setAttribute (T("dstChannel"), fc->destChannel); + e->setAttribute (T("srcFilter"), (int) fc->sourceNodeId); + e->setAttribute (T("srcChannel"), fc->sourceChannelIndex); + e->setAttribute (T("dstFilter"), (int) fc->destNodeId); + e->setAttribute (T("dstChannel"), fc->destChannelIndex); xml->addChildElement (e); } @@ -627,10 +405,8 @@ void FilterGraph::restoreFromXml (const XmlElement& xml) forEachXmlChildElementWithTagName (xml, e, T("FILTER")) { - FilterInGraph* f = FilterInGraph::createFromXml (*this, *e); - - if (f != 0) - addFilter (f); + createNodeFromXml (*e); + changed(); } forEachXmlChildElementWithTagName (xml, e, T("CONNECTION")) @@ -641,171 +417,5 @@ void FilterGraph::restoreFromXml (const XmlElement& xml) e->getIntAttribute (T("dstChannel"))); } - removeIllegalConnections(); -} - -//============================================================================== -FilterGraphPlayer::FilterGraphPlayer (FilterGraph& graph_) - : graph (graph_), - sampleRate (44100.0), - blockSize (512), - deviceManager (0), - inputChannelData (0), - totalNumInputChannels (0), - outputChannelData (0), - totalNumOutputChannels (0) -{ - setAudioDeviceManager (0); - keyState.addListener (&messageCollector); - graph.addChangeListener (this); -} - -FilterGraphPlayer::~FilterGraphPlayer() -{ - graph.removeChangeListener (this); - keyState.removeListener (&messageCollector); -} - -void FilterGraphPlayer::setAudioDeviceManager (AudioDeviceManager* dm) -{ - if (deviceManager != 0) - { - deviceManager->removeMidiInputCallback (this); - deviceManager->setAudioCallback (0); - } - - deviceManager = dm; - - if (dm != 0) - { - dm->addMidiInputCallback (String::empty, this); - dm->setAudioCallback (this); - } -} - -int FilterGraphPlayer::compareElements (FilterInGraph* const first, FilterInGraph* const second) throw() -{ - const bool firstIsInputToSecond = first->owner.isAnInputTo (first->uid, second->uid); - const bool secondInInputToFirst = first->owner.isAnInputTo (second->uid, first->uid); - - if (firstIsInputToSecond == secondInInputToFirst) - return 0; - - return firstIsInputToSecond ? -1 : 1; -} - -void FilterGraphPlayer::update() -{ - ReferenceCountedArray filtersBeingRemoved (filters); - - ReferenceCountedArray newFilters (graph.filters); - int i; - for (i = newFilters.size(); --i >= 0;) - if (filters.contains (newFilters.getUnchecked(i))) - newFilters.remove (i); - - for (i = filtersBeingRemoved.size(); --i >= 0;) - if (graph.filters.contains (filtersBeingRemoved.getUnchecked(i))) - filtersBeingRemoved.remove (i); - - // prepare any new filters for use.. - for (i = 0; i < newFilters.size(); ++i) - newFilters.getUnchecked(i)->filter->prepareToPlay (sampleRate, blockSize); - - ReferenceCountedArray sortedFilters (graph.filters); - sortedFilters.sort (*this, true); - - for (i = sortedFilters.size(); --i >= 0;) - { - PlayerAwareFilter* const specialFilter = dynamic_cast (sortedFilters.getUnchecked(i)->filter); - - if (specialFilter != 0) - specialFilter->setPlayer (this); - } - - { - const ScopedLock sl (processLock); - - filters = sortedFilters; - connections.clear(); - - for (int i = 0; i < graph.connections.size(); ++i) - connections.add (new FilterConnection (*graph.connections.getUnchecked(i))); - } - - // release any old ones.. - for (int i = 0; i < filtersBeingRemoved.size(); ++i) - { - filtersBeingRemoved.getUnchecked(i)->filter->releaseResources(); - - PlayerAwareFilter* const specialFilter = dynamic_cast (filtersBeingRemoved.getUnchecked(i)->filter); - - if (specialFilter != 0) - specialFilter->setPlayer (0); - } -} - -void FilterGraphPlayer::changeListenerCallback (void*) -{ - update(); -} - -void FilterGraphPlayer::audioDeviceIOCallback (const float** inputChannelData_, - int totalNumInputChannels_, - float** outputChannelData_, - int totalNumOutputChannels_, - int numSamples) -{ - incomingMidi.clear(); - messageCollector.removeNextBlockOfMessages (incomingMidi, numSamples); - - for (int i = 0; i < totalNumOutputChannels_; ++i) - if (outputChannelData_[i] != 0) - zeromem (outputChannelData_[i], sizeof (float) * numSamples); - - const ScopedLock sl (processLock); - - inputChannelData = inputChannelData_; - totalNumInputChannels = totalNumInputChannels_; - outputChannelData = outputChannelData_; - totalNumOutputChannels = totalNumOutputChannels_; - - for (int i = 0; i < filters.size(); ++i) - { - FilterInGraph* const cf = filters.getUnchecked(i); - - cf->renderBlock (numSamples, filters, connections); - } -} - -void FilterGraphPlayer::audioDeviceAboutToStart (double sampleRate_, int numSamplesPerBlock) -{ - const ScopedLock sl (processLock); - - sampleRate = sampleRate_; - blockSize = numSamplesPerBlock; - - messageCollector.reset (sampleRate_); - - for (int i = 0; i < filters.size(); ++i) - { - filters.getUnchecked(i)->filter->prepareToPlay (sampleRate, blockSize); - filters.getUnchecked(i)->prepareBuffers (blockSize); - } - - graph.sendChangeMessage (&graph); -} - -void FilterGraphPlayer::audioDeviceStopped() -{ - const ScopedLock sl (processLock); - - for (int i = 0; i < filters.size(); ++i) - filters.getUnchecked(i)->filter->releaseResources(); -} - -void FilterGraphPlayer::handleIncomingMidiMessage (MidiInput*, const MidiMessage& message) -{ -// keyState.processNextMidiEvent (message); - messageCollector.addMessageToQueue (message); + graph.removeIllegalConnections(); } diff --git a/extras/audio plugin host/src/host/FilterGraph.h b/extras/audio plugin host/src/host/FilterGraph.h index 58c45f7b18..3b8e05b3d1 100644 --- a/extras/audio plugin host/src/host/FilterGraph.h +++ b/extras/audio plugin host/src/host/FilterGraph.h @@ -32,12 +32,8 @@ #ifndef __JUCE_FILTERGRAPH_JUCEHEADER__ #define __JUCE_FILTERGRAPH_JUCEHEADER__ -#include "../plugins/juce_AudioPluginInstance.h" -#include "../plugins/juce_PluginDescription.h" - class FilterInGraph; class FilterGraph; -class FilterGraphPlayer; const char* const filenameSuffix = ".filtergraph"; const char* const filenameWildcard = "*.filtergraph"; @@ -75,7 +71,7 @@ private: /** Represents one of the filters in a FilterGraph. */ -class FilterInGraph : public ReferenceCountedObject +/*class FilterInGraph : public ReferenceCountedObject { public: //============================================================================== @@ -129,7 +125,7 @@ private: FilterInGraph (const FilterInGraph&); const FilterInGraph& operator= (const FilterInGraph&); }; - +*/ //============================================================================== /** @@ -143,11 +139,12 @@ public: ~FilterGraph(); //============================================================================== - int getNumFilters() const throw() { return filters.size(); } - FilterInGraph* getFilter (const int index) const throw() { return filters [index]; } - FilterInGraph* getFilterForUID (const uint32 uid) const throw(); + AudioProcessorGraph& getGraph() throw() { return graph; } + + int getNumFilters() const throw(); + const AudioProcessorGraph::Node::Ptr getNode (const int index) const throw(); + const AudioProcessorGraph::Node::Ptr getNodeForId (const uint32 uid) const throw(); - void addFilter (const FilterInGraph::Ptr& newFilter); void addFilter (const PluginDescription* desc, double x, double y); void removeFilter (const uint32 filterUID); @@ -155,14 +152,15 @@ public: void removeIllegalConnections(); - //============================================================================== - int getNumConnections() const throw() { return connections.size(); } - const FilterConnection* getConnection (const int index) const throw() { return connections [index]; } + void setNodePosition (const int nodeId, double x, double y); + void getNodePosition (const int nodeId, double& x, double& y) const; - const FilterConnection* getConnectionBetween (uint32 sourceFilterUID, int sourceFilterChannel, - uint32 destFilterUID, int destFilterChannel) const throw(); + //============================================================================== + int getNumConnections() const throw(); + const AudioProcessorGraph::Connection* getConnection (const int index) const throw(); - bool isAnInputTo (const uint32 possibleInput, const uint32 possibleDestination) const throw(); + const AudioProcessorGraph::Connection* getConnectionBetween (uint32 sourceFilterUID, int sourceFilterChannel, + uint32 destFilterUID, int destFilterChannel) const throw(); bool canConnect (uint32 sourceFilterUID, int sourceFilterChannel, uint32 destFilterUID, int destFilterChannel) const throw(); @@ -177,6 +175,9 @@ public: void clear(); + + //============================================================================== + XmlElement* createXml() const; void restoreFromXml (const XmlElement& xml); @@ -195,14 +196,17 @@ public: juce_UseDebuggingNewOperator private: - friend class FilterGraphPlayer; - ReferenceCountedArray filters; - OwnedArray connections; + //friend class FilterGraphPlayer; + //ReferenceCountedArray filters; + //OwnedArray connections; + + AudioProcessorGraph graph; + AudioProcessorPlayer player; uint32 lastUID; uint32 getNextUID() throw(); - bool isAnInputTo (const uint32 possibleInput, const uint32 possibleDestination, int recursionCheck) const throw(); + void createNodeFromXml (const XmlElement& xml); FilterGraph (const FilterGraph&); const FilterGraph& operator= (const FilterGraph&); @@ -213,7 +217,7 @@ private: /** */ -class FilterGraphPlayer : public AudioIODeviceCallback, +/*class FilterGraphPlayer : public AudioIODeviceCallback, public MidiInputCallback, public ChangeListener @@ -274,6 +278,6 @@ private: FilterGraphPlayer (const FilterGraphPlayer&); const FilterGraphPlayer& operator= (const FilterGraphPlayer&); }; - +*/ #endif diff --git a/extras/audio plugin host/src/host/GraphEditorPanel.cpp b/extras/audio plugin host/src/host/GraphEditorPanel.cpp index bd18de39ee..654e5d068d 100644 --- a/extras/audio plugin host/src/host/GraphEditorPanel.cpp +++ b/extras/audio plugin host/src/host/GraphEditorPanel.cpp @@ -35,6 +35,95 @@ #include "MainHostWindow.h" +//============================================================================== +class PluginWindow; +static Array activePluginWindows; + +PluginWindow::PluginWindow (Component* const uiComp, + AudioProcessorGraph::Node* owner_, + const bool isGeneric_) + : DocumentWindow (uiComp->getName(), Colours::lightblue, + DocumentWindow::minimiseButton | DocumentWindow::closeButton), + owner (owner_), + isGeneric (isGeneric_) +{ + setSize (400, 300); + + setContentComponent (uiComp, true, true); + + setTopLeftPosition (owner->properties.getIntValue ("uiLastX", Random::getSystemRandom().nextInt (500)), + owner->properties.getIntValue ("uiLastY", Random::getSystemRandom().nextInt (500))); + setVisible (true); + + activePluginWindows.add (this); +} + +void PluginWindow::closeCurrentlyOpenWindowsFor (const uint32 nodeId) +{ + for (int i = activePluginWindows.size(); --i >= 0;) + if (activePluginWindows.getUnchecked(i)->owner->id == nodeId) + delete activePluginWindows.getUnchecked(i); +} + +void PluginWindow::closeAllCurrentlyOpenWindows() +{ + for (int i = activePluginWindows.size(); --i >= 0;) + delete activePluginWindows.getUnchecked(i); +} + +PluginWindow* PluginWindow::getWindowFor (AudioProcessorGraph::Node* node, + bool useGenericView) +{ + for (int i = activePluginWindows.size(); --i >= 0;) + if (activePluginWindows.getUnchecked(i)->owner == node + && activePluginWindows.getUnchecked(i)->isGeneric == useGenericView) + return activePluginWindows.getUnchecked(i); + + AudioProcessorEditor* ui = 0; + + if (! useGenericView) + { + ui = node->processor->createEditorIfNeeded(); + + if (ui == 0) + useGenericView = true; + } + + if (useGenericView) + { + ui = new GenericAudioProcessorEditor (node->processor); + } + + if (ui != 0) + { + AudioPluginInstance* const plugin = dynamic_cast (node->processor); + + if (plugin != 0) + ui->setName (plugin->getName()); + + return new PluginWindow (ui, node, useGenericView); + } + + return 0; +} + +PluginWindow::~PluginWindow() +{ + activePluginWindows.removeValue (this); + setContentComponent (0); +} + +void PluginWindow::moved() +{ + owner->properties.setValue ("uiLastX", getX()); + owner->properties.setValue ("uiLastY", getY()); +} + +void PluginWindow::closeButtonPressed() +{ + delete this; +} + //============================================================================== class PinComponent : public Component, public SettableTooltipClient @@ -47,16 +136,16 @@ public: index (index_), isInput (isInput_) { - const FilterInGraph* const f = graph.getFilterForUID (filterID_); + const AudioProcessorGraph::Node::Ptr node (graph.getNodeForId (filterID_)); - if (f != 0) + if (node != 0) { String tip; if (isInput) - tip = f->filter->getInputChannelName (index_); + tip = node->processor->getInputChannelName (index_); else - tip = f->filter->getOutputChannelName (index_); + tip = node->processor->getOutputChannelName (index_); if (tip.isEmpty()) { @@ -185,10 +274,15 @@ public: } else if (r == 3 || r == 4) { - FilterInGraph* const f = graph.getFilterForUID (filterID); + AudioProcessorGraph::Node::Ptr f (graph.getNodeForId (filterID)); if (f != 0) - f->showUI (r == 4); + { + PluginWindow* const w = PluginWindow::getWindowFor (f, r == 4); + + if (w != 0) + w->toFront (true); + } } } } @@ -203,15 +297,11 @@ public: if (getParentComponent() != 0) getParentComponent()->globalPositionToRelative (x, y); - const FilterInGraph::Ptr f (graph.getFilterForUID (filterID)); - - if (f != 0) - { - f->setPosition ((x + getWidth() / 2) / (double) getParentWidth(), - (y + getHeight() / 2) / (double) getParentHeight()); + graph.setNodePosition (filterID, + (x + getWidth() / 2) / (double) getParentWidth(), + (y + getHeight() / 2) / (double) getParentHeight()); - getGraphPanel()->updateComponents(); - } + getGraphPanel()->updateComponents(); } } @@ -219,10 +309,15 @@ public: { if (e.mouseWasClicked() && e.getNumberOfClicks() == 2) { - const FilterInGraph::Ptr f (graph.getFilterForUID (filterID)); + const AudioProcessorGraph::Node::Ptr f (graph.getNodeForId (filterID)); if (f != 0) - f->showUI (false); + { + PluginWindow* const w = PluginWindow::getWindowFor (f, false); + + if (w != 0) + w->toFront (true); + } } else if (! e.mouseWasClicked()) { @@ -295,7 +390,7 @@ public: void update() { - const FilterInGraph* const f = graph.getFilterForUID (filterID); + const AudioProcessorGraph::Node::Ptr f (graph.getNodeForId (filterID)); if (f == 0) { @@ -303,12 +398,12 @@ public: return; } - numIns = f->filter->getNumInputChannels(); - if (f->filter->acceptsMidi()) + numIns = f->processor->getNumInputChannels(); + if (f->processor->acceptsMidi()) ++numIns; - numOuts = f->filter->getNumOutputChannels(); - if (f->filter->producesMidi()) + numOuts = f->processor->getNumOutputChannels(); + if (f->processor->producesMidi()) ++numOuts; int w = 100; @@ -316,14 +411,20 @@ public: w = jmax (w, (jmax (numIns, numOuts) + 1) * 20); - const int textWidth = font.getStringWidth (f->filter->getName()); + const int textWidth = font.getStringWidth (f->processor->getName()); w = jmax (w, 16 + jmin (textWidth, 300)); if (textWidth > 300) h = 100; setSize (w, h); - setCentreRelative ((float) f->getX(), (float) f->getY()); + setName (f->processor->getName()); + + { + double x, y; + graph.getNodePosition (filterID, x, y); + setCentreRelative ((float) x, (float) y); + } if (numIns != numInputs || numOuts != numOutputs) { @@ -333,20 +434,18 @@ public: deleteAllChildren(); int i; - for (i = 0; i < f->filter->getNumInputChannels(); ++i) + for (i = 0; i < f->processor->getNumInputChannels(); ++i) addAndMakeVisible (new PinComponent (graph, filterID, i, true)); - if (f->filter->acceptsMidi()) + if (f->processor->acceptsMidi()) addAndMakeVisible (new PinComponent (graph, filterID, FilterGraph::midiChannelNumber, true)); - for (i = 0; i < f->filter->getNumOutputChannels(); ++i) + for (i = 0; i < f->processor->getNumOutputChannels(); ++i) addAndMakeVisible (new PinComponent (graph, filterID, i, false)); - if (f->filter->producesMidi()) + if (f->processor->producesMidi()) addAndMakeVisible (new PinComponent (graph, filterID, FilterGraph::midiChannelNumber, false)); - setName (f->filter->getName()); - resized(); } } @@ -679,17 +778,17 @@ FilterComponent* GraphEditorPanel::getComponentForFilter (const uint32 filterID) return 0; } -ConnectorComponent* GraphEditorPanel::getComponentForConnection (const FilterConnection& conn) const +ConnectorComponent* GraphEditorPanel::getComponentForConnection (const AudioProcessorGraph::Connection& conn) const { for (int i = getNumChildComponents(); --i >= 0;) { ConnectorComponent* const c = dynamic_cast (getChildComponent (i)); if (c != 0 - && c->sourceFilterID == conn.sourceFilterID - && c->destFilterID == conn.destFilterID - && c->sourceFilterChannel == conn.sourceChannel - && c->destFilterChannel == conn.destChannel) + && c->sourceFilterID == conn.sourceNodeId + && c->destFilterID == conn.destNodeId + && c->sourceFilterChannel == conn.sourceChannelIndex + && c->destFilterChannel == conn.destChannelIndex) { return c; } @@ -759,11 +858,11 @@ void GraphEditorPanel::updateComponents() for (i = graph.getNumFilters(); --i >= 0;) { - const FilterInGraph* const f = graph.getFilter (i); + const AudioProcessorGraph::Node::Ptr f (graph.getNode (i)); - if (getComponentForFilter (f->uid) == 0) + if (getComponentForFilter (f->id) == 0) { - FilterComponent* const comp = new FilterComponent (graph, f->uid); + FilterComponent* const comp = new FilterComponent (graph, f->id); addAndMakeVisible (comp); comp->update(); } @@ -771,15 +870,15 @@ void GraphEditorPanel::updateComponents() for (i = graph.getNumConnections(); --i >= 0;) { - const FilterConnection* const c = graph.getConnection (i); + const AudioProcessorGraph::Connection* const c = graph.getConnection (i); if (getComponentForConnection (*c) == 0) { ConnectorComponent* const comp = new ConnectorComponent (graph); addAndMakeVisible (comp); - comp->setInput (c->sourceFilterID, c->sourceChannel); - comp->setOutput (c->destFilterID, c->destChannel); + comp->setInput (c->sourceNodeId, c->sourceChannelIndex); + comp->setOutput (c->destNodeId, c->destChannelIndex); } } } @@ -942,24 +1041,27 @@ GraphDocumentComponent::GraphDocumentComponent (AudioDeviceManager* deviceManage { addAndMakeVisible (graphPanel = new GraphEditorPanel (graph)); - graphPlayer = new FilterGraphPlayer (graph); + graphPlayer.setProcessor (&graph.getGraph()); + + keyState.addListener (&graphPlayer.getMidiMessageCollector()); - addAndMakeVisible (keyboardComp = new MidiKeyboardComponent (graphPlayer->keyState, + addAndMakeVisible (keyboardComp = new MidiKeyboardComponent (keyState, MidiKeyboardComponent::horizontalKeyboard)); addAndMakeVisible (statusBar = new TooltipBar()); - graphPlayer->setAudioDeviceManager (deviceManager); + deviceManager->setAudioCallback (&graphPlayer); graphPanel->updateComponents(); } GraphDocumentComponent::~GraphDocumentComponent() { + deviceManager->setAudioCallback (0); deleteAllChildren(); - graphPlayer->setAudioDeviceManager (0); - deleteAndZero (graphPlayer); + graphPlayer.setProcessor (0); + keyState.removeListener (&graphPlayer.getMidiMessageCollector()); graph.clear(); } diff --git a/extras/audio plugin host/src/host/GraphEditorPanel.h b/extras/audio plugin host/src/host/GraphEditorPanel.h index 14c85da1ae..9b43bb3df0 100644 --- a/extras/audio plugin host/src/host/GraphEditorPanel.h +++ b/extras/audio plugin host/src/host/GraphEditorPanel.h @@ -32,7 +32,6 @@ #ifndef __JUCE_FILTERGRAPHEDITOR_JUCEHEADER__ #define __JUCE_FILTERGRAPHEDITOR_JUCEHEADER__ -#include "../plugins/juce_KnownPluginList.h" #include "FilterGraph.h" class FilterComponent; @@ -57,7 +56,7 @@ public: void createNewPlugin (const PluginDescription* desc, int x, int y); FilterComponent* getComponentForFilter (const uint32 filterID) const; - ConnectorComponent* getComponentForConnection (const FilterConnection& conn) const; + ConnectorComponent* getComponentForConnection (const AudioProcessorGraph::Connection& conn) const; PinComponent* findPinAt (const int x, const int y) const; void resized(); @@ -110,12 +109,39 @@ public: private: AudioDeviceManager* deviceManager; - FilterGraphPlayer* graphPlayer; + AudioProcessorPlayer graphPlayer; + MidiKeyboardState keyState; GraphEditorPanel* graphPanel; Component* keyboardComp; Component* statusBar; }; +//============================================================================== +/** A desktop window containing a plugin's UI. +*/ +class PluginWindow : public DocumentWindow +{ + PluginWindow (Component* const uiComp, + AudioProcessorGraph::Node* owner_, + const bool isGeneric_); + +public: + static PluginWindow* getWindowFor (AudioProcessorGraph::Node* node, + bool useGenericView); + + static void closeCurrentlyOpenWindowsFor (const uint32 nodeId); + + static void closeAllCurrentlyOpenWindows(); + + ~PluginWindow(); + + void moved(); + void closeButtonPressed(); + +private: + AudioProcessorGraph::Node* owner; + bool isGeneric; +}; #endif diff --git a/extras/audio plugin host/src/host/InternalFilters.cpp b/extras/audio plugin host/src/host/InternalFilters.cpp index 9780fc994b..e51213f4a8 100644 --- a/extras/audio plugin host/src/host/InternalFilters.cpp +++ b/extras/audio plugin host/src/host/InternalFilters.cpp @@ -33,319 +33,22 @@ #include "InternalFilters.h" #include "FilterGraph.h" - -//============================================================================== -class InternalFilterBase : public AudioPluginInstance, - public FilterGraphPlayer::PlayerAwareFilter -{ -public: - InternalFilterBase() - : player (0) - { - } - - ~InternalFilterBase() - { - } - - void setPlayer (FilterGraphPlayer* p) - { - player = p; - playerChanged(); - } - - virtual void playerChanged() {} - - const String getManufacturer() const { return "Raw Material Software"; } - const String getFormatName() const { return "Internal"; } - const File getFile() const { return File::nonexistent; } - int getUID() const { return getName().hashCode(); } - int getSamplesLatency() const { return 0; } - - AudioProcessorEditor* createEditor() { return 0; } - int getNumParameters() { return 0; } - const String getParameterName (int) { return String::empty; } - float getParameter (int) { return 0; } - const String getParameterText (int) { return String::empty; } - void setParameter (int, float) {} - int getNumPrograms() { return 0; } - int getCurrentProgram() { return 0; } - void setCurrentProgram (int) {} - const String getProgramName (int) { return String::empty; } - void changeProgramName (int, const String&) {} - void getStateInformation (MemoryBlock&) {} - void setStateInformation (const void*, int) {} - -protected: - FilterGraphPlayer* player; - - AudioIODevice* getAudioDevice() const - { - if (player != 0) - { - AudioDeviceManager* const dm = player->getAudioDeviceManager(); - - jassert (dm != 0); - if (dm != 0) - return dm->getCurrentAudioDevice(); - } - - return 0; - } -}; - -//============================================================================== -class AudioInputDeviceFilter : public InternalFilterBase -{ -public: - AudioInputDeviceFilter (const int numChannels) - { - setPlayConfigDetails (0, numChannels, getSampleRate(), getBlockSize()); - } - - ~AudioInputDeviceFilter() {} - - const String getName() const { return "Audio Input"; } - const String getVersion() const { return "1.0"; } - bool isInstrument() const { return true; } - const String getCategory() const { return "I/O devices"; } - bool acceptsMidi() const { return false; } - bool producesMidi() const { return false; } - - void playerChanged() - { - AudioIODevice* const dev = getAudioDevice(); - - if (dev != 0) - setPlayConfigDetails (0, dev->getActiveInputChannels().countNumberOfSetBits(), getSampleRate(), getBlockSize()); - } - - void prepareToPlay (double /*sampleRate*/, int /*estimatedSamplesPerBlock*/) - { - playerChanged(); - } - - void releaseResources() - { - } - - void processBlock (AudioSampleBuffer& buffer, MidiBuffer&) - { - int n = 0; - - for (int i = 0; i < player->totalNumInputChannels; ++i) - { - if (n >= getNumOutputChannels()) - break; - - if (player->inputChannelData [i] != 0) - { - buffer.copyFrom (n, 0, player->inputChannelData [i], buffer.getNumSamples()); - ++n; - } - } - } - - const String getInputChannelName (const int channelIndex) const - { - return String (channelIndex + 1); - } - - const String getOutputChannelName (const int channelIndex) const - { - AudioIODevice* const dev = getAudioDevice(); - - if (dev != 0) - return dev->getInputChannelNames() [channelIndex]; - - return "Input Channel " + String (channelIndex + 1); - } - - bool isInputChannelStereoPair (int) const - { - return true; - } - - bool isOutputChannelStereoPair (int) const - { - return true; - } -}; - - -//============================================================================== -class AudioOutputDeviceFilter : public InternalFilterBase -{ -public: - AudioOutputDeviceFilter (const int numChannels) - { - setPlayConfigDetails (numChannels, 0, getSampleRate(), getBlockSize()); - } - - ~AudioOutputDeviceFilter() {} - - const String getName() const { return "Audio Output"; } - const String getVersion() const { return "1.0"; } - bool isInstrument() const { return false; } - const String getCategory() const { return "I/O devices"; } - bool acceptsMidi() const { return false; } - bool producesMidi() const { return false; } - - void playerChanged() - { - AudioIODevice* const dev = getAudioDevice(); - - if (dev != 0) - setPlayConfigDetails (dev->getActiveOutputChannels().countNumberOfSetBits(), 0, getSampleRate(), getBlockSize()); - } - - void prepareToPlay (double /*sampleRate*/, int /*estimatedSamplesPerBlock*/) - { - playerChanged(); - } - - void releaseResources() - { - } - - void processBlock (AudioSampleBuffer& buffer, MidiBuffer&) - { - int n = 0; - - for (int i = 0; i < player->totalNumOutputChannels; ++i) - { - float* const dst = player->outputChannelData [i]; - - if (dst != 0) - { - if (n >= getNumInputChannels()) - { - zeromem (dst, sizeof (float) * buffer.getNumSamples()); - } - else - { - memcpy (dst, buffer.getSampleData (n), - sizeof (float) * buffer.getNumSamples()); - } - - ++n; - } - } - } - - const String getInputChannelName (const int channelIndex) const - { - AudioIODevice* const dev = getAudioDevice(); - - if (dev != 0) - return dev->getOutputChannelNames() [channelIndex]; - - return "Output Channel " + String (channelIndex + 1); - } - - const String getOutputChannelName (const int channelIndex) const - { - return String (channelIndex + 1); - } - - bool isInputChannelStereoPair (int) const - { - return true; - } - - bool isOutputChannelStereoPair (int) const - { - return true; - } -}; - - -//============================================================================== -class MidiInputDeviceFilter : public InternalFilterBase -{ -public: - MidiInputDeviceFilter() - { - } - - ~MidiInputDeviceFilter() {} - - const String getName() const { return "Midi Input"; } - const String getVersion() const { return "1.0"; } - bool isInstrument() const { return true; } - const String getCategory() const { return "I/O devices"; } - bool acceptsMidi() const { return false; } - bool producesMidi() const { return true; } - - void playerChanged() - { - if (player != 0) - { - AudioDeviceManager* const dm = player->getAudioDeviceManager(); - - (void) dm; - jassert (dm != 0); - } - } - - void prepareToPlay (double /*sampleRate*/, int /*estimatedSamplesPerBlock*/) - { - playerChanged(); - } - - void releaseResources() - { - } - - void processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiBuffer) - { - midiBuffer.clear(); - midiBuffer.addEvents (player->incomingMidi, 0, buffer.getNumSamples(), 0); - } - - const String getInputChannelName (const int channelIndex) const - { - return String (channelIndex + 1); - } - - const String getOutputChannelName (const int channelIndex) const - { - AudioIODevice* const dev = getAudioDevice(); - - if (dev != 0) - return dev->getInputChannelNames() [channelIndex]; - - return "Midi Input"; - } - - bool isInputChannelStereoPair (int) const - { - return true; - } - - bool isOutputChannelStereoPair (int) const - { - return true; - } -}; - - //============================================================================== InternalPluginFormat::InternalPluginFormat() { { - AudioInputDeviceFilter f (2); - audioInDesc.fillInFromInstance (f); + AudioProcessorGraph::AudioGraphIOProcessor p (AudioProcessorGraph::AudioGraphIOProcessor::audioOutputNode); + p.fillInPluginDescription (audioOutDesc); } { - AudioOutputDeviceFilter f (2); - audioOutDesc.fillInFromInstance (f); + AudioProcessorGraph::AudioGraphIOProcessor p (AudioProcessorGraph::AudioGraphIOProcessor::audioInputNode); + p.fillInPluginDescription (audioInDesc); } { - MidiInputDeviceFilter f; - midiInDesc.fillInFromInstance (f); + AudioProcessorGraph::AudioGraphIOProcessor p (AudioProcessorGraph::AudioGraphIOProcessor::midiInputNode); + p.fillInPluginDescription (midiInDesc); } } @@ -353,15 +56,15 @@ AudioPluginInstance* InternalPluginFormat::createInstanceFromDescription (const { if (desc.name == audioOutDesc.name) { - return new AudioOutputDeviceFilter (desc.numInputChannels); + return new AudioProcessorGraph::AudioGraphIOProcessor (AudioProcessorGraph::AudioGraphIOProcessor::audioOutputNode); } else if (desc.name == audioInDesc.name) { - return new AudioInputDeviceFilter (desc.numOutputChannels); + return new AudioProcessorGraph::AudioGraphIOProcessor (AudioProcessorGraph::AudioGraphIOProcessor::audioInputNode); } else if (desc.name == midiInDesc.name) { - return new MidiInputDeviceFilter(); + return new AudioProcessorGraph::AudioGraphIOProcessor (AudioProcessorGraph::AudioGraphIOProcessor::midiInputNode); } return 0; diff --git a/extras/audio plugin host/src/host/InternalFilters.h b/extras/audio plugin host/src/host/InternalFilters.h index bb4a4800b5..c853cd5803 100644 --- a/extras/audio plugin host/src/host/InternalFilters.h +++ b/extras/audio plugin host/src/host/InternalFilters.h @@ -33,7 +33,7 @@ #define __JUCE_MAINHOSTWINDOW_JUCEHEADER__xxxxx #include "FilterGraph.h" -#include "../plugins/juce_AudioPluginFormat.h" + //============================================================================== /** diff --git a/extras/audio plugin host/src/host/MainHostWindow.cpp b/extras/audio plugin host/src/host/MainHostWindow.cpp index 49161de786..780c952026 100644 --- a/extras/audio plugin host/src/host/MainHostWindow.cpp +++ b/extras/audio plugin host/src/host/MainHostWindow.cpp @@ -32,7 +32,7 @@ #include "../../../../juce.h" #include "MainHostWindow.h" #include "InternalFilters.h" -#include "../plugins/juce_PluginListComponent.h" + //============================================================================== class PluginListWindow : public DocumentWindow @@ -85,6 +85,13 @@ MainHostWindow::MainHostWindow() : DocumentWindow (JUCEApplication::getInstance()->getApplicationName(), Colours::lightgrey, DocumentWindow::allButtons) { + XmlElement* const savedAudioState = ApplicationProperties::getInstance()->getUserSettings() + ->getXmlValue (T("audioDeviceState")); + + deviceManager.initialise (256, 256, savedAudioState, true); + + delete savedAudioState; + setResizable (true, false); setResizeLimits (500, 400, 10000, 10000); centreWithSize (800, 600); @@ -104,13 +111,6 @@ MainHostWindow::MainHostWindow() InternalPluginFormat internalFormat; internalFormat.getAllTypes (internalTypes); - XmlElement* const savedAudioState = ApplicationProperties::getInstance()->getUserSettings() - ->getXmlValue (T("audioDeviceState")); - - deviceManager.initialise (256, 256, savedAudioState, true); - - delete savedAudioState; - XmlElement* const savedPluginList = ApplicationProperties::getInstance() ->getUserSettings() ->getXmlValue (T("pluginList")); diff --git a/extras/audio plugin host/src/host/MainHostWindow.h b/extras/audio plugin host/src/host/MainHostWindow.h index 7f1cf39497..4beac9362d 100644 --- a/extras/audio plugin host/src/host/MainHostWindow.h +++ b/extras/audio plugin host/src/host/MainHostWindow.h @@ -34,7 +34,6 @@ #include "FilterGraph.h" #include "GraphEditorPanel.h" -#include "../plugins/juce_KnownPluginList.h" //============================================================================== diff --git a/extras/audio plugin host/src/plugins/formats/juce_AudioUnitPluginFormat.cpp b/extras/audio plugin host/src/plugins/formats/juce_AudioUnitPluginFormat.cpp index d8f31ea099..321ba599e0 100644 --- a/extras/audio plugin host/src/plugins/formats/juce_AudioUnitPluginFormat.cpp +++ b/extras/audio plugin host/src/plugins/formats/juce_AudioUnitPluginFormat.cpp @@ -1,1269 +1,1281 @@ -/* - ============================================================================== - - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-7 by Raw Material Software ltd. - - ------------------------------------------------------------------------------ - - JUCE can be redistributed and/or modified under the terms of the - GNU General Public License, as published by the Free Software Foundation; - either version 2 of the License, or (at your option) any later version. - - JUCE is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with JUCE; if not, visit www.gnu.org/licenses or write to the - Free Software Foundation, Inc., 59 Temple Place, Suite 330, - Boston, MA 02111-1307 USA - - ------------------------------------------------------------------------------ - - If you'd like to release a closed-source product which uses JUCE, commercial - licenses are also available: visit www.rawmaterialsoftware.com/juce for - more information. - - ============================================================================== -*/ - -#if ! (defined (LINUX) || defined (_WIN32)) - -#include -#include -#include - -#include "../../../../../juce.h" -#include "juce_AudioUnitPluginFormat.h" -#include "../juce_PluginDescription.h" - -#if JUCE_PLUGINHOST_AU && JUCE_MAC - -BEGIN_JUCE_NAMESPACE - extern void juce_callAnyTimersSynchronously(); - extern bool juce_isHIViewCreatedByJuce (HIViewRef view); - extern bool juce_isWindowCreatedByJuce (WindowRef window); -END_JUCE_NAMESPACE - -#if MACOS_10_3_OR_EARLIER - #define kAudioUnitType_Generator 'augn' -#endif - -// Change this to disable logging of various activities -#ifndef AU_LOGGING - #define AU_LOGGING 1 -#endif - -#if AU_LOGGING - #define log(a) Logger::writeToLog(a); -#else - #define log(a) -#endif - -static int insideCallback = 0; - -//============================================================================== -class AudioUnitPluginWindow; - -//============================================================================== -class AudioUnitPluginInstance : public AudioPluginInstance -{ -public: - //============================================================================== - ~AudioUnitPluginInstance(); - - //============================================================================== - // AudioPluginInstance methods: - - const String getName() const { return pluginName; } - const String getManufacturer() const { return manufacturer; } - const String getVersion() const { return version; } - bool isInstrument() const { return componentDesc.componentType == kAudioUnitType_MusicDevice; } - const String getCategory() const; - const String getFormatName() const { return "AudioUnit"; } - const File getFile() const { return file; } - int getUID() const { return file.hashCode(); } - bool acceptsMidi() const { return wantsMidiMessages; } - bool producesMidi() const { return false; } - - //============================================================================== - // AudioProcessor methods: - - void prepareToPlay (double sampleRate, int estimatedSamplesPerBlock); - void releaseResources(); - void processBlock (AudioSampleBuffer& buffer, - MidiBuffer& midiMessages); - - AudioProcessorEditor* createEditor(); - - const String getInputChannelName (const int index) const; - bool isInputChannelStereoPair (int index) const; - - const String getOutputChannelName (const int index) const; - bool isOutputChannelStereoPair (int index) const; - - //============================================================================== - int getNumParameters(); - float getParameter (int index); - void setParameter (int index, float newValue); - const String getParameterName (int index); - const String getParameterText (int index); - bool isParameterAutomatable (int index) const; - - //============================================================================== - int getNumPrograms(); - int getCurrentProgram(); - void setCurrentProgram (int index); - const String getProgramName (int index); - void changeProgramName (int index, const String& newName); - - //============================================================================== - void getStateInformation (MemoryBlock& destData); - void getCurrentProgramStateInformation (MemoryBlock& destData); - void setStateInformation (const void* data, int sizeInBytes); - void setCurrentProgramStateInformation (const void* data, int sizeInBytes); - - //============================================================================== - juce_UseDebuggingNewOperator - -private: - friend class AudioUnitPluginWindow; - friend class AudioUnitPluginFormat; - - ComponentDescription componentDesc; - String pluginName, manufacturer, version; - File file; - CriticalSection lock; - bool initialised, wantsMidiMessages, wasPlaying; - - AudioBufferList* outputBufferList; - AudioTimeStamp timeStamp; - AudioSampleBuffer* currentBuffer; - - AudioUnit audioUnit; - Array parameterIds; - - //============================================================================== - bool getComponentDescFromFile (const File& file); - void initialise(); - - //============================================================================== - OSStatus renderGetInput (AudioUnitRenderActionFlags* ioActionFlags, - const AudioTimeStamp* inTimeStamp, - UInt32 inBusNumber, - UInt32 inNumberFrames, - AudioBufferList* ioData) const; - - static OSStatus renderGetInputCallback (void* inRefCon, - AudioUnitRenderActionFlags* ioActionFlags, - const AudioTimeStamp* inTimeStamp, - UInt32 inBusNumber, - UInt32 inNumberFrames, - AudioBufferList* ioData) - { - return ((AudioUnitPluginInstance*) inRefCon) - ->renderGetInput (ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, ioData); - } - - OSStatus getBeatAndTempo (Float64* outCurrentBeat, Float64* outCurrentTempo) const; - OSStatus getMusicalTimeLocation (UInt32* outDeltaSampleOffsetToNextBeat, Float32* outTimeSig_Numerator, - UInt32* outTimeSig_Denominator, Float64* outCurrentMeasureDownBeat) const; - OSStatus getTransportState (Boolean* outIsPlaying, Boolean* outTransportStateChanged, - Float64* outCurrentSampleInTimeLine, Boolean* outIsCycling, - Float64* outCycleStartBeat, Float64* outCycleEndBeat); - - static OSStatus getBeatAndTempoCallback (void* inHostUserData, Float64* outCurrentBeat, Float64* outCurrentTempo) - { - return ((AudioUnitPluginInstance*) inHostUserData)->getBeatAndTempo (outCurrentBeat, outCurrentTempo); - } - - static OSStatus getMusicalTimeLocationCallback (void* inHostUserData, UInt32* outDeltaSampleOffsetToNextBeat, - Float32* outTimeSig_Numerator, UInt32* outTimeSig_Denominator, - Float64* outCurrentMeasureDownBeat) - { - return ((AudioUnitPluginInstance*) inHostUserData) - ->getMusicalTimeLocation (outDeltaSampleOffsetToNextBeat, outTimeSig_Numerator, - outTimeSig_Denominator, outCurrentMeasureDownBeat); - } - - static OSStatus getTransportStateCallback (void* inHostUserData, Boolean* outIsPlaying, Boolean* outTransportStateChanged, - Float64* outCurrentSampleInTimeLine, Boolean* outIsCycling, - Float64* outCycleStartBeat, Float64* outCycleEndBeat) - { - return ((AudioUnitPluginInstance*) inHostUserData) - ->getTransportState (outIsPlaying, outTransportStateChanged, - outCurrentSampleInTimeLine, outIsCycling, - outCycleStartBeat, outCycleEndBeat); - } - - //============================================================================== - void getNumChannels (int& numIns, int& numOuts) - { - numIns = 0; - numOuts = 0; - - AUChannelInfo supportedChannels [128]; - UInt32 supportedChannelsSize = sizeof (supportedChannels); - - if (AudioUnitGetProperty (audioUnit, kAudioUnitProperty_SupportedNumChannels, kAudioUnitScope_Global, - 0, supportedChannels, &supportedChannelsSize) == noErr - && supportedChannelsSize > 0) - { - for (int i = 0; i < supportedChannelsSize / sizeof (AUChannelInfo); ++i) - { - numIns = jmax (numIns, supportedChannels[i].inChannels); - numOuts = jmax (numOuts, supportedChannels[i].outChannels); - } - } - else - { - // (this really means the plugin will take any number of ins/outs as long - // as they are the same) - numIns = numOuts = 2; - } - } - - //============================================================================== - AudioUnitPluginInstance (const File& file); -}; - -//============================================================================== -AudioUnitPluginInstance::AudioUnitPluginInstance (const File& file_) - : file (file_), - initialised (false), - wantsMidiMessages (false), - audioUnit (0), - outputBufferList (0), - currentBuffer (0) -{ - try - { - ++insideCallback; - - log (T("Opening AU: ") + file.getFullPathName()); - - if (getComponentDescFromFile (file)) - { - ComponentRecord* const comp = FindNextComponent (0, &componentDesc); - - if (comp != 0) - { - audioUnit = (AudioUnit) OpenComponent (comp); - - wantsMidiMessages = componentDesc.componentType == kAudioUnitType_MusicDevice - || componentDesc.componentType == kAudioUnitType_MusicEffect; - } - } - - --insideCallback; - } - catch (...) - { - --insideCallback; - } -} - -AudioUnitPluginInstance::~AudioUnitPluginInstance() -{ - { - const ScopedLock sl (lock); - - jassert (insideCallback == 0); - - if (audioUnit != 0) - { - AudioUnitUninitialize (audioUnit); - CloseComponent (audioUnit); - audioUnit = 0; - } - } - - juce_free (outputBufferList); -} - -bool AudioUnitPluginInstance::getComponentDescFromFile (const File& file) -{ - zerostruct (componentDesc); - - if (! file.hasFileExtension (T(".component"))) - return false; - - const String filename (file.getFullPathName()); - const char* const utf8 = filename.toUTF8(); - CFURLRef url = CFURLCreateFromFileSystemRepresentation (0, (const UInt8*) utf8, - strlen (utf8), file.isDirectory()); - if (url != 0) - { - CFBundleRef bundleRef = CFBundleCreate (kCFAllocatorDefault, url); - CFRelease (url); - - if (bundleRef != 0) - { - CFTypeRef name = CFBundleGetValueForInfoDictionaryKey (bundleRef, CFSTR("CFBundleName")); - - if (name != 0 && CFGetTypeID (name) == CFStringGetTypeID()) - pluginName = PlatformUtilities::cfStringToJuceString ((CFStringRef) name); - - if (pluginName.isEmpty()) - pluginName = file.getFileNameWithoutExtension(); - - CFTypeRef versionString = CFBundleGetValueForInfoDictionaryKey (bundleRef, CFSTR("CFBundleVersion")); - - if (versionString != 0 && CFGetTypeID (versionString) == CFStringGetTypeID()) - version = PlatformUtilities::cfStringToJuceString ((CFStringRef) versionString); - - CFTypeRef manuString = CFBundleGetValueForInfoDictionaryKey (bundleRef, CFSTR("CFBundleGetInfoString")); - - if (manuString != 0 && CFGetTypeID (manuString) == CFStringGetTypeID()) - manufacturer = PlatformUtilities::cfStringToJuceString ((CFStringRef) manuString); - - short resFileId = CFBundleOpenBundleResourceMap (bundleRef); - UseResFile (resFileId); - - for (int i = 1; i <= Count1Resources ('thng'); ++i) - { - Handle h = Get1IndResource ('thng', i); - - if (h != 0) - { - HLock (h); - const uint32* const types = (const uint32*) *h; - - if (types[0] == kAudioUnitType_MusicDevice - || types[0] == kAudioUnitType_MusicEffect - || types[0] == kAudioUnitType_Effect - || types[0] == kAudioUnitType_Generator - || types[0] == kAudioUnitType_Panner) - { - componentDesc.componentType = types[0]; - componentDesc.componentSubType = types[1]; - componentDesc.componentManufacturer = types[2]; - break; - } - - HUnlock (h); - ReleaseResource (h); - } - } - - CFBundleCloseBundleResourceMap (bundleRef, resFileId); - CFRelease (bundleRef); - } - } - - return componentDesc.componentType != 0 && componentDesc.componentSubType != 0; -} - -//============================================================================== -void AudioUnitPluginInstance::initialise() -{ - if (initialised || audioUnit == 0) - return; - - log (T("Initialising AU: ") + pluginName); - - parameterIds.clear(); - - { - UInt32 paramListSize = 0; - AudioUnitGetProperty (audioUnit, kAudioUnitProperty_ParameterList, kAudioUnitScope_Global, - 0, 0, ¶mListSize); - - if (paramListSize > 0) - { - parameterIds.insertMultiple (0, 0, paramListSize / sizeof (int)); - - AudioUnitGetProperty (audioUnit, kAudioUnitProperty_ParameterList, kAudioUnitScope_Global, - 0, ¶meterIds.getReference(0), ¶mListSize); - } - } - - { - AURenderCallbackStruct info; - zerostruct (info); - info.inputProcRefCon = this; - info.inputProc = renderGetInputCallback; - - AudioUnitSetProperty (audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, - 0, &info, sizeof (info)); - } - - { - HostCallbackInfo info; - zerostruct (info); - info.hostUserData = this; - info.beatAndTempoProc = getBeatAndTempoCallback; - info.musicalTimeLocationProc = getMusicalTimeLocationCallback; - info.transportStateProc = getTransportStateCallback; - - AudioUnitSetProperty (audioUnit, kAudioUnitProperty_HostCallbacks, kAudioUnitScope_Global, - 0, &info, sizeof (info)); - } - - int numIns, numOuts; - getNumChannels (numIns, numOuts); - setPlayConfigDetails (numIns, numOuts, 0, 0); - - initialised = AudioUnitInitialize (audioUnit) == noErr; - - setLatencySamples (0); -} - - -//============================================================================== -void AudioUnitPluginInstance::prepareToPlay (double sampleRate_, - int samplesPerBlockExpected) -{ - initialise(); - - if (initialised) - { - int numIns, numOuts; - getNumChannels (numIns, numOuts); - - setPlayConfigDetails (numIns, numOuts, sampleRate_, samplesPerBlockExpected); - - Float64 latencySecs = 0.0; - UInt32 latencySize = sizeof (latencySecs); - AudioUnitGetProperty (audioUnit, kAudioUnitProperty_Latency, kAudioUnitScope_Global, - 0, &latencySecs, &latencySize); - - setLatencySamples (roundDoubleToInt (latencySecs * sampleRate_)); - - AudioUnitReset (audioUnit, kAudioUnitScope_Input, 0); - AudioUnitReset (audioUnit, kAudioUnitScope_Output, 0); - AudioUnitReset (audioUnit, kAudioUnitScope_Global, 0); - - AudioStreamBasicDescription stream; - zerostruct (stream); - stream.mSampleRate = sampleRate_; - stream.mFormatID = kAudioFormatLinearPCM; - stream.mFormatFlags = kAudioFormatFlagsNativeFloatPacked | kAudioFormatFlagIsNonInterleaved; - stream.mFramesPerPacket = 1; - stream.mBytesPerPacket = 4; - stream.mBytesPerFrame = 4; - stream.mBitsPerChannel = 32; - stream.mChannelsPerFrame = numIns; - - OSStatus err = AudioUnitSetProperty (audioUnit, - kAudioUnitProperty_StreamFormat, - kAudioUnitScope_Input, - 0, &stream, sizeof (stream)); - - stream.mChannelsPerFrame = numOuts; - - err = AudioUnitSetProperty (audioUnit, - kAudioUnitProperty_StreamFormat, - kAudioUnitScope_Output, - 0, &stream, sizeof (stream)); - - juce_free (outputBufferList); - outputBufferList = (AudioBufferList*) juce_calloc (sizeof (AudioBufferList) + sizeof (AudioBuffer) * (numOuts + 1)); - outputBufferList->mNumberBuffers = numOuts; - - for (int i = numOuts; --i >= 0;) - outputBufferList->mBuffers[i].mNumberChannels = 1; - - zerostruct (timeStamp); - timeStamp.mSampleTime = 0; - timeStamp.mHostTime = AudioGetCurrentHostTime(); - timeStamp.mFlags = kAudioTimeStampSampleTimeValid | kAudioTimeStampHostTimeValid; - - currentBuffer = 0; - wasPlaying = false; - } -} - -void AudioUnitPluginInstance::releaseResources() -{ - if (initialised) - { - AudioUnitReset (audioUnit, kAudioUnitScope_Input, 0); - AudioUnitReset (audioUnit, kAudioUnitScope_Output, 0); - AudioUnitReset (audioUnit, kAudioUnitScope_Global, 0); - - juce_free (outputBufferList); - outputBufferList = 0; - currentBuffer = 0; - } -} - -OSStatus AudioUnitPluginInstance::renderGetInput (AudioUnitRenderActionFlags* ioActionFlags, - const AudioTimeStamp* inTimeStamp, - UInt32 inBusNumber, - UInt32 inNumberFrames, - AudioBufferList* ioData) const -{ - if (inBusNumber == 0 - && currentBuffer != 0) - { - jassert (inNumberFrames == currentBuffer->getNumSamples()); // if this ever happens, might need to add extra handling - - for (int i = 0; i < ioData->mNumberBuffers; ++i) - { - if (i < currentBuffer->getNumChannels()) - { - memcpy (ioData->mBuffers[i].mData, - currentBuffer->getSampleData (i, 0), - sizeof (float) * inNumberFrames); - } - else - { - zeromem (ioData->mBuffers[i].mData, sizeof (float) * inNumberFrames); - } - } - } - - return noErr; -} - -void AudioUnitPluginInstance::processBlock (AudioSampleBuffer& buffer, - MidiBuffer& midiMessages) -{ - const int numSamples = buffer.getNumSamples(); - - if (initialised) - { - AudioUnitRenderActionFlags flags = 0; - - timeStamp.mHostTime = AudioGetCurrentHostTime(); - - for (int i = getNumOutputChannels(); --i >= 0;) - { - outputBufferList->mBuffers[i].mDataByteSize = sizeof (float) * numSamples; - outputBufferList->mBuffers[i].mData = buffer.getSampleData (i, 0); - } - - currentBuffer = &buffer; - - if (wantsMidiMessages) - { - const uint8* midiEventData; - int midiEventSize, midiEventPosition; - MidiBuffer::Iterator i (midiMessages); - - while (i.getNextEvent (midiEventData, midiEventSize, midiEventPosition)) - { - if (midiEventSize <= 3) - MusicDeviceMIDIEvent (audioUnit, - midiEventData[0], midiEventData[1], midiEventData[2], - midiEventPosition); - else - MusicDeviceSysEx (audioUnit, midiEventData, midiEventSize); - } - - midiMessages.clear(); - } - - AudioUnitRender (audioUnit, &flags, &timeStamp, - 0, numSamples, outputBufferList); - - timeStamp.mSampleTime += numSamples; - } - else - { - // Not initialised, so just bypass.. - for (int i = getNumInputChannels(); i < getNumOutputChannels(); ++i) - buffer.clear (i, 0, buffer.getNumSamples()); - } -} - -//============================================================================== -OSStatus AudioUnitPluginInstance::getBeatAndTempo (Float64* outCurrentBeat, Float64* outCurrentTempo) const -{ - AudioPlayHead* const ph = getPlayHead(); - AudioPlayHead::CurrentPositionInfo result; - - if (ph != 0 && ph->getCurrentPosition (result)) - { - *outCurrentBeat = result.ppqPosition; - *outCurrentTempo = result.bpm; - } - else - { - *outCurrentBeat = 0; - *outCurrentTempo = 120.0; - } - - return noErr; -} - -OSStatus AudioUnitPluginInstance::getMusicalTimeLocation (UInt32* outDeltaSampleOffsetToNextBeat, - Float32* outTimeSig_Numerator, - UInt32* outTimeSig_Denominator, - Float64* outCurrentMeasureDownBeat) const -{ - AudioPlayHead* const ph = getPlayHead(); - AudioPlayHead::CurrentPositionInfo result; - - if (ph != 0 && ph->getCurrentPosition (result)) - { - *outTimeSig_Numerator = result.timeSigNumerator; - *outTimeSig_Denominator = result.timeSigDenominator; - - *outDeltaSampleOffsetToNextBeat = 0; //xxx - *outCurrentMeasureDownBeat = result.ppqPositionOfLastBarStart; //xxx wrong - } - else - { - *outDeltaSampleOffsetToNextBeat = 0; - *outTimeSig_Numerator = 4; - *outTimeSig_Denominator = 4; - *outCurrentMeasureDownBeat = 0; - } - - return noErr; -} - -OSStatus AudioUnitPluginInstance::getTransportState (Boolean* outIsPlaying, - Boolean* outTransportStateChanged, - Float64* outCurrentSampleInTimeLine, - Boolean* outIsCycling, - Float64* outCycleStartBeat, - Float64* outCycleEndBeat) -{ - AudioPlayHead* const ph = getPlayHead(); - AudioPlayHead::CurrentPositionInfo result; - - if (ph != 0 && ph->getCurrentPosition (result)) - { - *outIsPlaying = result.isPlaying; - *outTransportStateChanged = result.isPlaying != wasPlaying; - wasPlaying = result.isPlaying; - *outCurrentSampleInTimeLine = roundDoubleToInt (result.timeInSeconds * getSampleRate()); - *outIsCycling = false; - *outCycleStartBeat = 0; - *outCycleEndBeat = 0; - } - else - { - *outIsPlaying = false; - *outTransportStateChanged = false; - *outCurrentSampleInTimeLine = 0; - *outIsCycling = false; - *outCycleStartBeat = 0; - *outCycleEndBeat = 0; - } - - return noErr; -} - - -//============================================================================== -static VoidArray activeWindows; - -//============================================================================== -class AudioUnitPluginWindow : public AudioProcessorEditor, - public Timer -{ -public: - //============================================================================== - AudioUnitPluginWindow (AudioUnitPluginInstance& plugin_) - : AudioProcessorEditor (&plugin_), - plugin (plugin_), - isOpen (false), - pluginWantsKeys (false), - wasShowing (false), - recursiveResize (false), - viewComponent (0), - pluginViewRef (0) - { - movementWatcher = new CompMovementWatcher (this); - - activeWindows.add (this); - - setOpaque (true); - setVisible (true); - setSize (1, 1); - - ComponentDescription viewList [16]; - UInt32 viewListSize = sizeof (viewList); - AudioUnitGetProperty (plugin.audioUnit, kAudioUnitProperty_GetUIComponentList, kAudioUnitScope_Global, - 0, &viewList, &viewListSize); - - componentRecord = FindNextComponent (0, &viewList[0]); - } - - ~AudioUnitPluginWindow() - { - deleteAndZero (movementWatcher); - - closePluginWindow(); - - activeWindows.removeValue (this); - plugin.editorBeingDeleted (this); - } - - bool isValid() const throw() { return componentRecord != 0; } - - //============================================================================== - void componentMovedOrResized() - { - if (recursiveResize) - return; - - Component* const topComp = getTopLevelComponent(); - - if (topComp->getPeer() != 0) - { - int x = 0, y = 0; - relativePositionToOtherComponent (topComp, x, y); - - recursiveResize = true; - - if (pluginViewRef != 0) - { - HIRect r; - r.origin.x = (float) x; - r.origin.y = (float) y; - r.size.width = (float) getWidth(); - r.size.height = (float) getHeight(); - HIViewSetFrame (pluginViewRef, &r); - } - - recursiveResize = false; - } - } - - void componentVisibilityChanged() - { - const bool isShowingNow = isShowing(); - - if (wasShowing != isShowingNow) - { - wasShowing = isShowingNow; - - if (isShowingNow) - openPluginWindow(); - else - closePluginWindow(); - } - - componentMovedOrResized(); - } - - void componentPeerChanged() - { - closePluginWindow(); - openPluginWindow(); - } - - void timerCallback() - { - if (pluginViewRef != 0) - { - HIRect bounds; - HIViewGetBounds (pluginViewRef, &bounds); - const int w = jmax (32, (int) bounds.size.width); - const int h = jmax (32, (int) bounds.size.height); - - if (w != getWidth() || h != getHeight()) - { - setSize (w, h); - startTimer (50); - } - else - { - startTimer (jlimit (50, 500, getTimerInterval() + 20)); - } - } - } - - //============================================================================== - bool keyStateChanged() - { - return pluginWantsKeys; - } - - bool keyPressed (const KeyPress&) - { - return pluginWantsKeys; - } - - //============================================================================== - void paint (Graphics& g) - { - if (isOpen) - { - ComponentPeer* const peer = getPeer(); - - if (peer != 0) - { - peer->addMaskedRegion (getScreenX() - peer->getScreenX(), - getScreenY() - peer->getScreenY(), - getWidth(), getHeight()); - } - } - else - { - g.fillAll (Colours::black); - } - } - - //============================================================================== - void broughtToFront() - { - activeWindows.removeValue (this); - activeWindows.add (this); - } - - //============================================================================== - juce_UseDebuggingNewOperator - -private: - AudioUnitPluginInstance& plugin; - bool isOpen, wasShowing, recursiveResize; - bool pluginWantsKeys; - - ComponentRecord* componentRecord; - AudioUnitCarbonView viewComponent; - HIViewRef pluginViewRef; - - //============================================================================== - void openPluginWindow() - { - if (isOpen || getWindowHandle() == 0 || componentRecord == 0) - return; - - log (T("Opening AU GUI: ") + plugin.getName()); - isOpen = true; - - pluginWantsKeys = true; //xxx any way to find this out? Does it matter? - - viewComponent = (AudioUnitCarbonView) OpenComponent (componentRecord); - - if (viewComponent != 0) - { - Float32Point pos = { getScreenX() - getTopLevelComponent()->getScreenX(), - getScreenY() - getTopLevelComponent()->getScreenY() }; - Float32Point size = { 250, 200 }; - - AudioUnitCarbonViewCreate (viewComponent, - plugin.audioUnit, - (WindowRef) getWindowHandle(), - HIViewGetRoot ((WindowRef) getWindowHandle()), - &pos, &size, - (ControlRef*) &pluginViewRef); - } - - timerCallback(); // to set our comp to the right size - repaint(); - } - - //============================================================================== - void closePluginWindow() - { - stopTimer(); - - if (isOpen) - { - log (T("Closing AU GUI: ") + plugin.getName()); - isOpen = false; - - if (viewComponent != 0) - CloseComponent (viewComponent); - - pluginViewRef = 0; - } - } - - //============================================================================== - class CompMovementWatcher : public ComponentMovementWatcher - { - public: - CompMovementWatcher (AudioUnitPluginWindow* const owner_) - : ComponentMovementWatcher (owner_), - owner (owner_) - { - } - - //============================================================================== - void componentMovedOrResized (bool /*wasMoved*/, bool /*wasResized*/) - { - owner->componentMovedOrResized(); - } - - void componentPeerChanged() - { - owner->componentPeerChanged(); - } - - void componentVisibilityChanged (Component&) - { - owner->componentVisibilityChanged(); - } - - private: - AudioUnitPluginWindow* const owner; - }; - - CompMovementWatcher* movementWatcher; -}; - -//============================================================================== -AudioProcessorEditor* AudioUnitPluginInstance::createEditor() -{ - AudioUnitPluginWindow* w = new AudioUnitPluginWindow (*this); - - if (! w->isValid()) - deleteAndZero (w); - - return w; -} - - -//============================================================================== -const String AudioUnitPluginInstance::getCategory() const -{ - const char* result = 0; - - switch (componentDesc.componentType) - { - case kAudioUnitType_Effect: - case kAudioUnitType_MusicEffect: - result = "Effect"; - break; - case kAudioUnitType_MusicDevice: - result = "Synth"; - break; - case kAudioUnitType_Generator: - result = "Generator"; - break; - case kAudioUnitType_Panner: - result = "Panner"; - break; - default: - break; - } - - return result; -} - -//============================================================================== -int AudioUnitPluginInstance::getNumParameters() -{ - return parameterIds.size(); -} - -float AudioUnitPluginInstance::getParameter (int index) -{ - const ScopedLock sl (lock); - - Float32 value = 0.0f; - - if (audioUnit != 0 && ((unsigned int) index) < (unsigned int) parameterIds.size()) - { - AudioUnitGetParameter (audioUnit, - (UInt32) parameterIds.getUnchecked (index), - kAudioUnitScope_Global, 0, - &value); - } - - return value; -} - -void AudioUnitPluginInstance::setParameter (int index, float newValue) -{ - const ScopedLock sl (lock); - - if (audioUnit != 0 && ((unsigned int) index) < (unsigned int) parameterIds.size()) - { - AudioUnitSetParameter (audioUnit, - (UInt32) parameterIds.getUnchecked (index), - kAudioUnitScope_Global, 0, - newValue, 0); - } -} - -const String AudioUnitPluginInstance::getParameterName (int index) -{ - AudioUnitParameterInfo info; - zerostruct (info); - UInt32 sz = sizeof (info); - - String name; - - if (AudioUnitGetProperty (audioUnit, - kAudioUnitProperty_ParameterInfo, - kAudioUnitScope_Global, - parameterIds [index], &info, &sz) == noErr) - { - if ((info.flags & kAudioUnitParameterFlag_HasCFNameString) != 0) - name = PlatformUtilities::cfStringToJuceString (info.cfNameString); - else - name = String (info.name, sizeof (info.name)); - } - - return name; -} - -const String AudioUnitPluginInstance::getParameterText (int index) -{ - return String (getParameter (index)); -} - -bool AudioUnitPluginInstance::isParameterAutomatable (int index) const -{ - AudioUnitParameterInfo info; - UInt32 sz = sizeof (info); - - if (AudioUnitGetProperty (audioUnit, - kAudioUnitProperty_ParameterInfo, - kAudioUnitScope_Global, - parameterIds [index], &info, &sz) == noErr) - { - return (info.flags & kAudioUnitParameterFlag_NonRealTime) == 0; - } - - return true; -} - -//============================================================================== -int AudioUnitPluginInstance::getNumPrograms() -{ - CFArrayRef presets; - UInt32 sz = sizeof (CFArrayRef); - int num = 0; - - if (AudioUnitGetProperty (audioUnit, - kAudioUnitProperty_FactoryPresets, - kAudioUnitScope_Global, - 0, &presets, &sz) == noErr) - { - num = (int) CFArrayGetCount (presets); - CFRelease (presets); - } - - return num; -} - -int AudioUnitPluginInstance::getCurrentProgram() -{ - AUPreset current; - current.presetNumber = 0; - UInt32 sz = sizeof (AUPreset); - - AudioUnitGetProperty (audioUnit, - kAudioUnitProperty_FactoryPresets, - kAudioUnitScope_Global, - 0, ¤t, &sz); - - return current.presetNumber; -} - -void AudioUnitPluginInstance::setCurrentProgram (int newIndex) -{ - AUPreset current; - current.presetNumber = newIndex; - current.presetName = 0; - - AudioUnitSetProperty (audioUnit, - kAudioUnitProperty_FactoryPresets, - kAudioUnitScope_Global, - 0, ¤t, sizeof (AUPreset)); -} - -const String AudioUnitPluginInstance::getProgramName (int index) -{ - String s; - CFArrayRef presets; - UInt32 sz = sizeof (CFArrayRef); - - if (AudioUnitGetProperty (audioUnit, - kAudioUnitProperty_FactoryPresets, - kAudioUnitScope_Global, - 0, &presets, &sz) == noErr) - { - for (CFIndex i = 0; i < CFArrayGetCount (presets); ++i) - { - const AUPreset* p = (const AUPreset*) CFArrayGetValueAtIndex (presets, i); - - if (p != 0 && p->presetNumber == index) - { - s = PlatformUtilities::cfStringToJuceString (p->presetName); - break; - } - } - - CFRelease (presets); - } - - return s; -} - -void AudioUnitPluginInstance::changeProgramName (int index, const String& newName) -{ - jassertfalse // xxx not implemented! -} - -//============================================================================== -const String AudioUnitPluginInstance::getInputChannelName (const int index) const -{ - if (((unsigned int) index) < (unsigned int) getNumInputChannels()) - return T("Input ") + String (index + 1); - - return String::empty; -} - -bool AudioUnitPluginInstance::isInputChannelStereoPair (int index) const -{ - if (((unsigned int) index) >= (unsigned int) getNumInputChannels()) - return false; - - - return true; -} - -const String AudioUnitPluginInstance::getOutputChannelName (const int index) const -{ - if (((unsigned int) index) < (unsigned int) getNumOutputChannels()) - return T("Output ") + String (index + 1); - - return String::empty; -} - -bool AudioUnitPluginInstance::isOutputChannelStereoPair (int index) const -{ - if (((unsigned int) index) >= (unsigned int) getNumOutputChannels()) - return false; - - return true; -} - -//============================================================================== -void AudioUnitPluginInstance::getStateInformation (MemoryBlock& destData) -{ - getCurrentProgramStateInformation (destData); -} - -void AudioUnitPluginInstance::getCurrentProgramStateInformation (MemoryBlock& destData) -{ - CFPropertyListRef propertyList = 0; - UInt32 sz = sizeof (CFPropertyListRef); - - if (AudioUnitGetProperty (audioUnit, - kAudioUnitProperty_ClassInfo, - kAudioUnitScope_Global, - 0, &propertyList, &sz) == noErr) - { - CFWriteStreamRef stream = CFWriteStreamCreateWithAllocatedBuffers (kCFAllocatorDefault, kCFAllocatorDefault); - CFWriteStreamOpen (stream); - - CFIndex bytesWritten = CFPropertyListWriteToStream (propertyList, stream, kCFPropertyListBinaryFormat_v1_0, 0); - CFWriteStreamClose (stream); - - CFDataRef data = (CFDataRef) CFWriteStreamCopyProperty (stream, kCFStreamPropertyDataWritten); - - destData.setSize (bytesWritten); - destData.copyFrom (CFDataGetBytePtr (data), 0, destData.getSize()); - CFRelease (data); - - CFRelease (stream); - CFRelease (propertyList); - } -} - -void AudioUnitPluginInstance::setStateInformation (const void* data, int sizeInBytes) -{ - setCurrentProgramStateInformation (data, sizeInBytes); -} - -void AudioUnitPluginInstance::setCurrentProgramStateInformation (const void* data, int sizeInBytes) -{ - CFReadStreamRef stream = CFReadStreamCreateWithBytesNoCopy (kCFAllocatorDefault, - (const UInt8*) data, - sizeInBytes, - kCFAllocatorNull); - CFReadStreamOpen (stream); - - CFPropertyListFormat format = kCFPropertyListBinaryFormat_v1_0; - CFPropertyListRef propertyList = CFPropertyListCreateFromStream (kCFAllocatorDefault, - stream, - 0, - kCFPropertyListImmutable, - &format, - 0); - CFRelease (stream); - - if (propertyList != 0) - AudioUnitSetProperty (audioUnit, - kAudioUnitProperty_ClassInfo, - kAudioUnitScope_Global, - 0, &propertyList, sizeof (propertyList)); -} - -//============================================================================== -//============================================================================== -AudioUnitPluginFormat::AudioUnitPluginFormat() -{ -} - -AudioUnitPluginFormat::~AudioUnitPluginFormat() -{ -} - -void AudioUnitPluginFormat::findAllTypesForFile (OwnedArray & results, - const File& file) -{ - if (! fileMightContainThisPluginType (file)) - return; - - PluginDescription desc; - desc.file = file; - desc.uid = 0; - - AudioUnitPluginInstance* instance = dynamic_cast (createInstanceFromDescription (desc)); - - if (instance == 0) - return; - - try - { - desc.fillInFromInstance (*instance); - - results.add (new PluginDescription (desc)); - } - catch (...) - { - // crashed while loading... - } - - deleteAndZero (instance); -} - -AudioPluginInstance* AudioUnitPluginFormat::createInstanceFromDescription (const PluginDescription& desc) -{ - AudioUnitPluginInstance* result = 0; - - if (fileMightContainThisPluginType (desc.file)) - { - result = new AudioUnitPluginInstance (desc.file); - - if (result->audioUnit != 0) - { - result->initialise(); - } - else - { - deleteAndZero (result); - } - } - - return result; -} - -bool AudioUnitPluginFormat::fileMightContainThisPluginType (const File& f) -{ - return f.hasFileExtension (T(".component")) - && f.isDirectory(); -} - -const FileSearchPath AudioUnitPluginFormat::getDefaultLocationsToSearch() -{ - return FileSearchPath ("~/Library/Audio/Plug-Ins/Components;/Library/Audio/Plug-Ins/Components"); -} - -#endif -#endif +/* + ============================================================================== + + This file is part of the JUCE library - "Jules' Utility Class Extensions" + Copyright 2004-7 by Raw Material Software ltd. + + ------------------------------------------------------------------------------ + + JUCE can be redistributed and/or modified under the terms of the + GNU General Public License, as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + JUCE is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with JUCE; if not, visit www.gnu.org/licenses or write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------------ + + If you'd like to release a closed-source product which uses JUCE, commercial + licenses are also available: visit www.rawmaterialsoftware.com/juce for + more information. + + ============================================================================== +*/ + +#if ! (defined (LINUX) || defined (_WIN32)) + +#include +#include +#include + +#include "../../../../../juce.h" +#include "juce_AudioUnitPluginFormat.h" +#include "../juce_PluginDescription.h" + +#if JUCE_PLUGINHOST_AU && JUCE_MAC + +BEGIN_JUCE_NAMESPACE + extern void juce_callAnyTimersSynchronously(); + extern bool juce_isHIViewCreatedByJuce (HIViewRef view); + extern bool juce_isWindowCreatedByJuce (WindowRef window); +END_JUCE_NAMESPACE + +#if MACOS_10_3_OR_EARLIER + #define kAudioUnitType_Generator 'augn' +#endif + +// Change this to disable logging of various activities +#ifndef AU_LOGGING + #define AU_LOGGING 1 +#endif + +#if AU_LOGGING + #define log(a) Logger::writeToLog(a); +#else + #define log(a) +#endif + +static int insideCallback = 0; + +//============================================================================== +class AudioUnitPluginWindow; + +//============================================================================== +class AudioUnitPluginInstance : public AudioPluginInstance +{ +public: + //============================================================================== + ~AudioUnitPluginInstance(); + + //============================================================================== + // AudioPluginInstance methods: + + void fillInPluginDescription (PluginDescription& desc) const + { + desc.name = pluginName; + desc.file = file; + desc.uid = ((int) componentDesc.componentType) + ^ ((int) componentDesc.componentSubType) + ^ ((int) componentDesc.componentManufacturer); + desc.lastFileModTime = file.getLastModificationTime(); + desc.pluginFormatName = "AudioUnit"; + desc.category = getCategory(); + desc.manufacturerName = manufacturer; + desc.version = version; + desc.numInputChannels = getNumInputChannels(); + desc.numOutputChannels = getNumOutputChannels(); + desc.isInstrument = (componentDesc.componentType == kAudioUnitType_MusicDevice); + } + + const String getName() const { return pluginName; } + bool acceptsMidi() const { return wantsMidiMessages; } + bool producesMidi() const { return false; } + + //============================================================================== + // AudioProcessor methods: + + void prepareToPlay (double sampleRate, int estimatedSamplesPerBlock); + void releaseResources(); + void processBlock (AudioSampleBuffer& buffer, + MidiBuffer& midiMessages); + + AudioProcessorEditor* createEditor(); + + const String getInputChannelName (const int index) const; + bool isInputChannelStereoPair (int index) const; + + const String getOutputChannelName (const int index) const; + bool isOutputChannelStereoPair (int index) const; + + //============================================================================== + int getNumParameters(); + float getParameter (int index); + void setParameter (int index, float newValue); + const String getParameterName (int index); + const String getParameterText (int index); + bool isParameterAutomatable (int index) const; + + //============================================================================== + int getNumPrograms(); + int getCurrentProgram(); + void setCurrentProgram (int index); + const String getProgramName (int index); + void changeProgramName (int index, const String& newName); + + //============================================================================== + void getStateInformation (MemoryBlock& destData); + void getCurrentProgramStateInformation (MemoryBlock& destData); + void setStateInformation (const void* data, int sizeInBytes); + void setCurrentProgramStateInformation (const void* data, int sizeInBytes); + + //============================================================================== + juce_UseDebuggingNewOperator + +private: + friend class AudioUnitPluginWindow; + friend class AudioUnitPluginFormat; + + ComponentDescription componentDesc; + String pluginName, manufacturer, version; + File file; + CriticalSection lock; + bool initialised, wantsMidiMessages, wasPlaying; + + AudioBufferList* outputBufferList; + AudioTimeStamp timeStamp; + AudioSampleBuffer* currentBuffer; + + AudioUnit audioUnit; + Array parameterIds; + + //============================================================================== + bool getComponentDescFromFile (const File& file); + void initialise(); + + //============================================================================== + OSStatus renderGetInput (AudioUnitRenderActionFlags* ioActionFlags, + const AudioTimeStamp* inTimeStamp, + UInt32 inBusNumber, + UInt32 inNumberFrames, + AudioBufferList* ioData) const; + + static OSStatus renderGetInputCallback (void* inRefCon, + AudioUnitRenderActionFlags* ioActionFlags, + const AudioTimeStamp* inTimeStamp, + UInt32 inBusNumber, + UInt32 inNumberFrames, + AudioBufferList* ioData) + { + return ((AudioUnitPluginInstance*) inRefCon) + ->renderGetInput (ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, ioData); + } + + OSStatus getBeatAndTempo (Float64* outCurrentBeat, Float64* outCurrentTempo) const; + OSStatus getMusicalTimeLocation (UInt32* outDeltaSampleOffsetToNextBeat, Float32* outTimeSig_Numerator, + UInt32* outTimeSig_Denominator, Float64* outCurrentMeasureDownBeat) const; + OSStatus getTransportState (Boolean* outIsPlaying, Boolean* outTransportStateChanged, + Float64* outCurrentSampleInTimeLine, Boolean* outIsCycling, + Float64* outCycleStartBeat, Float64* outCycleEndBeat); + + static OSStatus getBeatAndTempoCallback (void* inHostUserData, Float64* outCurrentBeat, Float64* outCurrentTempo) + { + return ((AudioUnitPluginInstance*) inHostUserData)->getBeatAndTempo (outCurrentBeat, outCurrentTempo); + } + + static OSStatus getMusicalTimeLocationCallback (void* inHostUserData, UInt32* outDeltaSampleOffsetToNextBeat, + Float32* outTimeSig_Numerator, UInt32* outTimeSig_Denominator, + Float64* outCurrentMeasureDownBeat) + { + return ((AudioUnitPluginInstance*) inHostUserData) + ->getMusicalTimeLocation (outDeltaSampleOffsetToNextBeat, outTimeSig_Numerator, + outTimeSig_Denominator, outCurrentMeasureDownBeat); + } + + static OSStatus getTransportStateCallback (void* inHostUserData, Boolean* outIsPlaying, Boolean* outTransportStateChanged, + Float64* outCurrentSampleInTimeLine, Boolean* outIsCycling, + Float64* outCycleStartBeat, Float64* outCycleEndBeat) + { + return ((AudioUnitPluginInstance*) inHostUserData) + ->getTransportState (outIsPlaying, outTransportStateChanged, + outCurrentSampleInTimeLine, outIsCycling, + outCycleStartBeat, outCycleEndBeat); + } + + //============================================================================== + void getNumChannels (int& numIns, int& numOuts) + { + numIns = 0; + numOuts = 0; + + AUChannelInfo supportedChannels [128]; + UInt32 supportedChannelsSize = sizeof (supportedChannels); + + if (AudioUnitGetProperty (audioUnit, kAudioUnitProperty_SupportedNumChannels, kAudioUnitScope_Global, + 0, supportedChannels, &supportedChannelsSize) == noErr + && supportedChannelsSize > 0) + { + for (int i = 0; i < supportedChannelsSize / sizeof (AUChannelInfo); ++i) + { + numIns = jmax (numIns, supportedChannels[i].inChannels); + numOuts = jmax (numOuts, supportedChannels[i].outChannels); + } + } + else + { + // (this really means the plugin will take any number of ins/outs as long + // as they are the same) + numIns = numOuts = 2; + } + } + + const String getCategory() const; + + //============================================================================== + AudioUnitPluginInstance (const File& file); +}; + +//============================================================================== +AudioUnitPluginInstance::AudioUnitPluginInstance (const File& file_) + : file (file_), + initialised (false), + wantsMidiMessages (false), + audioUnit (0), + outputBufferList (0), + currentBuffer (0) +{ + try + { + ++insideCallback; + + log (T("Opening AU: ") + file.getFullPathName()); + + if (getComponentDescFromFile (file)) + { + ComponentRecord* const comp = FindNextComponent (0, &componentDesc); + + if (comp != 0) + { + audioUnit = (AudioUnit) OpenComponent (comp); + + wantsMidiMessages = componentDesc.componentType == kAudioUnitType_MusicDevice + || componentDesc.componentType == kAudioUnitType_MusicEffect; + } + } + + --insideCallback; + } + catch (...) + { + --insideCallback; + } +} + +AudioUnitPluginInstance::~AudioUnitPluginInstance() +{ + { + const ScopedLock sl (lock); + + jassert (insideCallback == 0); + + if (audioUnit != 0) + { + AudioUnitUninitialize (audioUnit); + CloseComponent (audioUnit); + audioUnit = 0; + } + } + + juce_free (outputBufferList); +} + +bool AudioUnitPluginInstance::getComponentDescFromFile (const File& file) +{ + zerostruct (componentDesc); + + if (! file.hasFileExtension (T(".component"))) + return false; + + const String filename (file.getFullPathName()); + const char* const utf8 = filename.toUTF8(); + CFURLRef url = CFURLCreateFromFileSystemRepresentation (0, (const UInt8*) utf8, + strlen (utf8), file.isDirectory()); + if (url != 0) + { + CFBundleRef bundleRef = CFBundleCreate (kCFAllocatorDefault, url); + CFRelease (url); + + if (bundleRef != 0) + { + CFTypeRef name = CFBundleGetValueForInfoDictionaryKey (bundleRef, CFSTR("CFBundleName")); + + if (name != 0 && CFGetTypeID (name) == CFStringGetTypeID()) + pluginName = PlatformUtilities::cfStringToJuceString ((CFStringRef) name); + + if (pluginName.isEmpty()) + pluginName = file.getFileNameWithoutExtension(); + + CFTypeRef versionString = CFBundleGetValueForInfoDictionaryKey (bundleRef, CFSTR("CFBundleVersion")); + + if (versionString != 0 && CFGetTypeID (versionString) == CFStringGetTypeID()) + version = PlatformUtilities::cfStringToJuceString ((CFStringRef) versionString); + + CFTypeRef manuString = CFBundleGetValueForInfoDictionaryKey (bundleRef, CFSTR("CFBundleGetInfoString")); + + if (manuString != 0 && CFGetTypeID (manuString) == CFStringGetTypeID()) + manufacturer = PlatformUtilities::cfStringToJuceString ((CFStringRef) manuString); + + short resFileId = CFBundleOpenBundleResourceMap (bundleRef); + UseResFile (resFileId); + + for (int i = 1; i <= Count1Resources ('thng'); ++i) + { + Handle h = Get1IndResource ('thng', i); + + if (h != 0) + { + HLock (h); + const uint32* const types = (const uint32*) *h; + + if (types[0] == kAudioUnitType_MusicDevice + || types[0] == kAudioUnitType_MusicEffect + || types[0] == kAudioUnitType_Effect + || types[0] == kAudioUnitType_Generator + || types[0] == kAudioUnitType_Panner) + { + componentDesc.componentType = types[0]; + componentDesc.componentSubType = types[1]; + componentDesc.componentManufacturer = types[2]; + break; + } + + HUnlock (h); + ReleaseResource (h); + } + } + + CFBundleCloseBundleResourceMap (bundleRef, resFileId); + CFRelease (bundleRef); + } + } + + return componentDesc.componentType != 0 && componentDesc.componentSubType != 0; +} + +//============================================================================== +void AudioUnitPluginInstance::initialise() +{ + if (initialised || audioUnit == 0) + return; + + log (T("Initialising AU: ") + pluginName); + + parameterIds.clear(); + + { + UInt32 paramListSize = 0; + AudioUnitGetProperty (audioUnit, kAudioUnitProperty_ParameterList, kAudioUnitScope_Global, + 0, 0, ¶mListSize); + + if (paramListSize > 0) + { + parameterIds.insertMultiple (0, 0, paramListSize / sizeof (int)); + + AudioUnitGetProperty (audioUnit, kAudioUnitProperty_ParameterList, kAudioUnitScope_Global, + 0, ¶meterIds.getReference(0), ¶mListSize); + } + } + + { + AURenderCallbackStruct info; + zerostruct (info); + info.inputProcRefCon = this; + info.inputProc = renderGetInputCallback; + + AudioUnitSetProperty (audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, + 0, &info, sizeof (info)); + } + + { + HostCallbackInfo info; + zerostruct (info); + info.hostUserData = this; + info.beatAndTempoProc = getBeatAndTempoCallback; + info.musicalTimeLocationProc = getMusicalTimeLocationCallback; + info.transportStateProc = getTransportStateCallback; + + AudioUnitSetProperty (audioUnit, kAudioUnitProperty_HostCallbacks, kAudioUnitScope_Global, + 0, &info, sizeof (info)); + } + + int numIns, numOuts; + getNumChannels (numIns, numOuts); + setPlayConfigDetails (numIns, numOuts, 0, 0); + + initialised = AudioUnitInitialize (audioUnit) == noErr; + + setLatencySamples (0); +} + + +//============================================================================== +void AudioUnitPluginInstance::prepareToPlay (double sampleRate_, + int samplesPerBlockExpected) +{ + initialise(); + + if (initialised) + { + int numIns, numOuts; + getNumChannels (numIns, numOuts); + + setPlayConfigDetails (numIns, numOuts, sampleRate_, samplesPerBlockExpected); + + Float64 latencySecs = 0.0; + UInt32 latencySize = sizeof (latencySecs); + AudioUnitGetProperty (audioUnit, kAudioUnitProperty_Latency, kAudioUnitScope_Global, + 0, &latencySecs, &latencySize); + + setLatencySamples (roundDoubleToInt (latencySecs * sampleRate_)); + + AudioUnitReset (audioUnit, kAudioUnitScope_Input, 0); + AudioUnitReset (audioUnit, kAudioUnitScope_Output, 0); + AudioUnitReset (audioUnit, kAudioUnitScope_Global, 0); + + AudioStreamBasicDescription stream; + zerostruct (stream); + stream.mSampleRate = sampleRate_; + stream.mFormatID = kAudioFormatLinearPCM; + stream.mFormatFlags = kAudioFormatFlagsNativeFloatPacked | kAudioFormatFlagIsNonInterleaved; + stream.mFramesPerPacket = 1; + stream.mBytesPerPacket = 4; + stream.mBytesPerFrame = 4; + stream.mBitsPerChannel = 32; + stream.mChannelsPerFrame = numIns; + + OSStatus err = AudioUnitSetProperty (audioUnit, + kAudioUnitProperty_StreamFormat, + kAudioUnitScope_Input, + 0, &stream, sizeof (stream)); + + stream.mChannelsPerFrame = numOuts; + + err = AudioUnitSetProperty (audioUnit, + kAudioUnitProperty_StreamFormat, + kAudioUnitScope_Output, + 0, &stream, sizeof (stream)); + + juce_free (outputBufferList); + outputBufferList = (AudioBufferList*) juce_calloc (sizeof (AudioBufferList) + sizeof (AudioBuffer) * (numOuts + 1)); + outputBufferList->mNumberBuffers = numOuts; + + for (int i = numOuts; --i >= 0;) + outputBufferList->mBuffers[i].mNumberChannels = 1; + + zerostruct (timeStamp); + timeStamp.mSampleTime = 0; + timeStamp.mHostTime = AudioGetCurrentHostTime(); + timeStamp.mFlags = kAudioTimeStampSampleTimeValid | kAudioTimeStampHostTimeValid; + + currentBuffer = 0; + wasPlaying = false; + } +} + +void AudioUnitPluginInstance::releaseResources() +{ + if (initialised) + { + AudioUnitReset (audioUnit, kAudioUnitScope_Input, 0); + AudioUnitReset (audioUnit, kAudioUnitScope_Output, 0); + AudioUnitReset (audioUnit, kAudioUnitScope_Global, 0); + + juce_free (outputBufferList); + outputBufferList = 0; + currentBuffer = 0; + } +} + +OSStatus AudioUnitPluginInstance::renderGetInput (AudioUnitRenderActionFlags* ioActionFlags, + const AudioTimeStamp* inTimeStamp, + UInt32 inBusNumber, + UInt32 inNumberFrames, + AudioBufferList* ioData) const +{ + if (inBusNumber == 0 + && currentBuffer != 0) + { + jassert (inNumberFrames == currentBuffer->getNumSamples()); // if this ever happens, might need to add extra handling + + for (int i = 0; i < ioData->mNumberBuffers; ++i) + { + if (i < currentBuffer->getNumChannels()) + { + memcpy (ioData->mBuffers[i].mData, + currentBuffer->getSampleData (i, 0), + sizeof (float) * inNumberFrames); + } + else + { + zeromem (ioData->mBuffers[i].mData, sizeof (float) * inNumberFrames); + } + } + } + + return noErr; +} + +void AudioUnitPluginInstance::processBlock (AudioSampleBuffer& buffer, + MidiBuffer& midiMessages) +{ + const int numSamples = buffer.getNumSamples(); + + if (initialised) + { + AudioUnitRenderActionFlags flags = 0; + + timeStamp.mHostTime = AudioGetCurrentHostTime(); + + for (int i = getNumOutputChannels(); --i >= 0;) + { + outputBufferList->mBuffers[i].mDataByteSize = sizeof (float) * numSamples; + outputBufferList->mBuffers[i].mData = buffer.getSampleData (i, 0); + } + + currentBuffer = &buffer; + + if (wantsMidiMessages) + { + const uint8* midiEventData; + int midiEventSize, midiEventPosition; + MidiBuffer::Iterator i (midiMessages); + + while (i.getNextEvent (midiEventData, midiEventSize, midiEventPosition)) + { + if (midiEventSize <= 3) + MusicDeviceMIDIEvent (audioUnit, + midiEventData[0], midiEventData[1], midiEventData[2], + midiEventPosition); + else + MusicDeviceSysEx (audioUnit, midiEventData, midiEventSize); + } + + midiMessages.clear(); + } + + AudioUnitRender (audioUnit, &flags, &timeStamp, + 0, numSamples, outputBufferList); + + timeStamp.mSampleTime += numSamples; + } + else + { + // Not initialised, so just bypass.. + for (int i = getNumInputChannels(); i < getNumOutputChannels(); ++i) + buffer.clear (i, 0, buffer.getNumSamples()); + } +} + +//============================================================================== +OSStatus AudioUnitPluginInstance::getBeatAndTempo (Float64* outCurrentBeat, Float64* outCurrentTempo) const +{ + AudioPlayHead* const ph = getPlayHead(); + AudioPlayHead::CurrentPositionInfo result; + + if (ph != 0 && ph->getCurrentPosition (result)) + { + *outCurrentBeat = result.ppqPosition; + *outCurrentTempo = result.bpm; + } + else + { + *outCurrentBeat = 0; + *outCurrentTempo = 120.0; + } + + return noErr; +} + +OSStatus AudioUnitPluginInstance::getMusicalTimeLocation (UInt32* outDeltaSampleOffsetToNextBeat, + Float32* outTimeSig_Numerator, + UInt32* outTimeSig_Denominator, + Float64* outCurrentMeasureDownBeat) const +{ + AudioPlayHead* const ph = getPlayHead(); + AudioPlayHead::CurrentPositionInfo result; + + if (ph != 0 && ph->getCurrentPosition (result)) + { + *outTimeSig_Numerator = result.timeSigNumerator; + *outTimeSig_Denominator = result.timeSigDenominator; + + *outDeltaSampleOffsetToNextBeat = 0; //xxx + *outCurrentMeasureDownBeat = result.ppqPositionOfLastBarStart; //xxx wrong + } + else + { + *outDeltaSampleOffsetToNextBeat = 0; + *outTimeSig_Numerator = 4; + *outTimeSig_Denominator = 4; + *outCurrentMeasureDownBeat = 0; + } + + return noErr; +} + +OSStatus AudioUnitPluginInstance::getTransportState (Boolean* outIsPlaying, + Boolean* outTransportStateChanged, + Float64* outCurrentSampleInTimeLine, + Boolean* outIsCycling, + Float64* outCycleStartBeat, + Float64* outCycleEndBeat) +{ + AudioPlayHead* const ph = getPlayHead(); + AudioPlayHead::CurrentPositionInfo result; + + if (ph != 0 && ph->getCurrentPosition (result)) + { + *outIsPlaying = result.isPlaying; + *outTransportStateChanged = result.isPlaying != wasPlaying; + wasPlaying = result.isPlaying; + *outCurrentSampleInTimeLine = roundDoubleToInt (result.timeInSeconds * getSampleRate()); + *outIsCycling = false; + *outCycleStartBeat = 0; + *outCycleEndBeat = 0; + } + else + { + *outIsPlaying = false; + *outTransportStateChanged = false; + *outCurrentSampleInTimeLine = 0; + *outIsCycling = false; + *outCycleStartBeat = 0; + *outCycleEndBeat = 0; + } + + return noErr; +} + + +//============================================================================== +static VoidArray activeWindows; + +//============================================================================== +class AudioUnitPluginWindow : public AudioProcessorEditor, + public Timer +{ +public: + //============================================================================== + AudioUnitPluginWindow (AudioUnitPluginInstance& plugin_) + : AudioProcessorEditor (&plugin_), + plugin (plugin_), + isOpen (false), + pluginWantsKeys (false), + wasShowing (false), + recursiveResize (false), + viewComponent (0), + pluginViewRef (0) + { + movementWatcher = new CompMovementWatcher (this); + + activeWindows.add (this); + + setOpaque (true); + setVisible (true); + setSize (1, 1); + + ComponentDescription viewList [16]; + UInt32 viewListSize = sizeof (viewList); + AudioUnitGetProperty (plugin.audioUnit, kAudioUnitProperty_GetUIComponentList, kAudioUnitScope_Global, + 0, &viewList, &viewListSize); + + componentRecord = FindNextComponent (0, &viewList[0]); + } + + ~AudioUnitPluginWindow() + { + deleteAndZero (movementWatcher); + + closePluginWindow(); + + activeWindows.removeValue (this); + plugin.editorBeingDeleted (this); + } + + bool isValid() const throw() { return componentRecord != 0; } + + //============================================================================== + void componentMovedOrResized() + { + if (recursiveResize) + return; + + Component* const topComp = getTopLevelComponent(); + + if (topComp->getPeer() != 0) + { + int x = 0, y = 0; + relativePositionToOtherComponent (topComp, x, y); + + recursiveResize = true; + + if (pluginViewRef != 0) + { + HIRect r; + r.origin.x = (float) x; + r.origin.y = (float) y; + r.size.width = (float) getWidth(); + r.size.height = (float) getHeight(); + HIViewSetFrame (pluginViewRef, &r); + } + + recursiveResize = false; + } + } + + void componentVisibilityChanged() + { + const bool isShowingNow = isShowing(); + + if (wasShowing != isShowingNow) + { + wasShowing = isShowingNow; + + if (isShowingNow) + openPluginWindow(); + else + closePluginWindow(); + } + + componentMovedOrResized(); + } + + void componentPeerChanged() + { + closePluginWindow(); + openPluginWindow(); + } + + void timerCallback() + { + if (pluginViewRef != 0) + { + HIRect bounds; + HIViewGetBounds (pluginViewRef, &bounds); + const int w = jmax (32, (int) bounds.size.width); + const int h = jmax (32, (int) bounds.size.height); + + if (w != getWidth() || h != getHeight()) + { + setSize (w, h); + startTimer (50); + } + else + { + startTimer (jlimit (50, 500, getTimerInterval() + 20)); + } + } + } + + //============================================================================== + bool keyStateChanged() + { + return pluginWantsKeys; + } + + bool keyPressed (const KeyPress&) + { + return pluginWantsKeys; + } + + //============================================================================== + void paint (Graphics& g) + { + if (isOpen) + { + ComponentPeer* const peer = getPeer(); + + if (peer != 0) + { + peer->addMaskedRegion (getScreenX() - peer->getScreenX(), + getScreenY() - peer->getScreenY(), + getWidth(), getHeight()); + } + } + else + { + g.fillAll (Colours::black); + } + } + + //============================================================================== + void broughtToFront() + { + activeWindows.removeValue (this); + activeWindows.add (this); + } + + //============================================================================== + juce_UseDebuggingNewOperator + +private: + AudioUnitPluginInstance& plugin; + bool isOpen, wasShowing, recursiveResize; + bool pluginWantsKeys; + + ComponentRecord* componentRecord; + AudioUnitCarbonView viewComponent; + HIViewRef pluginViewRef; + + //============================================================================== + void openPluginWindow() + { + if (isOpen || getWindowHandle() == 0 || componentRecord == 0) + return; + + log (T("Opening AU GUI: ") + plugin.getName()); + isOpen = true; + + pluginWantsKeys = true; //xxx any way to find this out? Does it matter? + + viewComponent = (AudioUnitCarbonView) OpenComponent (componentRecord); + + if (viewComponent != 0) + { + Float32Point pos = { getScreenX() - getTopLevelComponent()->getScreenX(), + getScreenY() - getTopLevelComponent()->getScreenY() }; + Float32Point size = { 250, 200 }; + + AudioUnitCarbonViewCreate (viewComponent, + plugin.audioUnit, + (WindowRef) getWindowHandle(), + HIViewGetRoot ((WindowRef) getWindowHandle()), + &pos, &size, + (ControlRef*) &pluginViewRef); + } + + timerCallback(); // to set our comp to the right size + repaint(); + } + + //============================================================================== + void closePluginWindow() + { + stopTimer(); + + if (isOpen) + { + log (T("Closing AU GUI: ") + plugin.getName()); + isOpen = false; + + if (viewComponent != 0) + CloseComponent (viewComponent); + + pluginViewRef = 0; + } + } + + //============================================================================== + class CompMovementWatcher : public ComponentMovementWatcher + { + public: + CompMovementWatcher (AudioUnitPluginWindow* const owner_) + : ComponentMovementWatcher (owner_), + owner (owner_) + { + } + + //============================================================================== + void componentMovedOrResized (bool /*wasMoved*/, bool /*wasResized*/) + { + owner->componentMovedOrResized(); + } + + void componentPeerChanged() + { + owner->componentPeerChanged(); + } + + void componentVisibilityChanged (Component&) + { + owner->componentVisibilityChanged(); + } + + private: + AudioUnitPluginWindow* const owner; + }; + + CompMovementWatcher* movementWatcher; +}; + +//============================================================================== +AudioProcessorEditor* AudioUnitPluginInstance::createEditor() +{ + AudioUnitPluginWindow* w = new AudioUnitPluginWindow (*this); + + if (! w->isValid()) + deleteAndZero (w); + + return w; +} + + +//============================================================================== +const String AudioUnitPluginInstance::getCategory() const +{ + const char* result = 0; + + switch (componentDesc.componentType) + { + case kAudioUnitType_Effect: + case kAudioUnitType_MusicEffect: + result = "Effect"; + break; + case kAudioUnitType_MusicDevice: + result = "Synth"; + break; + case kAudioUnitType_Generator: + result = "Generator"; + break; + case kAudioUnitType_Panner: + result = "Panner"; + break; + default: + break; + } + + return result; +} + +//============================================================================== +int AudioUnitPluginInstance::getNumParameters() +{ + return parameterIds.size(); +} + +float AudioUnitPluginInstance::getParameter (int index) +{ + const ScopedLock sl (lock); + + Float32 value = 0.0f; + + if (audioUnit != 0 && ((unsigned int) index) < (unsigned int) parameterIds.size()) + { + AudioUnitGetParameter (audioUnit, + (UInt32) parameterIds.getUnchecked (index), + kAudioUnitScope_Global, 0, + &value); + } + + return value; +} + +void AudioUnitPluginInstance::setParameter (int index, float newValue) +{ + const ScopedLock sl (lock); + + if (audioUnit != 0 && ((unsigned int) index) < (unsigned int) parameterIds.size()) + { + AudioUnitSetParameter (audioUnit, + (UInt32) parameterIds.getUnchecked (index), + kAudioUnitScope_Global, 0, + newValue, 0); + } +} + +const String AudioUnitPluginInstance::getParameterName (int index) +{ + AudioUnitParameterInfo info; + zerostruct (info); + UInt32 sz = sizeof (info); + + String name; + + if (AudioUnitGetProperty (audioUnit, + kAudioUnitProperty_ParameterInfo, + kAudioUnitScope_Global, + parameterIds [index], &info, &sz) == noErr) + { + if ((info.flags & kAudioUnitParameterFlag_HasCFNameString) != 0) + name = PlatformUtilities::cfStringToJuceString (info.cfNameString); + else + name = String (info.name, sizeof (info.name)); + } + + return name; +} + +const String AudioUnitPluginInstance::getParameterText (int index) +{ + return String (getParameter (index)); +} + +bool AudioUnitPluginInstance::isParameterAutomatable (int index) const +{ + AudioUnitParameterInfo info; + UInt32 sz = sizeof (info); + + if (AudioUnitGetProperty (audioUnit, + kAudioUnitProperty_ParameterInfo, + kAudioUnitScope_Global, + parameterIds [index], &info, &sz) == noErr) + { + return (info.flags & kAudioUnitParameterFlag_NonRealTime) == 0; + } + + return true; +} + +//============================================================================== +int AudioUnitPluginInstance::getNumPrograms() +{ + CFArrayRef presets; + UInt32 sz = sizeof (CFArrayRef); + int num = 0; + + if (AudioUnitGetProperty (audioUnit, + kAudioUnitProperty_FactoryPresets, + kAudioUnitScope_Global, + 0, &presets, &sz) == noErr) + { + num = (int) CFArrayGetCount (presets); + CFRelease (presets); + } + + return num; +} + +int AudioUnitPluginInstance::getCurrentProgram() +{ + AUPreset current; + current.presetNumber = 0; + UInt32 sz = sizeof (AUPreset); + + AudioUnitGetProperty (audioUnit, + kAudioUnitProperty_FactoryPresets, + kAudioUnitScope_Global, + 0, ¤t, &sz); + + return current.presetNumber; +} + +void AudioUnitPluginInstance::setCurrentProgram (int newIndex) +{ + AUPreset current; + current.presetNumber = newIndex; + current.presetName = 0; + + AudioUnitSetProperty (audioUnit, + kAudioUnitProperty_FactoryPresets, + kAudioUnitScope_Global, + 0, ¤t, sizeof (AUPreset)); +} + +const String AudioUnitPluginInstance::getProgramName (int index) +{ + String s; + CFArrayRef presets; + UInt32 sz = sizeof (CFArrayRef); + + if (AudioUnitGetProperty (audioUnit, + kAudioUnitProperty_FactoryPresets, + kAudioUnitScope_Global, + 0, &presets, &sz) == noErr) + { + for (CFIndex i = 0; i < CFArrayGetCount (presets); ++i) + { + const AUPreset* p = (const AUPreset*) CFArrayGetValueAtIndex (presets, i); + + if (p != 0 && p->presetNumber == index) + { + s = PlatformUtilities::cfStringToJuceString (p->presetName); + break; + } + } + + CFRelease (presets); + } + + return s; +} + +void AudioUnitPluginInstance::changeProgramName (int index, const String& newName) +{ + jassertfalse // xxx not implemented! +} + +//============================================================================== +const String AudioUnitPluginInstance::getInputChannelName (const int index) const +{ + if (((unsigned int) index) < (unsigned int) getNumInputChannels()) + return T("Input ") + String (index + 1); + + return String::empty; +} + +bool AudioUnitPluginInstance::isInputChannelStereoPair (int index) const +{ + if (((unsigned int) index) >= (unsigned int) getNumInputChannels()) + return false; + + + return true; +} + +const String AudioUnitPluginInstance::getOutputChannelName (const int index) const +{ + if (((unsigned int) index) < (unsigned int) getNumOutputChannels()) + return T("Output ") + String (index + 1); + + return String::empty; +} + +bool AudioUnitPluginInstance::isOutputChannelStereoPair (int index) const +{ + if (((unsigned int) index) >= (unsigned int) getNumOutputChannels()) + return false; + + return true; +} + +//============================================================================== +void AudioUnitPluginInstance::getStateInformation (MemoryBlock& destData) +{ + getCurrentProgramStateInformation (destData); +} + +void AudioUnitPluginInstance::getCurrentProgramStateInformation (MemoryBlock& destData) +{ + CFPropertyListRef propertyList = 0; + UInt32 sz = sizeof (CFPropertyListRef); + + if (AudioUnitGetProperty (audioUnit, + kAudioUnitProperty_ClassInfo, + kAudioUnitScope_Global, + 0, &propertyList, &sz) == noErr) + { + CFWriteStreamRef stream = CFWriteStreamCreateWithAllocatedBuffers (kCFAllocatorDefault, kCFAllocatorDefault); + CFWriteStreamOpen (stream); + + CFIndex bytesWritten = CFPropertyListWriteToStream (propertyList, stream, kCFPropertyListBinaryFormat_v1_0, 0); + CFWriteStreamClose (stream); + + CFDataRef data = (CFDataRef) CFWriteStreamCopyProperty (stream, kCFStreamPropertyDataWritten); + + destData.setSize (bytesWritten); + destData.copyFrom (CFDataGetBytePtr (data), 0, destData.getSize()); + CFRelease (data); + + CFRelease (stream); + CFRelease (propertyList); + } +} + +void AudioUnitPluginInstance::setStateInformation (const void* data, int sizeInBytes) +{ + setCurrentProgramStateInformation (data, sizeInBytes); +} + +void AudioUnitPluginInstance::setCurrentProgramStateInformation (const void* data, int sizeInBytes) +{ + CFReadStreamRef stream = CFReadStreamCreateWithBytesNoCopy (kCFAllocatorDefault, + (const UInt8*) data, + sizeInBytes, + kCFAllocatorNull); + CFReadStreamOpen (stream); + + CFPropertyListFormat format = kCFPropertyListBinaryFormat_v1_0; + CFPropertyListRef propertyList = CFPropertyListCreateFromStream (kCFAllocatorDefault, + stream, + 0, + kCFPropertyListImmutable, + &format, + 0); + CFRelease (stream); + + if (propertyList != 0) + AudioUnitSetProperty (audioUnit, + kAudioUnitProperty_ClassInfo, + kAudioUnitScope_Global, + 0, &propertyList, sizeof (propertyList)); +} + +//============================================================================== +//============================================================================== +AudioUnitPluginFormat::AudioUnitPluginFormat() +{ +} + +AudioUnitPluginFormat::~AudioUnitPluginFormat() +{ +} + +void AudioUnitPluginFormat::findAllTypesForFile (OwnedArray & results, + const File& file) +{ + if (! fileMightContainThisPluginType (file)) + return; + + PluginDescription desc; + desc.file = file; + desc.uid = 0; + + AudioUnitPluginInstance* instance = dynamic_cast (createInstanceFromDescription (desc)); + + if (instance == 0) + return; + + try + { + instance->fillInPluginDescription (desc); + + results.add (new PluginDescription (desc)); + } + catch (...) + { + // crashed while loading... + } + + deleteAndZero (instance); +} + +AudioPluginInstance* AudioUnitPluginFormat::createInstanceFromDescription (const PluginDescription& desc) +{ + AudioUnitPluginInstance* result = 0; + + if (fileMightContainThisPluginType (desc.file)) + { + result = new AudioUnitPluginInstance (desc.file); + + if (result->audioUnit != 0) + { + result->initialise(); + } + else + { + deleteAndZero (result); + } + } + + return result; +} + +bool AudioUnitPluginFormat::fileMightContainThisPluginType (const File& f) +{ + return f.hasFileExtension (T(".component")) + && f.isDirectory(); +} + +const FileSearchPath AudioUnitPluginFormat::getDefaultLocationsToSearch() +{ + return FileSearchPath ("~/Library/Audio/Plug-Ins/Components;/Library/Audio/Plug-Ins/Components"); +} + +#endif +#endif diff --git a/extras/audio plugin host/src/plugins/formats/juce_VSTPluginFormat.cpp b/extras/audio plugin host/src/plugins/formats/juce_VSTPluginFormat.cpp index b3578d14a6..3c28512e60 100644 --- a/extras/audio plugin host/src/plugins/formats/juce_VSTPluginFormat.cpp +++ b/extras/audio plugin host/src/plugins/formats/juce_VSTPluginFormat.cpp @@ -1,2998 +1,3008 @@ -/* - ============================================================================== - - This file is part of the JUCE library - "Jules' Utility Class Extensions" - Copyright 2004-7 by Raw Material Software ltd. - - ------------------------------------------------------------------------------ - - JUCE can be redistributed and/or modified under the terms of the - GNU General Public License, as published by the Free Software Foundation; - either version 2 of the License, or (at your option) any later version. - - JUCE is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with JUCE; if not, visit www.gnu.org/licenses or write to the - Free Software Foundation, Inc., 59 Temple Place, Suite 330, - Boston, MA 02111-1307 USA - - ------------------------------------------------------------------------------ - - If you'd like to release a closed-source product which uses JUCE, commercial - licenses are also available: visit www.rawmaterialsoftware.com/juce for - more information. - - ============================================================================== -*/ - -#ifndef JUCE_PLUGINHOST_VST // xxx temporary.. - #define JUCE_PLUGINHOST_VST 1 -#endif - -#if JUCE_PLUGINHOST_VST - -#ifdef _WIN32 - #define _WIN32_WINNT 0x500 - #define STRICT - #include - #include - #pragma warning (disable : 4312) -#elif defined (LINUX) - #include - #include - #include - #include - #include - #undef Font - #undef KeyPress - #undef Drawable - #undef Time -#else - #include -#endif - -//============================================================================== -#include "../../../../../juce.h" -#include "juce_VSTPluginFormat.h" - -//============================================================================== -#undef PRAGMA_ALIGN_SUPPORTED -#define VST_FORCE_DEPRECATED 0 - -#ifdef _MSC_VER - #pragma warning (push) - #pragma warning (disable: 4996) -#endif - -/* Obviously you're going to need the Steinberg vstsdk2.4 folder in - your include path if you want to add VST support... -*/ -#include "pluginterfaces/vst2.x/aeffectx.h" - -#ifdef _MSC_VER - #pragma warning (pop) -#endif - -//============================================================================== -#if JUCE_LINUX - #define Font JUCE_NAMESPACE::Font - #define KeyPress JUCE_NAMESPACE::KeyPress - #define Drawable JUCE_NAMESPACE::Drawable - #define Time JUCE_NAMESPACE::Time -#endif - -#include "../juce_PluginDescription.h" - -#if ! JUCE_WIN32 - #define _fpreset() - #define _clearfp() -#endif - -BEGIN_JUCE_NAMESPACE - extern void juce_callAnyTimersSynchronously(); -END_JUCE_NAMESPACE - - -//============================================================================== -const int fxbVersionNum = 1; - -struct fxProgram -{ - long chunkMagic; // 'CcnK' - long byteSize; // of this chunk, excl. magic + byteSize - long fxMagic; // 'FxCk' - long version; - long fxID; // fx unique id - long fxVersion; - long numParams; - char prgName[28]; - float params[1]; // variable no. of parameters -}; - -struct fxSet -{ - long chunkMagic; // 'CcnK' - long byteSize; // of this chunk, excl. magic + byteSize - long fxMagic; // 'FxBk' - long version; - long fxID; // fx unique id - long fxVersion; - long numPrograms; - char future[128]; - fxProgram programs[1]; // variable no. of programs -}; - -struct fxChunkSet -{ - long chunkMagic; // 'CcnK' - long byteSize; // of this chunk, excl. magic + byteSize - long fxMagic; // 'FxCh', 'FPCh', or 'FBCh' - long version; - long fxID; // fx unique id - long fxVersion; - long numPrograms; - char future[128]; - long chunkSize; - char chunk[8]; // variable -}; - -struct fxProgramSet -{ - long chunkMagic; // 'CcnK' - long byteSize; // of this chunk, excl. magic + byteSize - long fxMagic; // 'FxCh', 'FPCh', or 'FBCh' - long version; - long fxID; // fx unique id - long fxVersion; - long numPrograms; - char name[28]; - long chunkSize; - char chunk[8]; // variable -}; - - -#ifdef JUCE_LITTLE_ENDIAN - static long swap (const long x) throw() { return (long) swapByteOrder ((uint32) x); } - - static float swapFloat (const float x) throw() - { - union { uint32 asInt; float asFloat; } n; - n.asFloat = x; - n.asInt = swapByteOrder (n.asInt); - return n.asFloat; - } -#else - #define swap(x) (x) - #define swapFloat(x) (x) -#endif - -//============================================================================== -typedef AEffect* (*MainCall) (audioMasterCallback); - -static VstIntPtr VSTCALLBACK audioMaster (AEffect* effect, VstInt32 opcode, VstInt32 index, VstIntPtr value, void* ptr, float opt); - -#if JUCE_PPC - static void* audioMasterCoerced = 0; -#endif - -static int shellUIDToCreate = 0; -static int insideVSTCallback = 0; - -class VSTPluginWindow; - -//============================================================================== -// Change this to disable logging of various VST activities -#ifndef VST_LOGGING - #define VST_LOGGING 1 -#endif - -#if VST_LOGGING - #define log(a) Logger::writeToLog(a); -#else - #define log(a) -#endif - -//============================================================================== -#if JUCE_MAC -BEGIN_JUCE_NAMESPACE - extern bool juce_isHIViewCreatedByJuce (HIViewRef view); - extern bool juce_isWindowCreatedByJuce (WindowRef window); -END_JUCE_NAMESPACE - -#if JUCE_PPC -static void* NewCFMFromMachO (void* const machofp) throw() -{ - void* result = juce_malloc (8); - - ((void**) result)[0] = machofp; - ((void**) result)[1] = result; - - return result; -} -#endif -#endif - -//============================================================================== -#if JUCE_LINUX - -BEGIN_JUCE_NAMESPACE - extern Display* display; - extern XContext improbableNumber; -END_JUCE_NAMESPACE - -typedef void (*EventProcPtr) (XEvent* ev); - -static bool xErrorTriggered; - -static int temporaryErrorHandler (Display*, XErrorEvent*) -{ - xErrorTriggered = true; - return 0; -} - -static int getPropertyFromXWindow (Window handle, Atom atom) -{ - XErrorHandler oldErrorHandler = XSetErrorHandler (temporaryErrorHandler); - xErrorTriggered = false; - - int userSize; - unsigned long bytes, userCount; - unsigned char* data; - Atom userType; - - XGetWindowProperty (display, handle, atom, 0, 1, false, AnyPropertyType, - &userType, &userSize, &userCount, &bytes, &data); - - XSetErrorHandler (oldErrorHandler); - - return (userCount == 1 && ! xErrorTriggered) ? *(int*) data - : 0; -} - -static Window getChildWindow (Window windowToCheck) -{ - Window rootWindow, parentWindow; - Window* childWindows; - unsigned int numChildren; - - XQueryTree (display, - windowToCheck, - &rootWindow, - &parentWindow, - &childWindows, - &numChildren); - - if (numChildren > 0) - return childWindows [0]; - - return 0; -} - -static void translateJuceToXButtonModifiers (const MouseEvent& e, XEvent& ev) throw() -{ - if (e.mods.isLeftButtonDown()) - { - ev.xbutton.button = Button1; - ev.xbutton.state |= Button1Mask; - } - else if (e.mods.isRightButtonDown()) - { - ev.xbutton.button = Button3; - ev.xbutton.state |= Button3Mask; - } - else if (e.mods.isMiddleButtonDown()) - { - ev.xbutton.button = Button2; - ev.xbutton.state |= Button2Mask; - } -} - -static void translateJuceToXMotionModifiers (const MouseEvent& e, XEvent& ev) throw() -{ - if (e.mods.isLeftButtonDown()) - ev.xmotion.state |= Button1Mask; - else if (e.mods.isRightButtonDown()) - ev.xmotion.state |= Button3Mask; - else if (e.mods.isMiddleButtonDown()) - ev.xmotion.state |= Button2Mask; -} - -static void translateJuceToXCrossingModifiers (const MouseEvent& e, XEvent& ev) throw() -{ - if (e.mods.isLeftButtonDown()) - ev.xcrossing.state |= Button1Mask; - else if (e.mods.isRightButtonDown()) - ev.xcrossing.state |= Button3Mask; - else if (e.mods.isMiddleButtonDown()) - ev.xcrossing.state |= Button2Mask; -} - -static void translateJuceToXMouseWheelModifiers (const MouseEvent& e, const float increment, XEvent& ev) throw() -{ - if (increment < 0) - { - ev.xbutton.button = Button5; - ev.xbutton.state |= Button5Mask; - } - else if (increment > 0) - { - ev.xbutton.button = Button4; - ev.xbutton.state |= Button4Mask; - } -} - -#endif - -//============================================================================== -static VoidArray activeModules; - -//============================================================================== -class ModuleHandle : public ReferenceCountedObject -{ -public: - //============================================================================== - File file; - MainCall moduleMain; - String pluginName; - - //============================================================================== - static ModuleHandle* findOrCreateModule (const File& file) - { - for (int i = activeModules.size(); --i >= 0;) - { - ModuleHandle* const module = (ModuleHandle*) activeModules.getUnchecked(i); - - if (module->file == file) - return module; - } - - _fpreset(); // (doesn't do any harm) - ++insideVSTCallback; - shellUIDToCreate = 0; - - log ("Attempting to load VST: " + file.getFullPathName()); - - ModuleHandle* m = new ModuleHandle (file); - - if (! m->open()) - deleteAndZero (m); - - --insideVSTCallback; - _fpreset(); // (doesn't do any harm) - - return m; - } - - //============================================================================== - ModuleHandle (const File& file_) - : file (file_), - moduleMain (0), -#if JUCE_WIN32 || JUCE_LINUX - hModule (0) -#elif JUCE_MAC - fragId (0), - resHandle (0), - bundleRef (0), - resFileId (0) -#endif - { - activeModules.add (this); - -#if JUCE_WIN32 || JUCE_LINUX - fullParentDirectoryPathName = file_.getParentDirectory().getFullPathName(); -#elif JUCE_MAC - PlatformUtilities::makeFSSpecFromPath (&parentDirFSSpec, file_.getParentDirectory().getFullPathName()); -#endif - } - - ~ModuleHandle() - { - activeModules.removeValue (this); - - close(); - } - - //============================================================================== - juce_UseDebuggingNewOperator - - //============================================================================== -#if JUCE_WIN32 || JUCE_LINUX - void* hModule; - String fullParentDirectoryPathName; - - bool open() - { -#if JUCE_WIN32 - static bool timePeriodSet = false; - - if (! timePeriodSet) - { - timePeriodSet = true; - timeBeginPeriod (2); - } -#endif - - pluginName = file.getFileNameWithoutExtension(); - - hModule = Process::loadDynamicLibrary (file.getFullPathName()); - - moduleMain = (MainCall) Process::getProcedureEntryPoint (hModule, "VSTPluginMain"); - - if (moduleMain == 0) - moduleMain = (MainCall) Process::getProcedureEntryPoint (hModule, "main"); - - return moduleMain != 0; - } - - void close() - { - _fpreset(); // (doesn't do any harm) - - Process::freeDynamicLibrary (hModule); - } - - void closeEffect (AEffect* eff) - { - eff->dispatcher (eff, effClose, 0, 0, 0, 0); - } - -#else - CFragConnectionID fragId; - Handle resHandle; - CFBundleRef bundleRef; - FSSpec parentDirFSSpec; - short resFileId; - - bool open() - { - bool ok = false; - const String filename (file.getFullPathName()); - - if (file.hasFileExtension (T(".vst"))) - { - const char* const utf8 = filename.toUTF8(); - CFURLRef url = CFURLCreateFromFileSystemRepresentation (0, (const UInt8*) utf8, - strlen (utf8), file.isDirectory()); - - if (url != 0) - { - bundleRef = CFBundleCreate (kCFAllocatorDefault, url); - CFRelease (url); - - if (bundleRef != 0) - { - if (CFBundleLoadExecutable (bundleRef)) - { - moduleMain = (MainCall) CFBundleGetFunctionPointerForName (bundleRef, CFSTR("main_macho")); - - if (moduleMain == 0) - moduleMain = (MainCall) CFBundleGetFunctionPointerForName (bundleRef, CFSTR("VSTPluginMain")); - - if (moduleMain != 0) - { - CFTypeRef name = CFBundleGetValueForInfoDictionaryKey (bundleRef, CFSTR("CFBundleName")); - - if (name != 0) - { - if (CFGetTypeID (name) == CFStringGetTypeID()) - { - char buffer[1024]; - - if (CFStringGetCString ((CFStringRef) name, buffer, sizeof (buffer), CFStringGetSystemEncoding())) - pluginName = buffer; - } - } - - if (pluginName.isEmpty()) - pluginName = file.getFileNameWithoutExtension(); - - resFileId = CFBundleOpenBundleResourceMap (bundleRef); - - ok = true; - } - } - - if (! ok) - { - CFBundleUnloadExecutable (bundleRef); - CFRelease (bundleRef); - bundleRef = 0; - } - } - } - } -#if JUCE_PPC - else - { - FSRef fn; - - if (FSPathMakeRef ((UInt8*) (const char*) filename, &fn, 0) == noErr) - { - resFileId = FSOpenResFile (&fn, fsRdPerm); - - if (resFileId != -1) - { - const int numEffs = Count1Resources ('aEff'); - - for (int i = 0; i < numEffs; ++i) - { - resHandle = Get1IndResource ('aEff', i + 1); - - if (resHandle != 0) - { - OSType type; - Str255 name; - SInt16 id; - GetResInfo (resHandle, &id, &type, name); - pluginName = String ((const char*) name + 1, name[0]); - DetachResource (resHandle); - HLock (resHandle); - - Ptr ptr; - Str255 errorText; - - OSErr err = GetMemFragment (*resHandle, GetHandleSize (resHandle), - name, kPrivateCFragCopy, - &fragId, &ptr, errorText); - - if (err == noErr) - { - moduleMain = (MainCall) newMachOFromCFM (ptr); - ok = true; - } - else - { - HUnlock (resHandle); - } - - break; - } - } - - if (! ok) - CloseResFile (resFileId); - } - } - } -#endif - - return ok; - } - - void close() - { -#if JUCE_PPC - if (fragId != 0) - { - if (moduleMain != 0) - disposeMachOFromCFM ((void*) moduleMain); - - CloseConnection (&fragId); - HUnlock (resHandle); - - if (resFileId != 0) - CloseResFile (resFileId); - } - else -#endif - if (bundleRef != 0) - { - CFBundleCloseBundleResourceMap (bundleRef, resFileId); - - if (CFGetRetainCount (bundleRef) == 1) - CFBundleUnloadExecutable (bundleRef); - - if (CFGetRetainCount (bundleRef) > 0) - CFRelease (bundleRef); - } - } - - void closeEffect (AEffect* eff) - { -#if JUCE_PPC - if (fragId != 0) - { - VoidArray thingsToDelete; - thingsToDelete.add ((void*) eff->dispatcher); - thingsToDelete.add ((void*) eff->process); - thingsToDelete.add ((void*) eff->setParameter); - thingsToDelete.add ((void*) eff->getParameter); - thingsToDelete.add ((void*) eff->processReplacing); - - eff->dispatcher (eff, effClose, 0, 0, 0, 0); - - for (int i = thingsToDelete.size(); --i >= 0;) - disposeMachOFromCFM (thingsToDelete[i]); - } - else -#endif - { - eff->dispatcher (eff, effClose, 0, 0, 0, 0); - } - } - -#if JUCE_PPC - static void* newMachOFromCFM (void* cfmfp) - { - if (cfmfp == 0) - return 0; - - UInt32* const mfp = (UInt32*) juce_malloc (sizeof (UInt32) * 6); - - mfp[0] = 0x3d800000 | ((UInt32) cfmfp >> 16); - mfp[1] = 0x618c0000 | ((UInt32) cfmfp & 0xffff); - mfp[2] = 0x800c0000; - mfp[3] = 0x804c0004; - mfp[4] = 0x7c0903a6; - mfp[5] = 0x4e800420; - - MakeDataExecutable (mfp, sizeof (UInt32) * 6); - return mfp; - } - - static void disposeMachOFromCFM (void* ptr) - { - juce_free (ptr); - } - - void coerceAEffectFunctionCalls (AEffect* eff) - { - if (fragId != 0) - { - eff->dispatcher = (AEffectDispatcherProc) newMachOFromCFM ((void*) eff->dispatcher); - eff->process = (AEffectProcessProc) newMachOFromCFM ((void*) eff->process); - eff->setParameter = (AEffectSetParameterProc) newMachOFromCFM ((void*) eff->setParameter); - eff->getParameter = (AEffectGetParameterProc) newMachOFromCFM ((void*) eff->getParameter); - eff->processReplacing = (AEffectProcessProc) newMachOFromCFM ((void*) eff->processReplacing); - } - } -#endif - -#endif -}; - - -//============================================================================== -/** - An instance of a plugin, created by a VSTPluginFormat. - -*/ -class VSTPluginInstance : public AudioPluginInstance, - private Timer, - private AsyncUpdater -{ -public: - //============================================================================== - ~VSTPluginInstance(); - - //============================================================================== - // AudioPluginInstance methods: - - const String getName() const { return name; } - const String getManufacturer() const; - const String getVersion() const; - bool isInstrument() const { return effect != 0 && (effect->flags & effFlagsIsSynth) != 0; } - const String getCategory() const; - const String getFormatName() const { return "VST"; } - const File getFile() const { return module->file; } - int getUID() const; - bool acceptsMidi() const { return wantsMidiMessages; } - bool producesMidi() const { return dispatch (effCanDo, 0, 0, (void*) "sendVstMidiEvent", 0) > 0; } - - //============================================================================== - // AudioProcessor methods: - - void prepareToPlay (double sampleRate, int estimatedSamplesPerBlock); - void releaseResources(); - void processBlock (AudioSampleBuffer& buffer, - MidiBuffer& midiMessages); - - AudioProcessorEditor* createEditor(); - - const String getInputChannelName (const int index) const; - bool isInputChannelStereoPair (int index) const; - - const String getOutputChannelName (const int index) const; - bool isOutputChannelStereoPair (int index) const; - - //============================================================================== - int getNumParameters() { return effect != 0 ? effect->numParams : 0; } - float getParameter (int index); - void setParameter (int index, float newValue); - const String getParameterName (int index); - const String getParameterText (int index); - bool isParameterAutomatable (int index) const; - - //============================================================================== - int getNumPrograms() { return effect != 0 ? effect->numPrograms : 0; } - int getCurrentProgram() { return dispatch (effGetProgram, 0, 0, 0, 0); } - void setCurrentProgram (int index); - const String getProgramName (int index); - void changeProgramName (int index, const String& newName); - - //============================================================================== - void getStateInformation (MemoryBlock& destData); - void getCurrentProgramStateInformation (MemoryBlock& destData); - void setStateInformation (const void* data, int sizeInBytes); - void setCurrentProgramStateInformation (const void* data, int sizeInBytes); - - //============================================================================== - void timerCallback(); - void handleAsyncUpdate(); - VstIntPtr handleCallback (VstInt32 opcode, VstInt32 index, VstInt32 value, void *ptr, float opt); - - //============================================================================== - juce_UseDebuggingNewOperator - -private: - friend class VSTPluginWindow; - friend class VSTPluginFormat; - - AEffect* effect; - String name; - CriticalSection lock; - bool wantsMidiMessages, initialised, isPowerOn; - mutable StringArray programNames; - AudioSampleBuffer tempBuffer; - CriticalSection midiInLock; - MidiBuffer incomingMidi; - void* midiEventsToSend; - int numAllocatedMidiEvents; - VstTimeInfo vstHostTime; - float** channels; - - ReferenceCountedObjectPtr module; - - //============================================================================== - int dispatch (const int opcode, const int index, const int value, void* const ptr, float opt) const; - bool restoreProgramSettings (const fxProgram* const prog); - const String getCurrentProgramName(); - void setParamsInProgramBlock (fxProgram* const prog) throw(); - void updateStoredProgramNames(); - void initialise(); - void ensureMidiEventSize (int numEventsNeeded); - void freeMidiEvents(); - void handleMidiFromPlugin (const VstEvents* const events); - void createTempParameterStore (MemoryBlock& dest); - void restoreFromTempParameterStore (const MemoryBlock& mb); - const String getParameterLabel (int index) const; - - bool usesChunks() const throw() { return effect != 0 && (effect->flags & effFlagsProgramChunks) != 0; } - void getChunkData (MemoryBlock& mb, bool isPreset, int maxSizeMB) const; - void setChunkData (const char* data, int size, bool isPreset); - bool loadFromFXBFile (const void* data, int numBytes); - bool saveToFXBFile (MemoryBlock& dest, bool isFXB, int maxSizeMB); - - int getVersionNumber() const throw() { return effect != 0 ? effect->version : 0; } - bool hasEditor() const throw() { return effect != 0 && (effect->flags & effFlagsHasEditor) != 0; } - void setPower (const bool on); - - VSTPluginInstance (const ReferenceCountedObjectPtr & module); -}; - -//============================================================================== -VSTPluginInstance::VSTPluginInstance (const ReferenceCountedObjectPtr & module_) - : effect (0), - wantsMidiMessages (false), - initialised (false), - isPowerOn (false), - numAllocatedMidiEvents (0), - midiEventsToSend (0), - tempBuffer (1, 1), - channels (0), - module (module_) -{ - try - { - _fpreset(); - - ++insideVSTCallback; - - name = module->pluginName; - log (T("Creating VST instance: ") + name); - -#if JUCE_MAC - if (module->resFileId != 0) - UseResFile (module->resFileId); - -#if JUCE_PPC - if (module->fragId != 0) - { - static void* audioMasterCoerced = 0; - if (audioMasterCoerced == 0) - audioMasterCoerced = NewCFMFromMachO ((void*) &audioMaster); - - effect = module->moduleMain ((audioMasterCallback) audioMasterCoerced); - } - else -#endif -#endif - { - effect = module->moduleMain (&audioMaster); - } - - --insideVSTCallback; - - if (effect != 0 && effect->magic == kEffectMagic) - { -#if JUCE_PPC - module->coerceAEffectFunctionCalls (effect); -#endif - - jassert (effect->resvd2 == 0); - jassert (effect->object != 0); - - _fpreset(); // some dodgy plugs fuck around with this - } - else - { - effect = 0; - } - } - catch (...) - { - --insideVSTCallback; - } -} - -VSTPluginInstance::~VSTPluginInstance() -{ - { - const ScopedLock sl (lock); - - jassert (insideVSTCallback == 0); - - if (effect != 0 && effect->magic == kEffectMagic) - { - try - { -#if JUCE_MAC - if (module->resFileId != 0) - UseResFile (module->resFileId); -#endif - - // Must delete any editors before deleting the plugin instance! - jassert (getActiveEditor() == 0); - - _fpreset(); // some dodgy plugs fuck around with this - - module->closeEffect (effect); - } - catch (...) - {} - } - - module = 0; - effect = 0; - } - - freeMidiEvents(); - - juce_free (channels); - channels = 0; -} - -//============================================================================== -void VSTPluginInstance::initialise() -{ - if (initialised || effect == 0) - return; - - log (T("Initialising VST: ") + module->pluginName); - initialised = true; - - dispatch (effIdentify, 0, 0, 0, 0); - - // this code would ask the plugin for its name, but so few plugins - // actually bother implementing this correctly, that it's better to - // just ignore it and use the file name instead. -/* { - char buffer [256]; - zerostruct (buffer); - dispatch (effGetEffectName, 0, 0, buffer, 0); - - name = String (buffer).trim(); - if (name.isEmpty()) - name = module->pluginName; - } -*/ - - if (getSampleRate() > 0) - dispatch (effSetSampleRate, 0, 0, 0, (float) getSampleRate()); - - if (getBlockSize() > 0) - dispatch (effSetBlockSize, 0, jmax (32, getBlockSize()), 0, 0); - - dispatch (effOpen, 0, 0, 0, 0); - - setPlayConfigDetails (effect->numInputs, effect->numOutputs, - getSampleRate(), getBlockSize()); - - if (getNumPrograms() > 1) - setCurrentProgram (0); - else - dispatch (effSetProgram, 0, 0, 0, 0); - - int i; - for (i = effect->numInputs; --i >= 0;) - dispatch (effConnectInput, i, 1, 0, 0); - - for (i = effect->numOutputs; --i >= 0;) - dispatch (effConnectOutput, i, 1, 0, 0); - - updateStoredProgramNames(); - - wantsMidiMessages = dispatch (effCanDo, 0, 0, (void*) "receiveVstMidiEvent", 0) > 0; - - setLatencySamples (effect->initialDelay); -} - - -//============================================================================== -void VSTPluginInstance::prepareToPlay (double sampleRate_, - int samplesPerBlockExpected) -{ - setPlayConfigDetails (effect->numInputs, effect->numOutputs, - sampleRate_, samplesPerBlockExpected); - - setLatencySamples (effect->initialDelay); - - juce_free (channels); - channels = (float**) juce_calloc (sizeof (float*) * jmax (16, getNumOutputChannels() + 2, getNumInputChannels() + 2)); - - vstHostTime.tempo = 120.0; - vstHostTime.timeSigNumerator = 4; - vstHostTime.timeSigDenominator = 4; - vstHostTime.sampleRate = sampleRate_; - vstHostTime.samplePos = 0; - vstHostTime.flags = kVstNanosValid; /*| kVstTransportPlaying | kVstTempoValid | kVstTimeSigValid*/; - - initialise(); - - if (initialised) - { - wantsMidiMessages = wantsMidiMessages - || (dispatch (effCanDo, 0, 0, (void*) "receiveVstMidiEvent", 0) > 0); - - if (wantsMidiMessages) - ensureMidiEventSize (256); - else - freeMidiEvents(); - - incomingMidi.clear(); - - dispatch (effSetSampleRate, 0, 0, 0, (float) sampleRate_); - dispatch (effSetBlockSize, 0, jmax (16, samplesPerBlockExpected), 0, 0); - - tempBuffer.setSize (jmax (1, effect->numOutputs), samplesPerBlockExpected); - - if (! isPowerOn) - setPower (true); - - // dodgy hack to force some plugins to initialise the sample rate.. - if ((! hasEditor()) && getNumParameters() > 0) - { - const float old = getParameter (0); - setParameter (0, (old < 0.5f) ? 1.0f : 0.0f); - setParameter (0, old); - } - - dispatch (effStartProcess, 0, 0, 0, 0); - } -} - -void VSTPluginInstance::releaseResources() -{ - if (initialised) - { - dispatch (effStopProcess, 0, 0, 0, 0); - setPower (false); - } - - tempBuffer.setSize (1, 1); - incomingMidi.clear(); - - freeMidiEvents(); - juce_free (channels); - channels = 0; -} - -void VSTPluginInstance::processBlock (AudioSampleBuffer& buffer, - MidiBuffer& midiMessages) -{ - const int numSamples = buffer.getNumSamples(); - - if (initialised) - { -#if JUCE_WIN32 - vstHostTime.nanoSeconds = timeGetTime() * 1000000.0; -#elif JUCE_LINUX - timeval micro; - gettimeofday (µ, 0); - vstHostTime.nanoSeconds = micro.tv_usec * 1000.0; -#elif JUCE_MAC - UnsignedWide micro; - Microseconds (µ); - vstHostTime.nanoSeconds = micro.lo * 1000.0; -#endif - - if (wantsMidiMessages) - { - MidiBuffer::Iterator iter (midiMessages); - - int eventIndex = 0; - const uint8* midiData; - int numBytesOfMidiData, samplePosition; - - while (iter.getNextEvent (midiData, numBytesOfMidiData, samplePosition)) - { - if (numBytesOfMidiData < 4) - { - ensureMidiEventSize (eventIndex); - VstMidiEvent* const e - = (VstMidiEvent*) ((VstEvents*) midiEventsToSend)->events [eventIndex++]; - - // check that some plugin hasn't messed up our objects - jassert (e->type == kVstMidiType); - jassert (e->byteSize == 24); - - e->deltaFrames = jlimit (0, numSamples - 1, samplePosition); - e->noteLength = 0; - e->noteOffset = 0; - e->midiData[0] = midiData[0]; - e->midiData[1] = midiData[1]; - e->midiData[2] = midiData[2]; - e->detune = 0; - e->noteOffVelocity = 0; - } - } - - if (midiEventsToSend == 0) - ensureMidiEventSize (1); - - ((VstEvents*) midiEventsToSend)->numEvents = eventIndex; - - try - { - effect->dispatcher (effect, effProcessEvents, 0, 0, midiEventsToSend, 0); - } - catch (...) - {} - } - - int i; - const int maxChans = jmax (effect->numInputs, effect->numOutputs); - - for (i = 0; i < maxChans; ++i) - channels[i] = buffer.getSampleData (i); - - channels [maxChans] = 0; - - _clearfp(); - - if ((effect->flags & effFlagsCanReplacing) != 0) - { - try - { - effect->processReplacing (effect, channels, channels, numSamples); - } - catch (...) - {} - } - else - { - tempBuffer.setSize (effect->numOutputs, numSamples); - tempBuffer.clear(); - - float* outs [64]; - - for (i = effect->numOutputs; --i >= 0;) - outs[i] = tempBuffer.getSampleData (i); - - outs [effect->numOutputs] = 0; - - try - { - effect->process (effect, channels, outs, numSamples); - } - catch (...) - {} - - for (i = effect->numOutputs; --i >= 0;) - buffer.copyFrom (i, 0, outs[i], numSamples); - } - } - else - { - // Not initialised, so just bypass.. - for (int i = getNumInputChannels(); i < getNumOutputChannels(); ++i) - buffer.clear (i, 0, buffer.getNumSamples()); - } - - { - // copy any incoming midi.. - const ScopedLock sl (midiInLock); - - midiMessages = incomingMidi; - incomingMidi.clear(); - } -} - -//============================================================================== -void VSTPluginInstance::ensureMidiEventSize (int numEventsNeeded) -{ - if (numEventsNeeded > numAllocatedMidiEvents) - { - numEventsNeeded = (numEventsNeeded + 32) & ~31; - - const int size = 20 + sizeof (VstEvent*) * numEventsNeeded; - - if (midiEventsToSend == 0) - midiEventsToSend = juce_calloc (size); - else - midiEventsToSend = juce_realloc (midiEventsToSend, size); - - for (int i = numAllocatedMidiEvents; i < numEventsNeeded; ++i) - { - VstMidiEvent* const e = (VstMidiEvent*) juce_calloc (sizeof (VstMidiEvent)); - e->type = kVstMidiType; - e->byteSize = 24; - - ((VstEvents*) midiEventsToSend)->events[i] = (VstEvent*) e; - } - - numAllocatedMidiEvents = numEventsNeeded; - } -} - -void VSTPluginInstance::freeMidiEvents() -{ - if (midiEventsToSend != 0) - { - for (int i = numAllocatedMidiEvents; --i >= 0;) - juce_free (((VstEvents*) midiEventsToSend)->events[i]); - - juce_free (midiEventsToSend); - midiEventsToSend = 0; - numAllocatedMidiEvents = 0; - } -} - -void VSTPluginInstance::handleMidiFromPlugin (const VstEvents* const events) -{ - if (events != 0) - { - const ScopedLock sl (midiInLock); - - for (int i = 0; i < events->numEvents; ++i) - { - const VstEvent* const e = events->events[i]; - - if (e->type == kVstMidiType) - { - incomingMidi.addEvent ((const uint8*) ((const VstMidiEvent*) e)->midiData, - 3, e->deltaFrames); - } - } - } -} - -//============================================================================== -static Array activeWindows; - -//============================================================================== -class VSTPluginWindow : public AudioProcessorEditor, - public Timer -{ -public: - //============================================================================== - VSTPluginWindow (VSTPluginInstance& plugin_) - : AudioProcessorEditor (&plugin_), - plugin (plugin_), - isOpen (false), - wasShowing (false), - pluginRefusesToResize (false), - pluginWantsKeys (false), - alreadyInside (false), - recursiveResize (false) - { -#if JUCE_WIN32 - sizeCheckCount = 0; - pluginHWND = 0; -#elif JUCE_LINUX - pluginWindow = None; - pluginProc = None; -#else - pluginViewRef = 0; -#endif - - movementWatcher = new CompMovementWatcher (this); - - activeWindows.add (this); - - setSize (1, 1); - setOpaque (true); - setVisible (true); - } - - ~VSTPluginWindow() - { - deleteAndZero (movementWatcher); - - closePluginWindow(); - - activeWindows.removeValue (this); - plugin.editorBeingDeleted (this); - } - - //============================================================================== - void componentMovedOrResized() - { - if (recursiveResize) - return; - - Component* const topComp = getTopLevelComponent(); - - if (topComp->getPeer() != 0) - { - int x = 0, y = 0; - relativePositionToOtherComponent (topComp, x, y); - - recursiveResize = true; - -#if JUCE_MAC - if (pluginViewRef != 0) - { - HIRect r; - r.origin.x = (float) x; - r.origin.y = (float) y; - r.size.width = (float) getWidth(); - r.size.height = (float) getHeight(); - HIViewSetFrame (pluginViewRef, &r); - } - else if (pluginWindowRef != 0) - { - Rect r; - r.left = getScreenX(); - r.top = getScreenY(); - r.right = r.left + getWidth(); - r.bottom = r.top + getHeight(); - - WindowGroupRef group = GetWindowGroup (pluginWindowRef); - WindowGroupAttributes atts; - GetWindowGroupAttributes (group, &atts); - ChangeWindowGroupAttributes (group, 0, kWindowGroupAttrMoveTogether); - - SetWindowBounds (pluginWindowRef, kWindowContentRgn, &r); - - if ((atts & kWindowGroupAttrMoveTogether) != 0) - ChangeWindowGroupAttributes (group, kWindowGroupAttrMoveTogether, 0); - } - else - { - repaint(); - } -#elif JUCE_WIN32 - if (pluginHWND != 0) - MoveWindow (pluginHWND, x, y, getWidth(), getHeight(), TRUE); -#elif JUCE_LINUX - if (pluginWindow != 0) - { - XResizeWindow (display, pluginWindow, getWidth(), getHeight()); - XMoveWindow (display, pluginWindow, x, y); - XMapRaised (display, pluginWindow); - } -#endif - - recursiveResize = false; - } - } - - void componentVisibilityChanged() - { - const bool isShowingNow = isShowing(); - - if (wasShowing != isShowingNow) - { - wasShowing = isShowingNow; - - if (isShowingNow) - openPluginWindow(); - else - closePluginWindow(); - } - - componentMovedOrResized(); - } - - void componentPeerChanged() - { - closePluginWindow(); - openPluginWindow(); - } - - //============================================================================== - bool keyStateChanged() - { - return pluginWantsKeys; - } - - bool keyPressed (const KeyPress&) - { - return pluginWantsKeys; - } - - //============================================================================== - void paint (Graphics& g) - { - if (isOpen) - { - ComponentPeer* const peer = getPeer(); - - if (peer != 0) - { - peer->addMaskedRegion (getScreenX() - peer->getScreenX(), - getScreenY() - peer->getScreenY(), - getWidth(), getHeight()); - -#if JUCE_MAC - if (pluginViewRef == 0) - { - ERect r; - r.left = getScreenX() - peer->getScreenX(); - r.right = r.left + getWidth(); - r.top = getScreenY() - peer->getScreenY(); - r.bottom = r.top + getHeight(); - - dispatch (effEditDraw, 0, 0, &r, 0); - } -#elif JUCE_LINUX - if (pluginWindow != 0) - { - const Rectangle clip (g.getClipBounds()); - - XEvent ev; - zerostruct (ev); - ev.xexpose.type = Expose; - ev.xexpose.display = display; - ev.xexpose.window = pluginWindow; - ev.xexpose.x = clip.getX(); - ev.xexpose.y = clip.getY(); - ev.xexpose.width = clip.getWidth(); - ev.xexpose.height = clip.getHeight(); - - sendEventToChild (&ev); - } -#endif - } - } - else - { - g.fillAll (Colours::black); - } - } - - //============================================================================== - void timerCallback() - { -#if JUCE_WIN32 - if (--sizeCheckCount <= 0) - { - sizeCheckCount = 10; - - checkPluginWindowSize(); - } -#endif - - try - { - static bool reentrant = false; - - if (! reentrant) - { - reentrant = true; - plugin.dispatch (effEditIdle, 0, 0, 0, 0); - reentrant = false; - } - } - catch (...) - {} - } - - //============================================================================== - void mouseDown (const MouseEvent& e) - { -#if JUCE_MAC - if (! alreadyInside) - { - alreadyInside = true; - toFront (true); - dispatch (effEditMouse, e.x, e.y, 0, 0); - alreadyInside = false; - } - else - { - PostEvent (::mouseDown, 0); - } -#elif JUCE_LINUX - - if (pluginWindow == 0) - return; - - toFront (true); - - XEvent ev; - zerostruct (ev); - ev.xbutton.display = display; - ev.xbutton.type = ButtonPress; - ev.xbutton.window = pluginWindow; - ev.xbutton.root = RootWindow (display, DefaultScreen (display)); - ev.xbutton.time = CurrentTime; - ev.xbutton.x = e.x; - ev.xbutton.y = e.y; - ev.xbutton.x_root = e.getScreenX(); - ev.xbutton.y_root = e.getScreenY(); - - translateJuceToXButtonModifiers (e, ev); - - sendEventToChild (&ev); - -#else - (void) e; - - toFront (true); -#endif - } - - void broughtToFront() - { - activeWindows.removeValue (this); - activeWindows.add (this); - -#if JUCE_MAC - dispatch (effEditTop, 0, 0, 0, 0); -#endif - } - - //============================================================================== - juce_UseDebuggingNewOperator - -private: - VSTPluginInstance& plugin; - bool isOpen, wasShowing, recursiveResize; - bool pluginWantsKeys, pluginRefusesToResize, alreadyInside; - -#if JUCE_WIN32 - HWND pluginHWND; - void* originalWndProc; - int sizeCheckCount; -#elif JUCE_MAC - HIViewRef pluginViewRef; - WindowRef pluginWindowRef; -#elif JUCE_LINUX - Window pluginWindow; - EventProcPtr pluginProc; -#endif - - //============================================================================== - void openPluginWindow() - { - if (isOpen || getWindowHandle() == 0) - return; - - log (T("Opening VST UI: ") + plugin.getName()); - isOpen = true; - - ERect* rect = 0; - dispatch (effEditGetRect, 0, 0, &rect, 0); - dispatch (effEditOpen, 0, 0, getWindowHandle(), 0); - - // do this before and after like in the steinberg example - dispatch (effEditGetRect, 0, 0, &rect, 0); - dispatch (effGetProgram, 0, 0, 0, 0); // also in steinberg code - - // Install keyboard hooks - pluginWantsKeys = (dispatch (effKeysRequired, 0, 0, 0, 0) == 0); - -#if JUCE_WIN32 - originalWndProc = 0; - pluginHWND = GetWindow ((HWND) getWindowHandle(), GW_CHILD); - - if (pluginHWND == 0) - { - isOpen = false; - setSize (300, 150); - return; - } - - #pragma warning (push) - #pragma warning (disable: 4244) - - originalWndProc = (void*) GetWindowLongPtr (pluginHWND, GWL_WNDPROC); - - if (! pluginWantsKeys) - SetWindowLongPtr (pluginHWND, GWL_WNDPROC, (LONG_PTR) vstHookWndProc); - - #pragma warning (pop) - - int w, h; - RECT r; - GetWindowRect (pluginHWND, &r); - w = r.right - r.left; - h = r.bottom - r.top; - - if (rect != 0) - { - const int rw = rect->right - rect->left; - const int rh = rect->bottom - rect->top; - - if ((rw > 50 && rh > 50 && rw < 2000 && rh < 2000 && rw != w && rh != h) - || ((w == 0 && rw > 0) || (h == 0 && rh > 0))) - { - // very dodgy logic to decide which size is right. - if (abs (rw - w) > 350 || abs (rh - h) > 350) - { - SetWindowPos (pluginHWND, 0, - 0, 0, rw, rh, - SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER); - - GetWindowRect (pluginHWND, &r); - - w = r.right - r.left; - h = r.bottom - r.top; - - pluginRefusesToResize = (w != rw) || (h != rh); - - w = rw; - h = rh; - } - } - } -#elif JUCE_MAC - HIViewRef root = HIViewGetRoot ((WindowRef) getWindowHandle()); - HIViewFindByID (root, kHIViewWindowContentID, &root); - pluginViewRef = HIViewGetFirstSubview (root); - - while (pluginViewRef != 0 && juce_isHIViewCreatedByJuce (pluginViewRef)) - pluginViewRef = HIViewGetNextView (pluginViewRef); - - pluginWindowRef = 0; - - if (pluginViewRef == 0) - { - WindowGroupRef ourGroup = GetWindowGroup ((WindowRef) getWindowHandle()); - //DebugPrintWindowGroup (ourGroup); - //DebugPrintAllWindowGroups(); - - GetIndexedWindow (ourGroup, 1, - kWindowGroupContentsVisible, - &pluginWindowRef); - - if (pluginWindowRef == (WindowRef) getWindowHandle() - || juce_isWindowCreatedByJuce (pluginWindowRef)) - pluginWindowRef = 0; - } - - int w = 250, h = 150; - - if (rect != 0) - { - w = rect->right - rect->left; - h = rect->bottom - rect->top; - - if (w == 0 || h == 0) - { - w = 250; - h = 150; - } - } - -#elif JUCE_LINUX - pluginWindow = getChildWindow ((Window) getWindowHandle()); - - if (pluginWindow != 0) - pluginProc = (EventProcPtr) getPropertyFromXWindow (pluginWindow, - XInternAtom (display, "_XEventProc", False)); - - int w = 250, h = 150; - - if (rect != 0) - { - w = rect->right - rect->left; - h = rect->bottom - rect->top; - - if (w == 0 || h == 0) - { - w = 250; - h = 150; - } - } - - if (pluginWindow != 0) - XMapRaised (display, pluginWindow); -#endif - - // double-check it's not too tiny - w = jmax (w, 32); - h = jmax (h, 32); - - setSize (w, h); - -#if JUCE_WIN32 - checkPluginWindowSize(); -#endif - - startTimer (18 + JUCE_NAMESPACE::Random::getSystemRandom().nextInt (5)); - repaint(); - } - - //============================================================================== - void closePluginWindow() - { - if (isOpen) - { - log (T("Closing VST UI: ") + plugin.getName()); - isOpen = false; - - dispatch (effEditClose, 0, 0, 0, 0); - -#if JUCE_WIN32 - #pragma warning (push) - #pragma warning (disable: 4244) - - if (pluginHWND != 0 && IsWindow (pluginHWND)) - SetWindowLongPtr (pluginHWND, GWL_WNDPROC, (LONG_PTR) originalWndProc); - - #pragma warning (pop) - - stopTimer(); - - if (pluginHWND != 0 && IsWindow (pluginHWND)) - DestroyWindow (pluginHWND); - - pluginHWND = 0; -#elif JUCE_MAC - dispatch (effEditSleep, 0, 0, 0, 0); - pluginViewRef = 0; - stopTimer(); -#elif JUCE_LINUX - stopTimer(); - pluginWindow = 0; - pluginProc = 0; -#endif - } - } - - //============================================================================== -#if JUCE_WIN32 - void checkPluginWindowSize() throw() - { - RECT r; - GetWindowRect (pluginHWND, &r); - const int w = r.right - r.left; - const int h = r.bottom - r.top; - - if (isShowing() && w > 0 && h > 0 - && (w != getWidth() || h != getHeight()) - && ! pluginRefusesToResize) - { - setSize (w, h); - sizeCheckCount = 0; - } - } -#endif - - //============================================================================== - class CompMovementWatcher : public ComponentMovementWatcher - { - public: - CompMovementWatcher (VSTPluginWindow* const owner_) - : ComponentMovementWatcher (owner_), - owner (owner_) - { - } - - //============================================================================== - void componentMovedOrResized (bool /*wasMoved*/, bool /*wasResized*/) - { - owner->componentMovedOrResized(); - } - - void componentPeerChanged() - { - owner->componentPeerChanged(); - } - - void componentVisibilityChanged (Component&) - { - owner->componentVisibilityChanged(); - } - - private: - VSTPluginWindow* const owner; - }; - - CompMovementWatcher* movementWatcher; - - //============================================================================== - int dispatch (const int opcode, const int index, const int value, void* const ptr, float opt) - { - return plugin.dispatch (opcode, index, value, ptr, opt); - } - - //============================================================================== - // hooks to get keyboard events from VST windows.. -#if JUCE_WIN32 - static LRESULT CALLBACK vstHookWndProc (HWND hW, UINT message, WPARAM wParam, LPARAM lParam) - { - for (int i = activeWindows.size(); --i >= 0;) - { - const VSTPluginWindow* const w = (const VSTPluginWindow*) activeWindows.getUnchecked (i); - - if (w->pluginHWND == hW) - { - if (message == WM_CHAR - || message == WM_KEYDOWN - || message == WM_SYSKEYDOWN - || message == WM_KEYUP - || message == WM_SYSKEYUP - || message == WM_APPCOMMAND) - { - SendMessage ((HWND) w->getTopLevelComponent()->getWindowHandle(), - message, wParam, lParam); - } - - return CallWindowProc ((WNDPROC) (w->originalWndProc), - (HWND) w->pluginHWND, - message, - wParam, - lParam); - } - } - - return DefWindowProc (hW, message, wParam, lParam); - } -#endif - -#if JUCE_LINUX - //============================================================================== - // overload mouse/keyboard events to forward them to the plugin's inner window.. - void sendEventToChild (XEvent* event) - { - if (pluginProc != 0) - { - // if the plugin publishes an event procedure, pass the event directly.. - pluginProc (event); - } - else if (pluginWindow != 0) - { - // if the plugin has a window, then send the event to the window so that - // its message thread will pick it up.. - XSendEvent (display, pluginWindow, False, 0L, event); - XFlush (display); - } - } - - void mouseEnter (const MouseEvent& e) - { - if (pluginWindow != 0) - { - XEvent ev; - zerostruct (ev); - ev.xcrossing.display = display; - ev.xcrossing.type = EnterNotify; - ev.xcrossing.window = pluginWindow; - ev.xcrossing.root = RootWindow (display, DefaultScreen (display)); - ev.xcrossing.time = CurrentTime; - ev.xcrossing.x = e.x; - ev.xcrossing.y = e.y; - ev.xcrossing.x_root = e.getScreenX(); - ev.xcrossing.y_root = e.getScreenY(); - ev.xcrossing.mode = NotifyNormal; // NotifyGrab, NotifyUngrab - ev.xcrossing.detail = NotifyAncestor; // NotifyVirtual, NotifyInferior, NotifyNonlinear,NotifyNonlinearVirtual - - translateJuceToXCrossingModifiers (e, ev); - - sendEventToChild (&ev); - } - } - - void mouseExit (const MouseEvent& e) - { - if (pluginWindow != 0) - { - XEvent ev; - zerostruct (ev); - ev.xcrossing.display = display; - ev.xcrossing.type = LeaveNotify; - ev.xcrossing.window = pluginWindow; - ev.xcrossing.root = RootWindow (display, DefaultScreen (display)); - ev.xcrossing.time = CurrentTime; - ev.xcrossing.x = e.x; - ev.xcrossing.y = e.y; - ev.xcrossing.x_root = e.getScreenX(); - ev.xcrossing.y_root = e.getScreenY(); - ev.xcrossing.mode = NotifyNormal; // NotifyGrab, NotifyUngrab - ev.xcrossing.detail = NotifyAncestor; // NotifyVirtual, NotifyInferior, NotifyNonlinear,NotifyNonlinearVirtual - ev.xcrossing.focus = hasKeyboardFocus (true); // TODO - yes ? - - translateJuceToXCrossingModifiers (e, ev); - - sendEventToChild (&ev); - } - } - - void mouseMove (const MouseEvent& e) - { - if (pluginWindow != 0) - { - XEvent ev; - zerostruct (ev); - ev.xmotion.display = display; - ev.xmotion.type = MotionNotify; - ev.xmotion.window = pluginWindow; - ev.xmotion.root = RootWindow (display, DefaultScreen (display)); - ev.xmotion.time = CurrentTime; - ev.xmotion.is_hint = NotifyNormal; - ev.xmotion.x = e.x; - ev.xmotion.y = e.y; - ev.xmotion.x_root = e.getScreenX(); - ev.xmotion.y_root = e.getScreenY(); - - sendEventToChild (&ev); - } - } - - void mouseDrag (const MouseEvent& e) - { - if (pluginWindow != 0) - { - XEvent ev; - zerostruct (ev); - ev.xmotion.display = display; - ev.xmotion.type = MotionNotify; - ev.xmotion.window = pluginWindow; - ev.xmotion.root = RootWindow (display, DefaultScreen (display)); - ev.xmotion.time = CurrentTime; - ev.xmotion.x = e.x ; - ev.xmotion.y = e.y; - ev.xmotion.x_root = e.getScreenX(); - ev.xmotion.y_root = e.getScreenY(); - ev.xmotion.is_hint = NotifyNormal; - - translateJuceToXMotionModifiers (e, ev); - sendEventToChild (&ev); - } - } - - void mouseUp (const MouseEvent& e) - { - if (pluginWindow != 0) - { - XEvent ev; - zerostruct (ev); - ev.xbutton.display = display; - ev.xbutton.type = ButtonRelease; - ev.xbutton.window = pluginWindow; - ev.xbutton.root = RootWindow (display, DefaultScreen (display)); - ev.xbutton.time = CurrentTime; - ev.xbutton.x = e.x; - ev.xbutton.y = e.y; - ev.xbutton.x_root = e.getScreenX(); - ev.xbutton.y_root = e.getScreenY(); - - translateJuceToXButtonModifiers (e, ev); - sendEventToChild (&ev); - } - } - - void mouseWheelMove (const MouseEvent& e, - float incrementX, - float incrementY) - { - if (pluginWindow != 0) - { - XEvent ev; - zerostruct (ev); - ev.xbutton.display = display; - ev.xbutton.type = ButtonPress; - ev.xbutton.window = pluginWindow; - ev.xbutton.root = RootWindow (display, DefaultScreen (display)); - ev.xbutton.time = CurrentTime; - ev.xbutton.x = e.x; - ev.xbutton.y = e.y; - ev.xbutton.x_root = e.getScreenX(); - ev.xbutton.y_root = e.getScreenY(); - - translateJuceToXMouseWheelModifiers (e, incrementY, ev); - sendEventToChild (&ev); - - // TODO - put a usleep here ? - - ev.xbutton.type = ButtonRelease; - sendEventToChild (&ev); - } - } -#endif - -}; - -//============================================================================== -AudioProcessorEditor* VSTPluginInstance::createEditor() -{ - if (hasEditor()) - return new VSTPluginWindow (*this); - - return 0; -} - - -//============================================================================== -void VSTPluginInstance::handleAsyncUpdate() -{ - // indicates that something about the plugin has changed.. - updateHostDisplay(); -} - -//============================================================================== -bool VSTPluginInstance::restoreProgramSettings (const fxProgram* const prog) -{ - if (swap (prog->chunkMagic) == 'CcnK' && swap (prog->fxMagic) == 'FxCk') - { - changeProgramName (getCurrentProgram(), prog->prgName); - - for (int i = 0; i < swap (prog->numParams); ++i) - setParameter (i, swapFloat (prog->params[i])); - - return true; - } - - return false; -} - -bool VSTPluginInstance::loadFromFXBFile (const void* const data, - const int dataSize) -{ - if (dataSize < 28) - return false; - - const fxSet* const set = (const fxSet*) data; - - if ((swap (set->chunkMagic) != 'CcnK' && swap (set->chunkMagic) != 'KncC') - || swap (set->version) > fxbVersionNum) - return false; - - if (swap (set->fxMagic) == 'FxBk') - { - // bank of programs - if (swap (set->numPrograms) >= 0) - { - const int oldProg = getCurrentProgram(); - const int numParams = swap (((const fxProgram*) (set->programs))->numParams); - const int progLen = sizeof (fxProgram) + (numParams - 1) * sizeof (float); - - for (int i = 0; i < swap (set->numPrograms); ++i) - { - if (i != oldProg) - { - const fxProgram* const prog = (const fxProgram*) (((const char*) (set->programs)) + i * progLen); - if (((const char*) prog) - ((const char*) set) >= dataSize) - return false; - - if (swap (set->numPrograms) > 0) - setCurrentProgram (i); - - if (! restoreProgramSettings (prog)) - return false; - } - } - - if (swap (set->numPrograms) > 0) - setCurrentProgram (oldProg); - - const fxProgram* const prog = (const fxProgram*) (((const char*) (set->programs)) + oldProg * progLen); - if (((const char*) prog) - ((const char*) set) >= dataSize) - return false; - - if (! restoreProgramSettings (prog)) - return false; - } - } - else if (swap (set->fxMagic) == 'FxCk') - { - // single program - const fxProgram* const prog = (const fxProgram*) data; - - if (swap (prog->chunkMagic) != 'CcnK') - return false; - - changeProgramName (getCurrentProgram(), prog->prgName); - - for (int i = 0; i < swap (prog->numParams); ++i) - setParameter (i, swapFloat (prog->params[i])); - } - else if (swap (set->fxMagic) == 'FBCh' || swap (set->fxMagic) == 'hCBF') - { - // non-preset chunk - const fxChunkSet* const cset = (const fxChunkSet*) data; - - if (swap (cset->chunkSize) + sizeof (fxChunkSet) - 8 > (unsigned int) dataSize) - return false; - - setChunkData (cset->chunk, swap (cset->chunkSize), false); - } - else if (swap (set->fxMagic) == 'FPCh' || swap (set->fxMagic) == 'hCPF') - { - // preset chunk - const fxProgramSet* const cset = (const fxProgramSet*) data; - - if (swap (cset->chunkSize) + sizeof (fxProgramSet) - 8 > (unsigned int) dataSize) - return false; - - setChunkData (cset->chunk, swap (cset->chunkSize), true); - - changeProgramName (getCurrentProgram(), cset->name); - } - else - { - return false; - } - - return true; -} - -//============================================================================== -void VSTPluginInstance::setParamsInProgramBlock (fxProgram* const prog) throw() -{ - const int numParams = getNumParameters(); - - prog->chunkMagic = swap ('CcnK'); - prog->byteSize = 0; - prog->fxMagic = swap ('FxCk'); - prog->version = swap (fxbVersionNum); - prog->fxID = swap (getUID()); - prog->fxVersion = swap (getVersionNumber()); - prog->numParams = swap (numParams); - - getCurrentProgramName().copyToBuffer (prog->prgName, sizeof (prog->prgName) - 1); - - for (int i = 0; i < numParams; ++i) - prog->params[i] = swapFloat (getParameter (i)); -} - -bool VSTPluginInstance::saveToFXBFile (MemoryBlock& dest, bool isFXB, int maxSizeMB) -{ - const int numPrograms = getNumPrograms(); - const int numParams = getNumParameters(); - - if (usesChunks()) - { - if (isFXB) - { - MemoryBlock chunk; - getChunkData (chunk, false, maxSizeMB); - - const int totalLen = sizeof (fxChunkSet) + chunk.getSize() - 8; - dest.setSize (totalLen, true); - - fxChunkSet* const set = (fxChunkSet*) dest.getData(); - set->chunkMagic = swap ('CcnK'); - set->byteSize = 0; - set->fxMagic = swap ('FBCh'); - set->version = swap (fxbVersionNum); - set->fxID = swap (getUID()); - set->fxVersion = swap (getVersionNumber()); - set->numPrograms = swap (numPrograms); - set->chunkSize = swap (chunk.getSize()); - - chunk.copyTo (set->chunk, 0, chunk.getSize()); - } - else - { - MemoryBlock chunk; - getChunkData (chunk, true, maxSizeMB); - - const int totalLen = sizeof (fxProgramSet) + chunk.getSize() - 8; - dest.setSize (totalLen, true); - - fxProgramSet* const set = (fxProgramSet*) dest.getData(); - set->chunkMagic = swap ('CcnK'); - set->byteSize = 0; - set->fxMagic = swap ('FPCh'); - set->version = swap (fxbVersionNum); - set->fxID = swap (getUID()); - set->fxVersion = swap (getVersionNumber()); - set->numPrograms = swap (numPrograms); - set->chunkSize = swap (chunk.getSize()); - - getCurrentProgramName().copyToBuffer (set->name, sizeof (set->name) - 1); - chunk.copyTo (set->chunk, 0, chunk.getSize()); - } - } - else - { - if (isFXB) - { - const int progLen = sizeof (fxProgram) + (numParams - 1) * sizeof (float); - const int len = (sizeof (fxSet) - sizeof (fxProgram)) + progLen * jmax (1, numPrograms); - dest.setSize (len, true); - - fxSet* const set = (fxSet*) dest.getData(); - set->chunkMagic = swap ('CcnK'); - set->byteSize = 0; - set->fxMagic = swap ('FxBk'); - set->version = swap (fxbVersionNum); - set->fxID = swap (getUID()); - set->fxVersion = swap (getVersionNumber()); - set->numPrograms = swap (numPrograms); - - const int oldProgram = getCurrentProgram(); - MemoryBlock oldSettings; - createTempParameterStore (oldSettings); - - setParamsInProgramBlock ((fxProgram*) (((char*) (set->programs)) + oldProgram * progLen)); - - for (int i = 0; i < numPrograms; ++i) - { - if (i != oldProgram) - { - setCurrentProgram (i); - setParamsInProgramBlock ((fxProgram*) (((char*) (set->programs)) + i * progLen)); - } - } - - setCurrentProgram (oldProgram); - restoreFromTempParameterStore (oldSettings); - } - else - { - const int totalLen = sizeof (fxProgram) + (numParams - 1) * sizeof (float); - dest.setSize (totalLen, true); - - setParamsInProgramBlock ((fxProgram*) dest.getData()); - } - } - - return true; -} - -void VSTPluginInstance::getChunkData (MemoryBlock& mb, bool isPreset, int maxSizeMB) const -{ - if (usesChunks()) - { - void* data = 0; - const int bytes = dispatch (effGetChunk, isPreset ? 1 : 0, 0, &data, 0.0f); - - if (data != 0 && bytes <= maxSizeMB * 1024 * 1024) - { - mb.setSize (bytes); - mb.copyFrom (data, 0, bytes); - } - } -} - -void VSTPluginInstance::setChunkData (const char* data, int size, bool isPreset) -{ - if (size > 0 && usesChunks()) - { - dispatch (effSetChunk, isPreset ? 1 : 0, size, (void*) data, 0.0f); - - if (! isPreset) - updateStoredProgramNames(); - } -} - -//============================================================================== -void VSTPluginInstance::timerCallback() -{ - if (dispatch (effIdle, 0, 0, 0, 0) == 0) - stopTimer(); -} - -int VSTPluginInstance::dispatch (const int opcode, const int index, const int value, void* const ptr, float opt) const -{ - const ScopedLock sl (lock); - - ++insideVSTCallback; - int result = 0; - - try - { - if (effect != 0) - { -#if JUCE_MAC - if (module->resFileId != 0) - UseResFile (module->resFileId); - - CGrafPtr oldPort; - - if (getActiveEditor() != 0) - { - int x = 0, y = 0; - getActiveEditor()->relativePositionToOtherComponent (getActiveEditor()->getTopLevelComponent(), x, y); - - GetPort (&oldPort); - SetPortWindowPort ((WindowRef) getActiveEditor()->getWindowHandle()); - SetOrigin (-x, -y); - } -#endif - - result = effect->dispatcher (effect, opcode, index, value, ptr, opt); - -#if JUCE_MAC - if (getActiveEditor() != 0) - SetPort (oldPort); - - module->resFileId = CurResFile(); -#endif - - --insideVSTCallback; - return result; - } - } - catch (...) - { - //char s[512]; - //sprintf (s, "dispatcher (%d, %d, %d, %x, %f)", opcode, index, value, (int)ptr, opt); - } - - --insideVSTCallback; - return result; -} - -//============================================================================== -// handles non plugin-specific callbacks.. -static VstIntPtr handleGeneralCallback (VstInt32 opcode, VstInt32 index, VstInt32 value, void *ptr, float opt) -{ - (void) index; - (void) value; - (void) opt; - - switch (opcode) - { - case audioMasterCanDo: - { - static const char* canDos[] = { "supplyIdle", - "sendVstEvents", - "sendVstMidiEvent", - "sendVstTimeInfo", - "receiveVstEvents", - "receiveVstMidiEvent", - "supportShell", - "shellCategory" }; - - for (int i = 0; i < numElementsInArray (canDos); ++i) - if (strcmp (canDos[i], (const char*) ptr) == 0) - return 1; - - return 0; - } - - case audioMasterVersion: - return 0x2400; - case audioMasterCurrentId: - return shellUIDToCreate; - case audioMasterGetNumAutomatableParameters: - return 0; - case audioMasterGetAutomationState: - return 1; - - case audioMasterGetVendorVersion: - return 0x0101; - case audioMasterGetVendorString: - case audioMasterGetProductString: - JUCEApplication::getInstance() - ->getApplicationName().copyToBuffer ((char*) ptr, jmin (kVstMaxVendorStrLen, kVstMaxProductStrLen) - 1); - break; - - case audioMasterGetSampleRate: - return 44100; - - case audioMasterGetBlockSize: - return 512; - - case audioMasterSetOutputSampleRate: - return 0; - - default: - DBG ("*** Unhandled VST Callback: " + String ((int) opcode)); - break; - } - - return 0; -} - -// handles callbacks for a specific plugin -VstIntPtr VSTPluginInstance::handleCallback (VstInt32 opcode, VstInt32 index, VstInt32 value, void *ptr, float opt) -{ - switch (opcode) - { - case audioMasterAutomate: - sendParamChangeMessageToListeners (index, opt); - break; - - case audioMasterProcessEvents: - handleMidiFromPlugin ((const VstEvents*) ptr); - break; - - case audioMasterGetTime: - #ifdef _MSC_VER - #pragma warning (push) - #pragma warning (disable: 4311) - #endif - - return (VstIntPtr) &vstHostTime; - - #ifdef _MSC_VER - #pragma warning (pop) - #endif - break; - - case audioMasterIdle: - if (insideVSTCallback == 0 && MessageManager::getInstance()->isThisTheMessageThread()) - { - ++insideVSTCallback; -#if JUCE_MAC - if (getActiveEditor() != 0) - dispatch (effEditIdle, 0, 0, 0, 0); -#endif - const MessageManagerLock mml; - - juce_callAnyTimersSynchronously(); - - handleUpdateNowIfNeeded(); - - for (int i = ComponentPeer::getNumPeers(); --i >= 0;) - ComponentPeer::getPeer (i)->performAnyPendingRepaintsNow(); - - --insideVSTCallback; - } - break; - - case audioMasterUpdateDisplay: - triggerAsyncUpdate(); - break; - - case audioMasterTempoAt: - // returns (10000 * bpm) - break; - - case audioMasterNeedIdle: - startTimer (50); - break; - - case audioMasterSizeWindow: - if (getActiveEditor() != 0) - getActiveEditor()->setSize (index, value); - - return 1; - - case audioMasterGetSampleRate: - return (VstIntPtr) getSampleRate(); - - case audioMasterGetBlockSize: - return (VstIntPtr) getBlockSize(); - - case audioMasterWantMidi: - wantsMidiMessages = true; - break; - - case audioMasterGetDirectory: - #if JUCE_MAC - return (VstIntPtr) (void*) &module->parentDirFSSpec; - #else - return (VstIntPtr) (pointer_sized_uint) (const char*) module->fullParentDirectoryPathName; - #endif - - case audioMasterGetAutomationState: - // returns 0: not supported, 1: off, 2:read, 3:write, 4:read/write - break; - - // none of these are handled (yet).. - case audioMasterBeginEdit: - case audioMasterEndEdit: - case audioMasterSetTime: - case audioMasterPinConnected: - case audioMasterGetParameterQuantization: - case audioMasterIOChanged: - case audioMasterGetInputLatency: - case audioMasterGetOutputLatency: - case audioMasterGetPreviousPlug: - case audioMasterGetNextPlug: - case audioMasterWillReplaceOrAccumulate: - case audioMasterGetCurrentProcessLevel: - case audioMasterOfflineStart: - case audioMasterOfflineRead: - case audioMasterOfflineWrite: - case audioMasterOfflineGetCurrentPass: - case audioMasterOfflineGetCurrentMetaPass: - case audioMasterVendorSpecific: - case audioMasterSetIcon: - case audioMasterGetLanguage: - case audioMasterOpenWindow: - case audioMasterCloseWindow: - break; - - default: - return handleGeneralCallback (opcode, index, value, ptr, opt); - } - - return 0; -} - -// entry point for all callbacks from the plugin -static VstIntPtr VSTCALLBACK audioMaster (AEffect* effect, VstInt32 opcode, VstInt32 index, VstIntPtr value, void* ptr, float opt) -{ - try - { - if (effect != 0 && effect->resvd2 != 0) - { - return ((VSTPluginInstance*)(effect->resvd2)) - ->handleCallback (opcode, index, value, ptr, opt); - } - - return handleGeneralCallback (opcode, index, value, ptr, opt); - } - catch (...) - { - return 0; - } -} - -//============================================================================== -const String VSTPluginInstance::getManufacturer() const -{ - char buffer [kVstMaxVendorStrLen + 8]; - zerostruct (buffer); - dispatch (effGetVendorString, 0, 0, buffer, 0); - return buffer; -} - -const String VSTPluginInstance::getVersion() const -{ - int v = dispatch (effGetVendorVersion, 0, 0, 0, 0); - - String s; - - if (v != 0) - { - int versionBits[4]; - int n = 0; - - while (v != 0) - { - versionBits [n++] = (v & 0xff); - v >>= 8; - } - - s << 'V'; - - while (n > 0) - { - s << versionBits [--n]; - - if (n > 0) - s << '.'; - } - } - - return s; -} - -int VSTPluginInstance::getUID() const -{ - int uid = effect != 0 ? effect->uniqueID : 0; - - if (uid == 0) - uid = getFile().hashCode(); - - return uid; -} - -const String VSTPluginInstance::getCategory() const -{ - const char* result = 0; - - switch (dispatch (effGetPlugCategory, 0, 0, 0, 0)) - { - case kPlugCategEffect: - result = "Effect"; - break; - - case kPlugCategSynth: - result = "Synth"; - break; - - case kPlugCategAnalysis: - result = "Anaylsis"; - break; - - case kPlugCategMastering: - result = "Mastering"; - break; - - case kPlugCategSpacializer: - result = "Spacial"; - break; - - case kPlugCategRoomFx: - result = "Reverb"; - break; - - case kPlugSurroundFx: - result = "Surround"; - break; - - case kPlugCategRestoration: - result = "Restoration"; - break; - - case kPlugCategGenerator: - result = "Tone generation"; - break; - - default: - break; - } - - return result; -} - -//============================================================================== -float VSTPluginInstance::getParameter (int index) -{ - if (effect != 0 && ((unsigned int) index) < (unsigned int) effect->numParams) - { - try - { - const ScopedLock sl (lock); - return effect->getParameter (effect, index); - } - catch (...) - { - } - } - - return 0.0f; -} - -void VSTPluginInstance::setParameter (int index, float newValue) -{ - if (effect != 0 && ((unsigned int) index) < (unsigned int) effect->numParams) - { - try - { - const ScopedLock sl (lock); - - if (effect->getParameter (effect, index) != newValue) - effect->setParameter (effect, index, newValue); - } - catch (...) - { - } - } -} - -const String VSTPluginInstance::getParameterName (int index) -{ - if (effect != 0) - { - jassert (index >= 0 && index < effect->numParams); - - char nm [256]; - zerostruct (nm); - dispatch (effGetParamName, index, 0, nm, 0); - return String (nm).trim(); - } - - return String::empty; -} - -const String VSTPluginInstance::getParameterLabel (int index) const -{ - if (effect != 0) - { - jassert (index >= 0 && index < effect->numParams); - - char nm [256]; - zerostruct (nm); - dispatch (effGetParamLabel, index, 0, nm, 0); - return String (nm).trim(); - } - - return String::empty; -} - -const String VSTPluginInstance::getParameterText (int index) -{ - if (effect != 0) - { - jassert (index >= 0 && index < effect->numParams); - - char nm [256]; - zerostruct (nm); - dispatch (effGetParamDisplay, index, 0, nm, 0); - return String (nm).trim(); - } - - return String::empty; -} - -bool VSTPluginInstance::isParameterAutomatable (int index) const -{ - if (effect != 0) - { - jassert (index >= 0 && index < effect->numParams); - return dispatch (effCanBeAutomated, index, 0, 0, 0) != 0; - } - - return false; -} - -void VSTPluginInstance::createTempParameterStore (MemoryBlock& dest) -{ - dest.setSize (64 + 4 * getNumParameters()); - dest.fillWith (0); - - getCurrentProgramName().copyToBuffer ((char*) dest.getData(), 63); - - float* const p = (float*) (((char*) dest.getData()) + 64); - for (int i = 0; i < getNumParameters(); ++i) - p[i] = getParameter(i); -} - -void VSTPluginInstance::restoreFromTempParameterStore (const MemoryBlock& m) -{ - changeProgramName (getCurrentProgram(), (const char*) m); - - float* p = (float*) (((char*) m.getData()) + 64); - for (int i = 0; i < getNumParameters(); ++i) - setParameter (i, p[i]); -} - -//============================================================================== -void VSTPluginInstance::setCurrentProgram (int newIndex) -{ - if (getNumPrograms() > 0 && newIndex != getCurrentProgram()) - dispatch (effSetProgram, 0, jlimit (0, getNumPrograms() - 1, newIndex), 0, 0); -} - -const String VSTPluginInstance::getProgramName (int index) -{ - if (index == getCurrentProgram()) - { - return getCurrentProgramName(); - } - else if (effect != 0) - { - char nm [256]; - zerostruct (nm); - - if (dispatch (effGetProgramNameIndexed, - jlimit (0, getNumPrograms(), index), - -1, nm, 0) != 0) - { - return String (nm).trim(); - } - } - - return programNames [index]; -} - -void VSTPluginInstance::changeProgramName (int index, const String& newName) -{ - if (index == getCurrentProgram()) - { - if (getNumPrograms() > 0 && newName != getCurrentProgramName()) - dispatch (effSetProgramName, 0, 0, (void*) (const char*) newName.substring (0, 24), 0.0f); - } - else - { - jassertfalse // xxx not implemented! - } -} - -void VSTPluginInstance::updateStoredProgramNames() -{ - if (effect != 0 && getNumPrograms() > 0) - { - char nm [256]; - zerostruct (nm); - - // only do this if the plugin can't use indexed names.. - if (dispatch (effGetProgramNameIndexed, 0, -1, nm, 0) == 0) - { - const int oldProgram = getCurrentProgram(); - MemoryBlock oldSettings; - createTempParameterStore (oldSettings); - - for (int i = 0; i < getNumPrograms(); ++i) - { - setCurrentProgram (i); - getCurrentProgramName(); // (this updates the list) - } - - setCurrentProgram (oldProgram); - restoreFromTempParameterStore (oldSettings); - } - } -} - -const String VSTPluginInstance::getCurrentProgramName() -{ - if (effect != 0) - { - char nm [256]; - zerostruct (nm); - dispatch (effGetProgramName, 0, 0, nm, 0); - - const int index = getCurrentProgram(); - if (programNames[index].isEmpty()) - { - while (programNames.size() < index) - programNames.add (String::empty); - - programNames.set (index, String (nm).trim()); - } - - return String (nm).trim(); - } - - return String::empty; -} - -//============================================================================== -const String VSTPluginInstance::getInputChannelName (const int index) const -{ - if (index >= 0 && index < getNumInputChannels()) - { - VstPinProperties pinProps; - if (dispatch (effGetInputProperties, index, 0, &pinProps, 0.0f) != 0) - return String (pinProps.label, sizeof (pinProps.label)); - } - - return String::empty; -} - -bool VSTPluginInstance::isInputChannelStereoPair (int index) const -{ - if (index < 0 || index >= getNumInputChannels()) - return false; - - VstPinProperties pinProps; - if (dispatch (effGetInputProperties, index, 0, &pinProps, 0.0f) != 0) - return (pinProps.flags & kVstPinIsStereo) != 0; - - return true; -} - -const String VSTPluginInstance::getOutputChannelName (const int index) const -{ - if (index >= 0 && index < getNumOutputChannels()) - { - VstPinProperties pinProps; - if (dispatch (effGetOutputProperties, index, 0, &pinProps, 0.0f) != 0) - return String (pinProps.label, sizeof (pinProps.label)); - } - - return String::empty; -} - -bool VSTPluginInstance::isOutputChannelStereoPair (int index) const -{ - if (index < 0 || index >= getNumOutputChannels()) - return false; - - VstPinProperties pinProps; - if (dispatch (effGetOutputProperties, index, 0, &pinProps, 0.0f) != 0) - return (pinProps.flags & kVstPinIsStereo) != 0; - - return true; -} - -//============================================================================== -void VSTPluginInstance::setPower (const bool on) -{ - dispatch (effMainsChanged, 0, on ? 1 : 0, 0, 0); - isPowerOn = on; -} - -//============================================================================== -const int defaultMaxSizeMB = 64; - -void VSTPluginInstance::getStateInformation (MemoryBlock& destData) -{ - saveToFXBFile (destData, true, defaultMaxSizeMB); -} - -void VSTPluginInstance::getCurrentProgramStateInformation (MemoryBlock& destData) -{ - saveToFXBFile (destData, false, defaultMaxSizeMB); -} - -void VSTPluginInstance::setStateInformation (const void* data, int sizeInBytes) -{ - loadFromFXBFile (data, sizeInBytes); -} - -void VSTPluginInstance::setCurrentProgramStateInformation (const void* data, int sizeInBytes) -{ - loadFromFXBFile (data, sizeInBytes); -} - -//============================================================================== -//============================================================================== -VSTPluginFormat::VSTPluginFormat() -{ -} - -VSTPluginFormat::~VSTPluginFormat() -{ -} - -void VSTPluginFormat::findAllTypesForFile (OwnedArray & results, - const File& file) -{ - if (! fileMightContainThisPluginType (file)) - return; - - PluginDescription desc; - desc.file = file; - desc.uid = 0; - - VSTPluginInstance* instance = dynamic_cast (createInstanceFromDescription (desc)); - - if (instance == 0) - return; - - try - { -#if JUCE_MAC - if (instance->module->resFileId != 0) - UseResFile (instance->module->resFileId); -#endif - - desc.fillInFromInstance (*instance); - - VstPlugCategory category = (VstPlugCategory) instance->dispatch (effGetPlugCategory, 0, 0, 0, 0); - - if (category != kPlugCategShell) - { - // Normal plugin... - results.add (new PluginDescription (desc)); - - ++insideVSTCallback; - instance->dispatch (effOpen, 0, 0, 0, 0); - --insideVSTCallback; - } - else - { - // It's a shell plugin, so iterate all the subtypes... - char shellEffectName [64]; - - for (;;) - { - zerostruct (shellEffectName); - const int uid = instance->dispatch (effShellGetNextPlugin, 0, 0, shellEffectName, 0); - - if (uid == 0) - { - break; - } - else - { - desc.uid = uid; - desc.name = shellEffectName; - - bool alreadyThere = false; - - for (int i = results.size(); --i >= 0;) - { - PluginDescription* const d = results.getUnchecked(i); - - if (d->isDuplicateOf (desc)) - { - alreadyThere = true; - break; - } - } - - if (! alreadyThere) - results.add (new PluginDescription (desc)); - } - } - } - } - catch (...) - { - // crashed while loading... - } - - deleteAndZero (instance); -} - -AudioPluginInstance* VSTPluginFormat::createInstanceFromDescription (const PluginDescription& desc) -{ - VSTPluginInstance* result = 0; - - if (fileMightContainThisPluginType (desc.file)) - { - const File previousWorkingDirectory (File::getCurrentWorkingDirectory()); - desc.file.getParentDirectory().setAsCurrentWorkingDirectory(); - - const ReferenceCountedObjectPtr module (ModuleHandle::findOrCreateModule (desc.file)); - - if (module != 0) - { - shellUIDToCreate = desc.uid; - - result = new VSTPluginInstance (module); - - if (result->effect != 0) - { - result->effect->resvd2 = (VstIntPtr) (pointer_sized_int) result; - result->initialise(); - } - else - { - deleteAndZero (result); - } - } - - previousWorkingDirectory.setAsCurrentWorkingDirectory(); - } - - return result; -} - -bool VSTPluginFormat::fileMightContainThisPluginType (const File& f) -{ -#if JUCE_MAC - if (f.isDirectory() && f.hasFileExtension (T(".vst"))) - return true; - -#if JUCE_PPC - FSRef fileRef; - if (PlatformUtilities::makeFSRefFromPath (&fileRef, f.getFullPathName())) - { - const short resFileId = FSOpenResFile (&fileRef, fsRdPerm); - - if (resFileId != -1) - { - const int numEffects = Count1Resources ('aEff'); - CloseResFile (resFileId); - - if (numEffects > 0) - return true; - } - } -#endif - - return false; -#elif JUCE_WIN32 - return f.existsAsFile() - && f.hasFileExtension (T(".dll")); -#elif JUCE_LINUX - return f.existsAsFile() - && f.hasFileExtension (T(".so")); -#endif -} - -const FileSearchPath VSTPluginFormat::getDefaultLocationsToSearch() -{ -#if JUCE_MAC - return FileSearchPath ("~/Library/Audio/Plug-Ins/VST;/Library/Audio/Plug-Ins/VST"); -#elif JUCE_WIN32 - const String programFiles (File::getSpecialLocation (File::globalApplicationsDirectory).getFullPathName()); - - return FileSearchPath (programFiles + "\\Steinberg\\VstPlugins"); -#elif JUCE_LINUX - return FileSearchPath ("/usr/lib/vst"); -#endif -} - -#endif +/* + ============================================================================== + + This file is part of the JUCE library - "Jules' Utility Class Extensions" + Copyright 2004-7 by Raw Material Software ltd. + + ------------------------------------------------------------------------------ + + JUCE can be redistributed and/or modified under the terms of the + GNU General Public License, as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + JUCE is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with JUCE; if not, visit www.gnu.org/licenses or write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------------ + + If you'd like to release a closed-source product which uses JUCE, commercial + licenses are also available: visit www.rawmaterialsoftware.com/juce for + more information. + + ============================================================================== +*/ + +#ifndef JUCE_PLUGINHOST_VST // xxx temporary.. + #define JUCE_PLUGINHOST_VST 1 +#endif + +#if JUCE_PLUGINHOST_VST + +#ifdef _WIN32 + #define _WIN32_WINNT 0x500 + #define STRICT + #include + #include + #pragma warning (disable : 4312) +#elif defined (LINUX) + #include + #include + #include + #include + #include + #undef Font + #undef KeyPress + #undef Drawable + #undef Time +#else + #include +#endif + +//============================================================================== +#include "../../../../../juce.h" +#include "juce_VSTPluginFormat.h" + +//============================================================================== +#undef PRAGMA_ALIGN_SUPPORTED +#define VST_FORCE_DEPRECATED 0 + +#ifdef _MSC_VER + #pragma warning (push) + #pragma warning (disable: 4996) +#endif + +/* Obviously you're going to need the Steinberg vstsdk2.4 folder in + your include path if you want to add VST support... +*/ +#include "pluginterfaces/vst2.x/aeffectx.h" + +#ifdef _MSC_VER + #pragma warning (pop) +#endif + +//============================================================================== +#if JUCE_LINUX + #define Font JUCE_NAMESPACE::Font + #define KeyPress JUCE_NAMESPACE::KeyPress + #define Drawable JUCE_NAMESPACE::Drawable + #define Time JUCE_NAMESPACE::Time +#endif + +#include "../juce_PluginDescription.h" + +#if ! JUCE_WIN32 + #define _fpreset() + #define _clearfp() +#endif + +BEGIN_JUCE_NAMESPACE + extern void juce_callAnyTimersSynchronously(); +END_JUCE_NAMESPACE + + +//============================================================================== +const int fxbVersionNum = 1; + +struct fxProgram +{ + long chunkMagic; // 'CcnK' + long byteSize; // of this chunk, excl. magic + byteSize + long fxMagic; // 'FxCk' + long version; + long fxID; // fx unique id + long fxVersion; + long numParams; + char prgName[28]; + float params[1]; // variable no. of parameters +}; + +struct fxSet +{ + long chunkMagic; // 'CcnK' + long byteSize; // of this chunk, excl. magic + byteSize + long fxMagic; // 'FxBk' + long version; + long fxID; // fx unique id + long fxVersion; + long numPrograms; + char future[128]; + fxProgram programs[1]; // variable no. of programs +}; + +struct fxChunkSet +{ + long chunkMagic; // 'CcnK' + long byteSize; // of this chunk, excl. magic + byteSize + long fxMagic; // 'FxCh', 'FPCh', or 'FBCh' + long version; + long fxID; // fx unique id + long fxVersion; + long numPrograms; + char future[128]; + long chunkSize; + char chunk[8]; // variable +}; + +struct fxProgramSet +{ + long chunkMagic; // 'CcnK' + long byteSize; // of this chunk, excl. magic + byteSize + long fxMagic; // 'FxCh', 'FPCh', or 'FBCh' + long version; + long fxID; // fx unique id + long fxVersion; + long numPrograms; + char name[28]; + long chunkSize; + char chunk[8]; // variable +}; + + +#ifdef JUCE_LITTLE_ENDIAN + static long swap (const long x) throw() { return (long) swapByteOrder ((uint32) x); } + + static float swapFloat (const float x) throw() + { + union { uint32 asInt; float asFloat; } n; + n.asFloat = x; + n.asInt = swapByteOrder (n.asInt); + return n.asFloat; + } +#else + #define swap(x) (x) + #define swapFloat(x) (x) +#endif + +//============================================================================== +typedef AEffect* (*MainCall) (audioMasterCallback); + +static VstIntPtr VSTCALLBACK audioMaster (AEffect* effect, VstInt32 opcode, VstInt32 index, VstIntPtr value, void* ptr, float opt); + +#if JUCE_PPC + static void* audioMasterCoerced = 0; +#endif + +static int shellUIDToCreate = 0; +static int insideVSTCallback = 0; + +class VSTPluginWindow; + +//============================================================================== +// Change this to disable logging of various VST activities +#ifndef VST_LOGGING + #define VST_LOGGING 1 +#endif + +#if VST_LOGGING + #define log(a) Logger::writeToLog(a); +#else + #define log(a) +#endif + +//============================================================================== +#if JUCE_MAC +BEGIN_JUCE_NAMESPACE + extern bool juce_isHIViewCreatedByJuce (HIViewRef view); + extern bool juce_isWindowCreatedByJuce (WindowRef window); +END_JUCE_NAMESPACE + +#if JUCE_PPC +static void* NewCFMFromMachO (void* const machofp) throw() +{ + void* result = juce_malloc (8); + + ((void**) result)[0] = machofp; + ((void**) result)[1] = result; + + return result; +} +#endif +#endif + +//============================================================================== +#if JUCE_LINUX + +BEGIN_JUCE_NAMESPACE + extern Display* display; + extern XContext improbableNumber; +END_JUCE_NAMESPACE + +typedef void (*EventProcPtr) (XEvent* ev); + +static bool xErrorTriggered; + +static int temporaryErrorHandler (Display*, XErrorEvent*) +{ + xErrorTriggered = true; + return 0; +} + +static int getPropertyFromXWindow (Window handle, Atom atom) +{ + XErrorHandler oldErrorHandler = XSetErrorHandler (temporaryErrorHandler); + xErrorTriggered = false; + + int userSize; + unsigned long bytes, userCount; + unsigned char* data; + Atom userType; + + XGetWindowProperty (display, handle, atom, 0, 1, false, AnyPropertyType, + &userType, &userSize, &userCount, &bytes, &data); + + XSetErrorHandler (oldErrorHandler); + + return (userCount == 1 && ! xErrorTriggered) ? *(int*) data + : 0; +} + +static Window getChildWindow (Window windowToCheck) +{ + Window rootWindow, parentWindow; + Window* childWindows; + unsigned int numChildren; + + XQueryTree (display, + windowToCheck, + &rootWindow, + &parentWindow, + &childWindows, + &numChildren); + + if (numChildren > 0) + return childWindows [0]; + + return 0; +} + +static void translateJuceToXButtonModifiers (const MouseEvent& e, XEvent& ev) throw() +{ + if (e.mods.isLeftButtonDown()) + { + ev.xbutton.button = Button1; + ev.xbutton.state |= Button1Mask; + } + else if (e.mods.isRightButtonDown()) + { + ev.xbutton.button = Button3; + ev.xbutton.state |= Button3Mask; + } + else if (e.mods.isMiddleButtonDown()) + { + ev.xbutton.button = Button2; + ev.xbutton.state |= Button2Mask; + } +} + +static void translateJuceToXMotionModifiers (const MouseEvent& e, XEvent& ev) throw() +{ + if (e.mods.isLeftButtonDown()) + ev.xmotion.state |= Button1Mask; + else if (e.mods.isRightButtonDown()) + ev.xmotion.state |= Button3Mask; + else if (e.mods.isMiddleButtonDown()) + ev.xmotion.state |= Button2Mask; +} + +static void translateJuceToXCrossingModifiers (const MouseEvent& e, XEvent& ev) throw() +{ + if (e.mods.isLeftButtonDown()) + ev.xcrossing.state |= Button1Mask; + else if (e.mods.isRightButtonDown()) + ev.xcrossing.state |= Button3Mask; + else if (e.mods.isMiddleButtonDown()) + ev.xcrossing.state |= Button2Mask; +} + +static void translateJuceToXMouseWheelModifiers (const MouseEvent& e, const float increment, XEvent& ev) throw() +{ + if (increment < 0) + { + ev.xbutton.button = Button5; + ev.xbutton.state |= Button5Mask; + } + else if (increment > 0) + { + ev.xbutton.button = Button4; + ev.xbutton.state |= Button4Mask; + } +} + +#endif + +//============================================================================== +static VoidArray activeModules; + +//============================================================================== +class ModuleHandle : public ReferenceCountedObject +{ +public: + //============================================================================== + File file; + MainCall moduleMain; + String pluginName; + + //============================================================================== + static ModuleHandle* findOrCreateModule (const File& file) + { + for (int i = activeModules.size(); --i >= 0;) + { + ModuleHandle* const module = (ModuleHandle*) activeModules.getUnchecked(i); + + if (module->file == file) + return module; + } + + _fpreset(); // (doesn't do any harm) + ++insideVSTCallback; + shellUIDToCreate = 0; + + log ("Attempting to load VST: " + file.getFullPathName()); + + ModuleHandle* m = new ModuleHandle (file); + + if (! m->open()) + deleteAndZero (m); + + --insideVSTCallback; + _fpreset(); // (doesn't do any harm) + + return m; + } + + //============================================================================== + ModuleHandle (const File& file_) + : file (file_), + moduleMain (0), +#if JUCE_WIN32 || JUCE_LINUX + hModule (0) +#elif JUCE_MAC + fragId (0), + resHandle (0), + bundleRef (0), + resFileId (0) +#endif + { + activeModules.add (this); + +#if JUCE_WIN32 || JUCE_LINUX + fullParentDirectoryPathName = file_.getParentDirectory().getFullPathName(); +#elif JUCE_MAC + PlatformUtilities::makeFSSpecFromPath (&parentDirFSSpec, file_.getParentDirectory().getFullPathName()); +#endif + } + + ~ModuleHandle() + { + activeModules.removeValue (this); + + close(); + } + + //============================================================================== + juce_UseDebuggingNewOperator + + //============================================================================== +#if JUCE_WIN32 || JUCE_LINUX + void* hModule; + String fullParentDirectoryPathName; + + bool open() + { +#if JUCE_WIN32 + static bool timePeriodSet = false; + + if (! timePeriodSet) + { + timePeriodSet = true; + timeBeginPeriod (2); + } +#endif + + pluginName = file.getFileNameWithoutExtension(); + + hModule = Process::loadDynamicLibrary (file.getFullPathName()); + + moduleMain = (MainCall) Process::getProcedureEntryPoint (hModule, "VSTPluginMain"); + + if (moduleMain == 0) + moduleMain = (MainCall) Process::getProcedureEntryPoint (hModule, "main"); + + return moduleMain != 0; + } + + void close() + { + _fpreset(); // (doesn't do any harm) + + Process::freeDynamicLibrary (hModule); + } + + void closeEffect (AEffect* eff) + { + eff->dispatcher (eff, effClose, 0, 0, 0, 0); + } + +#else + CFragConnectionID fragId; + Handle resHandle; + CFBundleRef bundleRef; + FSSpec parentDirFSSpec; + short resFileId; + + bool open() + { + bool ok = false; + const String filename (file.getFullPathName()); + + if (file.hasFileExtension (T(".vst"))) + { + const char* const utf8 = filename.toUTF8(); + CFURLRef url = CFURLCreateFromFileSystemRepresentation (0, (const UInt8*) utf8, + strlen (utf8), file.isDirectory()); + + if (url != 0) + { + bundleRef = CFBundleCreate (kCFAllocatorDefault, url); + CFRelease (url); + + if (bundleRef != 0) + { + if (CFBundleLoadExecutable (bundleRef)) + { + moduleMain = (MainCall) CFBundleGetFunctionPointerForName (bundleRef, CFSTR("main_macho")); + + if (moduleMain == 0) + moduleMain = (MainCall) CFBundleGetFunctionPointerForName (bundleRef, CFSTR("VSTPluginMain")); + + if (moduleMain != 0) + { + CFTypeRef name = CFBundleGetValueForInfoDictionaryKey (bundleRef, CFSTR("CFBundleName")); + + if (name != 0) + { + if (CFGetTypeID (name) == CFStringGetTypeID()) + { + char buffer[1024]; + + if (CFStringGetCString ((CFStringRef) name, buffer, sizeof (buffer), CFStringGetSystemEncoding())) + pluginName = buffer; + } + } + + if (pluginName.isEmpty()) + pluginName = file.getFileNameWithoutExtension(); + + resFileId = CFBundleOpenBundleResourceMap (bundleRef); + + ok = true; + } + } + + if (! ok) + { + CFBundleUnloadExecutable (bundleRef); + CFRelease (bundleRef); + bundleRef = 0; + } + } + } + } +#if JUCE_PPC + else + { + FSRef fn; + + if (FSPathMakeRef ((UInt8*) (const char*) filename, &fn, 0) == noErr) + { + resFileId = FSOpenResFile (&fn, fsRdPerm); + + if (resFileId != -1) + { + const int numEffs = Count1Resources ('aEff'); + + for (int i = 0; i < numEffs; ++i) + { + resHandle = Get1IndResource ('aEff', i + 1); + + if (resHandle != 0) + { + OSType type; + Str255 name; + SInt16 id; + GetResInfo (resHandle, &id, &type, name); + pluginName = String ((const char*) name + 1, name[0]); + DetachResource (resHandle); + HLock (resHandle); + + Ptr ptr; + Str255 errorText; + + OSErr err = GetMemFragment (*resHandle, GetHandleSize (resHandle), + name, kPrivateCFragCopy, + &fragId, &ptr, errorText); + + if (err == noErr) + { + moduleMain = (MainCall) newMachOFromCFM (ptr); + ok = true; + } + else + { + HUnlock (resHandle); + } + + break; + } + } + + if (! ok) + CloseResFile (resFileId); + } + } + } +#endif + + return ok; + } + + void close() + { +#if JUCE_PPC + if (fragId != 0) + { + if (moduleMain != 0) + disposeMachOFromCFM ((void*) moduleMain); + + CloseConnection (&fragId); + HUnlock (resHandle); + + if (resFileId != 0) + CloseResFile (resFileId); + } + else +#endif + if (bundleRef != 0) + { + CFBundleCloseBundleResourceMap (bundleRef, resFileId); + + if (CFGetRetainCount (bundleRef) == 1) + CFBundleUnloadExecutable (bundleRef); + + if (CFGetRetainCount (bundleRef) > 0) + CFRelease (bundleRef); + } + } + + void closeEffect (AEffect* eff) + { +#if JUCE_PPC + if (fragId != 0) + { + VoidArray thingsToDelete; + thingsToDelete.add ((void*) eff->dispatcher); + thingsToDelete.add ((void*) eff->process); + thingsToDelete.add ((void*) eff->setParameter); + thingsToDelete.add ((void*) eff->getParameter); + thingsToDelete.add ((void*) eff->processReplacing); + + eff->dispatcher (eff, effClose, 0, 0, 0, 0); + + for (int i = thingsToDelete.size(); --i >= 0;) + disposeMachOFromCFM (thingsToDelete[i]); + } + else +#endif + { + eff->dispatcher (eff, effClose, 0, 0, 0, 0); + } + } + +#if JUCE_PPC + static void* newMachOFromCFM (void* cfmfp) + { + if (cfmfp == 0) + return 0; + + UInt32* const mfp = (UInt32*) juce_malloc (sizeof (UInt32) * 6); + + mfp[0] = 0x3d800000 | ((UInt32) cfmfp >> 16); + mfp[1] = 0x618c0000 | ((UInt32) cfmfp & 0xffff); + mfp[2] = 0x800c0000; + mfp[3] = 0x804c0004; + mfp[4] = 0x7c0903a6; + mfp[5] = 0x4e800420; + + MakeDataExecutable (mfp, sizeof (UInt32) * 6); + return mfp; + } + + static void disposeMachOFromCFM (void* ptr) + { + juce_free (ptr); + } + + void coerceAEffectFunctionCalls (AEffect* eff) + { + if (fragId != 0) + { + eff->dispatcher = (AEffectDispatcherProc) newMachOFromCFM ((void*) eff->dispatcher); + eff->process = (AEffectProcessProc) newMachOFromCFM ((void*) eff->process); + eff->setParameter = (AEffectSetParameterProc) newMachOFromCFM ((void*) eff->setParameter); + eff->getParameter = (AEffectGetParameterProc) newMachOFromCFM ((void*) eff->getParameter); + eff->processReplacing = (AEffectProcessProc) newMachOFromCFM ((void*) eff->processReplacing); + } + } +#endif + +#endif +}; + + +//============================================================================== +/** + An instance of a plugin, created by a VSTPluginFormat. + +*/ +class VSTPluginInstance : public AudioPluginInstance, + private Timer, + private AsyncUpdater +{ +public: + //============================================================================== + ~VSTPluginInstance(); + + //============================================================================== + // AudioPluginInstance methods: + + void fillInPluginDescription (PluginDescription& desc) const + { + desc.name = name; + desc.file = module->file; + desc.uid = getUID(); + desc.lastFileModTime = file.getLastModificationTime(); + desc.pluginFormatName = "VST"; + desc.category = getCategory(); + + { + char buffer [kVstMaxVendorStrLen + 8]; + zerostruct (buffer); + dispatch (effGetVendorString, 0, 0, buffer, 0); + desc.manufacturerName = buffer; + } + + desc.version = getVersion(); + desc.numInputChannels = instance.getNumInputChannels(); + desc.numOutputChannels = instance.getNumOutputChannels(); + desc.isInstrument = (effect != 0 && (effect->flags & effFlagsIsSynth) != 0); + } + + int getUID() const; + bool acceptsMidi() const { return wantsMidiMessages; } + bool producesMidi() const { return dispatch (effCanDo, 0, 0, (void*) "sendVstMidiEvent", 0) > 0; } + + //============================================================================== + // AudioProcessor methods: + + void prepareToPlay (double sampleRate, int estimatedSamplesPerBlock); + void releaseResources(); + void processBlock (AudioSampleBuffer& buffer, + MidiBuffer& midiMessages); + + AudioProcessorEditor* createEditor(); + + const String getInputChannelName (const int index) const; + bool isInputChannelStereoPair (int index) const; + + const String getOutputChannelName (const int index) const; + bool isOutputChannelStereoPair (int index) const; + + //============================================================================== + int getNumParameters() { return effect != 0 ? effect->numParams : 0; } + float getParameter (int index); + void setParameter (int index, float newValue); + const String getParameterName (int index); + const String getParameterText (int index); + bool isParameterAutomatable (int index) const; + + //============================================================================== + int getNumPrograms() { return effect != 0 ? effect->numPrograms : 0; } + int getCurrentProgram() { return dispatch (effGetProgram, 0, 0, 0, 0); } + void setCurrentProgram (int index); + const String getProgramName (int index); + void changeProgramName (int index, const String& newName); + + //============================================================================== + void getStateInformation (MemoryBlock& destData); + void getCurrentProgramStateInformation (MemoryBlock& destData); + void setStateInformation (const void* data, int sizeInBytes); + void setCurrentProgramStateInformation (const void* data, int sizeInBytes); + + //============================================================================== + void timerCallback(); + void handleAsyncUpdate(); + VstIntPtr handleCallback (VstInt32 opcode, VstInt32 index, VstInt32 value, void *ptr, float opt); + + //============================================================================== + juce_UseDebuggingNewOperator + +private: + friend class VSTPluginWindow; + friend class VSTPluginFormat; + + AEffect* effect; + String name; + CriticalSection lock; + bool wantsMidiMessages, initialised, isPowerOn; + mutable StringArray programNames; + AudioSampleBuffer tempBuffer; + CriticalSection midiInLock; + MidiBuffer incomingMidi; + void* midiEventsToSend; + int numAllocatedMidiEvents; + VstTimeInfo vstHostTime; + float** channels; + + ReferenceCountedObjectPtr module; + + //============================================================================== + int dispatch (const int opcode, const int index, const int value, void* const ptr, float opt) const; + bool restoreProgramSettings (const fxProgram* const prog); + const String getCurrentProgramName(); + void setParamsInProgramBlock (fxProgram* const prog) throw(); + void updateStoredProgramNames(); + void initialise(); + void ensureMidiEventSize (int numEventsNeeded); + void freeMidiEvents(); + void handleMidiFromPlugin (const VstEvents* const events); + void createTempParameterStore (MemoryBlock& dest); + void restoreFromTempParameterStore (const MemoryBlock& mb); + const String getParameterLabel (int index) const; + + bool usesChunks() const throw() { return effect != 0 && (effect->flags & effFlagsProgramChunks) != 0; } + void getChunkData (MemoryBlock& mb, bool isPreset, int maxSizeMB) const; + void setChunkData (const char* data, int size, bool isPreset); + bool loadFromFXBFile (const void* data, int numBytes); + bool saveToFXBFile (MemoryBlock& dest, bool isFXB, int maxSizeMB); + + int getVersionNumber() const throw() { return effect != 0 ? effect->version : 0; } + const String getVersion() const throw(); + const String getCategory() const throw(); + + bool hasEditor() const throw() { return effect != 0 && (effect->flags & effFlagsHasEditor) != 0; } + void setPower (const bool on); + + VSTPluginInstance (const ReferenceCountedObjectPtr & module); +}; + +//============================================================================== +VSTPluginInstance::VSTPluginInstance (const ReferenceCountedObjectPtr & module_) + : effect (0), + wantsMidiMessages (false), + initialised (false), + isPowerOn (false), + numAllocatedMidiEvents (0), + midiEventsToSend (0), + tempBuffer (1, 1), + channels (0), + module (module_) +{ + try + { + _fpreset(); + + ++insideVSTCallback; + + name = module->pluginName; + log (T("Creating VST instance: ") + name); + +#if JUCE_MAC + if (module->resFileId != 0) + UseResFile (module->resFileId); + +#if JUCE_PPC + if (module->fragId != 0) + { + static void* audioMasterCoerced = 0; + if (audioMasterCoerced == 0) + audioMasterCoerced = NewCFMFromMachO ((void*) &audioMaster); + + effect = module->moduleMain ((audioMasterCallback) audioMasterCoerced); + } + else +#endif +#endif + { + effect = module->moduleMain (&audioMaster); + } + + --insideVSTCallback; + + if (effect != 0 && effect->magic == kEffectMagic) + { +#if JUCE_PPC + module->coerceAEffectFunctionCalls (effect); +#endif + + jassert (effect->resvd2 == 0); + jassert (effect->object != 0); + + _fpreset(); // some dodgy plugs fuck around with this + } + else + { + effect = 0; + } + } + catch (...) + { + --insideVSTCallback; + } +} + +VSTPluginInstance::~VSTPluginInstance() +{ + { + const ScopedLock sl (lock); + + jassert (insideVSTCallback == 0); + + if (effect != 0 && effect->magic == kEffectMagic) + { + try + { +#if JUCE_MAC + if (module->resFileId != 0) + UseResFile (module->resFileId); +#endif + + // Must delete any editors before deleting the plugin instance! + jassert (getActiveEditor() == 0); + + _fpreset(); // some dodgy plugs fuck around with this + + module->closeEffect (effect); + } + catch (...) + {} + } + + module = 0; + effect = 0; + } + + freeMidiEvents(); + + juce_free (channels); + channels = 0; +} + +//============================================================================== +void VSTPluginInstance::initialise() +{ + if (initialised || effect == 0) + return; + + log (T("Initialising VST: ") + module->pluginName); + initialised = true; + + dispatch (effIdentify, 0, 0, 0, 0); + + // this code would ask the plugin for its name, but so few plugins + // actually bother implementing this correctly, that it's better to + // just ignore it and use the file name instead. +/* { + char buffer [256]; + zerostruct (buffer); + dispatch (effGetEffectName, 0, 0, buffer, 0); + + name = String (buffer).trim(); + if (name.isEmpty()) + name = module->pluginName; + } +*/ + + if (getSampleRate() > 0) + dispatch (effSetSampleRate, 0, 0, 0, (float) getSampleRate()); + + if (getBlockSize() > 0) + dispatch (effSetBlockSize, 0, jmax (32, getBlockSize()), 0, 0); + + dispatch (effOpen, 0, 0, 0, 0); + + setPlayConfigDetails (effect->numInputs, effect->numOutputs, + getSampleRate(), getBlockSize()); + + if (getNumPrograms() > 1) + setCurrentProgram (0); + else + dispatch (effSetProgram, 0, 0, 0, 0); + + int i; + for (i = effect->numInputs; --i >= 0;) + dispatch (effConnectInput, i, 1, 0, 0); + + for (i = effect->numOutputs; --i >= 0;) + dispatch (effConnectOutput, i, 1, 0, 0); + + updateStoredProgramNames(); + + wantsMidiMessages = dispatch (effCanDo, 0, 0, (void*) "receiveVstMidiEvent", 0) > 0; + + setLatencySamples (effect->initialDelay); +} + + +//============================================================================== +void VSTPluginInstance::prepareToPlay (double sampleRate_, + int samplesPerBlockExpected) +{ + setPlayConfigDetails (effect->numInputs, effect->numOutputs, + sampleRate_, samplesPerBlockExpected); + + setLatencySamples (effect->initialDelay); + + juce_free (channels); + channels = (float**) juce_calloc (sizeof (float*) * jmax (16, getNumOutputChannels() + 2, getNumInputChannels() + 2)); + + vstHostTime.tempo = 120.0; + vstHostTime.timeSigNumerator = 4; + vstHostTime.timeSigDenominator = 4; + vstHostTime.sampleRate = sampleRate_; + vstHostTime.samplePos = 0; + vstHostTime.flags = kVstNanosValid; /*| kVstTransportPlaying | kVstTempoValid | kVstTimeSigValid*/; + + initialise(); + + if (initialised) + { + wantsMidiMessages = wantsMidiMessages + || (dispatch (effCanDo, 0, 0, (void*) "receiveVstMidiEvent", 0) > 0); + + if (wantsMidiMessages) + ensureMidiEventSize (256); + else + freeMidiEvents(); + + incomingMidi.clear(); + + dispatch (effSetSampleRate, 0, 0, 0, (float) sampleRate_); + dispatch (effSetBlockSize, 0, jmax (16, samplesPerBlockExpected), 0, 0); + + tempBuffer.setSize (jmax (1, effect->numOutputs), samplesPerBlockExpected); + + if (! isPowerOn) + setPower (true); + + // dodgy hack to force some plugins to initialise the sample rate.. + if ((! hasEditor()) && getNumParameters() > 0) + { + const float old = getParameter (0); + setParameter (0, (old < 0.5f) ? 1.0f : 0.0f); + setParameter (0, old); + } + + dispatch (effStartProcess, 0, 0, 0, 0); + } +} + +void VSTPluginInstance::releaseResources() +{ + if (initialised) + { + dispatch (effStopProcess, 0, 0, 0, 0); + setPower (false); + } + + tempBuffer.setSize (1, 1); + incomingMidi.clear(); + + freeMidiEvents(); + juce_free (channels); + channels = 0; +} + +void VSTPluginInstance::processBlock (AudioSampleBuffer& buffer, + MidiBuffer& midiMessages) +{ + const int numSamples = buffer.getNumSamples(); + + if (initialised) + { +#if JUCE_WIN32 + vstHostTime.nanoSeconds = timeGetTime() * 1000000.0; +#elif JUCE_LINUX + timeval micro; + gettimeofday (µ, 0); + vstHostTime.nanoSeconds = micro.tv_usec * 1000.0; +#elif JUCE_MAC + UnsignedWide micro; + Microseconds (µ); + vstHostTime.nanoSeconds = micro.lo * 1000.0; +#endif + + if (wantsMidiMessages) + { + MidiBuffer::Iterator iter (midiMessages); + + int eventIndex = 0; + const uint8* midiData; + int numBytesOfMidiData, samplePosition; + + while (iter.getNextEvent (midiData, numBytesOfMidiData, samplePosition)) + { + if (numBytesOfMidiData < 4) + { + ensureMidiEventSize (eventIndex); + VstMidiEvent* const e + = (VstMidiEvent*) ((VstEvents*) midiEventsToSend)->events [eventIndex++]; + + // check that some plugin hasn't messed up our objects + jassert (e->type == kVstMidiType); + jassert (e->byteSize == 24); + + e->deltaFrames = jlimit (0, numSamples - 1, samplePosition); + e->noteLength = 0; + e->noteOffset = 0; + e->midiData[0] = midiData[0]; + e->midiData[1] = midiData[1]; + e->midiData[2] = midiData[2]; + e->detune = 0; + e->noteOffVelocity = 0; + } + } + + if (midiEventsToSend == 0) + ensureMidiEventSize (1); + + ((VstEvents*) midiEventsToSend)->numEvents = eventIndex; + + try + { + effect->dispatcher (effect, effProcessEvents, 0, 0, midiEventsToSend, 0); + } + catch (...) + {} + } + + int i; + const int maxChans = jmax (effect->numInputs, effect->numOutputs); + + for (i = 0; i < maxChans; ++i) + channels[i] = buffer.getSampleData (i); + + channels [maxChans] = 0; + + _clearfp(); + + if ((effect->flags & effFlagsCanReplacing) != 0) + { + try + { + effect->processReplacing (effect, channels, channels, numSamples); + } + catch (...) + {} + } + else + { + tempBuffer.setSize (effect->numOutputs, numSamples); + tempBuffer.clear(); + + float* outs [64]; + + for (i = effect->numOutputs; --i >= 0;) + outs[i] = tempBuffer.getSampleData (i); + + outs [effect->numOutputs] = 0; + + try + { + effect->process (effect, channels, outs, numSamples); + } + catch (...) + {} + + for (i = effect->numOutputs; --i >= 0;) + buffer.copyFrom (i, 0, outs[i], numSamples); + } + } + else + { + // Not initialised, so just bypass.. + for (int i = getNumInputChannels(); i < getNumOutputChannels(); ++i) + buffer.clear (i, 0, buffer.getNumSamples()); + } + + { + // copy any incoming midi.. + const ScopedLock sl (midiInLock); + + midiMessages = incomingMidi; + incomingMidi.clear(); + } +} + +//============================================================================== +void VSTPluginInstance::ensureMidiEventSize (int numEventsNeeded) +{ + if (numEventsNeeded > numAllocatedMidiEvents) + { + numEventsNeeded = (numEventsNeeded + 32) & ~31; + + const int size = 20 + sizeof (VstEvent*) * numEventsNeeded; + + if (midiEventsToSend == 0) + midiEventsToSend = juce_calloc (size); + else + midiEventsToSend = juce_realloc (midiEventsToSend, size); + + for (int i = numAllocatedMidiEvents; i < numEventsNeeded; ++i) + { + VstMidiEvent* const e = (VstMidiEvent*) juce_calloc (sizeof (VstMidiEvent)); + e->type = kVstMidiType; + e->byteSize = 24; + + ((VstEvents*) midiEventsToSend)->events[i] = (VstEvent*) e; + } + + numAllocatedMidiEvents = numEventsNeeded; + } +} + +void VSTPluginInstance::freeMidiEvents() +{ + if (midiEventsToSend != 0) + { + for (int i = numAllocatedMidiEvents; --i >= 0;) + juce_free (((VstEvents*) midiEventsToSend)->events[i]); + + juce_free (midiEventsToSend); + midiEventsToSend = 0; + numAllocatedMidiEvents = 0; + } +} + +void VSTPluginInstance::handleMidiFromPlugin (const VstEvents* const events) +{ + if (events != 0) + { + const ScopedLock sl (midiInLock); + + for (int i = 0; i < events->numEvents; ++i) + { + const VstEvent* const e = events->events[i]; + + if (e->type == kVstMidiType) + { + incomingMidi.addEvent ((const uint8*) ((const VstMidiEvent*) e)->midiData, + 3, e->deltaFrames); + } + } + } +} + +//============================================================================== +static Array activeWindows; + +//============================================================================== +class VSTPluginWindow : public AudioProcessorEditor, + public Timer +{ +public: + //============================================================================== + VSTPluginWindow (VSTPluginInstance& plugin_) + : AudioProcessorEditor (&plugin_), + plugin (plugin_), + isOpen (false), + wasShowing (false), + pluginRefusesToResize (false), + pluginWantsKeys (false), + alreadyInside (false), + recursiveResize (false) + { +#if JUCE_WIN32 + sizeCheckCount = 0; + pluginHWND = 0; +#elif JUCE_LINUX + pluginWindow = None; + pluginProc = None; +#else + pluginViewRef = 0; +#endif + + movementWatcher = new CompMovementWatcher (this); + + activeWindows.add (this); + + setSize (1, 1); + setOpaque (true); + setVisible (true); + } + + ~VSTPluginWindow() + { + deleteAndZero (movementWatcher); + + closePluginWindow(); + + activeWindows.removeValue (this); + plugin.editorBeingDeleted (this); + } + + //============================================================================== + void componentMovedOrResized() + { + if (recursiveResize) + return; + + Component* const topComp = getTopLevelComponent(); + + if (topComp->getPeer() != 0) + { + int x = 0, y = 0; + relativePositionToOtherComponent (topComp, x, y); + + recursiveResize = true; + +#if JUCE_MAC + if (pluginViewRef != 0) + { + HIRect r; + r.origin.x = (float) x; + r.origin.y = (float) y; + r.size.width = (float) getWidth(); + r.size.height = (float) getHeight(); + HIViewSetFrame (pluginViewRef, &r); + } + else if (pluginWindowRef != 0) + { + Rect r; + r.left = getScreenX(); + r.top = getScreenY(); + r.right = r.left + getWidth(); + r.bottom = r.top + getHeight(); + + WindowGroupRef group = GetWindowGroup (pluginWindowRef); + WindowGroupAttributes atts; + GetWindowGroupAttributes (group, &atts); + ChangeWindowGroupAttributes (group, 0, kWindowGroupAttrMoveTogether); + + SetWindowBounds (pluginWindowRef, kWindowContentRgn, &r); + + if ((atts & kWindowGroupAttrMoveTogether) != 0) + ChangeWindowGroupAttributes (group, kWindowGroupAttrMoveTogether, 0); + } + else + { + repaint(); + } +#elif JUCE_WIN32 + if (pluginHWND != 0) + MoveWindow (pluginHWND, x, y, getWidth(), getHeight(), TRUE); +#elif JUCE_LINUX + if (pluginWindow != 0) + { + XResizeWindow (display, pluginWindow, getWidth(), getHeight()); + XMoveWindow (display, pluginWindow, x, y); + XMapRaised (display, pluginWindow); + } +#endif + + recursiveResize = false; + } + } + + void componentVisibilityChanged() + { + const bool isShowingNow = isShowing(); + + if (wasShowing != isShowingNow) + { + wasShowing = isShowingNow; + + if (isShowingNow) + openPluginWindow(); + else + closePluginWindow(); + } + + componentMovedOrResized(); + } + + void componentPeerChanged() + { + closePluginWindow(); + openPluginWindow(); + } + + //============================================================================== + bool keyStateChanged() + { + return pluginWantsKeys; + } + + bool keyPressed (const KeyPress&) + { + return pluginWantsKeys; + } + + //============================================================================== + void paint (Graphics& g) + { + if (isOpen) + { + ComponentPeer* const peer = getPeer(); + + if (peer != 0) + { + peer->addMaskedRegion (getScreenX() - peer->getScreenX(), + getScreenY() - peer->getScreenY(), + getWidth(), getHeight()); + +#if JUCE_MAC + if (pluginViewRef == 0) + { + ERect r; + r.left = getScreenX() - peer->getScreenX(); + r.right = r.left + getWidth(); + r.top = getScreenY() - peer->getScreenY(); + r.bottom = r.top + getHeight(); + + dispatch (effEditDraw, 0, 0, &r, 0); + } +#elif JUCE_LINUX + if (pluginWindow != 0) + { + const Rectangle clip (g.getClipBounds()); + + XEvent ev; + zerostruct (ev); + ev.xexpose.type = Expose; + ev.xexpose.display = display; + ev.xexpose.window = pluginWindow; + ev.xexpose.x = clip.getX(); + ev.xexpose.y = clip.getY(); + ev.xexpose.width = clip.getWidth(); + ev.xexpose.height = clip.getHeight(); + + sendEventToChild (&ev); + } +#endif + } + } + else + { + g.fillAll (Colours::black); + } + } + + //============================================================================== + void timerCallback() + { +#if JUCE_WIN32 + if (--sizeCheckCount <= 0) + { + sizeCheckCount = 10; + + checkPluginWindowSize(); + } +#endif + + try + { + static bool reentrant = false; + + if (! reentrant) + { + reentrant = true; + plugin.dispatch (effEditIdle, 0, 0, 0, 0); + reentrant = false; + } + } + catch (...) + {} + } + + //============================================================================== + void mouseDown (const MouseEvent& e) + { +#if JUCE_MAC + if (! alreadyInside) + { + alreadyInside = true; + toFront (true); + dispatch (effEditMouse, e.x, e.y, 0, 0); + alreadyInside = false; + } + else + { + PostEvent (::mouseDown, 0); + } +#elif JUCE_LINUX + + if (pluginWindow == 0) + return; + + toFront (true); + + XEvent ev; + zerostruct (ev); + ev.xbutton.display = display; + ev.xbutton.type = ButtonPress; + ev.xbutton.window = pluginWindow; + ev.xbutton.root = RootWindow (display, DefaultScreen (display)); + ev.xbutton.time = CurrentTime; + ev.xbutton.x = e.x; + ev.xbutton.y = e.y; + ev.xbutton.x_root = e.getScreenX(); + ev.xbutton.y_root = e.getScreenY(); + + translateJuceToXButtonModifiers (e, ev); + + sendEventToChild (&ev); + +#else + (void) e; + + toFront (true); +#endif + } + + void broughtToFront() + { + activeWindows.removeValue (this); + activeWindows.add (this); + +#if JUCE_MAC + dispatch (effEditTop, 0, 0, 0, 0); +#endif + } + + //============================================================================== + juce_UseDebuggingNewOperator + +private: + VSTPluginInstance& plugin; + bool isOpen, wasShowing, recursiveResize; + bool pluginWantsKeys, pluginRefusesToResize, alreadyInside; + +#if JUCE_WIN32 + HWND pluginHWND; + void* originalWndProc; + int sizeCheckCount; +#elif JUCE_MAC + HIViewRef pluginViewRef; + WindowRef pluginWindowRef; +#elif JUCE_LINUX + Window pluginWindow; + EventProcPtr pluginProc; +#endif + + //============================================================================== + void openPluginWindow() + { + if (isOpen || getWindowHandle() == 0) + return; + + log (T("Opening VST UI: ") + plugin.getName()); + isOpen = true; + + ERect* rect = 0; + dispatch (effEditGetRect, 0, 0, &rect, 0); + dispatch (effEditOpen, 0, 0, getWindowHandle(), 0); + + // do this before and after like in the steinberg example + dispatch (effEditGetRect, 0, 0, &rect, 0); + dispatch (effGetProgram, 0, 0, 0, 0); // also in steinberg code + + // Install keyboard hooks + pluginWantsKeys = (dispatch (effKeysRequired, 0, 0, 0, 0) == 0); + +#if JUCE_WIN32 + originalWndProc = 0; + pluginHWND = GetWindow ((HWND) getWindowHandle(), GW_CHILD); + + if (pluginHWND == 0) + { + isOpen = false; + setSize (300, 150); + return; + } + + #pragma warning (push) + #pragma warning (disable: 4244) + + originalWndProc = (void*) GetWindowLongPtr (pluginHWND, GWL_WNDPROC); + + if (! pluginWantsKeys) + SetWindowLongPtr (pluginHWND, GWL_WNDPROC, (LONG_PTR) vstHookWndProc); + + #pragma warning (pop) + + int w, h; + RECT r; + GetWindowRect (pluginHWND, &r); + w = r.right - r.left; + h = r.bottom - r.top; + + if (rect != 0) + { + const int rw = rect->right - rect->left; + const int rh = rect->bottom - rect->top; + + if ((rw > 50 && rh > 50 && rw < 2000 && rh < 2000 && rw != w && rh != h) + || ((w == 0 && rw > 0) || (h == 0 && rh > 0))) + { + // very dodgy logic to decide which size is right. + if (abs (rw - w) > 350 || abs (rh - h) > 350) + { + SetWindowPos (pluginHWND, 0, + 0, 0, rw, rh, + SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER); + + GetWindowRect (pluginHWND, &r); + + w = r.right - r.left; + h = r.bottom - r.top; + + pluginRefusesToResize = (w != rw) || (h != rh); + + w = rw; + h = rh; + } + } + } +#elif JUCE_MAC + HIViewRef root = HIViewGetRoot ((WindowRef) getWindowHandle()); + HIViewFindByID (root, kHIViewWindowContentID, &root); + pluginViewRef = HIViewGetFirstSubview (root); + + while (pluginViewRef != 0 && juce_isHIViewCreatedByJuce (pluginViewRef)) + pluginViewRef = HIViewGetNextView (pluginViewRef); + + pluginWindowRef = 0; + + if (pluginViewRef == 0) + { + WindowGroupRef ourGroup = GetWindowGroup ((WindowRef) getWindowHandle()); + //DebugPrintWindowGroup (ourGroup); + //DebugPrintAllWindowGroups(); + + GetIndexedWindow (ourGroup, 1, + kWindowGroupContentsVisible, + &pluginWindowRef); + + if (pluginWindowRef == (WindowRef) getWindowHandle() + || juce_isWindowCreatedByJuce (pluginWindowRef)) + pluginWindowRef = 0; + } + + int w = 250, h = 150; + + if (rect != 0) + { + w = rect->right - rect->left; + h = rect->bottom - rect->top; + + if (w == 0 || h == 0) + { + w = 250; + h = 150; + } + } + +#elif JUCE_LINUX + pluginWindow = getChildWindow ((Window) getWindowHandle()); + + if (pluginWindow != 0) + pluginProc = (EventProcPtr) getPropertyFromXWindow (pluginWindow, + XInternAtom (display, "_XEventProc", False)); + + int w = 250, h = 150; + + if (rect != 0) + { + w = rect->right - rect->left; + h = rect->bottom - rect->top; + + if (w == 0 || h == 0) + { + w = 250; + h = 150; + } + } + + if (pluginWindow != 0) + XMapRaised (display, pluginWindow); +#endif + + // double-check it's not too tiny + w = jmax (w, 32); + h = jmax (h, 32); + + setSize (w, h); + +#if JUCE_WIN32 + checkPluginWindowSize(); +#endif + + startTimer (18 + JUCE_NAMESPACE::Random::getSystemRandom().nextInt (5)); + repaint(); + } + + //============================================================================== + void closePluginWindow() + { + if (isOpen) + { + log (T("Closing VST UI: ") + plugin.getName()); + isOpen = false; + + dispatch (effEditClose, 0, 0, 0, 0); + +#if JUCE_WIN32 + #pragma warning (push) + #pragma warning (disable: 4244) + + if (pluginHWND != 0 && IsWindow (pluginHWND)) + SetWindowLongPtr (pluginHWND, GWL_WNDPROC, (LONG_PTR) originalWndProc); + + #pragma warning (pop) + + stopTimer(); + + if (pluginHWND != 0 && IsWindow (pluginHWND)) + DestroyWindow (pluginHWND); + + pluginHWND = 0; +#elif JUCE_MAC + dispatch (effEditSleep, 0, 0, 0, 0); + pluginViewRef = 0; + stopTimer(); +#elif JUCE_LINUX + stopTimer(); + pluginWindow = 0; + pluginProc = 0; +#endif + } + } + + //============================================================================== +#if JUCE_WIN32 + void checkPluginWindowSize() throw() + { + RECT r; + GetWindowRect (pluginHWND, &r); + const int w = r.right - r.left; + const int h = r.bottom - r.top; + + if (isShowing() && w > 0 && h > 0 + && (w != getWidth() || h != getHeight()) + && ! pluginRefusesToResize) + { + setSize (w, h); + sizeCheckCount = 0; + } + } +#endif + + //============================================================================== + class CompMovementWatcher : public ComponentMovementWatcher + { + public: + CompMovementWatcher (VSTPluginWindow* const owner_) + : ComponentMovementWatcher (owner_), + owner (owner_) + { + } + + //============================================================================== + void componentMovedOrResized (bool /*wasMoved*/, bool /*wasResized*/) + { + owner->componentMovedOrResized(); + } + + void componentPeerChanged() + { + owner->componentPeerChanged(); + } + + void componentVisibilityChanged (Component&) + { + owner->componentVisibilityChanged(); + } + + private: + VSTPluginWindow* const owner; + }; + + CompMovementWatcher* movementWatcher; + + //============================================================================== + int dispatch (const int opcode, const int index, const int value, void* const ptr, float opt) + { + return plugin.dispatch (opcode, index, value, ptr, opt); + } + + //============================================================================== + // hooks to get keyboard events from VST windows.. +#if JUCE_WIN32 + static LRESULT CALLBACK vstHookWndProc (HWND hW, UINT message, WPARAM wParam, LPARAM lParam) + { + for (int i = activeWindows.size(); --i >= 0;) + { + const VSTPluginWindow* const w = (const VSTPluginWindow*) activeWindows.getUnchecked (i); + + if (w->pluginHWND == hW) + { + if (message == WM_CHAR + || message == WM_KEYDOWN + || message == WM_SYSKEYDOWN + || message == WM_KEYUP + || message == WM_SYSKEYUP + || message == WM_APPCOMMAND) + { + SendMessage ((HWND) w->getTopLevelComponent()->getWindowHandle(), + message, wParam, lParam); + } + + return CallWindowProc ((WNDPROC) (w->originalWndProc), + (HWND) w->pluginHWND, + message, + wParam, + lParam); + } + } + + return DefWindowProc (hW, message, wParam, lParam); + } +#endif + +#if JUCE_LINUX + //============================================================================== + // overload mouse/keyboard events to forward them to the plugin's inner window.. + void sendEventToChild (XEvent* event) + { + if (pluginProc != 0) + { + // if the plugin publishes an event procedure, pass the event directly.. + pluginProc (event); + } + else if (pluginWindow != 0) + { + // if the plugin has a window, then send the event to the window so that + // its message thread will pick it up.. + XSendEvent (display, pluginWindow, False, 0L, event); + XFlush (display); + } + } + + void mouseEnter (const MouseEvent& e) + { + if (pluginWindow != 0) + { + XEvent ev; + zerostruct (ev); + ev.xcrossing.display = display; + ev.xcrossing.type = EnterNotify; + ev.xcrossing.window = pluginWindow; + ev.xcrossing.root = RootWindow (display, DefaultScreen (display)); + ev.xcrossing.time = CurrentTime; + ev.xcrossing.x = e.x; + ev.xcrossing.y = e.y; + ev.xcrossing.x_root = e.getScreenX(); + ev.xcrossing.y_root = e.getScreenY(); + ev.xcrossing.mode = NotifyNormal; // NotifyGrab, NotifyUngrab + ev.xcrossing.detail = NotifyAncestor; // NotifyVirtual, NotifyInferior, NotifyNonlinear,NotifyNonlinearVirtual + + translateJuceToXCrossingModifiers (e, ev); + + sendEventToChild (&ev); + } + } + + void mouseExit (const MouseEvent& e) + { + if (pluginWindow != 0) + { + XEvent ev; + zerostruct (ev); + ev.xcrossing.display = display; + ev.xcrossing.type = LeaveNotify; + ev.xcrossing.window = pluginWindow; + ev.xcrossing.root = RootWindow (display, DefaultScreen (display)); + ev.xcrossing.time = CurrentTime; + ev.xcrossing.x = e.x; + ev.xcrossing.y = e.y; + ev.xcrossing.x_root = e.getScreenX(); + ev.xcrossing.y_root = e.getScreenY(); + ev.xcrossing.mode = NotifyNormal; // NotifyGrab, NotifyUngrab + ev.xcrossing.detail = NotifyAncestor; // NotifyVirtual, NotifyInferior, NotifyNonlinear,NotifyNonlinearVirtual + ev.xcrossing.focus = hasKeyboardFocus (true); // TODO - yes ? + + translateJuceToXCrossingModifiers (e, ev); + + sendEventToChild (&ev); + } + } + + void mouseMove (const MouseEvent& e) + { + if (pluginWindow != 0) + { + XEvent ev; + zerostruct (ev); + ev.xmotion.display = display; + ev.xmotion.type = MotionNotify; + ev.xmotion.window = pluginWindow; + ev.xmotion.root = RootWindow (display, DefaultScreen (display)); + ev.xmotion.time = CurrentTime; + ev.xmotion.is_hint = NotifyNormal; + ev.xmotion.x = e.x; + ev.xmotion.y = e.y; + ev.xmotion.x_root = e.getScreenX(); + ev.xmotion.y_root = e.getScreenY(); + + sendEventToChild (&ev); + } + } + + void mouseDrag (const MouseEvent& e) + { + if (pluginWindow != 0) + { + XEvent ev; + zerostruct (ev); + ev.xmotion.display = display; + ev.xmotion.type = MotionNotify; + ev.xmotion.window = pluginWindow; + ev.xmotion.root = RootWindow (display, DefaultScreen (display)); + ev.xmotion.time = CurrentTime; + ev.xmotion.x = e.x ; + ev.xmotion.y = e.y; + ev.xmotion.x_root = e.getScreenX(); + ev.xmotion.y_root = e.getScreenY(); + ev.xmotion.is_hint = NotifyNormal; + + translateJuceToXMotionModifiers (e, ev); + sendEventToChild (&ev); + } + } + + void mouseUp (const MouseEvent& e) + { + if (pluginWindow != 0) + { + XEvent ev; + zerostruct (ev); + ev.xbutton.display = display; + ev.xbutton.type = ButtonRelease; + ev.xbutton.window = pluginWindow; + ev.xbutton.root = RootWindow (display, DefaultScreen (display)); + ev.xbutton.time = CurrentTime; + ev.xbutton.x = e.x; + ev.xbutton.y = e.y; + ev.xbutton.x_root = e.getScreenX(); + ev.xbutton.y_root = e.getScreenY(); + + translateJuceToXButtonModifiers (e, ev); + sendEventToChild (&ev); + } + } + + void mouseWheelMove (const MouseEvent& e, + float incrementX, + float incrementY) + { + if (pluginWindow != 0) + { + XEvent ev; + zerostruct (ev); + ev.xbutton.display = display; + ev.xbutton.type = ButtonPress; + ev.xbutton.window = pluginWindow; + ev.xbutton.root = RootWindow (display, DefaultScreen (display)); + ev.xbutton.time = CurrentTime; + ev.xbutton.x = e.x; + ev.xbutton.y = e.y; + ev.xbutton.x_root = e.getScreenX(); + ev.xbutton.y_root = e.getScreenY(); + + translateJuceToXMouseWheelModifiers (e, incrementY, ev); + sendEventToChild (&ev); + + // TODO - put a usleep here ? + + ev.xbutton.type = ButtonRelease; + sendEventToChild (&ev); + } + } +#endif + +}; + +//============================================================================== +AudioProcessorEditor* VSTPluginInstance::createEditor() +{ + if (hasEditor()) + return new VSTPluginWindow (*this); + + return 0; +} + + +//============================================================================== +void VSTPluginInstance::handleAsyncUpdate() +{ + // indicates that something about the plugin has changed.. + updateHostDisplay(); +} + +//============================================================================== +bool VSTPluginInstance::restoreProgramSettings (const fxProgram* const prog) +{ + if (swap (prog->chunkMagic) == 'CcnK' && swap (prog->fxMagic) == 'FxCk') + { + changeProgramName (getCurrentProgram(), prog->prgName); + + for (int i = 0; i < swap (prog->numParams); ++i) + setParameter (i, swapFloat (prog->params[i])); + + return true; + } + + return false; +} + +bool VSTPluginInstance::loadFromFXBFile (const void* const data, + const int dataSize) +{ + if (dataSize < 28) + return false; + + const fxSet* const set = (const fxSet*) data; + + if ((swap (set->chunkMagic) != 'CcnK' && swap (set->chunkMagic) != 'KncC') + || swap (set->version) > fxbVersionNum) + return false; + + if (swap (set->fxMagic) == 'FxBk') + { + // bank of programs + if (swap (set->numPrograms) >= 0) + { + const int oldProg = getCurrentProgram(); + const int numParams = swap (((const fxProgram*) (set->programs))->numParams); + const int progLen = sizeof (fxProgram) + (numParams - 1) * sizeof (float); + + for (int i = 0; i < swap (set->numPrograms); ++i) + { + if (i != oldProg) + { + const fxProgram* const prog = (const fxProgram*) (((const char*) (set->programs)) + i * progLen); + if (((const char*) prog) - ((const char*) set) >= dataSize) + return false; + + if (swap (set->numPrograms) > 0) + setCurrentProgram (i); + + if (! restoreProgramSettings (prog)) + return false; + } + } + + if (swap (set->numPrograms) > 0) + setCurrentProgram (oldProg); + + const fxProgram* const prog = (const fxProgram*) (((const char*) (set->programs)) + oldProg * progLen); + if (((const char*) prog) - ((const char*) set) >= dataSize) + return false; + + if (! restoreProgramSettings (prog)) + return false; + } + } + else if (swap (set->fxMagic) == 'FxCk') + { + // single program + const fxProgram* const prog = (const fxProgram*) data; + + if (swap (prog->chunkMagic) != 'CcnK') + return false; + + changeProgramName (getCurrentProgram(), prog->prgName); + + for (int i = 0; i < swap (prog->numParams); ++i) + setParameter (i, swapFloat (prog->params[i])); + } + else if (swap (set->fxMagic) == 'FBCh' || swap (set->fxMagic) == 'hCBF') + { + // non-preset chunk + const fxChunkSet* const cset = (const fxChunkSet*) data; + + if (swap (cset->chunkSize) + sizeof (fxChunkSet) - 8 > (unsigned int) dataSize) + return false; + + setChunkData (cset->chunk, swap (cset->chunkSize), false); + } + else if (swap (set->fxMagic) == 'FPCh' || swap (set->fxMagic) == 'hCPF') + { + // preset chunk + const fxProgramSet* const cset = (const fxProgramSet*) data; + + if (swap (cset->chunkSize) + sizeof (fxProgramSet) - 8 > (unsigned int) dataSize) + return false; + + setChunkData (cset->chunk, swap (cset->chunkSize), true); + + changeProgramName (getCurrentProgram(), cset->name); + } + else + { + return false; + } + + return true; +} + +//============================================================================== +void VSTPluginInstance::setParamsInProgramBlock (fxProgram* const prog) throw() +{ + const int numParams = getNumParameters(); + + prog->chunkMagic = swap ('CcnK'); + prog->byteSize = 0; + prog->fxMagic = swap ('FxCk'); + prog->version = swap (fxbVersionNum); + prog->fxID = swap (getUID()); + prog->fxVersion = swap (getVersionNumber()); + prog->numParams = swap (numParams); + + getCurrentProgramName().copyToBuffer (prog->prgName, sizeof (prog->prgName) - 1); + + for (int i = 0; i < numParams; ++i) + prog->params[i] = swapFloat (getParameter (i)); +} + +bool VSTPluginInstance::saveToFXBFile (MemoryBlock& dest, bool isFXB, int maxSizeMB) +{ + const int numPrograms = getNumPrograms(); + const int numParams = getNumParameters(); + + if (usesChunks()) + { + if (isFXB) + { + MemoryBlock chunk; + getChunkData (chunk, false, maxSizeMB); + + const int totalLen = sizeof (fxChunkSet) + chunk.getSize() - 8; + dest.setSize (totalLen, true); + + fxChunkSet* const set = (fxChunkSet*) dest.getData(); + set->chunkMagic = swap ('CcnK'); + set->byteSize = 0; + set->fxMagic = swap ('FBCh'); + set->version = swap (fxbVersionNum); + set->fxID = swap (getUID()); + set->fxVersion = swap (getVersionNumber()); + set->numPrograms = swap (numPrograms); + set->chunkSize = swap (chunk.getSize()); + + chunk.copyTo (set->chunk, 0, chunk.getSize()); + } + else + { + MemoryBlock chunk; + getChunkData (chunk, true, maxSizeMB); + + const int totalLen = sizeof (fxProgramSet) + chunk.getSize() - 8; + dest.setSize (totalLen, true); + + fxProgramSet* const set = (fxProgramSet*) dest.getData(); + set->chunkMagic = swap ('CcnK'); + set->byteSize = 0; + set->fxMagic = swap ('FPCh'); + set->version = swap (fxbVersionNum); + set->fxID = swap (getUID()); + set->fxVersion = swap (getVersionNumber()); + set->numPrograms = swap (numPrograms); + set->chunkSize = swap (chunk.getSize()); + + getCurrentProgramName().copyToBuffer (set->name, sizeof (set->name) - 1); + chunk.copyTo (set->chunk, 0, chunk.getSize()); + } + } + else + { + if (isFXB) + { + const int progLen = sizeof (fxProgram) + (numParams - 1) * sizeof (float); + const int len = (sizeof (fxSet) - sizeof (fxProgram)) + progLen * jmax (1, numPrograms); + dest.setSize (len, true); + + fxSet* const set = (fxSet*) dest.getData(); + set->chunkMagic = swap ('CcnK'); + set->byteSize = 0; + set->fxMagic = swap ('FxBk'); + set->version = swap (fxbVersionNum); + set->fxID = swap (getUID()); + set->fxVersion = swap (getVersionNumber()); + set->numPrograms = swap (numPrograms); + + const int oldProgram = getCurrentProgram(); + MemoryBlock oldSettings; + createTempParameterStore (oldSettings); + + setParamsInProgramBlock ((fxProgram*) (((char*) (set->programs)) + oldProgram * progLen)); + + for (int i = 0; i < numPrograms; ++i) + { + if (i != oldProgram) + { + setCurrentProgram (i); + setParamsInProgramBlock ((fxProgram*) (((char*) (set->programs)) + i * progLen)); + } + } + + setCurrentProgram (oldProgram); + restoreFromTempParameterStore (oldSettings); + } + else + { + const int totalLen = sizeof (fxProgram) + (numParams - 1) * sizeof (float); + dest.setSize (totalLen, true); + + setParamsInProgramBlock ((fxProgram*) dest.getData()); + } + } + + return true; +} + +void VSTPluginInstance::getChunkData (MemoryBlock& mb, bool isPreset, int maxSizeMB) const +{ + if (usesChunks()) + { + void* data = 0; + const int bytes = dispatch (effGetChunk, isPreset ? 1 : 0, 0, &data, 0.0f); + + if (data != 0 && bytes <= maxSizeMB * 1024 * 1024) + { + mb.setSize (bytes); + mb.copyFrom (data, 0, bytes); + } + } +} + +void VSTPluginInstance::setChunkData (const char* data, int size, bool isPreset) +{ + if (size > 0 && usesChunks()) + { + dispatch (effSetChunk, isPreset ? 1 : 0, size, (void*) data, 0.0f); + + if (! isPreset) + updateStoredProgramNames(); + } +} + +//============================================================================== +void VSTPluginInstance::timerCallback() +{ + if (dispatch (effIdle, 0, 0, 0, 0) == 0) + stopTimer(); +} + +int VSTPluginInstance::dispatch (const int opcode, const int index, const int value, void* const ptr, float opt) const +{ + const ScopedLock sl (lock); + + ++insideVSTCallback; + int result = 0; + + try + { + if (effect != 0) + { +#if JUCE_MAC + if (module->resFileId != 0) + UseResFile (module->resFileId); + + CGrafPtr oldPort; + + if (getActiveEditor() != 0) + { + int x = 0, y = 0; + getActiveEditor()->relativePositionToOtherComponent (getActiveEditor()->getTopLevelComponent(), x, y); + + GetPort (&oldPort); + SetPortWindowPort ((WindowRef) getActiveEditor()->getWindowHandle()); + SetOrigin (-x, -y); + } +#endif + + result = effect->dispatcher (effect, opcode, index, value, ptr, opt); + +#if JUCE_MAC + if (getActiveEditor() != 0) + SetPort (oldPort); + + module->resFileId = CurResFile(); +#endif + + --insideVSTCallback; + return result; + } + } + catch (...) + { + //char s[512]; + //sprintf (s, "dispatcher (%d, %d, %d, %x, %f)", opcode, index, value, (int)ptr, opt); + } + + --insideVSTCallback; + return result; +} + +//============================================================================== +// handles non plugin-specific callbacks.. +static VstIntPtr handleGeneralCallback (VstInt32 opcode, VstInt32 index, VstInt32 value, void *ptr, float opt) +{ + (void) index; + (void) value; + (void) opt; + + switch (opcode) + { + case audioMasterCanDo: + { + static const char* canDos[] = { "supplyIdle", + "sendVstEvents", + "sendVstMidiEvent", + "sendVstTimeInfo", + "receiveVstEvents", + "receiveVstMidiEvent", + "supportShell", + "shellCategory" }; + + for (int i = 0; i < numElementsInArray (canDos); ++i) + if (strcmp (canDos[i], (const char*) ptr) == 0) + return 1; + + return 0; + } + + case audioMasterVersion: + return 0x2400; + case audioMasterCurrentId: + return shellUIDToCreate; + case audioMasterGetNumAutomatableParameters: + return 0; + case audioMasterGetAutomationState: + return 1; + + case audioMasterGetVendorVersion: + return 0x0101; + case audioMasterGetVendorString: + case audioMasterGetProductString: + JUCEApplication::getInstance() + ->getApplicationName().copyToBuffer ((char*) ptr, jmin (kVstMaxVendorStrLen, kVstMaxProductStrLen) - 1); + break; + + case audioMasterGetSampleRate: + return 44100; + + case audioMasterGetBlockSize: + return 512; + + case audioMasterSetOutputSampleRate: + return 0; + + default: + DBG ("*** Unhandled VST Callback: " + String ((int) opcode)); + break; + } + + return 0; +} + +// handles callbacks for a specific plugin +VstIntPtr VSTPluginInstance::handleCallback (VstInt32 opcode, VstInt32 index, VstInt32 value, void *ptr, float opt) +{ + switch (opcode) + { + case audioMasterAutomate: + sendParamChangeMessageToListeners (index, opt); + break; + + case audioMasterProcessEvents: + handleMidiFromPlugin ((const VstEvents*) ptr); + break; + + case audioMasterGetTime: + #ifdef _MSC_VER + #pragma warning (push) + #pragma warning (disable: 4311) + #endif + + return (VstIntPtr) &vstHostTime; + + #ifdef _MSC_VER + #pragma warning (pop) + #endif + break; + + case audioMasterIdle: + if (insideVSTCallback == 0 && MessageManager::getInstance()->isThisTheMessageThread()) + { + ++insideVSTCallback; +#if JUCE_MAC + if (getActiveEditor() != 0) + dispatch (effEditIdle, 0, 0, 0, 0); +#endif + const MessageManagerLock mml; + + juce_callAnyTimersSynchronously(); + + handleUpdateNowIfNeeded(); + + for (int i = ComponentPeer::getNumPeers(); --i >= 0;) + ComponentPeer::getPeer (i)->performAnyPendingRepaintsNow(); + + --insideVSTCallback; + } + break; + + case audioMasterUpdateDisplay: + triggerAsyncUpdate(); + break; + + case audioMasterTempoAt: + // returns (10000 * bpm) + break; + + case audioMasterNeedIdle: + startTimer (50); + break; + + case audioMasterSizeWindow: + if (getActiveEditor() != 0) + getActiveEditor()->setSize (index, value); + + return 1; + + case audioMasterGetSampleRate: + return (VstIntPtr) getSampleRate(); + + case audioMasterGetBlockSize: + return (VstIntPtr) getBlockSize(); + + case audioMasterWantMidi: + wantsMidiMessages = true; + break; + + case audioMasterGetDirectory: + #if JUCE_MAC + return (VstIntPtr) (void*) &module->parentDirFSSpec; + #else + return (VstIntPtr) (pointer_sized_uint) (const char*) module->fullParentDirectoryPathName; + #endif + + case audioMasterGetAutomationState: + // returns 0: not supported, 1: off, 2:read, 3:write, 4:read/write + break; + + // none of these are handled (yet).. + case audioMasterBeginEdit: + case audioMasterEndEdit: + case audioMasterSetTime: + case audioMasterPinConnected: + case audioMasterGetParameterQuantization: + case audioMasterIOChanged: + case audioMasterGetInputLatency: + case audioMasterGetOutputLatency: + case audioMasterGetPreviousPlug: + case audioMasterGetNextPlug: + case audioMasterWillReplaceOrAccumulate: + case audioMasterGetCurrentProcessLevel: + case audioMasterOfflineStart: + case audioMasterOfflineRead: + case audioMasterOfflineWrite: + case audioMasterOfflineGetCurrentPass: + case audioMasterOfflineGetCurrentMetaPass: + case audioMasterVendorSpecific: + case audioMasterSetIcon: + case audioMasterGetLanguage: + case audioMasterOpenWindow: + case audioMasterCloseWindow: + break; + + default: + return handleGeneralCallback (opcode, index, value, ptr, opt); + } + + return 0; +} + +// entry point for all callbacks from the plugin +static VstIntPtr VSTCALLBACK audioMaster (AEffect* effect, VstInt32 opcode, VstInt32 index, VstIntPtr value, void* ptr, float opt) +{ + try + { + if (effect != 0 && effect->resvd2 != 0) + { + return ((VSTPluginInstance*)(effect->resvd2)) + ->handleCallback (opcode, index, value, ptr, opt); + } + + return handleGeneralCallback (opcode, index, value, ptr, opt); + } + catch (...) + { + return 0; + } +} + +//============================================================================== +const String VSTPluginInstance::getVersion() const throw() +{ + int v = dispatch (effGetVendorVersion, 0, 0, 0, 0); + + String s; + + if (v != 0) + { + int versionBits[4]; + int n = 0; + + while (v != 0) + { + versionBits [n++] = (v & 0xff); + v >>= 8; + } + + s << 'V'; + + while (n > 0) + { + s << versionBits [--n]; + + if (n > 0) + s << '.'; + } + } + + return s; +} + +int VSTPluginInstance::getUID() const +{ + int uid = effect != 0 ? effect->uniqueID : 0; + + if (uid == 0) + uid = getFile().hashCode(); + + return uid; +} + +const String VSTPluginInstance::getCategory() const throw() +{ + const char* result = 0; + + switch (dispatch (effGetPlugCategory, 0, 0, 0, 0)) + { + case kPlugCategEffect: + result = "Effect"; + break; + + case kPlugCategSynth: + result = "Synth"; + break; + + case kPlugCategAnalysis: + result = "Anaylsis"; + break; + + case kPlugCategMastering: + result = "Mastering"; + break; + + case kPlugCategSpacializer: + result = "Spacial"; + break; + + case kPlugCategRoomFx: + result = "Reverb"; + break; + + case kPlugSurroundFx: + result = "Surround"; + break; + + case kPlugCategRestoration: + result = "Restoration"; + break; + + case kPlugCategGenerator: + result = "Tone generation"; + break; + + default: + break; + } + + return result; +} + +//============================================================================== +float VSTPluginInstance::getParameter (int index) +{ + if (effect != 0 && ((unsigned int) index) < (unsigned int) effect->numParams) + { + try + { + const ScopedLock sl (lock); + return effect->getParameter (effect, index); + } + catch (...) + { + } + } + + return 0.0f; +} + +void VSTPluginInstance::setParameter (int index, float newValue) +{ + if (effect != 0 && ((unsigned int) index) < (unsigned int) effect->numParams) + { + try + { + const ScopedLock sl (lock); + + if (effect->getParameter (effect, index) != newValue) + effect->setParameter (effect, index, newValue); + } + catch (...) + { + } + } +} + +const String VSTPluginInstance::getParameterName (int index) +{ + if (effect != 0) + { + jassert (index >= 0 && index < effect->numParams); + + char nm [256]; + zerostruct (nm); + dispatch (effGetParamName, index, 0, nm, 0); + return String (nm).trim(); + } + + return String::empty; +} + +const String VSTPluginInstance::getParameterLabel (int index) const +{ + if (effect != 0) + { + jassert (index >= 0 && index < effect->numParams); + + char nm [256]; + zerostruct (nm); + dispatch (effGetParamLabel, index, 0, nm, 0); + return String (nm).trim(); + } + + return String::empty; +} + +const String VSTPluginInstance::getParameterText (int index) +{ + if (effect != 0) + { + jassert (index >= 0 && index < effect->numParams); + + char nm [256]; + zerostruct (nm); + dispatch (effGetParamDisplay, index, 0, nm, 0); + return String (nm).trim(); + } + + return String::empty; +} + +bool VSTPluginInstance::isParameterAutomatable (int index) const +{ + if (effect != 0) + { + jassert (index >= 0 && index < effect->numParams); + return dispatch (effCanBeAutomated, index, 0, 0, 0) != 0; + } + + return false; +} + +void VSTPluginInstance::createTempParameterStore (MemoryBlock& dest) +{ + dest.setSize (64 + 4 * getNumParameters()); + dest.fillWith (0); + + getCurrentProgramName().copyToBuffer ((char*) dest.getData(), 63); + + float* const p = (float*) (((char*) dest.getData()) + 64); + for (int i = 0; i < getNumParameters(); ++i) + p[i] = getParameter(i); +} + +void VSTPluginInstance::restoreFromTempParameterStore (const MemoryBlock& m) +{ + changeProgramName (getCurrentProgram(), (const char*) m); + + float* p = (float*) (((char*) m.getData()) + 64); + for (int i = 0; i < getNumParameters(); ++i) + setParameter (i, p[i]); +} + +//============================================================================== +void VSTPluginInstance::setCurrentProgram (int newIndex) +{ + if (getNumPrograms() > 0 && newIndex != getCurrentProgram()) + dispatch (effSetProgram, 0, jlimit (0, getNumPrograms() - 1, newIndex), 0, 0); +} + +const String VSTPluginInstance::getProgramName (int index) +{ + if (index == getCurrentProgram()) + { + return getCurrentProgramName(); + } + else if (effect != 0) + { + char nm [256]; + zerostruct (nm); + + if (dispatch (effGetProgramNameIndexed, + jlimit (0, getNumPrograms(), index), + -1, nm, 0) != 0) + { + return String (nm).trim(); + } + } + + return programNames [index]; +} + +void VSTPluginInstance::changeProgramName (int index, const String& newName) +{ + if (index == getCurrentProgram()) + { + if (getNumPrograms() > 0 && newName != getCurrentProgramName()) + dispatch (effSetProgramName, 0, 0, (void*) (const char*) newName.substring (0, 24), 0.0f); + } + else + { + jassertfalse // xxx not implemented! + } +} + +void VSTPluginInstance::updateStoredProgramNames() +{ + if (effect != 0 && getNumPrograms() > 0) + { + char nm [256]; + zerostruct (nm); + + // only do this if the plugin can't use indexed names.. + if (dispatch (effGetProgramNameIndexed, 0, -1, nm, 0) == 0) + { + const int oldProgram = getCurrentProgram(); + MemoryBlock oldSettings; + createTempParameterStore (oldSettings); + + for (int i = 0; i < getNumPrograms(); ++i) + { + setCurrentProgram (i); + getCurrentProgramName(); // (this updates the list) + } + + setCurrentProgram (oldProgram); + restoreFromTempParameterStore (oldSettings); + } + } +} + +const String VSTPluginInstance::getCurrentProgramName() +{ + if (effect != 0) + { + char nm [256]; + zerostruct (nm); + dispatch (effGetProgramName, 0, 0, nm, 0); + + const int index = getCurrentProgram(); + if (programNames[index].isEmpty()) + { + while (programNames.size() < index) + programNames.add (String::empty); + + programNames.set (index, String (nm).trim()); + } + + return String (nm).trim(); + } + + return String::empty; +} + +//============================================================================== +const String VSTPluginInstance::getInputChannelName (const int index) const +{ + if (index >= 0 && index < getNumInputChannels()) + { + VstPinProperties pinProps; + if (dispatch (effGetInputProperties, index, 0, &pinProps, 0.0f) != 0) + return String (pinProps.label, sizeof (pinProps.label)); + } + + return String::empty; +} + +bool VSTPluginInstance::isInputChannelStereoPair (int index) const +{ + if (index < 0 || index >= getNumInputChannels()) + return false; + + VstPinProperties pinProps; + if (dispatch (effGetInputProperties, index, 0, &pinProps, 0.0f) != 0) + return (pinProps.flags & kVstPinIsStereo) != 0; + + return true; +} + +const String VSTPluginInstance::getOutputChannelName (const int index) const +{ + if (index >= 0 && index < getNumOutputChannels()) + { + VstPinProperties pinProps; + if (dispatch (effGetOutputProperties, index, 0, &pinProps, 0.0f) != 0) + return String (pinProps.label, sizeof (pinProps.label)); + } + + return String::empty; +} + +bool VSTPluginInstance::isOutputChannelStereoPair (int index) const +{ + if (index < 0 || index >= getNumOutputChannels()) + return false; + + VstPinProperties pinProps; + if (dispatch (effGetOutputProperties, index, 0, &pinProps, 0.0f) != 0) + return (pinProps.flags & kVstPinIsStereo) != 0; + + return true; +} + +//============================================================================== +void VSTPluginInstance::setPower (const bool on) +{ + dispatch (effMainsChanged, 0, on ? 1 : 0, 0, 0); + isPowerOn = on; +} + +//============================================================================== +const int defaultMaxSizeMB = 64; + +void VSTPluginInstance::getStateInformation (MemoryBlock& destData) +{ + saveToFXBFile (destData, true, defaultMaxSizeMB); +} + +void VSTPluginInstance::getCurrentProgramStateInformation (MemoryBlock& destData) +{ + saveToFXBFile (destData, false, defaultMaxSizeMB); +} + +void VSTPluginInstance::setStateInformation (const void* data, int sizeInBytes) +{ + loadFromFXBFile (data, sizeInBytes); +} + +void VSTPluginInstance::setCurrentProgramStateInformation (const void* data, int sizeInBytes) +{ + loadFromFXBFile (data, sizeInBytes); +} + +//============================================================================== +//============================================================================== +VSTPluginFormat::VSTPluginFormat() +{ +} + +VSTPluginFormat::~VSTPluginFormat() +{ +} + +void VSTPluginFormat::findAllTypesForFile (OwnedArray & results, + const File& file) +{ + if (! fileMightContainThisPluginType (file)) + return; + + PluginDescription desc; + desc.file = file; + desc.uid = 0; + + VSTPluginInstance* instance = dynamic_cast (createInstanceFromDescription (desc)); + + if (instance == 0) + return; + + try + { +#if JUCE_MAC + if (instance->module->resFileId != 0) + UseResFile (instance->module->resFileId); +#endif + + instance->fillInPluginDescription (desc); + + VstPlugCategory category = (VstPlugCategory) instance->dispatch (effGetPlugCategory, 0, 0, 0, 0); + + if (category != kPlugCategShell) + { + // Normal plugin... + results.add (new PluginDescription (desc)); + + ++insideVSTCallback; + instance->dispatch (effOpen, 0, 0, 0, 0); + --insideVSTCallback; + } + else + { + // It's a shell plugin, so iterate all the subtypes... + char shellEffectName [64]; + + for (;;) + { + zerostruct (shellEffectName); + const int uid = instance->dispatch (effShellGetNextPlugin, 0, 0, shellEffectName, 0); + + if (uid == 0) + { + break; + } + else + { + desc.uid = uid; + desc.name = shellEffectName; + + bool alreadyThere = false; + + for (int i = results.size(); --i >= 0;) + { + PluginDescription* const d = results.getUnchecked(i); + + if (d->isDuplicateOf (desc)) + { + alreadyThere = true; + break; + } + } + + if (! alreadyThere) + results.add (new PluginDescription (desc)); + } + } + } + } + catch (...) + { + // crashed while loading... + } + + deleteAndZero (instance); +} + +AudioPluginInstance* VSTPluginFormat::createInstanceFromDescription (const PluginDescription& desc) +{ + VSTPluginInstance* result = 0; + + if (fileMightContainThisPluginType (desc.file)) + { + const File previousWorkingDirectory (File::getCurrentWorkingDirectory()); + desc.file.getParentDirectory().setAsCurrentWorkingDirectory(); + + const ReferenceCountedObjectPtr module (ModuleHandle::findOrCreateModule (desc.file)); + + if (module != 0) + { + shellUIDToCreate = desc.uid; + + result = new VSTPluginInstance (module); + + if (result->effect != 0) + { + result->effect->resvd2 = (VstIntPtr) (pointer_sized_int) result; + result->initialise(); + } + else + { + deleteAndZero (result); + } + } + + previousWorkingDirectory.setAsCurrentWorkingDirectory(); + } + + return result; +} + +bool VSTPluginFormat::fileMightContainThisPluginType (const File& f) +{ +#if JUCE_MAC + if (f.isDirectory() && f.hasFileExtension (T(".vst"))) + return true; + +#if JUCE_PPC + FSRef fileRef; + if (PlatformUtilities::makeFSRefFromPath (&fileRef, f.getFullPathName())) + { + const short resFileId = FSOpenResFile (&fileRef, fsRdPerm); + + if (resFileId != -1) + { + const int numEffects = Count1Resources ('aEff'); + CloseResFile (resFileId); + + if (numEffects > 0) + return true; + } + } +#endif + + return false; +#elif JUCE_WIN32 + return f.existsAsFile() + && f.hasFileExtension (T(".dll")); +#elif JUCE_LINUX + return f.existsAsFile() + && f.hasFileExtension (T(".so")); +#endif +} + +const FileSearchPath VSTPluginFormat::getDefaultLocationsToSearch() +{ +#if JUCE_MAC + return FileSearchPath ("~/Library/Audio/Plug-Ins/VST;/Library/Audio/Plug-Ins/VST"); +#elif JUCE_WIN32 + const String programFiles (File::getSpecialLocation (File::globalApplicationsDirectory).getFullPathName()); + + return FileSearchPath (programFiles + "\\Steinberg\\VstPlugins"); +#elif JUCE_LINUX + return FileSearchPath ("/usr/lib/vst"); +#endif +} + +#endif diff --git a/extras/audio plugin host/src/plugins/juce_AudioPluginFormat.cpp b/extras/audio plugin host/src/plugins/juce_AudioPluginFormat.cpp index e2dd538495..4082a05a54 100644 --- a/extras/audio plugin host/src/plugins/juce_AudioPluginFormat.cpp +++ b/extras/audio plugin host/src/plugins/juce_AudioPluginFormat.cpp @@ -105,3 +105,27 @@ void AudioPluginFormatManager::addFormat (AudioPluginFormat* const format) throw { formats.add (format); } + +AudioPluginInstance* AudioPluginFormatManager::createInstance (const PluginDescription& description, + String& errorMessage) const +{ + AudioPluginInstance* result = 0; + + for (int i = 0; i < formats.size(); ++i) + { + result = formats.getUnchecked(i)->createInstanceFromDescription (*this); + + if (result != 0) + break; + } + + if (result == 0) + { + if (file != File::nonexistent && ! file.exists()) + errorMessage = TRANS ("This plug-in file no longer exists"); + else + errorMessage = TRANS ("This plug-in failed to load correctly"); + } + + return result; +} diff --git a/extras/audio plugin host/src/plugins/juce_AudioPluginFormat.h b/extras/audio plugin host/src/plugins/juce_AudioPluginFormat.h index 094eb1aada..4f48cd60f1 100644 --- a/extras/audio plugin host/src/plugins/juce_AudioPluginFormat.h +++ b/extras/audio plugin host/src/plugins/juce_AudioPluginFormat.h @@ -146,6 +146,19 @@ public: */ void addFormat (AudioPluginFormat* const format) throw(); + + //============================================================================== + /** Tries to load the type for this description, by trying all the formats + that this manager knows about. + + The caller is responsible for deleting the object that is returned. + + If it can't load the plugin, it returns 0 and leaves a message in the + errorMessage string. + */ + AudioPluginInstance* createInstance (const PluginDescription& description, + String& errorMessage) const; + //============================================================================== juce_UseDebuggingNewOperator diff --git a/extras/audio plugin host/src/plugins/juce_KnownPluginList.cpp b/extras/audio plugin host/src/plugins/juce_KnownPluginList.cpp index 754efcfa79..309d3ea072 100644 --- a/extras/audio plugin host/src/plugins/juce_KnownPluginList.cpp +++ b/extras/audio plugin host/src/plugins/juce_KnownPluginList.cpp @@ -60,6 +60,15 @@ PluginDescription* KnownPluginList::getTypeForFile (const File& file) const thro return 0; } +PluginDescription* KnownPluginList::getTypeForIdentifierString (const String& identifierString) const throw() +{ + for (int i = 0; i < types.size(); ++i) + if (types.getUnchecked(i)->createIdentifierString() == identifierString) + return types.getUnchecked(i); + + return 0; +} + bool KnownPluginList::addType (const PluginDescription& type) { for (int i = types.size(); --i >= 0;) @@ -341,7 +350,7 @@ public: { PluginDescription* const plugin = plugins.getUnchecked(i); - m.addItem (allPlugins.indexOf (plugin) + menuIdBase, + m.addItem (allPlugins.indexOf (plugin) + menuIdBase, plugin->name, true, false); } } diff --git a/extras/audio plugin host/src/plugins/juce_KnownPluginList.h b/extras/audio plugin host/src/plugins/juce_KnownPluginList.h index b2d4bb7bc7..1713203ef2 100644 --- a/extras/audio plugin host/src/plugins/juce_KnownPluginList.h +++ b/extras/audio plugin host/src/plugins/juce_KnownPluginList.h @@ -74,6 +74,13 @@ public: */ PluginDescription* getTypeForFile (const File& file) const throw(); + /** Looks for a type in the list which matches a plugin type ID. + + The identifierString parameter must have been created by + PluginDescription::createIdentifierString(). + */ + PluginDescription* getTypeForIdentifierString (const String& identifierString) const throw(); + /** Adds a type manually from its description. */ bool addType (const PluginDescription& type); diff --git a/extras/audio plugin host/src/plugins/juce_PluginDescription.cpp b/extras/audio plugin host/src/plugins/juce_PluginDescription.cpp index b292a2260c..619d26b4e9 100644 --- a/extras/audio plugin host/src/plugins/juce_PluginDescription.cpp +++ b/extras/audio plugin host/src/plugins/juce_PluginDescription.cpp @@ -85,44 +85,11 @@ bool PluginDescription::isDuplicateOf (const PluginDescription& other) const && uid == other.uid; } -void PluginDescription::fillInFromInstance (AudioPluginInstance& instance) throw() +const String PluginDescription::createIdentifierString() const throw() { - name = instance.getName(); - file = instance.getFile(); - uid = instance.getUID(); - lastFileModTime = file.getLastModificationTime(); - pluginFormatName = instance.getFormatName(); - category = instance.getCategory(); - manufacturerName = instance.getManufacturer(); - version = instance.getVersion(); - numInputChannels = instance.getNumInputChannels(); - numOutputChannels = instance.getNumOutputChannels(); - isInstrument = instance.isInstrument(); -} - -AudioPluginInstance* PluginDescription::createInstance (String& errorMessage) const -{ - AudioPluginInstance* result = 0; - - for (int i = 0; i < AudioPluginFormatManager::getInstance()->getNumFormats(); ++i) - { - AudioPluginFormat* const format = AudioPluginFormatManager::getInstance()->getFormat (i); - - result = format->createInstanceFromDescription (*this); - - if (result != 0) - break; - } - - if (result == 0) - { - if (file != File::nonexistent && ! file.exists()) - errorMessage = TRANS ("This plug-in file no longer exists"); - else - errorMessage = TRANS ("This plug-in failed to load correctly"); - } - - return result; + return pluginFormatName + + T("/") + pluginName + + T("/") + String::toHexString (file.getFileName().hashCode()); } XmlElement* PluginDescription::createXml() const diff --git a/extras/audio plugin host/src/plugins/juce_PluginDescription.h b/extras/audio plugin host/src/plugins/juce_PluginDescription.h index a6744b50a6..db709be3ba 100644 --- a/extras/audio plugin host/src/plugins/juce_PluginDescription.h +++ b/extras/audio plugin host/src/plugins/juce_PluginDescription.h @@ -85,6 +85,8 @@ public: Note that this might not be unique between formats, e.g. a VST and some other format might actually have the same id. + + @see createIdentifierString */ int uid; @@ -111,17 +113,6 @@ public: */ void fillInFromInstance (AudioPluginInstance& instance) throw(); - //============================================================================== - /** Tries to load the type for this description, by trying all the formats - available in the AudioPluginFormat. - - The caller is responsible for deleting the object that is returned. - - If it can't load the plugin, it returns 0 and leaves a message in the - errorMessage string. - */ - AudioPluginInstance* createInstance (String& errorMessage) const; - //============================================================================== /** Creates an XML object containing these details. @@ -136,6 +127,16 @@ public: */ bool loadFromXml (const XmlElement& xml); + //============================================================================== + /** Returns a string that can be saved and used to uniquely identify the + plugin again. + + This contains less info than the XML encoding, and is independent of the + plugin's file location, so can be used to store a plugin ID for use + across different machines. + */ + const String createIdentifierString() const throw(); + //============================================================================== juce_UseDebuggingNewOperator diff --git a/extras/audio plugins/demo/src/DemoJuceFilter.cpp b/extras/audio plugins/demo/src/DemoJuceFilter.cpp index c7bc07de0a..e19c116d88 100644 --- a/extras/audio plugins/demo/src/DemoJuceFilter.cpp +++ b/extras/audio plugins/demo/src/DemoJuceFilter.cpp @@ -124,6 +124,16 @@ bool DemoJuceFilter::isOutputChannelStereoPair (int index) const return false; } +bool DemoJuceFilter::acceptsMidi() const +{ + return true; +} + +bool DemoJuceFilter::producesMidi() const +{ + return true; +} + //============================================================================== void DemoJuceFilter::prepareToPlay (double sampleRate, int samplesPerBlock) { diff --git a/extras/audio plugins/demo/src/DemoJuceFilter.h b/extras/audio plugins/demo/src/DemoJuceFilter.h index ae8a0384c5..4fe80a5c3a 100644 --- a/extras/audio plugins/demo/src/DemoJuceFilter.h +++ b/extras/audio plugins/demo/src/DemoJuceFilter.h @@ -71,6 +71,9 @@ public: bool isInputChannelStereoPair (int index) const; bool isOutputChannelStereoPair (int index) const; + bool acceptsMidi() const; + bool producesMidi() const; + //============================================================================== int getNumPrograms() { return 0; } int getCurrentProgram() { return 0; } diff --git a/extras/audio plugins/wrapper/formats/Standalone/juce_AudioFilterStreamer.cpp b/extras/audio plugins/wrapper/formats/Standalone/juce_AudioFilterStreamer.cpp index 25e456f453..614c5980e6 100644 --- a/extras/audio plugins/wrapper/formats/Standalone/juce_AudioFilterStreamer.cpp +++ b/extras/audio plugins/wrapper/formats/Standalone/juce_AudioFilterStreamer.cpp @@ -101,15 +101,14 @@ void AudioFilterStreamer::audioDeviceIOCallback (const float** inputChannelData, zeromem (outChans[numOutsWanted++], sizeof (float) * numSamples); } -void AudioFilterStreamer::audioDeviceAboutToStart (double sampleRate_, - int numSamplesPerBlock) +void AudioFilterStreamer::audioDeviceAboutToStart (AudioIODevice* device) { - sampleRate = sampleRate_; + sampleRate = device->getCurrentSampleRate(); isPlaying = true; emptyBuffer.setSize (1 + filter.getNumOutputChannels(), - jmax (2048, numSamplesPerBlock * 2)); + jmax (2048, device->getCurrentBlockSizeSamples() * 2)); emptyBuffer.clear(); midiCollector.reset (sampleRate); diff --git a/extras/audio plugins/wrapper/formats/Standalone/juce_AudioFilterStreamer.h b/extras/audio plugins/wrapper/formats/Standalone/juce_AudioFilterStreamer.h index 8fbfc95be8..35923fa862 100644 --- a/extras/audio plugins/wrapper/formats/Standalone/juce_AudioFilterStreamer.h +++ b/extras/audio plugins/wrapper/formats/Standalone/juce_AudioFilterStreamer.h @@ -66,7 +66,7 @@ public: int totalNumOutputChannels, int numSamples); - void audioDeviceAboutToStart (double sampleRate, int numSamplesPerBlock); + void audioDeviceAboutToStart (AudioIODevice* device); void audioDeviceStopped(); void handleIncomingMidiMessage (MidiInput* source, const MidiMessage& message); diff --git a/extras/juce demo/build/macosx/jucedemo.xcodeproj/project.pbxproj b/extras/juce demo/build/macosx/jucedemo.xcodeproj/project.pbxproj index 986f5aaf7b..e4fbeea181 100644 --- a/extras/juce demo/build/macosx/jucedemo.xcodeproj/project.pbxproj +++ b/extras/juce demo/build/macosx/jucedemo.xcodeproj/project.pbxproj @@ -32,7 +32,7 @@ 84EAE2F309DAAF0B00288D0A /* AGL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84EAE2F109DAAF0B00288D0A /* AGL.framework */; }; 84EAE2F409DAAF0B00288D0A /* OpenGL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84EAE2F209DAAF0B00288D0A /* OpenGL.framework */; }; 84F8DB0B099CA8DD00E911ED /* QuickTimeDemo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84F8DB0A099CA8DD00E911ED /* QuickTimeDemo.cpp */; }; - 84FDB0950C15BDDD00CD0087 /* libjucedebug.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 84FDB0940C15BDCE00CD0087 /* libjucedebug.a */; }; + 84FDB0950C15BDDD00CD0087 /* libjuce.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 84FDB0940C15BDCE00CD0087 /* libjuce.a */; }; 8D0C4E8D0486CD37000505A6 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 0867D6AAFE840B52C02AAC07 /* InfoPlist.strings */; }; 8D0C4E920486CD37000505A6 /* Carbon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 20286C33FDCF999611CA2CEA /* Carbon.framework */; }; /* End PBXBuildFile section */ @@ -77,7 +77,7 @@ 84EAE2F909DAAF2F00288D0A /* Juce.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Juce.xcodeproj; path = ../../../../build/macosx/Juce.xcodeproj; sourceTree = SOURCE_ROOT; }; 84F8DB0A099CA8DD00E911ED /* QuickTimeDemo.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = QuickTimeDemo.cpp; sourceTree = ""; }; 8D0C4E960486CD37000505A6 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; - 8D0C4E970486CD37000505A6 /* jucedemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = jucedemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 8D0C4E970486CD37000505A6 /* jucedemo.app */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = wrapper.application; path = jucedemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -85,7 +85,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 84FDB0950C15BDDD00CD0087 /* libjucedebug.a in Frameworks */, + 84FDB0950C15BDDD00CD0087 /* libjuce.a in Frameworks */, 8D0C4E920486CD37000505A6 /* Carbon.framework in Frameworks */, 84C49F2606C0F3200066071B /* CoreAudio.framework in Frameworks */, 84C4A41106C0F9A00066071B /* IOKit.framework in Frameworks */, @@ -181,7 +181,7 @@ 84FDB0900C15BDCE00CD0087 /* Products */ = { isa = PBXGroup; children = ( - 84FDB0940C15BDCE00CD0087 /* libjucedebug.a */, + 84FDB0940C15BDCE00CD0087 /* libjuce.a */, ); name = Products; sourceTree = ""; @@ -244,10 +244,10 @@ /* End PBXProject section */ /* Begin PBXReferenceProxy section */ - 84FDB0940C15BDCE00CD0087 /* libjucedebug.a */ = { + 84FDB0940C15BDCE00CD0087 /* libjuce.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; - path = libjucedebug.a; + path = libjuce.a; remoteRef = 84FDB0930C15BDCE00CD0087 /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; diff --git a/extras/juce demo/src/BinaryData.cpp b/extras/juce demo/src/BinaryData.cpp index da871475b4..3af4a6f1ca 100644 --- a/extras/juce demo/src/BinaryData.cpp +++ b/extras/juce demo/src/BinaryData.cpp @@ -2022,68 +2022,70 @@ static const unsigned char temp4[] = {47,42,13,10,32,32,61,61,61,61,61,61,61,61, 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,13,10,32,32,32,32,98, 111,111,108,32,105,115,73,110,116,101,114,101,115,116,101,100,73,110,68,114,97,103,83,111,117,114,99,101,32,40,99,111,110,115,116,32,83,116,114,105, - 110,103,38,32,115,111,117,114,99,101,68,101,115,99,114,105,112,116,105,111,110,41,13,10,32,32,32,32,123,13,10,32,32,32,32,32,32,32,32,47, - 47,32,110,111,114,109,97,108,108,121,32,121,111,117,39,100,32,99,104,101,99,107,32,116,104,101,32,115,111,117,114,99,101,68,101,115,99,114,105,112, - 116,105,111,110,32,118,97,108,117,101,32,116,111,32,115,101,101,32,105,102,32,105,116,39,115,32,116,104,101,13,10,32,32,32,32,32,32,32,32,47, - 47,32,115,111,114,116,32,111,102,32,111,98,106,101,99,116,32,116,104,97,116,32,121,111,117,39,114,101,32,105,110,116,101,114,101,115,116,101,100,32, - 105,110,32,98,101,102,111,114,101,32,114,101,116,117,114,110,105,110,103,32,116,114,117,101,44,32,98,117,116,32,102,111,114,13,10,32,32,32,32,32, - 32,32,32,47,47,32,116,104,101,32,100,101,109,111,44,32,119,101,39,108,108,32,115,97,121,32,121,101,115,32,116,111,32,97,110,121,116,104,105,110, - 103,46,46,13,10,32,32,32,32,32,32,32,32,114,101,116,117,114,110,32,116,114,117,101,59,13,10,32,32,32,32,125,13,10,13,10,32,32,32,32, - 118,111,105,100,32,105,116,101,109,68,114,97,103,69,110,116,101,114,32,40,99,111,110,115,116,32,83,116,114,105,110,103,38,32,115,111,117,114,99,101, - 68,101,115,99,114,105,112,116,105,111,110,44,13,10,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,67,111, - 109,112,111,110,101,110,116,42,32,115,111,117,114,99,101,67,111,109,112,111,110,101,110,116,44,13,10,32,32,32,32,32,32,32,32,32,32,32,32,32, - 32,32,32,32,32,32,32,32,32,32,32,105,110,116,32,120,44,32,105,110,116,32,121,41,13,10,32,32,32,32,123,13,10,32,32,32,32,32,32,32, - 32,115,111,109,101,116,104,105,110,103,73,115,66,101,105,110,103,68,114,97,103,103,101,100,79,118,101,114,32,61,32,116,114,117,101,59,13,10,32,32, - 32,32,32,32,32,32,114,101,112,97,105,110,116,40,41,59,13,10,32,32,32,32,125,13,10,13,10,32,32,32,32,118,111,105,100,32,105,116,101,109, - 68,114,97,103,77,111,118,101,32,40,99,111,110,115,116,32,83,116,114,105,110,103,38,32,115,111,117,114,99,101,68,101,115,99,114,105,112,116,105,111, - 110,44,13,10,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,67,111,109,112,111,110,101,110,116,42,32,115,111, - 117,114,99,101,67,111,109,112,111,110,101,110,116,44,13,10,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,105, - 110,116,32,120,44,32,105,110,116,32,121,41,13,10,32,32,32,32,123,13,10,32,32,32,32,125,13,10,13,10,32,32,32,32,118,111,105,100,32,105, - 116,101,109,68,114,97,103,69,120,105,116,32,40,99,111,110,115,116,32,83,116,114,105,110,103,38,32,115,111,117,114,99,101,68,101,115,99,114,105,112, - 116,105,111,110,44,13,10,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,67,111,109,112,111,110,101,110,116,42, - 32,115,111,117,114,99,101,67,111,109,112,111,110,101,110,116,41,13,10,32,32,32,32,123,13,10,32,32,32,32,32,32,32,32,115,111,109,101,116,104, - 105,110,103,73,115,66,101,105,110,103,68,114,97,103,103,101,100,79,118,101,114,32,61,32,102,97,108,115,101,59,13,10,32,32,32,32,32,32,32,32, - 114,101,112,97,105,110,116,40,41,59,13,10,32,32,32,32,125,13,10,13,10,32,32,32,32,118,111,105,100,32,105,116,101,109,68,114,111,112,112,101, - 100,32,40,99,111,110,115,116,32,83,116,114,105,110,103,38,32,115,111,117,114,99,101,68,101,115,99,114,105,112,116,105,111,110,44,13,10,32,32,32, + 110,103,38,32,115,111,117,114,99,101,68,101,115,99,114,105,112,116,105,111,110,44,13,10,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32, 32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,67,111,109,112,111,110,101,110,116,42,32,115,111,117,114,99,101,67,111,109,112, - 111,110,101,110,116,44,13,10,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,105,110,116,32,120,44,32,105,110,116, - 32,121,41,13,10,32,32,32,32,123,13,10,32,32,32,32,32,32,32,32,109,101,115,115,97,103,101,32,61,32,84,40,34,108,97,115,116,32,114,111, - 119,115,32,100,114,111,112,112,101,100,58,32,34,41,32,43,32,115,111,117,114,99,101,68,101,115,99,114,105,112,116,105,111,110,59,13,10,13,10,32, - 32,32,32,32,32,32,32,115,111,109,101,116,104,105,110,103,73,115,66,101,105,110,103,68,114,97,103,103,101,100,79,118,101,114,32,61,32,102,97,108, - 115,101,59,13,10,32,32,32,32,32,32,32,32,114,101,112,97,105,110,116,40,41,59,13,10,32,32,32,32,125,13,10,125,59,13,10,13,10,13,10, - 47,47,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 111,110,101,110,116,41,13,10,32,32,32,32,123,13,10,32,32,32,32,32,32,32,32,47,47,32,110,111,114,109,97,108,108,121,32,121,111,117,39,100, + 32,99,104,101,99,107,32,116,104,101,32,115,111,117,114,99,101,68,101,115,99,114,105,112,116,105,111,110,32,118,97,108,117,101,32,116,111,32,115,101, + 101,32,105,102,32,105,116,39,115,32,116,104,101,13,10,32,32,32,32,32,32,32,32,47,47,32,115,111,114,116,32,111,102,32,111,98,106,101,99,116, + 32,116,104,97,116,32,121,111,117,39,114,101,32,105,110,116,101,114,101,115,116,101,100,32,105,110,32,98,101,102,111,114,101,32,114,101,116,117,114,110, + 105,110,103,32,116,114,117,101,44,32,98,117,116,32,102,111,114,13,10,32,32,32,32,32,32,32,32,47,47,32,116,104,101,32,100,101,109,111,44,32, + 119,101,39,108,108,32,115,97,121,32,121,101,115,32,116,111,32,97,110,121,116,104,105,110,103,46,46,13,10,32,32,32,32,32,32,32,32,114,101,116, + 117,114,110,32,116,114,117,101,59,13,10,32,32,32,32,125,13,10,13,10,32,32,32,32,118,111,105,100,32,105,116,101,109,68,114,97,103,69,110,116, + 101,114,32,40,99,111,110,115,116,32,83,116,114,105,110,103,38,32,115,111,117,114,99,101,68,101,115,99,114,105,112,116,105,111,110,44,13,10,32,32, + 32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,67,111,109,112,111,110,101,110,116,42,32,115,111,117,114,99,101,67, + 111,109,112,111,110,101,110,116,44,13,10,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,105,110,116,32,120, + 44,32,105,110,116,32,121,41,13,10,32,32,32,32,123,13,10,32,32,32,32,32,32,32,32,115,111,109,101,116,104,105,110,103,73,115,66,101,105,110, + 103,68,114,97,103,103,101,100,79,118,101,114,32,61,32,116,114,117,101,59,13,10,32,32,32,32,32,32,32,32,114,101,112,97,105,110,116,40,41,59, + 13,10,32,32,32,32,125,13,10,13,10,32,32,32,32,118,111,105,100,32,105,116,101,109,68,114,97,103,77,111,118,101,32,40,99,111,110,115,116,32, + 83,116,114,105,110,103,38,32,115,111,117,114,99,101,68,101,115,99,114,105,112,116,105,111,110,44,13,10,32,32,32,32,32,32,32,32,32,32,32,32, + 32,32,32,32,32,32,32,32,32,32,32,67,111,109,112,111,110,101,110,116,42,32,115,111,117,114,99,101,67,111,109,112,111,110,101,110,116,44,13,10, + 32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,105,110,116,32,120,44,32,105,110,116,32,121,41,13,10,32,32, + 32,32,123,13,10,32,32,32,32,125,13,10,13,10,32,32,32,32,118,111,105,100,32,105,116,101,109,68,114,97,103,69,120,105,116,32,40,99,111,110, + 115,116,32,83,116,114,105,110,103,38,32,115,111,117,114,99,101,68,101,115,99,114,105,112,116,105,111,110,44,13,10,32,32,32,32,32,32,32,32,32, + 32,32,32,32,32,32,32,32,32,32,32,32,32,32,67,111,109,112,111,110,101,110,116,42,32,115,111,117,114,99,101,67,111,109,112,111,110,101,110,116, + 41,13,10,32,32,32,32,123,13,10,32,32,32,32,32,32,32,32,115,111,109,101,116,104,105,110,103,73,115,66,101,105,110,103,68,114,97,103,103,101, + 100,79,118,101,114,32,61,32,102,97,108,115,101,59,13,10,32,32,32,32,32,32,32,32,114,101,112,97,105,110,116,40,41,59,13,10,32,32,32,32, + 125,13,10,13,10,32,32,32,32,118,111,105,100,32,105,116,101,109,68,114,111,112,112,101,100,32,40,99,111,110,115,116,32,83,116,114,105,110,103,38, + 32,115,111,117,114,99,101,68,101,115,99,114,105,112,116,105,111,110,44,13,10,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32, + 32,32,32,67,111,109,112,111,110,101,110,116,42,32,115,111,117,114,99,101,67,111,109,112,111,110,101,110,116,44,13,10,32,32,32,32,32,32,32,32, + 32,32,32,32,32,32,32,32,32,32,32,32,32,32,105,110,116,32,120,44,32,105,110,116,32,121,41,13,10,32,32,32,32,123,13,10,32,32,32,32, + 32,32,32,32,109,101,115,115,97,103,101,32,61,32,84,40,34,108,97,115,116,32,114,111,119,115,32,100,114,111,112,112,101,100,58,32,34,41,32,43, + 32,115,111,117,114,99,101,68,101,115,99,114,105,112,116,105,111,110,59,13,10,13,10,32,32,32,32,32,32,32,32,115,111,109,101,116,104,105,110,103, + 73,115,66,101,105,110,103,68,114,97,103,103,101,100,79,118,101,114,32,61,32,102,97,108,115,101,59,13,10,32,32,32,32,32,32,32,32,114,101,112, + 97,105,110,116,40,41,59,13,10,32,32,32,32,125,13,10,125,59,13,10,13,10,13,10,47,47,61,61,61,61,61,61,61,61,61,61,61,61,61,61, 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 13,10,99,108,97,115,115,32,68,114,97,103,65,110,100,68,114,111,112,68,101,109,111,32,32,58,32,112,117,98,108,105,99,32,67,111,109,112,111,110, - 101,110,116,44,13,10,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,112,117,98,108,105,99,32,68,114, - 97,103,65,110,100,68,114,111,112,67,111,110,116,97,105,110,101,114,13,10,123,13,10,32,32,32,32,47,47,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,13,10,99,108,97,115,115,32,68,114,97,103,65,110,100,68, + 114,111,112,68,101,109,111,32,32,58,32,112,117,98,108,105,99,32,67,111,109,112,111,110,101,110,116,44,13,10,32,32,32,32,32,32,32,32,32,32, + 32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,112,117,98,108,105,99,32,68,114,97,103,65,110,100,68,114,111,112,67,111,110,116,97,105,110, + 101,114,13,10,123,13,10,32,32,32,32,47,47,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,13,10,32,32,32,32,68,114,97,103,65,110,100, - 68,114,111,112,68,101,109,111,83,111,117,114,99,101,42,32,115,111,117,114,99,101,59,13,10,32,32,32,32,68,114,97,103,65,110,100,68,114,111,112, - 68,101,109,111,84,97,114,103,101,116,42,32,116,97,114,103,101,116,59,13,10,13,10,112,117,98,108,105,99,58,13,10,32,32,32,32,47,47,61,61, + 61,61,61,61,61,61,61,61,61,61,61,13,10,32,32,32,32,68,114,97,103,65,110,100,68,114,111,112,68,101,109,111,83,111,117,114,99,101,42,32, + 115,111,117,114,99,101,59,13,10,32,32,32,32,68,114,97,103,65,110,100,68,114,111,112,68,101,109,111,84,97,114,103,101,116,42,32,116,97,114,103, + 101,116,59,13,10,13,10,112,117,98,108,105,99,58,13,10,32,32,32,32,47,47,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,13,10,32,32, - 32,32,68,114,97,103,65,110,100,68,114,111,112,68,101,109,111,40,41,13,10,32,32,32,32,123,13,10,32,32,32,32,32,32,32,32,115,101,116,78, - 97,109,101,32,40,84,40,34,68,114,97,103,45,97,110,100,45,68,114,111,112,34,41,41,59,13,10,13,10,32,32,32,32,32,32,32,32,115,111,117, - 114,99,101,32,61,32,110,101,119,32,68,114,97,103,65,110,100,68,114,111,112,68,101,109,111,83,111,117,114,99,101,40,41,59,13,10,32,32,32,32, - 32,32,32,32,97,100,100,65,110,100,77,97,107,101,86,105,115,105,98,108,101,32,40,115,111,117,114,99,101,41,59,13,10,13,10,32,32,32,32,32, - 32,32,32,116,97,114,103,101,116,32,61,32,110,101,119,32,68,114,97,103,65,110,100,68,114,111,112,68,101,109,111,84,97,114,103,101,116,40,41,59, - 13,10,32,32,32,32,32,32,32,32,97,100,100,65,110,100,77,97,107,101,86,105,115,105,98,108,101,32,40,116,97,114,103,101,116,41,59,13,10,32, - 32,32,32,125,13,10,13,10,32,32,32,32,126,68,114,97,103,65,110,100,68,114,111,112,68,101,109,111,40,41,13,10,32,32,32,32,123,13,10,32, - 32,32,32,32,32,32,32,100,101,108,101,116,101,65,108,108,67,104,105,108,100,114,101,110,40,41,59,13,10,32,32,32,32,125,13,10,13,10,32,32, - 32,32,118,111,105,100,32,114,101,115,105,122,101,100,40,41,13,10,32,32,32,32,123,13,10,32,32,32,32,32,32,32,32,115,111,117,114,99,101,45, - 62,115,101,116,66,111,117,110,100,115,32,40,49,48,44,32,49,48,44,32,50,53,48,44,32,49,53,48,41,59,13,10,32,32,32,32,32,32,32,32, - 116,97,114,103,101,116,45,62,115,101,116,66,111,117,110,100,115,32,40,103,101,116,87,105,100,116,104,40,41,32,45,32,50,54,48,44,32,103,101,116, - 72,101,105,103,104,116,40,41,32,45,32,49,54,48,44,32,50,53,48,44,32,49,53,48,41,59,13,10,32,32,32,32,125,13,10,13,10,32,32,32, - 32,47,47,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,13,10,32,32,32,32,68,114,97,103,65,110,100,68,114,111,112,68,101,109, + 111,40,41,13,10,32,32,32,32,123,13,10,32,32,32,32,32,32,32,32,115,101,116,78,97,109,101,32,40,84,40,34,68,114,97,103,45,97,110,100, + 45,68,114,111,112,34,41,41,59,13,10,13,10,32,32,32,32,32,32,32,32,115,111,117,114,99,101,32,61,32,110,101,119,32,68,114,97,103,65,110, + 100,68,114,111,112,68,101,109,111,83,111,117,114,99,101,40,41,59,13,10,32,32,32,32,32,32,32,32,97,100,100,65,110,100,77,97,107,101,86,105, + 115,105,98,108,101,32,40,115,111,117,114,99,101,41,59,13,10,13,10,32,32,32,32,32,32,32,32,116,97,114,103,101,116,32,61,32,110,101,119,32, + 68,114,97,103,65,110,100,68,114,111,112,68,101,109,111,84,97,114,103,101,116,40,41,59,13,10,32,32,32,32,32,32,32,32,97,100,100,65,110,100, + 77,97,107,101,86,105,115,105,98,108,101,32,40,116,97,114,103,101,116,41,59,13,10,32,32,32,32,125,13,10,13,10,32,32,32,32,126,68,114,97, + 103,65,110,100,68,114,111,112,68,101,109,111,40,41,13,10,32,32,32,32,123,13,10,32,32,32,32,32,32,32,32,100,101,108,101,116,101,65,108,108, + 67,104,105,108,100,114,101,110,40,41,59,13,10,32,32,32,32,125,13,10,13,10,32,32,32,32,118,111,105,100,32,114,101,115,105,122,101,100,40,41, + 13,10,32,32,32,32,123,13,10,32,32,32,32,32,32,32,32,115,111,117,114,99,101,45,62,115,101,116,66,111,117,110,100,115,32,40,49,48,44,32, + 49,48,44,32,50,53,48,44,32,49,53,48,41,59,13,10,32,32,32,32,32,32,32,32,116,97,114,103,101,116,45,62,115,101,116,66,111,117,110,100, + 115,32,40,103,101,116,87,105,100,116,104,40,41,32,45,32,50,54,48,44,32,103,101,116,72,101,105,103,104,116,40,41,32,45,32,49,54,48,44,32, + 50,53,48,44,32,49,53,48,41,59,13,10,32,32,32,32,125,13,10,13,10,32,32,32,32,47,47,61,61,61,61,61,61,61,61,61,61,61,61,61, 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,13,10,32,32,32,32,47,47,32,40,110,101,101,100,32,116,111,32,112,117,116,32,116,104,105,115,32,105,110,32,116,111,32,100,105,115,97,109,98, - 105,103,117,97,116,101,32,116,104,101,32,110,101,119,47,100,101,108,101,116,101,32,111,112,101,114,97,116,111,114,115,32,117,115,101,100,32,105,110,32, - 116,104,101,13,10,32,32,32,32,47,47,32,116,119,111,32,98,97,115,101,32,99,108,97,115,115,101,115,41,46,13,10,32,32,32,32,106,117,99,101, - 95,85,115,101,68,101,98,117,103,103,105,110,103,78,101,119,79,112,101,114,97,116,111,114,13,10,125,59,13,10,13,10,13,10,47,47,61,61,61,61, + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,13,10,32,32,32,32,47,47,32,40,110,101,101,100,32, + 116,111,32,112,117,116,32,116,104,105,115,32,105,110,32,116,111,32,100,105,115,97,109,98,105,103,117,97,116,101,32,116,104,101,32,110,101,119,47,100, + 101,108,101,116,101,32,111,112,101,114,97,116,111,114,115,32,117,115,101,100,32,105,110,32,116,104,101,13,10,32,32,32,32,47,47,32,116,119,111,32, + 98,97,115,101,32,99,108,97,115,115,101,115,41,46,13,10,32,32,32,32,106,117,99,101,95,85,115,101,68,101,98,117,103,103,105,110,103,78,101,119, + 79,112,101,114,97,116,111,114,13,10,125,59,13,10,13,10,13,10,47,47,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, - 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,13,10,67,111,109,112, - 111,110,101,110,116,42,32,99,114,101,97,116,101,68,114,97,103,65,110,100,68,114,111,112,68,101,109,111,40,41,13,10,123,13,10,32,32,32,32,114, - 101,116,117,114,110,32,110,101,119,32,68,114,97,103,65,110,100,68,114,111,112,68,101,109,111,40,41,59,13,10,125,13,10,0,0}; + 61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,13,10,67,111,109,112,111,110,101,110,116,42,32,99,114,101,97,116,101,68,114,97, + 103,65,110,100,68,114,111,112,68,101,109,111,40,41,13,10,123,13,10,32,32,32,32,114,101,116,117,114,110,32,110,101,119,32,68,114,97,103,65,110, + 100,68,114,111,112,68,101,109,111,40,41,59,13,10,125,13,10,0,0}; const char* BinaryData::draganddropdemo_cpp = (const char*) temp4; static const unsigned char temp5[] = {47,42,13,10,32,32,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61,61, diff --git a/extras/juce demo/src/BinaryData.h b/extras/juce demo/src/BinaryData.h index 89d2c5743e..7779331dfc 100644 --- a/extras/juce demo/src/BinaryData.h +++ b/extras/juce demo/src/BinaryData.h @@ -15,7 +15,7 @@ namespace BinaryData const int demo_table_data_xmlSize = 5239; extern const char* draganddropdemo_cpp; - const int draganddropdemo_cppSize = 7873; + const int draganddropdemo_cppSize = 7937; extern const char* fontsandtextdemo_cpp; const int fontsandtextdemo_cppSize = 9064; diff --git a/extras/juce demo/src/binarydata/AudioDemo.cpp b/extras/juce demo/src/binarydata/AudioDemo.cpp index 799452309d..ee410345b8 100644 --- a/extras/juce demo/src/binarydata/AudioDemo.cpp +++ b/extras/juce demo/src/binarydata/AudioDemo.cpp @@ -342,7 +342,7 @@ public: } } - void audioDeviceAboutToStart (double sampleRate, int numSamplesPerBlock) + void audioDeviceAboutToStart (AudioIODevice*) { zeromem (circularBuffer, sizeof (float) * bufferSize); } @@ -504,10 +504,10 @@ public: waveformComponent->audioDeviceIOCallback (inputChannelData, totalNumInputChannels, outputChannelData, totalNumOutputChannels, numSamples); } - void audioDeviceAboutToStart (double sampleRate, int numSamplesPerBlock) + void audioDeviceAboutToStart (AudioIODevice* device) { - audioSourcePlayer.audioDeviceAboutToStart (sampleRate, numSamplesPerBlock); - waveformComponent->audioDeviceAboutToStart (sampleRate, numSamplesPerBlock); + audioSourcePlayer.audioDeviceAboutToStart (device); + waveformComponent->audioDeviceAboutToStart (device); } void audioDeviceStopped() diff --git a/extras/juce demo/src/binarydata/DragAndDropDemo.cpp b/extras/juce demo/src/binarydata/DragAndDropDemo.cpp index fdca202a06..5668bece7c 100644 --- a/extras/juce demo/src/binarydata/DragAndDropDemo.cpp +++ b/extras/juce demo/src/binarydata/DragAndDropDemo.cpp @@ -152,7 +152,8 @@ public: } //============================================================================== - bool isInterestedInDragSource (const String& sourceDescription) + bool isInterestedInDragSource (const String& sourceDescription, + Component* sourceComponent) { // normally you'd check the sourceDescription value to see if it's the // sort of object that you're interested in before returning true, but for diff --git a/extras/juce demo/src/demos/AudioDemo.cpp b/extras/juce demo/src/demos/AudioDemo.cpp index 799452309d..ee410345b8 100644 --- a/extras/juce demo/src/demos/AudioDemo.cpp +++ b/extras/juce demo/src/demos/AudioDemo.cpp @@ -342,7 +342,7 @@ public: } } - void audioDeviceAboutToStart (double sampleRate, int numSamplesPerBlock) + void audioDeviceAboutToStart (AudioIODevice*) { zeromem (circularBuffer, sizeof (float) * bufferSize); } @@ -504,10 +504,10 @@ public: waveformComponent->audioDeviceIOCallback (inputChannelData, totalNumInputChannels, outputChannelData, totalNumOutputChannels, numSamples); } - void audioDeviceAboutToStart (double sampleRate, int numSamplesPerBlock) + void audioDeviceAboutToStart (AudioIODevice* device) { - audioSourcePlayer.audioDeviceAboutToStart (sampleRate, numSamplesPerBlock); - waveformComponent->audioDeviceAboutToStart (sampleRate, numSamplesPerBlock); + audioSourcePlayer.audioDeviceAboutToStart (device); + waveformComponent->audioDeviceAboutToStart (device); } void audioDeviceStopped() diff --git a/extras/juce demo/src/demos/DragAndDropDemo.cpp b/extras/juce demo/src/demos/DragAndDropDemo.cpp index fdca202a06..5668bece7c 100644 --- a/extras/juce demo/src/demos/DragAndDropDemo.cpp +++ b/extras/juce demo/src/demos/DragAndDropDemo.cpp @@ -152,7 +152,8 @@ public: } //============================================================================== - bool isInterestedInDragSource (const String& sourceDescription) + bool isInterestedInDragSource (const String& sourceDescription, + Component* sourceComponent) { // normally you'd check the sourceDescription value to see if it's the // sort of object that you're interested in before returning true, but for diff --git a/juce.h b/juce.h index 5d390e24f9..1f88957283 100644 --- a/juce.h +++ b/juce.h @@ -169,6 +169,7 @@ END_JUCE_NAMESPACE #pragma comment(lib, "ole32.lib") #pragma comment(lib, "advapi32.lib") #pragma comment(lib, "ws2_32.lib") + #pragma comment(lib, "SetupAPI.Lib") //#pragma comment(lib, "gdiplus.lib") #if JUCE_OPENGL diff --git a/juce_Config.h b/juce_Config.h index ed813d00bd..b857f37554 100644 --- a/juce_Config.h +++ b/juce_Config.h @@ -137,6 +137,16 @@ #define JUCE_USE_XSHM 1 #endif +//============================================================================= +/** Enabling this builds support for VST audio plugins. + @see VSTPluginFormat, AudioPluginFormat, AudioPluginFormatManager, JUCE_PLUGINHOST_AU +*/ +#define JUCE_PLUGINHOST_VST 1 + +/** Enabling this builds support for AudioUnit audio plugins. + @see AudioUnitPluginFormat, AudioPluginFormat, AudioPluginFormatManager, JUCE_PLUGINHOST_VST +*/ +#define JUCE_PLUGINHOST_AU 1 //============================================================================= /** Disabling this will avoid linking to any UI code. This is handy for diff --git a/src/juce_app_includes.h b/src/juce_app_includes.h index e06a787d2a..d6497d061f 100644 --- a/src/juce_app_includes.h +++ b/src/juce_app_includes.h @@ -95,9 +95,15 @@ #ifndef __JUCE_AUDIOPROCESSOREDITOR_JUCEHEADER__ #include "juce_appframework/audio/processors/juce_AudioProcessorEditor.h" #endif +#ifndef __JUCE_AUDIOPROCESSORGRAPH_JUCEHEADER__ + #include "juce_appframework/audio/processors/juce_AudioProcessorGraph.h" +#endif #ifndef __JUCE_AUDIOPROCESSORLISTENER_JUCEHEADER__ #include "juce_appframework/audio/processors/juce_AudioProcessorListener.h" #endif +#ifndef __JUCE_AUDIOPROCESSORPLAYER_JUCEHEADER__ + #include "juce_appframework/audio/processors/juce_AudioProcessorPlayer.h" +#endif #ifndef __JUCE_GENERICAUDIOPROCESSOREDITOR_JUCEHEADER__ #include "juce_appframework/audio/processors/juce_GenericAudioProcessorEditor.h" #endif @@ -155,6 +161,39 @@ #ifndef __JUCE_SYNTHESISER_JUCEHEADER__ #include "juce_appframework/audio/synthesisers/juce_Synthesiser.h" #endif +#ifndef __JUCE_AUDIOUNITPLUGINFORMAT_JUCEHEADER__ + #include "juce_appframework/audio/plugins/formats/juce_AudioUnitPluginFormat.h" +#endif +#ifndef __JUCE_DIRECTXPLUGINFORMAT_JUCEHEADER__ + #include "juce_appframework/audio/plugins/formats/juce_DirectXPluginFormat.h" +#endif +#ifndef __JUCE_LADSPAPLUGINFORMAT_JUCEHEADER__ + #include "juce_appframework/audio/plugins/formats/juce_LADSPAPluginFormat.h" +#endif +#ifndef __JUCE_VSTPLUGINFORMAT_JUCEHEADER__ + #include "juce_appframework/audio/plugins/formats/juce_VSTPluginFormat.h" +#endif +#ifndef __JUCE_AUDIOPLUGINFORMAT_JUCEHEADER__ + #include "juce_appframework/audio/plugins/juce_AudioPluginFormat.h" +#endif +#ifndef __JUCE_AUDIOPLUGINFORMATMANAGER_JUCEHEADER__ + #include "juce_appframework/audio/plugins/juce_AudioPluginFormatManager.h" +#endif +#ifndef __JUCE_AUDIOPLUGININSTANCE_JUCEHEADER__ + #include "juce_appframework/audio/plugins/juce_AudioPluginInstance.h" +#endif +#ifndef __JUCE_KNOWNPLUGINLIST_JUCEHEADER__ + #include "juce_appframework/audio/plugins/juce_KnownPluginList.h" +#endif +#ifndef __JUCE_PLUGINDESCRIPTION_JUCEHEADER__ + #include "juce_appframework/audio/plugins/juce_PluginDescription.h" +#endif +#ifndef __JUCE_PLUGINDIRECTORYSCANNER_JUCEHEADER__ + #include "juce_appframework/audio/plugins/juce_PluginDirectoryScanner.h" +#endif +#ifndef __JUCE_PLUGINLISTCOMPONENT_JUCEHEADER__ + #include "juce_appframework/audio/plugins/juce_PluginListComponent.h" +#endif #ifndef __JUCE_AIFFAUDIOFORMAT_JUCEHEADER__ #include "juce_appframework/audio/audio_file_formats/juce_AiffAudioFormat.h" #endif diff --git a/src/juce_appframework/audio/plugins/formats/juce_AudioUnitPluginFormat.cpp b/src/juce_appframework/audio/plugins/formats/juce_AudioUnitPluginFormat.cpp new file mode 100644 index 0000000000..755a3ef115 --- /dev/null +++ b/src/juce_appframework/audio/plugins/formats/juce_AudioUnitPluginFormat.cpp @@ -0,0 +1,1284 @@ +/* + ============================================================================== + + This file is part of the JUCE library - "Jules' Utility Class Extensions" + Copyright 2004-7 by Raw Material Software ltd. + + ------------------------------------------------------------------------------ + + JUCE can be redistributed and/or modified under the terms of the + GNU General Public License, as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + JUCE is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with JUCE; if not, visit www.gnu.org/licenses or write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------------ + + If you'd like to release a closed-source product which uses JUCE, commercial + licenses are also available: visit www.rawmaterialsoftware.com/juce for + more information. + + ============================================================================== +*/ + +#if ! (defined (LINUX) || defined (_WIN32)) + +#include +#include +#include + +#include "../../../../juce_core/basics/juce_StandardHeader.h" + +BEGIN_JUCE_NAMESPACE + +#include "juce_AudioUnitPluginFormat.h" +#include "../juce_PluginDescription.h" + +#if JUCE_PLUGINHOST_AU && JUCE_MAC + +extern void juce_callAnyTimersSynchronously(); +extern bool juce_isHIViewCreatedByJuce (HIViewRef view); +extern bool juce_isWindowCreatedByJuce (WindowRef window); + +#if MACOS_10_3_OR_EARLIER + #define kAudioUnitType_Generator 'augn' +#endif + +// Change this to disable logging of various activities +#ifndef AU_LOGGING + #define AU_LOGGING 1 +#endif + +#if AU_LOGGING + #define log(a) Logger::writeToLog(a); +#else + #define log(a) +#endif + +static int insideCallback = 0; + +//============================================================================== +class AudioUnitPluginWindow; + +//============================================================================== +class AudioUnitPluginInstance : public AudioPluginInstance +{ +public: + //============================================================================== + ~AudioUnitPluginInstance(); + + //============================================================================== + // AudioPluginInstance methods: + + void fillInPluginDescription (PluginDescription& desc) const + { + desc.name = pluginName; + desc.file = file; + desc.uid = ((int) componentDesc.componentType) + ^ ((int) componentDesc.componentSubType) + ^ ((int) componentDesc.componentManufacturer); + desc.lastFileModTime = file.getLastModificationTime(); + desc.pluginFormatName = "AudioUnit"; + desc.category = getCategory(); + desc.manufacturerName = manufacturer; + desc.version = version; + desc.numInputChannels = instance.getNumInputChannels(); + desc.numOutputChannels = instance.getNumOutputChannels(); + desc.isInstrument = (componentDesc.componentType == kAudioUnitType_MusicDevice); + } + + bool acceptsMidi() const { return wantsMidiMessages; } + bool producesMidi() const { return false; } + + //============================================================================== + // AudioProcessor methods: + + void prepareToPlay (double sampleRate, int estimatedSamplesPerBlock); + void releaseResources(); + void processBlock (AudioSampleBuffer& buffer, + MidiBuffer& midiMessages); + + AudioProcessorEditor* createEditor(); + + const String getInputChannelName (const int index) const; + bool isInputChannelStereoPair (int index) const; + + const String getOutputChannelName (const int index) const; + bool isOutputChannelStereoPair (int index) const; + + //============================================================================== + int getNumParameters(); + float getParameter (int index); + void setParameter (int index, float newValue); + const String getParameterName (int index); + const String getParameterText (int index); + bool isParameterAutomatable (int index) const; + + //============================================================================== + int getNumPrograms(); + int getCurrentProgram(); + void setCurrentProgram (int index); + const String getProgramName (int index); + void changeProgramName (int index, const String& newName); + + //============================================================================== + void getStateInformation (MemoryBlock& destData); + void getCurrentProgramStateInformation (MemoryBlock& destData); + void setStateInformation (const void* data, int sizeInBytes); + void setCurrentProgramStateInformation (const void* data, int sizeInBytes); + + //============================================================================== + juce_UseDebuggingNewOperator + +private: + friend class AudioUnitPluginWindow; + friend class AudioUnitPluginFormat; + + ComponentDescription componentDesc; + String pluginName, manufacturer, version; + File file; + CriticalSection lock; + bool initialised, wantsMidiMessages, wasPlaying; + + AudioBufferList* outputBufferList; + AudioTimeStamp timeStamp; + AudioSampleBuffer* currentBuffer; + + AudioUnit audioUnit; + Array parameterIds; + + //============================================================================== + bool getComponentDescFromFile (const File& file); + void initialise(); + + //============================================================================== + OSStatus renderGetInput (AudioUnitRenderActionFlags* ioActionFlags, + const AudioTimeStamp* inTimeStamp, + UInt32 inBusNumber, + UInt32 inNumberFrames, + AudioBufferList* ioData) const; + + static OSStatus renderGetInputCallback (void* inRefCon, + AudioUnitRenderActionFlags* ioActionFlags, + const AudioTimeStamp* inTimeStamp, + UInt32 inBusNumber, + UInt32 inNumberFrames, + AudioBufferList* ioData) + { + return ((AudioUnitPluginInstance*) inRefCon) + ->renderGetInput (ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, ioData); + } + + OSStatus getBeatAndTempo (Float64* outCurrentBeat, Float64* outCurrentTempo) const; + OSStatus getMusicalTimeLocation (UInt32* outDeltaSampleOffsetToNextBeat, Float32* outTimeSig_Numerator, + UInt32* outTimeSig_Denominator, Float64* outCurrentMeasureDownBeat) const; + OSStatus getTransportState (Boolean* outIsPlaying, Boolean* outTransportStateChanged, + Float64* outCurrentSampleInTimeLine, Boolean* outIsCycling, + Float64* outCycleStartBeat, Float64* outCycleEndBeat); + + static OSStatus getBeatAndTempoCallback (void* inHostUserData, Float64* outCurrentBeat, Float64* outCurrentTempo) + { + return ((AudioUnitPluginInstance*) inHostUserData)->getBeatAndTempo (outCurrentBeat, outCurrentTempo); + } + + static OSStatus getMusicalTimeLocationCallback (void* inHostUserData, UInt32* outDeltaSampleOffsetToNextBeat, + Float32* outTimeSig_Numerator, UInt32* outTimeSig_Denominator, + Float64* outCurrentMeasureDownBeat) + { + return ((AudioUnitPluginInstance*) inHostUserData) + ->getMusicalTimeLocation (outDeltaSampleOffsetToNextBeat, outTimeSig_Numerator, + outTimeSig_Denominator, outCurrentMeasureDownBeat); + } + + static OSStatus getTransportStateCallback (void* inHostUserData, Boolean* outIsPlaying, Boolean* outTransportStateChanged, + Float64* outCurrentSampleInTimeLine, Boolean* outIsCycling, + Float64* outCycleStartBeat, Float64* outCycleEndBeat) + { + return ((AudioUnitPluginInstance*) inHostUserData) + ->getTransportState (outIsPlaying, outTransportStateChanged, + outCurrentSampleInTimeLine, outIsCycling, + outCycleStartBeat, outCycleEndBeat); + } + + //============================================================================== + void getNumChannels (int& numIns, int& numOuts) + { + numIns = 0; + numOuts = 0; + + AUChannelInfo supportedChannels [128]; + UInt32 supportedChannelsSize = sizeof (supportedChannels); + + if (AudioUnitGetProperty (audioUnit, kAudioUnitProperty_SupportedNumChannels, kAudioUnitScope_Global, + 0, supportedChannels, &supportedChannelsSize) == noErr + && supportedChannelsSize > 0) + { + for (int i = 0; i < supportedChannelsSize / sizeof (AUChannelInfo); ++i) + { + numIns = jmax (numIns, supportedChannels[i].inChannels); + numOuts = jmax (numOuts, supportedChannels[i].outChannels); + } + } + else + { + // (this really means the plugin will take any number of ins/outs as long + // as they are the same) + numIns = numOuts = 2; + } + } + + const String getCategory() const; + + //============================================================================== + AudioUnitPluginInstance (const File& file); +}; + +//============================================================================== +AudioUnitPluginInstance::AudioUnitPluginInstance (const File& file_) + : file (file_), + initialised (false), + wantsMidiMessages (false), + audioUnit (0), + outputBufferList (0), + currentBuffer (0) +{ + try + { + ++insideCallback; + + log (T("Opening AU: ") + file.getFullPathName()); + + if (getComponentDescFromFile (file)) + { + ComponentRecord* const comp = FindNextComponent (0, &componentDesc); + + if (comp != 0) + { + audioUnit = (AudioUnit) OpenComponent (comp); + + wantsMidiMessages = componentDesc.componentType == kAudioUnitType_MusicDevice + || componentDesc.componentType == kAudioUnitType_MusicEffect; + } + } + + --insideCallback; + } + catch (...) + { + --insideCallback; + } +} + +AudioUnitPluginInstance::~AudioUnitPluginInstance() +{ + { + const ScopedLock sl (lock); + + jassert (insideCallback == 0); + + if (audioUnit != 0) + { + AudioUnitUninitialize (audioUnit); + CloseComponent (audioUnit); + audioUnit = 0; + } + } + + juce_free (outputBufferList); +} + +bool AudioUnitPluginInstance::getComponentDescFromFile (const File& file) +{ + zerostruct (componentDesc); + + if (! file.hasFileExtension (T(".component"))) + return false; + + const String filename (file.getFullPathName()); + const char* const utf8 = filename.toUTF8(); + CFURLRef url = CFURLCreateFromFileSystemRepresentation (0, (const UInt8*) utf8, + strlen (utf8), file.isDirectory()); + if (url != 0) + { + CFBundleRef bundleRef = CFBundleCreate (kCFAllocatorDefault, url); + CFRelease (url); + + if (bundleRef != 0) + { + CFTypeRef name = CFBundleGetValueForInfoDictionaryKey (bundleRef, CFSTR("CFBundleName")); + + if (name != 0 && CFGetTypeID (name) == CFStringGetTypeID()) + pluginName = PlatformUtilities::cfStringToJuceString ((CFStringRef) name); + + if (pluginName.isEmpty()) + pluginName = file.getFileNameWithoutExtension(); + + CFTypeRef versionString = CFBundleGetValueForInfoDictionaryKey (bundleRef, CFSTR("CFBundleVersion")); + + if (versionString != 0 && CFGetTypeID (versionString) == CFStringGetTypeID()) + version = PlatformUtilities::cfStringToJuceString ((CFStringRef) versionString); + + CFTypeRef manuString = CFBundleGetValueForInfoDictionaryKey (bundleRef, CFSTR("CFBundleGetInfoString")); + + if (manuString != 0 && CFGetTypeID (manuString) == CFStringGetTypeID()) + manufacturer = PlatformUtilities::cfStringToJuceString ((CFStringRef) manuString); + + short resFileId = CFBundleOpenBundleResourceMap (bundleRef); + UseResFile (resFileId); + + for (int i = 1; i <= Count1Resources ('thng'); ++i) + { + Handle h = Get1IndResource ('thng', i); + + if (h != 0) + { + HLock (h); + const uint32* const types = (const uint32*) *h; + + if (types[0] == kAudioUnitType_MusicDevice + || types[0] == kAudioUnitType_MusicEffect + || types[0] == kAudioUnitType_Effect + || types[0] == kAudioUnitType_Generator + || types[0] == kAudioUnitType_Panner) + { + componentDesc.componentType = types[0]; + componentDesc.componentSubType = types[1]; + componentDesc.componentManufacturer = types[2]; + break; + } + + HUnlock (h); + ReleaseResource (h); + } + } + + CFBundleCloseBundleResourceMap (bundleRef, resFileId); + CFRelease (bundleRef); + } + } + + return componentDesc.componentType != 0 && componentDesc.componentSubType != 0; +} + +//============================================================================== +void AudioUnitPluginInstance::initialise() +{ + if (initialised || audioUnit == 0) + return; + + log (T("Initialising AU: ") + pluginName); + + parameterIds.clear(); + + { + UInt32 paramListSize = 0; + AudioUnitGetProperty (audioUnit, kAudioUnitProperty_ParameterList, kAudioUnitScope_Global, + 0, 0, ¶mListSize); + + if (paramListSize > 0) + { + parameterIds.insertMultiple (0, 0, paramListSize / sizeof (int)); + + AudioUnitGetProperty (audioUnit, kAudioUnitProperty_ParameterList, kAudioUnitScope_Global, + 0, ¶meterIds.getReference(0), ¶mListSize); + } + } + + { + AURenderCallbackStruct info; + zerostruct (info); + info.inputProcRefCon = this; + info.inputProc = renderGetInputCallback; + + AudioUnitSetProperty (audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, + 0, &info, sizeof (info)); + } + + { + HostCallbackInfo info; + zerostruct (info); + info.hostUserData = this; + info.beatAndTempoProc = getBeatAndTempoCallback; + info.musicalTimeLocationProc = getMusicalTimeLocationCallback; + info.transportStateProc = getTransportStateCallback; + + AudioUnitSetProperty (audioUnit, kAudioUnitProperty_HostCallbacks, kAudioUnitScope_Global, + 0, &info, sizeof (info)); + } + + int numIns, numOuts; + getNumChannels (numIns, numOuts); + setPlayConfigDetails (numIns, numOuts, 0, 0); + + initialised = AudioUnitInitialize (audioUnit) == noErr; + + setLatencySamples (0); +} + + +//============================================================================== +void AudioUnitPluginInstance::prepareToPlay (double sampleRate_, + int samplesPerBlockExpected) +{ + initialise(); + + if (initialised) + { + int numIns, numOuts; + getNumChannels (numIns, numOuts); + + setPlayConfigDetails (numIns, numOuts, sampleRate_, samplesPerBlockExpected); + + Float64 latencySecs = 0.0; + UInt32 latencySize = sizeof (latencySecs); + AudioUnitGetProperty (audioUnit, kAudioUnitProperty_Latency, kAudioUnitScope_Global, + 0, &latencySecs, &latencySize); + + setLatencySamples (roundDoubleToInt (latencySecs * sampleRate_)); + + AudioUnitReset (audioUnit, kAudioUnitScope_Input, 0); + AudioUnitReset (audioUnit, kAudioUnitScope_Output, 0); + AudioUnitReset (audioUnit, kAudioUnitScope_Global, 0); + + AudioStreamBasicDescription stream; + zerostruct (stream); + stream.mSampleRate = sampleRate_; + stream.mFormatID = kAudioFormatLinearPCM; + stream.mFormatFlags = kAudioFormatFlagsNativeFloatPacked | kAudioFormatFlagIsNonInterleaved; + stream.mFramesPerPacket = 1; + stream.mBytesPerPacket = 4; + stream.mBytesPerFrame = 4; + stream.mBitsPerChannel = 32; + stream.mChannelsPerFrame = numIns; + + OSStatus err = AudioUnitSetProperty (audioUnit, + kAudioUnitProperty_StreamFormat, + kAudioUnitScope_Input, + 0, &stream, sizeof (stream)); + + stream.mChannelsPerFrame = numOuts; + + err = AudioUnitSetProperty (audioUnit, + kAudioUnitProperty_StreamFormat, + kAudioUnitScope_Output, + 0, &stream, sizeof (stream)); + + juce_free (outputBufferList); + outputBufferList = (AudioBufferList*) juce_calloc (sizeof (AudioBufferList) + sizeof (AudioBuffer) * (numOuts + 1)); + outputBufferList->mNumberBuffers = numOuts; + + for (int i = numOuts; --i >= 0;) + outputBufferList->mBuffers[i].mNumberChannels = 1; + + zerostruct (timeStamp); + timeStamp.mSampleTime = 0; + timeStamp.mHostTime = AudioGetCurrentHostTime(); + timeStamp.mFlags = kAudioTimeStampSampleTimeValid | kAudioTimeStampHostTimeValid; + + currentBuffer = 0; + wasPlaying = false; + } +} + +void AudioUnitPluginInstance::releaseResources() +{ + if (initialised) + { + AudioUnitReset (audioUnit, kAudioUnitScope_Input, 0); + AudioUnitReset (audioUnit, kAudioUnitScope_Output, 0); + AudioUnitReset (audioUnit, kAudioUnitScope_Global, 0); + + juce_free (outputBufferList); + outputBufferList = 0; + currentBuffer = 0; + } +} + +OSStatus AudioUnitPluginInstance::renderGetInput (AudioUnitRenderActionFlags* ioActionFlags, + const AudioTimeStamp* inTimeStamp, + UInt32 inBusNumber, + UInt32 inNumberFrames, + AudioBufferList* ioData) const +{ + if (inBusNumber == 0 + && currentBuffer != 0) + { + jassert (inNumberFrames == currentBuffer->getNumSamples()); // if this ever happens, might need to add extra handling + + for (int i = 0; i < ioData->mNumberBuffers; ++i) + { + if (i < currentBuffer->getNumChannels()) + { + memcpy (ioData->mBuffers[i].mData, + currentBuffer->getSampleData (i, 0), + sizeof (float) * inNumberFrames); + } + else + { + zeromem (ioData->mBuffers[i].mData, sizeof (float) * inNumberFrames); + } + } + } + + return noErr; +} + +void AudioUnitPluginInstance::processBlock (AudioSampleBuffer& buffer, + MidiBuffer& midiMessages) +{ + const int numSamples = buffer.getNumSamples(); + + if (initialised) + { + AudioUnitRenderActionFlags flags = 0; + + timeStamp.mHostTime = AudioGetCurrentHostTime(); + + for (int i = getNumOutputChannels(); --i >= 0;) + { + outputBufferList->mBuffers[i].mDataByteSize = sizeof (float) * numSamples; + outputBufferList->mBuffers[i].mData = buffer.getSampleData (i, 0); + } + + currentBuffer = &buffer; + + if (wantsMidiMessages) + { + const uint8* midiEventData; + int midiEventSize, midiEventPosition; + MidiBuffer::Iterator i (midiMessages); + + while (i.getNextEvent (midiEventData, midiEventSize, midiEventPosition)) + { + if (midiEventSize <= 3) + MusicDeviceMIDIEvent (audioUnit, + midiEventData[0], midiEventData[1], midiEventData[2], + midiEventPosition); + else + MusicDeviceSysEx (audioUnit, midiEventData, midiEventSize); + } + + midiMessages.clear(); + } + + AudioUnitRender (audioUnit, &flags, &timeStamp, + 0, numSamples, outputBufferList); + + timeStamp.mSampleTime += numSamples; + } + else + { + // Not initialised, so just bypass.. + for (int i = getNumInputChannels(); i < getNumOutputChannels(); ++i) + buffer.clear (i, 0, buffer.getNumSamples()); + } +} + +//============================================================================== +OSStatus AudioUnitPluginInstance::getBeatAndTempo (Float64* outCurrentBeat, Float64* outCurrentTempo) const +{ + AudioPlayHead* const ph = getPlayHead(); + AudioPlayHead::CurrentPositionInfo result; + + if (ph != 0 && ph->getCurrentPosition (result)) + { + *outCurrentBeat = result.ppqPosition; + *outCurrentTempo = result.bpm; + } + else + { + *outCurrentBeat = 0; + *outCurrentTempo = 120.0; + } + + return noErr; +} + +OSStatus AudioUnitPluginInstance::getMusicalTimeLocation (UInt32* outDeltaSampleOffsetToNextBeat, + Float32* outTimeSig_Numerator, + UInt32* outTimeSig_Denominator, + Float64* outCurrentMeasureDownBeat) const +{ + AudioPlayHead* const ph = getPlayHead(); + AudioPlayHead::CurrentPositionInfo result; + + if (ph != 0 && ph->getCurrentPosition (result)) + { + *outTimeSig_Numerator = result.timeSigNumerator; + *outTimeSig_Denominator = result.timeSigDenominator; + + *outDeltaSampleOffsetToNextBeat = 0; //xxx + *outCurrentMeasureDownBeat = result.ppqPositionOfLastBarStart; //xxx wrong + } + else + { + *outDeltaSampleOffsetToNextBeat = 0; + *outTimeSig_Numerator = 4; + *outTimeSig_Denominator = 4; + *outCurrentMeasureDownBeat = 0; + } + + return noErr; +} + +OSStatus AudioUnitPluginInstance::getTransportState (Boolean* outIsPlaying, + Boolean* outTransportStateChanged, + Float64* outCurrentSampleInTimeLine, + Boolean* outIsCycling, + Float64* outCycleStartBeat, + Float64* outCycleEndBeat) +{ + AudioPlayHead* const ph = getPlayHead(); + AudioPlayHead::CurrentPositionInfo result; + + if (ph != 0 && ph->getCurrentPosition (result)) + { + *outIsPlaying = result.isPlaying; + *outTransportStateChanged = result.isPlaying != wasPlaying; + wasPlaying = result.isPlaying; + *outCurrentSampleInTimeLine = roundDoubleToInt (result.timeInSeconds * getSampleRate()); + *outIsCycling = false; + *outCycleStartBeat = 0; + *outCycleEndBeat = 0; + } + else + { + *outIsPlaying = false; + *outTransportStateChanged = false; + *outCurrentSampleInTimeLine = 0; + *outIsCycling = false; + *outCycleStartBeat = 0; + *outCycleEndBeat = 0; + } + + return noErr; +} + + +//============================================================================== +static VoidArray activeWindows; + +//============================================================================== +class AudioUnitPluginWindow : public AudioProcessorEditor, + public Timer +{ +public: + //============================================================================== + AudioUnitPluginWindow (AudioUnitPluginInstance& plugin_) + : AudioProcessorEditor (&plugin_), + plugin (plugin_), + isOpen (false), + pluginWantsKeys (false), + wasShowing (false), + recursiveResize (false), + viewComponent (0), + pluginViewRef (0) + { + movementWatcher = new CompMovementWatcher (this); + + activeWindows.add (this); + + setOpaque (true); + setVisible (true); + setSize (1, 1); + + ComponentDescription viewList [16]; + UInt32 viewListSize = sizeof (viewList); + AudioUnitGetProperty (plugin.audioUnit, kAudioUnitProperty_GetUIComponentList, kAudioUnitScope_Global, + 0, &viewList, &viewListSize); + + componentRecord = FindNextComponent (0, &viewList[0]); + } + + ~AudioUnitPluginWindow() + { + deleteAndZero (movementWatcher); + + closePluginWindow(); + + activeWindows.removeValue (this); + plugin.editorBeingDeleted (this); + } + + bool isValid() const throw() { return componentRecord != 0; } + + //============================================================================== + void componentMovedOrResized() + { + if (recursiveResize) + return; + + Component* const topComp = getTopLevelComponent(); + + if (topComp->getPeer() != 0) + { + int x = 0, y = 0; + relativePositionToOtherComponent (topComp, x, y); + + recursiveResize = true; + + if (pluginViewRef != 0) + { + HIRect r; + r.origin.x = (float) x; + r.origin.y = (float) y; + r.size.width = (float) getWidth(); + r.size.height = (float) getHeight(); + HIViewSetFrame (pluginViewRef, &r); + } + + recursiveResize = false; + } + } + + void componentVisibilityChanged() + { + const bool isShowingNow = isShowing(); + + if (wasShowing != isShowingNow) + { + wasShowing = isShowingNow; + + if (isShowingNow) + openPluginWindow(); + else + closePluginWindow(); + } + + componentMovedOrResized(); + } + + void componentPeerChanged() + { + closePluginWindow(); + openPluginWindow(); + } + + void timerCallback() + { + if (pluginViewRef != 0) + { + HIRect bounds; + HIViewGetBounds (pluginViewRef, &bounds); + const int w = jmax (32, (int) bounds.size.width); + const int h = jmax (32, (int) bounds.size.height); + + if (w != getWidth() || h != getHeight()) + { + setSize (w, h); + startTimer (50); + } + else + { + startTimer (jlimit (50, 500, getTimerInterval() + 20)); + } + } + } + + //============================================================================== + bool keyStateChanged() + { + return pluginWantsKeys; + } + + bool keyPressed (const KeyPress&) + { + return pluginWantsKeys; + } + + //============================================================================== + void paint (Graphics& g) + { + if (isOpen) + { + ComponentPeer* const peer = getPeer(); + + if (peer != 0) + { + peer->addMaskedRegion (getScreenX() - peer->getScreenX(), + getScreenY() - peer->getScreenY(), + getWidth(), getHeight()); + } + } + else + { + g.fillAll (Colours::black); + } + } + + //============================================================================== + void broughtToFront() + { + activeWindows.removeValue (this); + activeWindows.add (this); + } + + //============================================================================== + juce_UseDebuggingNewOperator + +private: + AudioUnitPluginInstance& plugin; + bool isOpen, wasShowing, recursiveResize; + bool pluginWantsKeys; + + ComponentRecord* componentRecord; + AudioUnitCarbonView viewComponent; + HIViewRef pluginViewRef; + + //============================================================================== + void openPluginWindow() + { + if (isOpen || getWindowHandle() == 0 || componentRecord == 0) + return; + + log (T("Opening AU GUI: ") + plugin.getName()); + isOpen = true; + + pluginWantsKeys = true; //xxx any way to find this out? Does it matter? + + viewComponent = (AudioUnitCarbonView) OpenComponent (componentRecord); + + if (viewComponent != 0) + { + Float32Point pos = { getScreenX() - getTopLevelComponent()->getScreenX(), + getScreenY() - getTopLevelComponent()->getScreenY() }; + Float32Point size = { 250, 200 }; + + AudioUnitCarbonViewCreate (viewComponent, + plugin.audioUnit, + (WindowRef) getWindowHandle(), + HIViewGetRoot ((WindowRef) getWindowHandle()), + &pos, &size, + (ControlRef*) &pluginViewRef); + } + + timerCallback(); // to set our comp to the right size + repaint(); + } + + //============================================================================== + void closePluginWindow() + { + stopTimer(); + + if (isOpen) + { + log (T("Closing AU GUI: ") + plugin.getName()); + isOpen = false; + + if (viewComponent != 0) + CloseComponent (viewComponent); + + pluginViewRef = 0; + } + } + + //============================================================================== + class CompMovementWatcher : public ComponentMovementWatcher + { + public: + CompMovementWatcher (AudioUnitPluginWindow* const owner_) + : ComponentMovementWatcher (owner_), + owner (owner_) + { + } + + //============================================================================== + void componentMovedOrResized (bool /*wasMoved*/, bool /*wasResized*/) + { + owner->componentMovedOrResized(); + } + + void componentPeerChanged() + { + owner->componentPeerChanged(); + } + + void componentVisibilityChanged (Component&) + { + owner->componentVisibilityChanged(); + } + + private: + AudioUnitPluginWindow* const owner; + }; + + CompMovementWatcher* movementWatcher; +}; + +//============================================================================== +AudioProcessorEditor* AudioUnitPluginInstance::createEditor() +{ + AudioUnitPluginWindow* w = new AudioUnitPluginWindow (*this); + + if (! w->isValid()) + deleteAndZero (w); + + return w; +} + + +//============================================================================== +const String AudioUnitPluginInstance::getCategory() const +{ + const char* result = 0; + + switch (componentDesc.componentType) + { + case kAudioUnitType_Effect: + case kAudioUnitType_MusicEffect: + result = "Effect"; + break; + case kAudioUnitType_MusicDevice: + result = "Synth"; + break; + case kAudioUnitType_Generator: + result = "Generator"; + break; + case kAudioUnitType_Panner: + result = "Panner"; + break; + default: + break; + } + + return result; +} + +//============================================================================== +int AudioUnitPluginInstance::getNumParameters() +{ + return parameterIds.size(); +} + +float AudioUnitPluginInstance::getParameter (int index) +{ + const ScopedLock sl (lock); + + Float32 value = 0.0f; + + if (audioUnit != 0 && ((unsigned int) index) < (unsigned int) parameterIds.size()) + { + AudioUnitGetParameter (audioUnit, + (UInt32) parameterIds.getUnchecked (index), + kAudioUnitScope_Global, 0, + &value); + } + + return value; +} + +void AudioUnitPluginInstance::setParameter (int index, float newValue) +{ + const ScopedLock sl (lock); + + if (audioUnit != 0 && ((unsigned int) index) < (unsigned int) parameterIds.size()) + { + AudioUnitSetParameter (audioUnit, + (UInt32) parameterIds.getUnchecked (index), + kAudioUnitScope_Global, 0, + newValue, 0); + } +} + +const String AudioUnitPluginInstance::getParameterName (int index) +{ + AudioUnitParameterInfo info; + zerostruct (info); + UInt32 sz = sizeof (info); + + String name; + + if (AudioUnitGetProperty (audioUnit, + kAudioUnitProperty_ParameterInfo, + kAudioUnitScope_Global, + parameterIds [index], &info, &sz) == noErr) + { + if ((info.flags & kAudioUnitParameterFlag_HasCFNameString) != 0) + name = PlatformUtilities::cfStringToJuceString (info.cfNameString); + else + name = String (info.name, sizeof (info.name)); + } + + return name; +} + +const String AudioUnitPluginInstance::getParameterText (int index) +{ + return String (getParameter (index)); +} + +bool AudioUnitPluginInstance::isParameterAutomatable (int index) const +{ + AudioUnitParameterInfo info; + UInt32 sz = sizeof (info); + + if (AudioUnitGetProperty (audioUnit, + kAudioUnitProperty_ParameterInfo, + kAudioUnitScope_Global, + parameterIds [index], &info, &sz) == noErr) + { + return (info.flags & kAudioUnitParameterFlag_NonRealTime) == 0; + } + + return true; +} + +//============================================================================== +int AudioUnitPluginInstance::getNumPrograms() +{ + CFArrayRef presets; + UInt32 sz = sizeof (CFArrayRef); + int num = 0; + + if (AudioUnitGetProperty (audioUnit, + kAudioUnitProperty_FactoryPresets, + kAudioUnitScope_Global, + 0, &presets, &sz) == noErr) + { + num = (int) CFArrayGetCount (presets); + CFRelease (presets); + } + + return num; +} + +int AudioUnitPluginInstance::getCurrentProgram() +{ + AUPreset current; + current.presetNumber = 0; + UInt32 sz = sizeof (AUPreset); + + AudioUnitGetProperty (audioUnit, + kAudioUnitProperty_FactoryPresets, + kAudioUnitScope_Global, + 0, ¤t, &sz); + + return current.presetNumber; +} + +void AudioUnitPluginInstance::setCurrentProgram (int newIndex) +{ + AUPreset current; + current.presetNumber = newIndex; + current.presetName = 0; + + AudioUnitSetProperty (audioUnit, + kAudioUnitProperty_FactoryPresets, + kAudioUnitScope_Global, + 0, ¤t, sizeof (AUPreset)); +} + +const String AudioUnitPluginInstance::getProgramName (int index) +{ + String s; + CFArrayRef presets; + UInt32 sz = sizeof (CFArrayRef); + + if (AudioUnitGetProperty (audioUnit, + kAudioUnitProperty_FactoryPresets, + kAudioUnitScope_Global, + 0, &presets, &sz) == noErr) + { + for (CFIndex i = 0; i < CFArrayGetCount (presets); ++i) + { + const AUPreset* p = (const AUPreset*) CFArrayGetValueAtIndex (presets, i); + + if (p != 0 && p->presetNumber == index) + { + s = PlatformUtilities::cfStringToJuceString (p->presetName); + break; + } + } + + CFRelease (presets); + } + + return s; +} + +void AudioUnitPluginInstance::changeProgramName (int index, const String& newName) +{ + jassertfalse // xxx not implemented! +} + +//============================================================================== +const String AudioUnitPluginInstance::getInputChannelName (const int index) const +{ + if (((unsigned int) index) < (unsigned int) getNumInputChannels()) + return T("Input ") + String (index + 1); + + return String::empty; +} + +bool AudioUnitPluginInstance::isInputChannelStereoPair (int index) const +{ + if (((unsigned int) index) >= (unsigned int) getNumInputChannels()) + return false; + + + return true; +} + +const String AudioUnitPluginInstance::getOutputChannelName (const int index) const +{ + if (((unsigned int) index) < (unsigned int) getNumOutputChannels()) + return T("Output ") + String (index + 1); + + return String::empty; +} + +bool AudioUnitPluginInstance::isOutputChannelStereoPair (int index) const +{ + if (((unsigned int) index) >= (unsigned int) getNumOutputChannels()) + return false; + + return true; +} + +//============================================================================== +void AudioUnitPluginInstance::getStateInformation (MemoryBlock& destData) +{ + getCurrentProgramStateInformation (destData); +} + +void AudioUnitPluginInstance::getCurrentProgramStateInformation (MemoryBlock& destData) +{ + CFPropertyListRef propertyList = 0; + UInt32 sz = sizeof (CFPropertyListRef); + + if (AudioUnitGetProperty (audioUnit, + kAudioUnitProperty_ClassInfo, + kAudioUnitScope_Global, + 0, &propertyList, &sz) == noErr) + { + CFWriteStreamRef stream = CFWriteStreamCreateWithAllocatedBuffers (kCFAllocatorDefault, kCFAllocatorDefault); + CFWriteStreamOpen (stream); + + CFIndex bytesWritten = CFPropertyListWriteToStream (propertyList, stream, kCFPropertyListBinaryFormat_v1_0, 0); + CFWriteStreamClose (stream); + + CFDataRef data = (CFDataRef) CFWriteStreamCopyProperty (stream, kCFStreamPropertyDataWritten); + + destData.setSize (bytesWritten); + destData.copyFrom (CFDataGetBytePtr (data), 0, destData.getSize()); + CFRelease (data); + + CFRelease (stream); + CFRelease (propertyList); + } +} + +void AudioUnitPluginInstance::setStateInformation (const void* data, int sizeInBytes) +{ + setCurrentProgramStateInformation (data, sizeInBytes); +} + +void AudioUnitPluginInstance::setCurrentProgramStateInformation (const void* data, int sizeInBytes) +{ + CFReadStreamRef stream = CFReadStreamCreateWithBytesNoCopy (kCFAllocatorDefault, + (const UInt8*) data, + sizeInBytes, + kCFAllocatorNull); + CFReadStreamOpen (stream); + + CFPropertyListFormat format = kCFPropertyListBinaryFormat_v1_0; + CFPropertyListRef propertyList = CFPropertyListCreateFromStream (kCFAllocatorDefault, + stream, + 0, + kCFPropertyListImmutable, + &format, + 0); + CFRelease (stream); + + if (propertyList != 0) + AudioUnitSetProperty (audioUnit, + kAudioUnitProperty_ClassInfo, + kAudioUnitScope_Global, + 0, &propertyList, sizeof (propertyList)); +} + +//============================================================================== +//============================================================================== +AudioUnitPluginFormat::AudioUnitPluginFormat() +{ +} + +AudioUnitPluginFormat::~AudioUnitPluginFormat() +{ +} + +void AudioUnitPluginFormat::findAllTypesForFile (OwnedArray & results, + const File& file) +{ + if (! fileMightContainThisPluginType (file)) + return; + + PluginDescription desc; + desc.file = file; + desc.uid = 0; + + AudioUnitPluginInstance* instance = dynamic_cast (createInstanceFromDescription (desc)); + + if (instance == 0) + return; + + try + { + desc.fillInFromInstance (*instance); + + results.add (new PluginDescription (desc)); + } + catch (...) + { + // crashed while loading... + } + + deleteAndZero (instance); +} + +AudioPluginInstance* AudioUnitPluginFormat::createInstanceFromDescription (const PluginDescription& desc) +{ + AudioUnitPluginInstance* result = 0; + + if (fileMightContainThisPluginType (desc.file)) + { + result = new AudioUnitPluginInstance (desc.file); + + if (result->audioUnit != 0) + { + result->initialise(); + } + else + { + deleteAndZero (result); + } + } + + return result; +} + +bool AudioUnitPluginFormat::fileMightContainThisPluginType (const File& f) +{ + return f.hasFileExtension (T(".component")) + && f.isDirectory(); +} + +const FileSearchPath AudioUnitPluginFormat::getDefaultLocationsToSearch() +{ + return FileSearchPath ("~/Library/Audio/Plug-Ins/Components;/Library/Audio/Plug-Ins/Components"); +} + +#endif + +END_JUCE_NAMESPACE + +#endif diff --git a/src/juce_appframework/audio/plugins/formats/juce_AudioUnitPluginFormat.h b/src/juce_appframework/audio/plugins/formats/juce_AudioUnitPluginFormat.h new file mode 100644 index 0000000000..175dbe22fc --- /dev/null +++ b/src/juce_appframework/audio/plugins/formats/juce_AudioUnitPluginFormat.h @@ -0,0 +1,67 @@ +/* + ============================================================================== + + This file is part of the JUCE library - "Jules' Utility Class Extensions" + Copyright 2004-7 by Raw Material Software ltd. + + ------------------------------------------------------------------------------ + + JUCE can be redistributed and/or modified under the terms of the + GNU General Public License, as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + JUCE is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with JUCE; if not, visit www.gnu.org/licenses or write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------------ + + If you'd like to release a closed-source product which uses JUCE, commercial + licenses are also available: visit www.rawmaterialsoftware.com/juce for + more information. + + ============================================================================== +*/ + +#ifndef __JUCE_AUDIOUNITPLUGINFORMAT_JUCEHEADER__ +#define __JUCE_AUDIOUNITPLUGINFORMAT_JUCEHEADER__ + +#include "../juce_AudioPluginFormat.h" + +#if JUCE_PLUGINHOST_AU && JUCE_MAC + +//============================================================================== +/** + Implements a plugin format manager for AudioUnits. +*/ +class JUCE_API AudioUnitPluginFormat : public AudioPluginFormat +{ +public: + //============================================================================== + AudioUnitPluginFormat(); + ~AudioUnitPluginFormat(); + + //============================================================================== + const String getName() const { return "AudioUnit"; } + void findAllTypesForFile (OwnedArray & results, const File& file); + AudioPluginInstance* createInstanceFromDescription (const PluginDescription& desc); + bool fileMightContainThisPluginType (const File& file); + const FileSearchPath getDefaultLocationsToSearch(); + + //============================================================================== + juce_UseDebuggingNewOperator + +private: + AudioUnitPluginFormat (const AudioUnitPluginFormat&); + const AudioUnitPluginFormat& operator= (const AudioUnitPluginFormat&); +}; + +#endif + +#endif // __JUCE_AUDIOUNITPLUGINFORMAT_JUCEHEADER__ diff --git a/src/juce_appframework/audio/plugins/formats/juce_DirectXPluginFormat.h b/src/juce_appframework/audio/plugins/formats/juce_DirectXPluginFormat.h new file mode 100644 index 0000000000..9210cbbb4e --- /dev/null +++ b/src/juce_appframework/audio/plugins/formats/juce_DirectXPluginFormat.h @@ -0,0 +1,71 @@ +/* + ============================================================================== + + This file is part of the JUCE library - "Jules' Utility Class Extensions" + Copyright 2004-7 by Raw Material Software ltd. + + ------------------------------------------------------------------------------ + + JUCE can be redistributed and/or modified under the terms of the + GNU General Public License, as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + JUCE is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with JUCE; if not, visit www.gnu.org/licenses or write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------------ + + If you'd like to release a closed-source product which uses JUCE, commercial + licenses are also available: visit www.rawmaterialsoftware.com/juce for + more information. + + ============================================================================== +*/ + +#ifndef __JUCE_DIRECTXPLUGINFORMAT_JUCEHEADER__ +#define __JUCE_DIRECTXPLUGINFORMAT_JUCEHEADER__ + +#include "../juce_AudioPluginFormat.h" + +#if JUCE_PLUGINHOST_DX && JUCE_WIN32 + + +// Sorry, this file is just a placeholder at the moment!... + + +//============================================================================== +/** + Implements a plugin format manager for DirectX plugins. +*/ +class JUCE_API DirectXPluginFormat : public AudioPluginFormat +{ +public: + //============================================================================== + DirectXPluginFormat(); + ~DirectXPluginFormat(); + + //============================================================================== + const String getName() const { return "DirectX"; } + void findAllTypesForFile (OwnedArray & results, const File& file); + AudioPluginInstance* createInstanceFromDescription (const PluginDescription& desc); + bool fileMightContainThisPluginType (const File& file); + const FileSearchPath getDefaultLocationsToSearch(); + + //============================================================================== + juce_UseDebuggingNewOperator + +private: + DirectXPluginFormat (const DirectXPluginFormat&); + const DirectXPluginFormat& operator= (const DirectXPluginFormat&); +}; + +#endif + +#endif // __JUCE_DIRECTXPLUGINFORMAT_JUCEHEADER__ diff --git a/src/juce_appframework/audio/plugins/formats/juce_LADSPAPluginFormat.h b/src/juce_appframework/audio/plugins/formats/juce_LADSPAPluginFormat.h new file mode 100644 index 0000000000..def44f2758 --- /dev/null +++ b/src/juce_appframework/audio/plugins/formats/juce_LADSPAPluginFormat.h @@ -0,0 +1,71 @@ +/* + ============================================================================== + + This file is part of the JUCE library - "Jules' Utility Class Extensions" + Copyright 2004-7 by Raw Material Software ltd. + + ------------------------------------------------------------------------------ + + JUCE can be redistributed and/or modified under the terms of the + GNU General Public License, as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + JUCE is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with JUCE; if not, visit www.gnu.org/licenses or write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------------ + + If you'd like to release a closed-source product which uses JUCE, commercial + licenses are also available: visit www.rawmaterialsoftware.com/juce for + more information. + + ============================================================================== +*/ + +#ifndef __JUCE_LADSPAPLUGINFORMAT_JUCEHEADER__ +#define __JUCE_LADSPAPLUGINFORMAT_JUCEHEADER__ + +#include "../juce_AudioPluginFormat.h" + +#if JUCE_PLUGINHOST_LADSPA && JUCE_LINUX + + +// Sorry, this file is just a placeholder at the moment!... + + +//============================================================================== +/** + Implements a plugin format manager for DirectX plugins. +*/ +class JUCE_API LADSPAPluginFormat : public AudioPluginFormat +{ +public: + //============================================================================== + LADSPAPluginFormat(); + ~LADSPAPluginFormat(); + + //============================================================================== + const String getName() const { return "LADSPA"; } + void findAllTypesForFile (OwnedArray & results, const File& file); + AudioPluginInstance* createInstanceFromDescription (const PluginDescription& desc); + bool fileMightContainThisPluginType (const File& file); + const FileSearchPath getDefaultLocationsToSearch(); + + //============================================================================== + juce_UseDebuggingNewOperator + +private: + LADSPAPluginFormat (const LADSPAPluginFormat&); + const LADSPAPluginFormat& operator= (const LADSPAPluginFormat&); +}; + +#endif + +#endif // __JUCE_LADSPAPLUGINFORMAT_JUCEHEADER__ diff --git a/src/juce_appframework/audio/plugins/formats/juce_VSTPluginFormat.cpp b/src/juce_appframework/audio/plugins/formats/juce_VSTPluginFormat.cpp new file mode 100644 index 0000000000..7ee776e02c --- /dev/null +++ b/src/juce_appframework/audio/plugins/formats/juce_VSTPluginFormat.cpp @@ -0,0 +1,3017 @@ +/* + ============================================================================== + + This file is part of the JUCE library - "Jules' Utility Class Extensions" + Copyright 2004-7 by Raw Material Software ltd. + + ------------------------------------------------------------------------------ + + JUCE can be redistributed and/or modified under the terms of the + GNU General Public License, as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + JUCE is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with JUCE; if not, visit www.gnu.org/licenses or write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------------ + + If you'd like to release a closed-source product which uses JUCE, commercial + licenses are also available: visit www.rawmaterialsoftware.com/juce for + more information. + + ============================================================================== +*/ + +#ifndef JUCE_PLUGINHOST_VST // xxx temporary.. + #define JUCE_PLUGINHOST_VST 1 +#endif + +#if JUCE_PLUGINHOST_VST + +#ifdef _WIN32 + #define _WIN32_WINNT 0x500 + #define STRICT + #include + #include + #pragma warning (disable : 4312) +#elif defined (LINUX) + #include + #include + #include + #include + #include + #undef Font + #undef KeyPress + #undef Drawable + #undef Time +#else + #include +#endif + +//============================================================================== +#include "../../../../juce_core/basics/juce_StandardHeader.h" + +BEGIN_JUCE_NAMESPACE + +#include "juce_VSTPluginFormat.h" +#include "../../../../juce_core/threads/juce_Process.h" +#include "../../../../juce_core/threads/juce_ScopedLock.h" +#include "../../../../juce_core/basics/juce_Random.h" +#include "../../../events/juce_Timer.h" +#include "../../../events/juce_AsyncUpdater.h" +#include "../../../events/juce_MessageManager.h" +#include "../../../gui/components/layout/juce_ComponentMovementWatcher.h" +#include "../../../application/juce_Application.h" + + +//============================================================================== +#undef PRAGMA_ALIGN_SUPPORTED +#define VST_FORCE_DEPRECATED 0 + +#ifdef _MSC_VER + #pragma warning (push) + #pragma warning (disable: 4996) +#endif + +/* Obviously you're going to need the Steinberg vstsdk2.4 folder in + your include path if you want to add VST support... +*/ +#include "pluginterfaces/vst2.x/aeffectx.h" + +#ifdef _MSC_VER + #pragma warning (pop) +#endif + +//============================================================================== +#if JUCE_LINUX + #define Font JUCE_NAMESPACE::Font + #define KeyPress JUCE_NAMESPACE::KeyPress + #define Drawable JUCE_NAMESPACE::Drawable + #define Time JUCE_NAMESPACE::Time +#endif + +#include "../juce_PluginDescription.h" + +#if ! JUCE_WIN32 + #define _fpreset() + #define _clearfp() +#endif + +extern void juce_callAnyTimersSynchronously(); + + +//============================================================================== +const int fxbVersionNum = 1; + +struct fxProgram +{ + long chunkMagic; // 'CcnK' + long byteSize; // of this chunk, excl. magic + byteSize + long fxMagic; // 'FxCk' + long version; + long fxID; // fx unique id + long fxVersion; + long numParams; + char prgName[28]; + float params[1]; // variable no. of parameters +}; + +struct fxSet +{ + long chunkMagic; // 'CcnK' + long byteSize; // of this chunk, excl. magic + byteSize + long fxMagic; // 'FxBk' + long version; + long fxID; // fx unique id + long fxVersion; + long numPrograms; + char future[128]; + fxProgram programs[1]; // variable no. of programs +}; + +struct fxChunkSet +{ + long chunkMagic; // 'CcnK' + long byteSize; // of this chunk, excl. magic + byteSize + long fxMagic; // 'FxCh', 'FPCh', or 'FBCh' + long version; + long fxID; // fx unique id + long fxVersion; + long numPrograms; + char future[128]; + long chunkSize; + char chunk[8]; // variable +}; + +struct fxProgramSet +{ + long chunkMagic; // 'CcnK' + long byteSize; // of this chunk, excl. magic + byteSize + long fxMagic; // 'FxCh', 'FPCh', or 'FBCh' + long version; + long fxID; // fx unique id + long fxVersion; + long numPrograms; + char name[28]; + long chunkSize; + char chunk[8]; // variable +}; + + +#ifdef JUCE_LITTLE_ENDIAN + static long swap (const long x) throw() { return (long) swapByteOrder ((uint32) x); } + + static float swapFloat (const float x) throw() + { + union { uint32 asInt; float asFloat; } n; + n.asFloat = x; + n.asInt = swapByteOrder (n.asInt); + return n.asFloat; + } +#else + #define swap(x) (x) + #define swapFloat(x) (x) +#endif + +//============================================================================== +typedef AEffect* (*MainCall) (audioMasterCallback); + +static VstIntPtr VSTCALLBACK audioMaster (AEffect* effect, VstInt32 opcode, VstInt32 index, VstIntPtr value, void* ptr, float opt); + +#if JUCE_PPC + static void* audioMasterCoerced = 0; +#endif + +static int shellUIDToCreate = 0; +static int insideVSTCallback = 0; + +class VSTPluginWindow; + +//============================================================================== +// Change this to disable logging of various VST activities +#ifndef VST_LOGGING + #define VST_LOGGING 1 +#endif + +#if VST_LOGGING + #define log(a) Logger::writeToLog(a); +#else + #define log(a) +#endif + +//============================================================================== +#if JUCE_MAC +extern bool juce_isHIViewCreatedByJuce (HIViewRef view); +extern bool juce_isWindowCreatedByJuce (WindowRef window); + +#if JUCE_PPC +static void* NewCFMFromMachO (void* const machofp) throw() +{ + void* result = juce_malloc (8); + + ((void**) result)[0] = machofp; + ((void**) result)[1] = result; + + return result; +} +#endif +#endif + +//============================================================================== +#if JUCE_LINUX + +extern Display* display; +extern XContext improbableNumber; + +typedef void (*EventProcPtr) (XEvent* ev); + +static bool xErrorTriggered; + +static int temporaryErrorHandler (Display*, XErrorEvent*) +{ + xErrorTriggered = true; + return 0; +} + +static int getPropertyFromXWindow (Window handle, Atom atom) +{ + XErrorHandler oldErrorHandler = XSetErrorHandler (temporaryErrorHandler); + xErrorTriggered = false; + + int userSize; + unsigned long bytes, userCount; + unsigned char* data; + Atom userType; + + XGetWindowProperty (display, handle, atom, 0, 1, false, AnyPropertyType, + &userType, &userSize, &userCount, &bytes, &data); + + XSetErrorHandler (oldErrorHandler); + + return (userCount == 1 && ! xErrorTriggered) ? *(int*) data + : 0; +} + +static Window getChildWindow (Window windowToCheck) +{ + Window rootWindow, parentWindow; + Window* childWindows; + unsigned int numChildren; + + XQueryTree (display, + windowToCheck, + &rootWindow, + &parentWindow, + &childWindows, + &numChildren); + + if (numChildren > 0) + return childWindows [0]; + + return 0; +} + +static void translateJuceToXButtonModifiers (const MouseEvent& e, XEvent& ev) throw() +{ + if (e.mods.isLeftButtonDown()) + { + ev.xbutton.button = Button1; + ev.xbutton.state |= Button1Mask; + } + else if (e.mods.isRightButtonDown()) + { + ev.xbutton.button = Button3; + ev.xbutton.state |= Button3Mask; + } + else if (e.mods.isMiddleButtonDown()) + { + ev.xbutton.button = Button2; + ev.xbutton.state |= Button2Mask; + } +} + +static void translateJuceToXMotionModifiers (const MouseEvent& e, XEvent& ev) throw() +{ + if (e.mods.isLeftButtonDown()) + ev.xmotion.state |= Button1Mask; + else if (e.mods.isRightButtonDown()) + ev.xmotion.state |= Button3Mask; + else if (e.mods.isMiddleButtonDown()) + ev.xmotion.state |= Button2Mask; +} + +static void translateJuceToXCrossingModifiers (const MouseEvent& e, XEvent& ev) throw() +{ + if (e.mods.isLeftButtonDown()) + ev.xcrossing.state |= Button1Mask; + else if (e.mods.isRightButtonDown()) + ev.xcrossing.state |= Button3Mask; + else if (e.mods.isMiddleButtonDown()) + ev.xcrossing.state |= Button2Mask; +} + +static void translateJuceToXMouseWheelModifiers (const MouseEvent& e, const float increment, XEvent& ev) throw() +{ + if (increment < 0) + { + ev.xbutton.button = Button5; + ev.xbutton.state |= Button5Mask; + } + else if (increment > 0) + { + ev.xbutton.button = Button4; + ev.xbutton.state |= Button4Mask; + } +} + +#endif + +//============================================================================== +static VoidArray activeModules; + +//============================================================================== +class ModuleHandle : public ReferenceCountedObject +{ +public: + //============================================================================== + File file; + MainCall moduleMain; + String pluginName; + + //============================================================================== + static ModuleHandle* findOrCreateModule (const File& file) + { + for (int i = activeModules.size(); --i >= 0;) + { + ModuleHandle* const module = (ModuleHandle*) activeModules.getUnchecked(i); + + if (module->file == file) + return module; + } + + _fpreset(); // (doesn't do any harm) + ++insideVSTCallback; + shellUIDToCreate = 0; + + log ("Attempting to load VST: " + file.getFullPathName()); + + ModuleHandle* m = new ModuleHandle (file); + + if (! m->open()) + deleteAndZero (m); + + --insideVSTCallback; + _fpreset(); // (doesn't do any harm) + + return m; + } + + //============================================================================== + ModuleHandle (const File& file_) + : file (file_), + moduleMain (0), +#if JUCE_WIN32 || JUCE_LINUX + hModule (0) +#elif JUCE_MAC + fragId (0), + resHandle (0), + bundleRef (0), + resFileId (0) +#endif + { + activeModules.add (this); + +#if JUCE_WIN32 || JUCE_LINUX + fullParentDirectoryPathName = file_.getParentDirectory().getFullPathName(); +#elif JUCE_MAC + PlatformUtilities::makeFSSpecFromPath (&parentDirFSSpec, file_.getParentDirectory().getFullPathName()); +#endif + } + + ~ModuleHandle() + { + activeModules.removeValue (this); + + close(); + } + + //============================================================================== + juce_UseDebuggingNewOperator + + //============================================================================== +#if JUCE_WIN32 || JUCE_LINUX + void* hModule; + String fullParentDirectoryPathName; + + bool open() + { +#if JUCE_WIN32 + static bool timePeriodSet = false; + + if (! timePeriodSet) + { + timePeriodSet = true; + timeBeginPeriod (2); + } +#endif + + pluginName = file.getFileNameWithoutExtension(); + + hModule = Process::loadDynamicLibrary (file.getFullPathName()); + + moduleMain = (MainCall) Process::getProcedureEntryPoint (hModule, "VSTPluginMain"); + + if (moduleMain == 0) + moduleMain = (MainCall) Process::getProcedureEntryPoint (hModule, "main"); + + return moduleMain != 0; + } + + void close() + { + _fpreset(); // (doesn't do any harm) + + Process::freeDynamicLibrary (hModule); + } + + void closeEffect (AEffect* eff) + { + eff->dispatcher (eff, effClose, 0, 0, 0, 0); + } + +#else + CFragConnectionID fragId; + Handle resHandle; + CFBundleRef bundleRef; + FSSpec parentDirFSSpec; + short resFileId; + + bool open() + { + bool ok = false; + const String filename (file.getFullPathName()); + + if (file.hasFileExtension (T(".vst"))) + { + const char* const utf8 = filename.toUTF8(); + CFURLRef url = CFURLCreateFromFileSystemRepresentation (0, (const UInt8*) utf8, + strlen (utf8), file.isDirectory()); + + if (url != 0) + { + bundleRef = CFBundleCreate (kCFAllocatorDefault, url); + CFRelease (url); + + if (bundleRef != 0) + { + if (CFBundleLoadExecutable (bundleRef)) + { + moduleMain = (MainCall) CFBundleGetFunctionPointerForName (bundleRef, CFSTR("main_macho")); + + if (moduleMain == 0) + moduleMain = (MainCall) CFBundleGetFunctionPointerForName (bundleRef, CFSTR("VSTPluginMain")); + + if (moduleMain != 0) + { + CFTypeRef name = CFBundleGetValueForInfoDictionaryKey (bundleRef, CFSTR("CFBundleName")); + + if (name != 0) + { + if (CFGetTypeID (name) == CFStringGetTypeID()) + { + char buffer[1024]; + + if (CFStringGetCString ((CFStringRef) name, buffer, sizeof (buffer), CFStringGetSystemEncoding())) + pluginName = buffer; + } + } + + if (pluginName.isEmpty()) + pluginName = file.getFileNameWithoutExtension(); + + resFileId = CFBundleOpenBundleResourceMap (bundleRef); + + ok = true; + } + } + + if (! ok) + { + CFBundleUnloadExecutable (bundleRef); + CFRelease (bundleRef); + bundleRef = 0; + } + } + } + } +#if JUCE_PPC + else + { + FSRef fn; + + if (FSPathMakeRef ((UInt8*) (const char*) filename, &fn, 0) == noErr) + { + resFileId = FSOpenResFile (&fn, fsRdPerm); + + if (resFileId != -1) + { + const int numEffs = Count1Resources ('aEff'); + + for (int i = 0; i < numEffs; ++i) + { + resHandle = Get1IndResource ('aEff', i + 1); + + if (resHandle != 0) + { + OSType type; + Str255 name; + SInt16 id; + GetResInfo (resHandle, &id, &type, name); + pluginName = String ((const char*) name + 1, name[0]); + DetachResource (resHandle); + HLock (resHandle); + + Ptr ptr; + Str255 errorText; + + OSErr err = GetMemFragment (*resHandle, GetHandleSize (resHandle), + name, kPrivateCFragCopy, + &fragId, &ptr, errorText); + + if (err == noErr) + { + moduleMain = (MainCall) newMachOFromCFM (ptr); + ok = true; + } + else + { + HUnlock (resHandle); + } + + break; + } + } + + if (! ok) + CloseResFile (resFileId); + } + } + } +#endif + + return ok; + } + + void close() + { +#if JUCE_PPC + if (fragId != 0) + { + if (moduleMain != 0) + disposeMachOFromCFM ((void*) moduleMain); + + CloseConnection (&fragId); + HUnlock (resHandle); + + if (resFileId != 0) + CloseResFile (resFileId); + } + else +#endif + if (bundleRef != 0) + { + CFBundleCloseBundleResourceMap (bundleRef, resFileId); + + if (CFGetRetainCount (bundleRef) == 1) + CFBundleUnloadExecutable (bundleRef); + + if (CFGetRetainCount (bundleRef) > 0) + CFRelease (bundleRef); + } + } + + void closeEffect (AEffect* eff) + { +#if JUCE_PPC + if (fragId != 0) + { + VoidArray thingsToDelete; + thingsToDelete.add ((void*) eff->dispatcher); + thingsToDelete.add ((void*) eff->process); + thingsToDelete.add ((void*) eff->setParameter); + thingsToDelete.add ((void*) eff->getParameter); + thingsToDelete.add ((void*) eff->processReplacing); + + eff->dispatcher (eff, effClose, 0, 0, 0, 0); + + for (int i = thingsToDelete.size(); --i >= 0;) + disposeMachOFromCFM (thingsToDelete[i]); + } + else +#endif + { + eff->dispatcher (eff, effClose, 0, 0, 0, 0); + } + } + +#if JUCE_PPC + static void* newMachOFromCFM (void* cfmfp) + { + if (cfmfp == 0) + return 0; + + UInt32* const mfp = (UInt32*) juce_malloc (sizeof (UInt32) * 6); + + mfp[0] = 0x3d800000 | ((UInt32) cfmfp >> 16); + mfp[1] = 0x618c0000 | ((UInt32) cfmfp & 0xffff); + mfp[2] = 0x800c0000; + mfp[3] = 0x804c0004; + mfp[4] = 0x7c0903a6; + mfp[5] = 0x4e800420; + + MakeDataExecutable (mfp, sizeof (UInt32) * 6); + return mfp; + } + + static void disposeMachOFromCFM (void* ptr) + { + juce_free (ptr); + } + + void coerceAEffectFunctionCalls (AEffect* eff) + { + if (fragId != 0) + { + eff->dispatcher = (AEffectDispatcherProc) newMachOFromCFM ((void*) eff->dispatcher); + eff->process = (AEffectProcessProc) newMachOFromCFM ((void*) eff->process); + eff->setParameter = (AEffectSetParameterProc) newMachOFromCFM ((void*) eff->setParameter); + eff->getParameter = (AEffectGetParameterProc) newMachOFromCFM ((void*) eff->getParameter); + eff->processReplacing = (AEffectProcessProc) newMachOFromCFM ((void*) eff->processReplacing); + } + } +#endif + +#endif +}; + + +//============================================================================== +/** + An instance of a plugin, created by a VSTPluginFormat. + +*/ +class VSTPluginInstance : public AudioPluginInstance, + private Timer, + private AsyncUpdater +{ +public: + //============================================================================== + ~VSTPluginInstance(); + + //============================================================================== + // AudioPluginInstance methods: + + void fillInPluginDescription (PluginDescription& desc) const + { + desc.name = name; + desc.file = module->file; + desc.uid = getUID(); + desc.lastFileModTime = desc.file.getLastModificationTime(); + desc.pluginFormatName = "VST"; + desc.category = getCategory(); + + { + char buffer [kVstMaxVendorStrLen + 8]; + zerostruct (buffer); + dispatch (effGetVendorString, 0, 0, buffer, 0); + desc.manufacturerName = buffer; + } + + desc.version = getVersion(); + desc.numInputChannels = getNumInputChannels(); + desc.numOutputChannels = getNumOutputChannels(); + desc.isInstrument = (effect != 0 && (effect->flags & effFlagsIsSynth) != 0); + } + + const String getName() const { return name; } + int getUID() const throw(); + bool acceptsMidi() const { return wantsMidiMessages; } + bool producesMidi() const { return dispatch (effCanDo, 0, 0, (void*) "sendVstMidiEvent", 0) > 0; } + + //============================================================================== + // AudioProcessor methods: + + void prepareToPlay (double sampleRate, int estimatedSamplesPerBlock); + void releaseResources(); + void processBlock (AudioSampleBuffer& buffer, + MidiBuffer& midiMessages); + + AudioProcessorEditor* createEditor(); + + const String getInputChannelName (const int index) const; + bool isInputChannelStereoPair (int index) const; + + const String getOutputChannelName (const int index) const; + bool isOutputChannelStereoPair (int index) const; + + //============================================================================== + int getNumParameters() { return effect != 0 ? effect->numParams : 0; } + float getParameter (int index); + void setParameter (int index, float newValue); + const String getParameterName (int index); + const String getParameterText (int index); + bool isParameterAutomatable (int index) const; + + //============================================================================== + int getNumPrograms() { return effect != 0 ? effect->numPrograms : 0; } + int getCurrentProgram() { return dispatch (effGetProgram, 0, 0, 0, 0); } + void setCurrentProgram (int index); + const String getProgramName (int index); + void changeProgramName (int index, const String& newName); + + //============================================================================== + void getStateInformation (MemoryBlock& destData); + void getCurrentProgramStateInformation (MemoryBlock& destData); + void setStateInformation (const void* data, int sizeInBytes); + void setCurrentProgramStateInformation (const void* data, int sizeInBytes); + + //============================================================================== + void timerCallback(); + void handleAsyncUpdate(); + VstIntPtr handleCallback (VstInt32 opcode, VstInt32 index, VstInt32 value, void *ptr, float opt); + + //============================================================================== + juce_UseDebuggingNewOperator + +private: + friend class VSTPluginWindow; + friend class VSTPluginFormat; + + AEffect* effect; + String name; + CriticalSection lock; + bool wantsMidiMessages, initialised, isPowerOn; + mutable StringArray programNames; + AudioSampleBuffer tempBuffer; + CriticalSection midiInLock; + MidiBuffer incomingMidi; + void* midiEventsToSend; + int numAllocatedMidiEvents; + VstTimeInfo vstHostTime; + float** channels; + + ReferenceCountedObjectPtr module; + + //============================================================================== + int dispatch (const int opcode, const int index, const int value, void* const ptr, float opt) const; + bool restoreProgramSettings (const fxProgram* const prog); + const String getCurrentProgramName(); + void setParamsInProgramBlock (fxProgram* const prog) throw(); + void updateStoredProgramNames(); + void initialise(); + void ensureMidiEventSize (int numEventsNeeded); + void freeMidiEvents(); + void handleMidiFromPlugin (const VstEvents* const events); + void createTempParameterStore (MemoryBlock& dest); + void restoreFromTempParameterStore (const MemoryBlock& mb); + const String getParameterLabel (int index) const; + + bool usesChunks() const throw() { return effect != 0 && (effect->flags & effFlagsProgramChunks) != 0; } + void getChunkData (MemoryBlock& mb, bool isPreset, int maxSizeMB) const; + void setChunkData (const char* data, int size, bool isPreset); + bool loadFromFXBFile (const void* data, int numBytes); + bool saveToFXBFile (MemoryBlock& dest, bool isFXB, int maxSizeMB); + + int getVersionNumber() const throw() { return effect != 0 ? effect->version : 0; } + const String getVersion() const throw(); + const String getCategory() const throw(); + + bool hasEditor() const throw() { return effect != 0 && (effect->flags & effFlagsHasEditor) != 0; } + void setPower (const bool on); + + VSTPluginInstance (const ReferenceCountedObjectPtr & module); +}; + +//============================================================================== +VSTPluginInstance::VSTPluginInstance (const ReferenceCountedObjectPtr & module_) + : effect (0), + wantsMidiMessages (false), + initialised (false), + isPowerOn (false), + numAllocatedMidiEvents (0), + midiEventsToSend (0), + tempBuffer (1, 1), + channels (0), + module (module_) +{ + try + { + _fpreset(); + + ++insideVSTCallback; + + name = module->pluginName; + log (T("Creating VST instance: ") + name); + +#if JUCE_MAC + if (module->resFileId != 0) + UseResFile (module->resFileId); + +#if JUCE_PPC + if (module->fragId != 0) + { + static void* audioMasterCoerced = 0; + if (audioMasterCoerced == 0) + audioMasterCoerced = NewCFMFromMachO ((void*) &audioMaster); + + effect = module->moduleMain ((audioMasterCallback) audioMasterCoerced); + } + else +#endif +#endif + { + effect = module->moduleMain (&audioMaster); + } + + --insideVSTCallback; + + if (effect != 0 && effect->magic == kEffectMagic) + { +#if JUCE_PPC + module->coerceAEffectFunctionCalls (effect); +#endif + + jassert (effect->resvd2 == 0); + jassert (effect->object != 0); + + _fpreset(); // some dodgy plugs fuck around with this + } + else + { + effect = 0; + } + } + catch (...) + { + --insideVSTCallback; + } +} + +VSTPluginInstance::~VSTPluginInstance() +{ + { + const ScopedLock sl (lock); + + jassert (insideVSTCallback == 0); + + if (effect != 0 && effect->magic == kEffectMagic) + { + try + { +#if JUCE_MAC + if (module->resFileId != 0) + UseResFile (module->resFileId); +#endif + + // Must delete any editors before deleting the plugin instance! + jassert (getActiveEditor() == 0); + + _fpreset(); // some dodgy plugs fuck around with this + + module->closeEffect (effect); + } + catch (...) + {} + } + + module = 0; + effect = 0; + } + + freeMidiEvents(); + + juce_free (channels); + channels = 0; +} + +//============================================================================== +void VSTPluginInstance::initialise() +{ + if (initialised || effect == 0) + return; + + log (T("Initialising VST: ") + module->pluginName); + initialised = true; + + dispatch (effIdentify, 0, 0, 0, 0); + + // this code would ask the plugin for its name, but so few plugins + // actually bother implementing this correctly, that it's better to + // just ignore it and use the file name instead. +/* { + char buffer [256]; + zerostruct (buffer); + dispatch (effGetEffectName, 0, 0, buffer, 0); + + name = String (buffer).trim(); + if (name.isEmpty()) + name = module->pluginName; + } +*/ + + if (getSampleRate() > 0) + dispatch (effSetSampleRate, 0, 0, 0, (float) getSampleRate()); + + if (getBlockSize() > 0) + dispatch (effSetBlockSize, 0, jmax (32, getBlockSize()), 0, 0); + + dispatch (effOpen, 0, 0, 0, 0); + + setPlayConfigDetails (effect->numInputs, effect->numOutputs, + getSampleRate(), getBlockSize()); + + if (getNumPrograms() > 1) + setCurrentProgram (0); + else + dispatch (effSetProgram, 0, 0, 0, 0); + + int i; + for (i = effect->numInputs; --i >= 0;) + dispatch (effConnectInput, i, 1, 0, 0); + + for (i = effect->numOutputs; --i >= 0;) + dispatch (effConnectOutput, i, 1, 0, 0); + + updateStoredProgramNames(); + + wantsMidiMessages = dispatch (effCanDo, 0, 0, (void*) "receiveVstMidiEvent", 0) > 0; + + setLatencySamples (effect->initialDelay); +} + + +//============================================================================== +void VSTPluginInstance::prepareToPlay (double sampleRate_, + int samplesPerBlockExpected) +{ + setPlayConfigDetails (effect->numInputs, effect->numOutputs, + sampleRate_, samplesPerBlockExpected); + + setLatencySamples (effect->initialDelay); + + juce_free (channels); + channels = (float**) juce_calloc (sizeof (float*) * jmax (16, getNumOutputChannels() + 2, getNumInputChannels() + 2)); + + vstHostTime.tempo = 120.0; + vstHostTime.timeSigNumerator = 4; + vstHostTime.timeSigDenominator = 4; + vstHostTime.sampleRate = sampleRate_; + vstHostTime.samplePos = 0; + vstHostTime.flags = kVstNanosValid; /*| kVstTransportPlaying | kVstTempoValid | kVstTimeSigValid*/; + + initialise(); + + if (initialised) + { + wantsMidiMessages = wantsMidiMessages + || (dispatch (effCanDo, 0, 0, (void*) "receiveVstMidiEvent", 0) > 0); + + if (wantsMidiMessages) + ensureMidiEventSize (256); + else + freeMidiEvents(); + + incomingMidi.clear(); + + dispatch (effSetSampleRate, 0, 0, 0, (float) sampleRate_); + dispatch (effSetBlockSize, 0, jmax (16, samplesPerBlockExpected), 0, 0); + + tempBuffer.setSize (jmax (1, effect->numOutputs), samplesPerBlockExpected); + + if (! isPowerOn) + setPower (true); + + // dodgy hack to force some plugins to initialise the sample rate.. + if ((! hasEditor()) && getNumParameters() > 0) + { + const float old = getParameter (0); + setParameter (0, (old < 0.5f) ? 1.0f : 0.0f); + setParameter (0, old); + } + + dispatch (effStartProcess, 0, 0, 0, 0); + } +} + +void VSTPluginInstance::releaseResources() +{ + if (initialised) + { + dispatch (effStopProcess, 0, 0, 0, 0); + setPower (false); + } + + tempBuffer.setSize (1, 1); + incomingMidi.clear(); + + freeMidiEvents(); + juce_free (channels); + channels = 0; +} + +void VSTPluginInstance::processBlock (AudioSampleBuffer& buffer, + MidiBuffer& midiMessages) +{ + const int numSamples = buffer.getNumSamples(); + + if (initialised) + { +#if JUCE_WIN32 + vstHostTime.nanoSeconds = timeGetTime() * 1000000.0; +#elif JUCE_LINUX + timeval micro; + gettimeofday (µ, 0); + vstHostTime.nanoSeconds = micro.tv_usec * 1000.0; +#elif JUCE_MAC + UnsignedWide micro; + Microseconds (µ); + vstHostTime.nanoSeconds = micro.lo * 1000.0; +#endif + + if (wantsMidiMessages) + { + MidiBuffer::Iterator iter (midiMessages); + + int eventIndex = 0; + const uint8* midiData; + int numBytesOfMidiData, samplePosition; + + while (iter.getNextEvent (midiData, numBytesOfMidiData, samplePosition)) + { + if (numBytesOfMidiData < 4) + { + ensureMidiEventSize (eventIndex); + VstMidiEvent* const e + = (VstMidiEvent*) ((VstEvents*) midiEventsToSend)->events [eventIndex++]; + + // check that some plugin hasn't messed up our objects + jassert (e->type == kVstMidiType); + jassert (e->byteSize == 24); + + e->deltaFrames = jlimit (0, numSamples - 1, samplePosition); + e->noteLength = 0; + e->noteOffset = 0; + e->midiData[0] = midiData[0]; + e->midiData[1] = midiData[1]; + e->midiData[2] = midiData[2]; + e->detune = 0; + e->noteOffVelocity = 0; + } + } + + if (midiEventsToSend == 0) + ensureMidiEventSize (1); + + ((VstEvents*) midiEventsToSend)->numEvents = eventIndex; + + try + { + effect->dispatcher (effect, effProcessEvents, 0, 0, midiEventsToSend, 0); + } + catch (...) + {} + } + + int i; + const int maxChans = jmax (effect->numInputs, effect->numOutputs); + + for (i = 0; i < maxChans; ++i) + channels[i] = buffer.getSampleData (i); + + channels [maxChans] = 0; + + _clearfp(); + + if ((effect->flags & effFlagsCanReplacing) != 0) + { + try + { + effect->processReplacing (effect, channels, channels, numSamples); + } + catch (...) + {} + } + else + { + tempBuffer.setSize (effect->numOutputs, numSamples); + tempBuffer.clear(); + + float* outs [64]; + + for (i = effect->numOutputs; --i >= 0;) + outs[i] = tempBuffer.getSampleData (i); + + outs [effect->numOutputs] = 0; + + try + { + effect->process (effect, channels, outs, numSamples); + } + catch (...) + {} + + for (i = effect->numOutputs; --i >= 0;) + buffer.copyFrom (i, 0, outs[i], numSamples); + } + } + else + { + // Not initialised, so just bypass.. + for (int i = getNumInputChannels(); i < getNumOutputChannels(); ++i) + buffer.clear (i, 0, buffer.getNumSamples()); + } + + { + // copy any incoming midi.. + const ScopedLock sl (midiInLock); + + midiMessages = incomingMidi; + incomingMidi.clear(); + } +} + +//============================================================================== +void VSTPluginInstance::ensureMidiEventSize (int numEventsNeeded) +{ + if (numEventsNeeded > numAllocatedMidiEvents) + { + numEventsNeeded = (numEventsNeeded + 32) & ~31; + + const int size = 20 + sizeof (VstEvent*) * numEventsNeeded; + + if (midiEventsToSend == 0) + midiEventsToSend = juce_calloc (size); + else + midiEventsToSend = juce_realloc (midiEventsToSend, size); + + for (int i = numAllocatedMidiEvents; i < numEventsNeeded; ++i) + { + VstMidiEvent* const e = (VstMidiEvent*) juce_calloc (sizeof (VstMidiEvent)); + e->type = kVstMidiType; + e->byteSize = 24; + + ((VstEvents*) midiEventsToSend)->events[i] = (VstEvent*) e; + } + + numAllocatedMidiEvents = numEventsNeeded; + } +} + +void VSTPluginInstance::freeMidiEvents() +{ + if (midiEventsToSend != 0) + { + for (int i = numAllocatedMidiEvents; --i >= 0;) + juce_free (((VstEvents*) midiEventsToSend)->events[i]); + + juce_free (midiEventsToSend); + midiEventsToSend = 0; + numAllocatedMidiEvents = 0; + } +} + +void VSTPluginInstance::handleMidiFromPlugin (const VstEvents* const events) +{ + if (events != 0) + { + const ScopedLock sl (midiInLock); + + for (int i = 0; i < events->numEvents; ++i) + { + const VstEvent* const e = events->events[i]; + + if (e->type == kVstMidiType) + { + incomingMidi.addEvent ((const uint8*) ((const VstMidiEvent*) e)->midiData, + 3, e->deltaFrames); + } + } + } +} + +//============================================================================== +static Array activeWindows; + +//============================================================================== +class VSTPluginWindow : public AudioProcessorEditor, + public Timer +{ +public: + //============================================================================== + VSTPluginWindow (VSTPluginInstance& plugin_) + : AudioProcessorEditor (&plugin_), + plugin (plugin_), + isOpen (false), + wasShowing (false), + pluginRefusesToResize (false), + pluginWantsKeys (false), + alreadyInside (false), + recursiveResize (false) + { +#if JUCE_WIN32 + sizeCheckCount = 0; + pluginHWND = 0; +#elif JUCE_LINUX + pluginWindow = None; + pluginProc = None; +#else + pluginViewRef = 0; +#endif + + movementWatcher = new CompMovementWatcher (this); + + activeWindows.add (this); + + setSize (1, 1); + setOpaque (true); + setVisible (true); + } + + ~VSTPluginWindow() + { + deleteAndZero (movementWatcher); + + closePluginWindow(); + + activeWindows.removeValue (this); + plugin.editorBeingDeleted (this); + } + + //============================================================================== + void componentMovedOrResized() + { + if (recursiveResize) + return; + + Component* const topComp = getTopLevelComponent(); + + if (topComp->getPeer() != 0) + { + int x = 0, y = 0; + relativePositionToOtherComponent (topComp, x, y); + + recursiveResize = true; + +#if JUCE_MAC + if (pluginViewRef != 0) + { + HIRect r; + r.origin.x = (float) x; + r.origin.y = (float) y; + r.size.width = (float) getWidth(); + r.size.height = (float) getHeight(); + HIViewSetFrame (pluginViewRef, &r); + } + else if (pluginWindowRef != 0) + { + Rect r; + r.left = getScreenX(); + r.top = getScreenY(); + r.right = r.left + getWidth(); + r.bottom = r.top + getHeight(); + + WindowGroupRef group = GetWindowGroup (pluginWindowRef); + WindowGroupAttributes atts; + GetWindowGroupAttributes (group, &atts); + ChangeWindowGroupAttributes (group, 0, kWindowGroupAttrMoveTogether); + + SetWindowBounds (pluginWindowRef, kWindowContentRgn, &r); + + if ((atts & kWindowGroupAttrMoveTogether) != 0) + ChangeWindowGroupAttributes (group, kWindowGroupAttrMoveTogether, 0); + } + else + { + repaint(); + } +#elif JUCE_WIN32 + if (pluginHWND != 0) + MoveWindow (pluginHWND, x, y, getWidth(), getHeight(), TRUE); +#elif JUCE_LINUX + if (pluginWindow != 0) + { + XResizeWindow (display, pluginWindow, getWidth(), getHeight()); + XMoveWindow (display, pluginWindow, x, y); + XMapRaised (display, pluginWindow); + } +#endif + + recursiveResize = false; + } + } + + void componentVisibilityChanged() + { + const bool isShowingNow = isShowing(); + + if (wasShowing != isShowingNow) + { + wasShowing = isShowingNow; + + if (isShowingNow) + openPluginWindow(); + else + closePluginWindow(); + } + + componentMovedOrResized(); + } + + void componentPeerChanged() + { + closePluginWindow(); + openPluginWindow(); + } + + //============================================================================== + bool keyStateChanged() + { + return pluginWantsKeys; + } + + bool keyPressed (const KeyPress&) + { + return pluginWantsKeys; + } + + //============================================================================== + void paint (Graphics& g) + { + if (isOpen) + { + ComponentPeer* const peer = getPeer(); + + if (peer != 0) + { + peer->addMaskedRegion (getScreenX() - peer->getScreenX(), + getScreenY() - peer->getScreenY(), + getWidth(), getHeight()); + +#if JUCE_MAC + if (pluginViewRef == 0) + { + ERect r; + r.left = getScreenX() - peer->getScreenX(); + r.right = r.left + getWidth(); + r.top = getScreenY() - peer->getScreenY(); + r.bottom = r.top + getHeight(); + + dispatch (effEditDraw, 0, 0, &r, 0); + } +#elif JUCE_LINUX + if (pluginWindow != 0) + { + const Rectangle clip (g.getClipBounds()); + + XEvent ev; + zerostruct (ev); + ev.xexpose.type = Expose; + ev.xexpose.display = display; + ev.xexpose.window = pluginWindow; + ev.xexpose.x = clip.getX(); + ev.xexpose.y = clip.getY(); + ev.xexpose.width = clip.getWidth(); + ev.xexpose.height = clip.getHeight(); + + sendEventToChild (&ev); + } +#endif + } + } + else + { + g.fillAll (Colours::black); + } + } + + //============================================================================== + void timerCallback() + { +#if JUCE_WIN32 + if (--sizeCheckCount <= 0) + { + sizeCheckCount = 10; + + checkPluginWindowSize(); + } +#endif + + try + { + static bool reentrant = false; + + if (! reentrant) + { + reentrant = true; + plugin.dispatch (effEditIdle, 0, 0, 0, 0); + reentrant = false; + } + } + catch (...) + {} + } + + //============================================================================== + void mouseDown (const MouseEvent& e) + { +#if JUCE_MAC + if (! alreadyInside) + { + alreadyInside = true; + toFront (true); + dispatch (effEditMouse, e.x, e.y, 0, 0); + alreadyInside = false; + } + else + { + PostEvent (::mouseDown, 0); + } +#elif JUCE_LINUX + + if (pluginWindow == 0) + return; + + toFront (true); + + XEvent ev; + zerostruct (ev); + ev.xbutton.display = display; + ev.xbutton.type = ButtonPress; + ev.xbutton.window = pluginWindow; + ev.xbutton.root = RootWindow (display, DefaultScreen (display)); + ev.xbutton.time = CurrentTime; + ev.xbutton.x = e.x; + ev.xbutton.y = e.y; + ev.xbutton.x_root = e.getScreenX(); + ev.xbutton.y_root = e.getScreenY(); + + translateJuceToXButtonModifiers (e, ev); + + sendEventToChild (&ev); + +#else + (void) e; + + toFront (true); +#endif + } + + void broughtToFront() + { + activeWindows.removeValue (this); + activeWindows.add (this); + +#if JUCE_MAC + dispatch (effEditTop, 0, 0, 0, 0); +#endif + } + + //============================================================================== + juce_UseDebuggingNewOperator + +private: + VSTPluginInstance& plugin; + bool isOpen, wasShowing, recursiveResize; + bool pluginWantsKeys, pluginRefusesToResize, alreadyInside; + +#if JUCE_WIN32 + HWND pluginHWND; + void* originalWndProc; + int sizeCheckCount; +#elif JUCE_MAC + HIViewRef pluginViewRef; + WindowRef pluginWindowRef; +#elif JUCE_LINUX + Window pluginWindow; + EventProcPtr pluginProc; +#endif + + //============================================================================== + void openPluginWindow() + { + if (isOpen || getWindowHandle() == 0) + return; + + log (T("Opening VST UI: ") + plugin.name); + isOpen = true; + + ERect* rect = 0; + dispatch (effEditGetRect, 0, 0, &rect, 0); + dispatch (effEditOpen, 0, 0, getWindowHandle(), 0); + + // do this before and after like in the steinberg example + dispatch (effEditGetRect, 0, 0, &rect, 0); + dispatch (effGetProgram, 0, 0, 0, 0); // also in steinberg code + + // Install keyboard hooks + pluginWantsKeys = (dispatch (effKeysRequired, 0, 0, 0, 0) == 0); + +#if JUCE_WIN32 + originalWndProc = 0; + pluginHWND = GetWindow ((HWND) getWindowHandle(), GW_CHILD); + + if (pluginHWND == 0) + { + isOpen = false; + setSize (300, 150); + return; + } + + #pragma warning (push) + #pragma warning (disable: 4244) + + originalWndProc = (void*) GetWindowLongPtr (pluginHWND, GWL_WNDPROC); + + if (! pluginWantsKeys) + SetWindowLongPtr (pluginHWND, GWL_WNDPROC, (LONG_PTR) vstHookWndProc); + + #pragma warning (pop) + + int w, h; + RECT r; + GetWindowRect (pluginHWND, &r); + w = r.right - r.left; + h = r.bottom - r.top; + + if (rect != 0) + { + const int rw = rect->right - rect->left; + const int rh = rect->bottom - rect->top; + + if ((rw > 50 && rh > 50 && rw < 2000 && rh < 2000 && rw != w && rh != h) + || ((w == 0 && rw > 0) || (h == 0 && rh > 0))) + { + // very dodgy logic to decide which size is right. + if (abs (rw - w) > 350 || abs (rh - h) > 350) + { + SetWindowPos (pluginHWND, 0, + 0, 0, rw, rh, + SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER); + + GetWindowRect (pluginHWND, &r); + + w = r.right - r.left; + h = r.bottom - r.top; + + pluginRefusesToResize = (w != rw) || (h != rh); + + w = rw; + h = rh; + } + } + } +#elif JUCE_MAC + HIViewRef root = HIViewGetRoot ((WindowRef) getWindowHandle()); + HIViewFindByID (root, kHIViewWindowContentID, &root); + pluginViewRef = HIViewGetFirstSubview (root); + + while (pluginViewRef != 0 && juce_isHIViewCreatedByJuce (pluginViewRef)) + pluginViewRef = HIViewGetNextView (pluginViewRef); + + pluginWindowRef = 0; + + if (pluginViewRef == 0) + { + WindowGroupRef ourGroup = GetWindowGroup ((WindowRef) getWindowHandle()); + //DebugPrintWindowGroup (ourGroup); + //DebugPrintAllWindowGroups(); + + GetIndexedWindow (ourGroup, 1, + kWindowGroupContentsVisible, + &pluginWindowRef); + + if (pluginWindowRef == (WindowRef) getWindowHandle() + || juce_isWindowCreatedByJuce (pluginWindowRef)) + pluginWindowRef = 0; + } + + int w = 250, h = 150; + + if (rect != 0) + { + w = rect->right - rect->left; + h = rect->bottom - rect->top; + + if (w == 0 || h == 0) + { + w = 250; + h = 150; + } + } + +#elif JUCE_LINUX + pluginWindow = getChildWindow ((Window) getWindowHandle()); + + if (pluginWindow != 0) + pluginProc = (EventProcPtr) getPropertyFromXWindow (pluginWindow, + XInternAtom (display, "_XEventProc", False)); + + int w = 250, h = 150; + + if (rect != 0) + { + w = rect->right - rect->left; + h = rect->bottom - rect->top; + + if (w == 0 || h == 0) + { + w = 250; + h = 150; + } + } + + if (pluginWindow != 0) + XMapRaised (display, pluginWindow); +#endif + + // double-check it's not too tiny + w = jmax (w, 32); + h = jmax (h, 32); + + setSize (w, h); + +#if JUCE_WIN32 + checkPluginWindowSize(); +#endif + + startTimer (18 + JUCE_NAMESPACE::Random::getSystemRandom().nextInt (5)); + repaint(); + } + + //============================================================================== + void closePluginWindow() + { + if (isOpen) + { + log (T("Closing VST UI: ") + plugin.getName()); + isOpen = false; + + dispatch (effEditClose, 0, 0, 0, 0); + +#if JUCE_WIN32 + #pragma warning (push) + #pragma warning (disable: 4244) + + if (pluginHWND != 0 && IsWindow (pluginHWND)) + SetWindowLongPtr (pluginHWND, GWL_WNDPROC, (LONG_PTR) originalWndProc); + + #pragma warning (pop) + + stopTimer(); + + if (pluginHWND != 0 && IsWindow (pluginHWND)) + DestroyWindow (pluginHWND); + + pluginHWND = 0; +#elif JUCE_MAC + dispatch (effEditSleep, 0, 0, 0, 0); + pluginViewRef = 0; + stopTimer(); +#elif JUCE_LINUX + stopTimer(); + pluginWindow = 0; + pluginProc = 0; +#endif + } + } + + //============================================================================== +#if JUCE_WIN32 + void checkPluginWindowSize() throw() + { + RECT r; + GetWindowRect (pluginHWND, &r); + const int w = r.right - r.left; + const int h = r.bottom - r.top; + + if (isShowing() && w > 0 && h > 0 + && (w != getWidth() || h != getHeight()) + && ! pluginRefusesToResize) + { + setSize (w, h); + sizeCheckCount = 0; + } + } +#endif + + //============================================================================== + class CompMovementWatcher : public ComponentMovementWatcher + { + public: + CompMovementWatcher (VSTPluginWindow* const owner_) + : ComponentMovementWatcher (owner_), + owner (owner_) + { + } + + //============================================================================== + void componentMovedOrResized (bool /*wasMoved*/, bool /*wasResized*/) + { + owner->componentMovedOrResized(); + } + + void componentPeerChanged() + { + owner->componentPeerChanged(); + } + + void componentVisibilityChanged (Component&) + { + owner->componentVisibilityChanged(); + } + + private: + VSTPluginWindow* const owner; + }; + + CompMovementWatcher* movementWatcher; + + //============================================================================== + int dispatch (const int opcode, const int index, const int value, void* const ptr, float opt) + { + return plugin.dispatch (opcode, index, value, ptr, opt); + } + + //============================================================================== + // hooks to get keyboard events from VST windows.. +#if JUCE_WIN32 + static LRESULT CALLBACK vstHookWndProc (HWND hW, UINT message, WPARAM wParam, LPARAM lParam) + { + for (int i = activeWindows.size(); --i >= 0;) + { + const VSTPluginWindow* const w = (const VSTPluginWindow*) activeWindows.getUnchecked (i); + + if (w->pluginHWND == hW) + { + if (message == WM_CHAR + || message == WM_KEYDOWN + || message == WM_SYSKEYDOWN + || message == WM_KEYUP + || message == WM_SYSKEYUP + || message == WM_APPCOMMAND) + { + SendMessage ((HWND) w->getTopLevelComponent()->getWindowHandle(), + message, wParam, lParam); + } + + return CallWindowProc ((WNDPROC) (w->originalWndProc), + (HWND) w->pluginHWND, + message, + wParam, + lParam); + } + } + + return DefWindowProc (hW, message, wParam, lParam); + } +#endif + +#if JUCE_LINUX + //============================================================================== + // overload mouse/keyboard events to forward them to the plugin's inner window.. + void sendEventToChild (XEvent* event) + { + if (pluginProc != 0) + { + // if the plugin publishes an event procedure, pass the event directly.. + pluginProc (event); + } + else if (pluginWindow != 0) + { + // if the plugin has a window, then send the event to the window so that + // its message thread will pick it up.. + XSendEvent (display, pluginWindow, False, 0L, event); + XFlush (display); + } + } + + void mouseEnter (const MouseEvent& e) + { + if (pluginWindow != 0) + { + XEvent ev; + zerostruct (ev); + ev.xcrossing.display = display; + ev.xcrossing.type = EnterNotify; + ev.xcrossing.window = pluginWindow; + ev.xcrossing.root = RootWindow (display, DefaultScreen (display)); + ev.xcrossing.time = CurrentTime; + ev.xcrossing.x = e.x; + ev.xcrossing.y = e.y; + ev.xcrossing.x_root = e.getScreenX(); + ev.xcrossing.y_root = e.getScreenY(); + ev.xcrossing.mode = NotifyNormal; // NotifyGrab, NotifyUngrab + ev.xcrossing.detail = NotifyAncestor; // NotifyVirtual, NotifyInferior, NotifyNonlinear,NotifyNonlinearVirtual + + translateJuceToXCrossingModifiers (e, ev); + + sendEventToChild (&ev); + } + } + + void mouseExit (const MouseEvent& e) + { + if (pluginWindow != 0) + { + XEvent ev; + zerostruct (ev); + ev.xcrossing.display = display; + ev.xcrossing.type = LeaveNotify; + ev.xcrossing.window = pluginWindow; + ev.xcrossing.root = RootWindow (display, DefaultScreen (display)); + ev.xcrossing.time = CurrentTime; + ev.xcrossing.x = e.x; + ev.xcrossing.y = e.y; + ev.xcrossing.x_root = e.getScreenX(); + ev.xcrossing.y_root = e.getScreenY(); + ev.xcrossing.mode = NotifyNormal; // NotifyGrab, NotifyUngrab + ev.xcrossing.detail = NotifyAncestor; // NotifyVirtual, NotifyInferior, NotifyNonlinear,NotifyNonlinearVirtual + ev.xcrossing.focus = hasKeyboardFocus (true); // TODO - yes ? + + translateJuceToXCrossingModifiers (e, ev); + + sendEventToChild (&ev); + } + } + + void mouseMove (const MouseEvent& e) + { + if (pluginWindow != 0) + { + XEvent ev; + zerostruct (ev); + ev.xmotion.display = display; + ev.xmotion.type = MotionNotify; + ev.xmotion.window = pluginWindow; + ev.xmotion.root = RootWindow (display, DefaultScreen (display)); + ev.xmotion.time = CurrentTime; + ev.xmotion.is_hint = NotifyNormal; + ev.xmotion.x = e.x; + ev.xmotion.y = e.y; + ev.xmotion.x_root = e.getScreenX(); + ev.xmotion.y_root = e.getScreenY(); + + sendEventToChild (&ev); + } + } + + void mouseDrag (const MouseEvent& e) + { + if (pluginWindow != 0) + { + XEvent ev; + zerostruct (ev); + ev.xmotion.display = display; + ev.xmotion.type = MotionNotify; + ev.xmotion.window = pluginWindow; + ev.xmotion.root = RootWindow (display, DefaultScreen (display)); + ev.xmotion.time = CurrentTime; + ev.xmotion.x = e.x ; + ev.xmotion.y = e.y; + ev.xmotion.x_root = e.getScreenX(); + ev.xmotion.y_root = e.getScreenY(); + ev.xmotion.is_hint = NotifyNormal; + + translateJuceToXMotionModifiers (e, ev); + sendEventToChild (&ev); + } + } + + void mouseUp (const MouseEvent& e) + { + if (pluginWindow != 0) + { + XEvent ev; + zerostruct (ev); + ev.xbutton.display = display; + ev.xbutton.type = ButtonRelease; + ev.xbutton.window = pluginWindow; + ev.xbutton.root = RootWindow (display, DefaultScreen (display)); + ev.xbutton.time = CurrentTime; + ev.xbutton.x = e.x; + ev.xbutton.y = e.y; + ev.xbutton.x_root = e.getScreenX(); + ev.xbutton.y_root = e.getScreenY(); + + translateJuceToXButtonModifiers (e, ev); + sendEventToChild (&ev); + } + } + + void mouseWheelMove (const MouseEvent& e, + float incrementX, + float incrementY) + { + if (pluginWindow != 0) + { + XEvent ev; + zerostruct (ev); + ev.xbutton.display = display; + ev.xbutton.type = ButtonPress; + ev.xbutton.window = pluginWindow; + ev.xbutton.root = RootWindow (display, DefaultScreen (display)); + ev.xbutton.time = CurrentTime; + ev.xbutton.x = e.x; + ev.xbutton.y = e.y; + ev.xbutton.x_root = e.getScreenX(); + ev.xbutton.y_root = e.getScreenY(); + + translateJuceToXMouseWheelModifiers (e, incrementY, ev); + sendEventToChild (&ev); + + // TODO - put a usleep here ? + + ev.xbutton.type = ButtonRelease; + sendEventToChild (&ev); + } + } +#endif + +}; + +//============================================================================== +AudioProcessorEditor* VSTPluginInstance::createEditor() +{ + if (hasEditor()) + return new VSTPluginWindow (*this); + + return 0; +} + + +//============================================================================== +void VSTPluginInstance::handleAsyncUpdate() +{ + // indicates that something about the plugin has changed.. + updateHostDisplay(); +} + +//============================================================================== +bool VSTPluginInstance::restoreProgramSettings (const fxProgram* const prog) +{ + if (swap (prog->chunkMagic) == 'CcnK' && swap (prog->fxMagic) == 'FxCk') + { + changeProgramName (getCurrentProgram(), prog->prgName); + + for (int i = 0; i < swap (prog->numParams); ++i) + setParameter (i, swapFloat (prog->params[i])); + + return true; + } + + return false; +} + +bool VSTPluginInstance::loadFromFXBFile (const void* const data, + const int dataSize) +{ + if (dataSize < 28) + return false; + + const fxSet* const set = (const fxSet*) data; + + if ((swap (set->chunkMagic) != 'CcnK' && swap (set->chunkMagic) != 'KncC') + || swap (set->version) > fxbVersionNum) + return false; + + if (swap (set->fxMagic) == 'FxBk') + { + // bank of programs + if (swap (set->numPrograms) >= 0) + { + const int oldProg = getCurrentProgram(); + const int numParams = swap (((const fxProgram*) (set->programs))->numParams); + const int progLen = sizeof (fxProgram) + (numParams - 1) * sizeof (float); + + for (int i = 0; i < swap (set->numPrograms); ++i) + { + if (i != oldProg) + { + const fxProgram* const prog = (const fxProgram*) (((const char*) (set->programs)) + i * progLen); + if (((const char*) prog) - ((const char*) set) >= dataSize) + return false; + + if (swap (set->numPrograms) > 0) + setCurrentProgram (i); + + if (! restoreProgramSettings (prog)) + return false; + } + } + + if (swap (set->numPrograms) > 0) + setCurrentProgram (oldProg); + + const fxProgram* const prog = (const fxProgram*) (((const char*) (set->programs)) + oldProg * progLen); + if (((const char*) prog) - ((const char*) set) >= dataSize) + return false; + + if (! restoreProgramSettings (prog)) + return false; + } + } + else if (swap (set->fxMagic) == 'FxCk') + { + // single program + const fxProgram* const prog = (const fxProgram*) data; + + if (swap (prog->chunkMagic) != 'CcnK') + return false; + + changeProgramName (getCurrentProgram(), prog->prgName); + + for (int i = 0; i < swap (prog->numParams); ++i) + setParameter (i, swapFloat (prog->params[i])); + } + else if (swap (set->fxMagic) == 'FBCh' || swap (set->fxMagic) == 'hCBF') + { + // non-preset chunk + const fxChunkSet* const cset = (const fxChunkSet*) data; + + if (swap (cset->chunkSize) + sizeof (fxChunkSet) - 8 > (unsigned int) dataSize) + return false; + + setChunkData (cset->chunk, swap (cset->chunkSize), false); + } + else if (swap (set->fxMagic) == 'FPCh' || swap (set->fxMagic) == 'hCPF') + { + // preset chunk + const fxProgramSet* const cset = (const fxProgramSet*) data; + + if (swap (cset->chunkSize) + sizeof (fxProgramSet) - 8 > (unsigned int) dataSize) + return false; + + setChunkData (cset->chunk, swap (cset->chunkSize), true); + + changeProgramName (getCurrentProgram(), cset->name); + } + else + { + return false; + } + + return true; +} + +//============================================================================== +void VSTPluginInstance::setParamsInProgramBlock (fxProgram* const prog) throw() +{ + const int numParams = getNumParameters(); + + prog->chunkMagic = swap ('CcnK'); + prog->byteSize = 0; + prog->fxMagic = swap ('FxCk'); + prog->version = swap (fxbVersionNum); + prog->fxID = swap (getUID()); + prog->fxVersion = swap (getVersionNumber()); + prog->numParams = swap (numParams); + + getCurrentProgramName().copyToBuffer (prog->prgName, sizeof (prog->prgName) - 1); + + for (int i = 0; i < numParams; ++i) + prog->params[i] = swapFloat (getParameter (i)); +} + +bool VSTPluginInstance::saveToFXBFile (MemoryBlock& dest, bool isFXB, int maxSizeMB) +{ + const int numPrograms = getNumPrograms(); + const int numParams = getNumParameters(); + + if (usesChunks()) + { + if (isFXB) + { + MemoryBlock chunk; + getChunkData (chunk, false, maxSizeMB); + + const int totalLen = sizeof (fxChunkSet) + chunk.getSize() - 8; + dest.setSize (totalLen, true); + + fxChunkSet* const set = (fxChunkSet*) dest.getData(); + set->chunkMagic = swap ('CcnK'); + set->byteSize = 0; + set->fxMagic = swap ('FBCh'); + set->version = swap (fxbVersionNum); + set->fxID = swap (getUID()); + set->fxVersion = swap (getVersionNumber()); + set->numPrograms = swap (numPrograms); + set->chunkSize = swap (chunk.getSize()); + + chunk.copyTo (set->chunk, 0, chunk.getSize()); + } + else + { + MemoryBlock chunk; + getChunkData (chunk, true, maxSizeMB); + + const int totalLen = sizeof (fxProgramSet) + chunk.getSize() - 8; + dest.setSize (totalLen, true); + + fxProgramSet* const set = (fxProgramSet*) dest.getData(); + set->chunkMagic = swap ('CcnK'); + set->byteSize = 0; + set->fxMagic = swap ('FPCh'); + set->version = swap (fxbVersionNum); + set->fxID = swap (getUID()); + set->fxVersion = swap (getVersionNumber()); + set->numPrograms = swap (numPrograms); + set->chunkSize = swap (chunk.getSize()); + + getCurrentProgramName().copyToBuffer (set->name, sizeof (set->name) - 1); + chunk.copyTo (set->chunk, 0, chunk.getSize()); + } + } + else + { + if (isFXB) + { + const int progLen = sizeof (fxProgram) + (numParams - 1) * sizeof (float); + const int len = (sizeof (fxSet) - sizeof (fxProgram)) + progLen * jmax (1, numPrograms); + dest.setSize (len, true); + + fxSet* const set = (fxSet*) dest.getData(); + set->chunkMagic = swap ('CcnK'); + set->byteSize = 0; + set->fxMagic = swap ('FxBk'); + set->version = swap (fxbVersionNum); + set->fxID = swap (getUID()); + set->fxVersion = swap (getVersionNumber()); + set->numPrograms = swap (numPrograms); + + const int oldProgram = getCurrentProgram(); + MemoryBlock oldSettings; + createTempParameterStore (oldSettings); + + setParamsInProgramBlock ((fxProgram*) (((char*) (set->programs)) + oldProgram * progLen)); + + for (int i = 0; i < numPrograms; ++i) + { + if (i != oldProgram) + { + setCurrentProgram (i); + setParamsInProgramBlock ((fxProgram*) (((char*) (set->programs)) + i * progLen)); + } + } + + setCurrentProgram (oldProgram); + restoreFromTempParameterStore (oldSettings); + } + else + { + const int totalLen = sizeof (fxProgram) + (numParams - 1) * sizeof (float); + dest.setSize (totalLen, true); + + setParamsInProgramBlock ((fxProgram*) dest.getData()); + } + } + + return true; +} + +void VSTPluginInstance::getChunkData (MemoryBlock& mb, bool isPreset, int maxSizeMB) const +{ + if (usesChunks()) + { + void* data = 0; + const int bytes = dispatch (effGetChunk, isPreset ? 1 : 0, 0, &data, 0.0f); + + if (data != 0 && bytes <= maxSizeMB * 1024 * 1024) + { + mb.setSize (bytes); + mb.copyFrom (data, 0, bytes); + } + } +} + +void VSTPluginInstance::setChunkData (const char* data, int size, bool isPreset) +{ + if (size > 0 && usesChunks()) + { + dispatch (effSetChunk, isPreset ? 1 : 0, size, (void*) data, 0.0f); + + if (! isPreset) + updateStoredProgramNames(); + } +} + +//============================================================================== +void VSTPluginInstance::timerCallback() +{ + if (dispatch (effIdle, 0, 0, 0, 0) == 0) + stopTimer(); +} + +int VSTPluginInstance::dispatch (const int opcode, const int index, const int value, void* const ptr, float opt) const +{ + const ScopedLock sl (lock); + + ++insideVSTCallback; + int result = 0; + + try + { + if (effect != 0) + { +#if JUCE_MAC + if (module->resFileId != 0) + UseResFile (module->resFileId); + + CGrafPtr oldPort; + + if (getActiveEditor() != 0) + { + int x = 0, y = 0; + getActiveEditor()->relativePositionToOtherComponent (getActiveEditor()->getTopLevelComponent(), x, y); + + GetPort (&oldPort); + SetPortWindowPort ((WindowRef) getActiveEditor()->getWindowHandle()); + SetOrigin (-x, -y); + } +#endif + + result = effect->dispatcher (effect, opcode, index, value, ptr, opt); + +#if JUCE_MAC + if (getActiveEditor() != 0) + SetPort (oldPort); + + module->resFileId = CurResFile(); +#endif + + --insideVSTCallback; + return result; + } + } + catch (...) + { + //char s[512]; + //sprintf (s, "dispatcher (%d, %d, %d, %x, %f)", opcode, index, value, (int)ptr, opt); + } + + --insideVSTCallback; + return result; +} + +//============================================================================== +// handles non plugin-specific callbacks.. +static VstIntPtr handleGeneralCallback (VstInt32 opcode, VstInt32 index, VstInt32 value, void *ptr, float opt) +{ + (void) index; + (void) value; + (void) opt; + + switch (opcode) + { + case audioMasterCanDo: + { + static const char* canDos[] = { "supplyIdle", + "sendVstEvents", + "sendVstMidiEvent", + "sendVstTimeInfo", + "receiveVstEvents", + "receiveVstMidiEvent", + "supportShell", + "shellCategory" }; + + for (int i = 0; i < numElementsInArray (canDos); ++i) + if (strcmp (canDos[i], (const char*) ptr) == 0) + return 1; + + return 0; + } + + case audioMasterVersion: + return 0x2400; + case audioMasterCurrentId: + return shellUIDToCreate; + case audioMasterGetNumAutomatableParameters: + return 0; + case audioMasterGetAutomationState: + return 1; + + case audioMasterGetVendorVersion: + return 0x0101; + case audioMasterGetVendorString: + case audioMasterGetProductString: + JUCEApplication::getInstance() + ->getApplicationName().copyToBuffer ((char*) ptr, jmin (kVstMaxVendorStrLen, kVstMaxProductStrLen) - 1); + break; + + case audioMasterGetSampleRate: + return 44100; + + case audioMasterGetBlockSize: + return 512; + + case audioMasterSetOutputSampleRate: + return 0; + + default: + DBG ("*** Unhandled VST Callback: " + String ((int) opcode)); + break; + } + + return 0; +} + +// handles callbacks for a specific plugin +VstIntPtr VSTPluginInstance::handleCallback (VstInt32 opcode, VstInt32 index, VstInt32 value, void *ptr, float opt) +{ + switch (opcode) + { + case audioMasterAutomate: + sendParamChangeMessageToListeners (index, opt); + break; + + case audioMasterProcessEvents: + handleMidiFromPlugin ((const VstEvents*) ptr); + break; + + case audioMasterGetTime: + #ifdef _MSC_VER + #pragma warning (push) + #pragma warning (disable: 4311) + #endif + + return (VstIntPtr) &vstHostTime; + + #ifdef _MSC_VER + #pragma warning (pop) + #endif + break; + + case audioMasterIdle: + if (insideVSTCallback == 0 && MessageManager::getInstance()->isThisTheMessageThread()) + { + ++insideVSTCallback; +#if JUCE_MAC + if (getActiveEditor() != 0) + dispatch (effEditIdle, 0, 0, 0, 0); +#endif + const MessageManagerLock mml; + + juce_callAnyTimersSynchronously(); + + handleUpdateNowIfNeeded(); + + for (int i = ComponentPeer::getNumPeers(); --i >= 0;) + ComponentPeer::getPeer (i)->performAnyPendingRepaintsNow(); + + --insideVSTCallback; + } + break; + + case audioMasterUpdateDisplay: + triggerAsyncUpdate(); + break; + + case audioMasterTempoAt: + // returns (10000 * bpm) + break; + + case audioMasterNeedIdle: + startTimer (50); + break; + + case audioMasterSizeWindow: + if (getActiveEditor() != 0) + getActiveEditor()->setSize (index, value); + + return 1; + + case audioMasterGetSampleRate: + return (VstIntPtr) getSampleRate(); + + case audioMasterGetBlockSize: + return (VstIntPtr) getBlockSize(); + + case audioMasterWantMidi: + wantsMidiMessages = true; + break; + + case audioMasterGetDirectory: + #if JUCE_MAC + return (VstIntPtr) (void*) &module->parentDirFSSpec; + #else + return (VstIntPtr) (pointer_sized_uint) (const char*) module->fullParentDirectoryPathName; + #endif + + case audioMasterGetAutomationState: + // returns 0: not supported, 1: off, 2:read, 3:write, 4:read/write + break; + + // none of these are handled (yet).. + case audioMasterBeginEdit: + case audioMasterEndEdit: + case audioMasterSetTime: + case audioMasterPinConnected: + case audioMasterGetParameterQuantization: + case audioMasterIOChanged: + case audioMasterGetInputLatency: + case audioMasterGetOutputLatency: + case audioMasterGetPreviousPlug: + case audioMasterGetNextPlug: + case audioMasterWillReplaceOrAccumulate: + case audioMasterGetCurrentProcessLevel: + case audioMasterOfflineStart: + case audioMasterOfflineRead: + case audioMasterOfflineWrite: + case audioMasterOfflineGetCurrentPass: + case audioMasterOfflineGetCurrentMetaPass: + case audioMasterVendorSpecific: + case audioMasterSetIcon: + case audioMasterGetLanguage: + case audioMasterOpenWindow: + case audioMasterCloseWindow: + break; + + default: + return handleGeneralCallback (opcode, index, value, ptr, opt); + } + + return 0; +} + +// entry point for all callbacks from the plugin +static VstIntPtr VSTCALLBACK audioMaster (AEffect* effect, VstInt32 opcode, VstInt32 index, VstIntPtr value, void* ptr, float opt) +{ + try + { + if (effect != 0 && effect->resvd2 != 0) + { + return ((VSTPluginInstance*)(effect->resvd2)) + ->handleCallback (opcode, index, value, ptr, opt); + } + + return handleGeneralCallback (opcode, index, value, ptr, opt); + } + catch (...) + { + return 0; + } +} + +//============================================================================== +const String VSTPluginInstance::getVersion() const throw() +{ + int v = dispatch (effGetVendorVersion, 0, 0, 0, 0); + + String s; + + if (v != 0) + { + int versionBits[4]; + int n = 0; + + while (v != 0) + { + versionBits [n++] = (v & 0xff); + v >>= 8; + } + + s << 'V'; + + while (n > 0) + { + s << versionBits [--n]; + + if (n > 0) + s << '.'; + } + } + + return s; +} + +int VSTPluginInstance::getUID() const throw() +{ + int uid = effect != 0 ? effect->uniqueID : 0; + + if (uid == 0) + uid = module->file.hashCode(); + + return uid; +} + +const String VSTPluginInstance::getCategory() const throw() +{ + const char* result = 0; + + switch (dispatch (effGetPlugCategory, 0, 0, 0, 0)) + { + case kPlugCategEffect: + result = "Effect"; + break; + + case kPlugCategSynth: + result = "Synth"; + break; + + case kPlugCategAnalysis: + result = "Anaylsis"; + break; + + case kPlugCategMastering: + result = "Mastering"; + break; + + case kPlugCategSpacializer: + result = "Spacial"; + break; + + case kPlugCategRoomFx: + result = "Reverb"; + break; + + case kPlugSurroundFx: + result = "Surround"; + break; + + case kPlugCategRestoration: + result = "Restoration"; + break; + + case kPlugCategGenerator: + result = "Tone generation"; + break; + + default: + break; + } + + return result; +} + +//============================================================================== +float VSTPluginInstance::getParameter (int index) +{ + if (effect != 0 && ((unsigned int) index) < (unsigned int) effect->numParams) + { + try + { + const ScopedLock sl (lock); + return effect->getParameter (effect, index); + } + catch (...) + { + } + } + + return 0.0f; +} + +void VSTPluginInstance::setParameter (int index, float newValue) +{ + if (effect != 0 && ((unsigned int) index) < (unsigned int) effect->numParams) + { + try + { + const ScopedLock sl (lock); + + if (effect->getParameter (effect, index) != newValue) + effect->setParameter (effect, index, newValue); + } + catch (...) + { + } + } +} + +const String VSTPluginInstance::getParameterName (int index) +{ + if (effect != 0) + { + jassert (index >= 0 && index < effect->numParams); + + char nm [256]; + zerostruct (nm); + dispatch (effGetParamName, index, 0, nm, 0); + return String (nm).trim(); + } + + return String::empty; +} + +const String VSTPluginInstance::getParameterLabel (int index) const +{ + if (effect != 0) + { + jassert (index >= 0 && index < effect->numParams); + + char nm [256]; + zerostruct (nm); + dispatch (effGetParamLabel, index, 0, nm, 0); + return String (nm).trim(); + } + + return String::empty; +} + +const String VSTPluginInstance::getParameterText (int index) +{ + if (effect != 0) + { + jassert (index >= 0 && index < effect->numParams); + + char nm [256]; + zerostruct (nm); + dispatch (effGetParamDisplay, index, 0, nm, 0); + return String (nm).trim(); + } + + return String::empty; +} + +bool VSTPluginInstance::isParameterAutomatable (int index) const +{ + if (effect != 0) + { + jassert (index >= 0 && index < effect->numParams); + return dispatch (effCanBeAutomated, index, 0, 0, 0) != 0; + } + + return false; +} + +void VSTPluginInstance::createTempParameterStore (MemoryBlock& dest) +{ + dest.setSize (64 + 4 * getNumParameters()); + dest.fillWith (0); + + getCurrentProgramName().copyToBuffer ((char*) dest.getData(), 63); + + float* const p = (float*) (((char*) dest.getData()) + 64); + for (int i = 0; i < getNumParameters(); ++i) + p[i] = getParameter(i); +} + +void VSTPluginInstance::restoreFromTempParameterStore (const MemoryBlock& m) +{ + changeProgramName (getCurrentProgram(), (const char*) m); + + float* p = (float*) (((char*) m.getData()) + 64); + for (int i = 0; i < getNumParameters(); ++i) + setParameter (i, p[i]); +} + +//============================================================================== +void VSTPluginInstance::setCurrentProgram (int newIndex) +{ + if (getNumPrograms() > 0 && newIndex != getCurrentProgram()) + dispatch (effSetProgram, 0, jlimit (0, getNumPrograms() - 1, newIndex), 0, 0); +} + +const String VSTPluginInstance::getProgramName (int index) +{ + if (index == getCurrentProgram()) + { + return getCurrentProgramName(); + } + else if (effect != 0) + { + char nm [256]; + zerostruct (nm); + + if (dispatch (effGetProgramNameIndexed, + jlimit (0, getNumPrograms(), index), + -1, nm, 0) != 0) + { + return String (nm).trim(); + } + } + + return programNames [index]; +} + +void VSTPluginInstance::changeProgramName (int index, const String& newName) +{ + if (index == getCurrentProgram()) + { + if (getNumPrograms() > 0 && newName != getCurrentProgramName()) + dispatch (effSetProgramName, 0, 0, (void*) (const char*) newName.substring (0, 24), 0.0f); + } + else + { + jassertfalse // xxx not implemented! + } +} + +void VSTPluginInstance::updateStoredProgramNames() +{ + if (effect != 0 && getNumPrograms() > 0) + { + char nm [256]; + zerostruct (nm); + + // only do this if the plugin can't use indexed names.. + if (dispatch (effGetProgramNameIndexed, 0, -1, nm, 0) == 0) + { + const int oldProgram = getCurrentProgram(); + MemoryBlock oldSettings; + createTempParameterStore (oldSettings); + + for (int i = 0; i < getNumPrograms(); ++i) + { + setCurrentProgram (i); + getCurrentProgramName(); // (this updates the list) + } + + setCurrentProgram (oldProgram); + restoreFromTempParameterStore (oldSettings); + } + } +} + +const String VSTPluginInstance::getCurrentProgramName() +{ + if (effect != 0) + { + char nm [256]; + zerostruct (nm); + dispatch (effGetProgramName, 0, 0, nm, 0); + + const int index = getCurrentProgram(); + if (programNames[index].isEmpty()) + { + while (programNames.size() < index) + programNames.add (String::empty); + + programNames.set (index, String (nm).trim()); + } + + return String (nm).trim(); + } + + return String::empty; +} + +//============================================================================== +const String VSTPluginInstance::getInputChannelName (const int index) const +{ + if (index >= 0 && index < getNumInputChannels()) + { + VstPinProperties pinProps; + if (dispatch (effGetInputProperties, index, 0, &pinProps, 0.0f) != 0) + return String (pinProps.label, sizeof (pinProps.label)); + } + + return String::empty; +} + +bool VSTPluginInstance::isInputChannelStereoPair (int index) const +{ + if (index < 0 || index >= getNumInputChannels()) + return false; + + VstPinProperties pinProps; + if (dispatch (effGetInputProperties, index, 0, &pinProps, 0.0f) != 0) + return (pinProps.flags & kVstPinIsStereo) != 0; + + return true; +} + +const String VSTPluginInstance::getOutputChannelName (const int index) const +{ + if (index >= 0 && index < getNumOutputChannels()) + { + VstPinProperties pinProps; + if (dispatch (effGetOutputProperties, index, 0, &pinProps, 0.0f) != 0) + return String (pinProps.label, sizeof (pinProps.label)); + } + + return String::empty; +} + +bool VSTPluginInstance::isOutputChannelStereoPair (int index) const +{ + if (index < 0 || index >= getNumOutputChannels()) + return false; + + VstPinProperties pinProps; + if (dispatch (effGetOutputProperties, index, 0, &pinProps, 0.0f) != 0) + return (pinProps.flags & kVstPinIsStereo) != 0; + + return true; +} + +//============================================================================== +void VSTPluginInstance::setPower (const bool on) +{ + dispatch (effMainsChanged, 0, on ? 1 : 0, 0, 0); + isPowerOn = on; +} + +//============================================================================== +const int defaultMaxSizeMB = 64; + +void VSTPluginInstance::getStateInformation (MemoryBlock& destData) +{ + saveToFXBFile (destData, true, defaultMaxSizeMB); +} + +void VSTPluginInstance::getCurrentProgramStateInformation (MemoryBlock& destData) +{ + saveToFXBFile (destData, false, defaultMaxSizeMB); +} + +void VSTPluginInstance::setStateInformation (const void* data, int sizeInBytes) +{ + loadFromFXBFile (data, sizeInBytes); +} + +void VSTPluginInstance::setCurrentProgramStateInformation (const void* data, int sizeInBytes) +{ + loadFromFXBFile (data, sizeInBytes); +} + +//============================================================================== +//============================================================================== +VSTPluginFormat::VSTPluginFormat() +{ +} + +VSTPluginFormat::~VSTPluginFormat() +{ +} + +void VSTPluginFormat::findAllTypesForFile (OwnedArray & results, + const File& file) +{ + if (! fileMightContainThisPluginType (file)) + return; + + PluginDescription desc; + desc.file = file; + desc.uid = 0; + + VSTPluginInstance* instance = dynamic_cast (createInstanceFromDescription (desc)); + + if (instance == 0) + return; + + try + { +#if JUCE_MAC + if (instance->module->resFileId != 0) + UseResFile (instance->module->resFileId); +#endif + + instance->fillInPluginDescription (desc); + + VstPlugCategory category = (VstPlugCategory) instance->dispatch (effGetPlugCategory, 0, 0, 0, 0); + + if (category != kPlugCategShell) + { + // Normal plugin... + results.add (new PluginDescription (desc)); + + ++insideVSTCallback; + instance->dispatch (effOpen, 0, 0, 0, 0); + --insideVSTCallback; + } + else + { + // It's a shell plugin, so iterate all the subtypes... + char shellEffectName [64]; + + for (;;) + { + zerostruct (shellEffectName); + const int uid = instance->dispatch (effShellGetNextPlugin, 0, 0, shellEffectName, 0); + + if (uid == 0) + { + break; + } + else + { + desc.uid = uid; + desc.name = shellEffectName; + + bool alreadyThere = false; + + for (int i = results.size(); --i >= 0;) + { + PluginDescription* const d = results.getUnchecked(i); + + if (d->isDuplicateOf (desc)) + { + alreadyThere = true; + break; + } + } + + if (! alreadyThere) + results.add (new PluginDescription (desc)); + } + } + } + } + catch (...) + { + // crashed while loading... + } + + deleteAndZero (instance); +} + +AudioPluginInstance* VSTPluginFormat::createInstanceFromDescription (const PluginDescription& desc) +{ + VSTPluginInstance* result = 0; + + if (fileMightContainThisPluginType (desc.file)) + { + const File previousWorkingDirectory (File::getCurrentWorkingDirectory()); + desc.file.getParentDirectory().setAsCurrentWorkingDirectory(); + + const ReferenceCountedObjectPtr module (ModuleHandle::findOrCreateModule (desc.file)); + + if (module != 0) + { + shellUIDToCreate = desc.uid; + + result = new VSTPluginInstance (module); + + if (result->effect != 0) + { + result->effect->resvd2 = (VstIntPtr) (pointer_sized_int) result; + result->initialise(); + } + else + { + deleteAndZero (result); + } + } + + previousWorkingDirectory.setAsCurrentWorkingDirectory(); + } + + return result; +} + +bool VSTPluginFormat::fileMightContainThisPluginType (const File& f) +{ +#if JUCE_MAC + if (f.isDirectory() && f.hasFileExtension (T(".vst"))) + return true; + +#if JUCE_PPC + FSRef fileRef; + if (PlatformUtilities::makeFSRefFromPath (&fileRef, f.getFullPathName())) + { + const short resFileId = FSOpenResFile (&fileRef, fsRdPerm); + + if (resFileId != -1) + { + const int numEffects = Count1Resources ('aEff'); + CloseResFile (resFileId); + + if (numEffects > 0) + return true; + } + } +#endif + + return false; +#elif JUCE_WIN32 + return f.existsAsFile() + && f.hasFileExtension (T(".dll")); +#elif JUCE_LINUX + return f.existsAsFile() + && f.hasFileExtension (T(".so")); +#endif +} + +const FileSearchPath VSTPluginFormat::getDefaultLocationsToSearch() +{ +#if JUCE_MAC + return FileSearchPath ("~/Library/Audio/Plug-Ins/VST;/Library/Audio/Plug-Ins/VST"); +#elif JUCE_WIN32 + const String programFiles (File::getSpecialLocation (File::globalApplicationsDirectory).getFullPathName()); + + return FileSearchPath (programFiles + "\\Steinberg\\VstPlugins"); +#elif JUCE_LINUX + return FileSearchPath ("/usr/lib/vst"); +#endif +} + +#endif + +END_JUCE_NAMESPACE diff --git a/src/juce_appframework/audio/plugins/formats/juce_VSTPluginFormat.h b/src/juce_appframework/audio/plugins/formats/juce_VSTPluginFormat.h new file mode 100644 index 0000000000..92e8244628 --- /dev/null +++ b/src/juce_appframework/audio/plugins/formats/juce_VSTPluginFormat.h @@ -0,0 +1,68 @@ +/* + ============================================================================== + + This file is part of the JUCE library - "Jules' Utility Class Extensions" + Copyright 2004-7 by Raw Material Software ltd. + + ------------------------------------------------------------------------------ + + JUCE can be redistributed and/or modified under the terms of the + GNU General Public License, as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + JUCE is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with JUCE; if not, visit www.gnu.org/licenses or write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------------ + + If you'd like to release a closed-source product which uses JUCE, commercial + licenses are also available: visit www.rawmaterialsoftware.com/juce for + more information. + + ============================================================================== +*/ + +#ifndef __JUCE_VSTPLUGINFORMAT_JUCEHEADER__ +#define __JUCE_VSTPLUGINFORMAT_JUCEHEADER__ + +#include "../juce_AudioPluginFormat.h" + +#if JUCE_PLUGINHOST_VST + + +//============================================================================== +/** + Implements a plugin format manager for VSTs. +*/ +class JUCE_API VSTPluginFormat : public AudioPluginFormat +{ +public: + //============================================================================== + VSTPluginFormat(); + ~VSTPluginFormat(); + + //============================================================================== + const String getName() const { return "VST"; } + void findAllTypesForFile (OwnedArray & results, const File& file); + AudioPluginInstance* createInstanceFromDescription (const PluginDescription& desc); + bool fileMightContainThisPluginType (const File& file); + const FileSearchPath getDefaultLocationsToSearch(); + + //============================================================================== + juce_UseDebuggingNewOperator + +private: + VSTPluginFormat (const VSTPluginFormat&); + const VSTPluginFormat& operator= (const VSTPluginFormat&); +}; + + +#endif +#endif // __JUCE_VSTPLUGINFORMAT_JUCEHEADER__ diff --git a/src/juce_appframework/audio/plugins/juce_AudioPluginFormat.cpp b/src/juce_appframework/audio/plugins/juce_AudioPluginFormat.cpp new file mode 100644 index 0000000000..04c382ef4c --- /dev/null +++ b/src/juce_appframework/audio/plugins/juce_AudioPluginFormat.cpp @@ -0,0 +1,49 @@ +/* + ============================================================================== + + This file is part of the JUCE library - "Jules' Utility Class Extensions" + Copyright 2004-7 by Raw Material Software ltd. + + ------------------------------------------------------------------------------ + + JUCE can be redistributed and/or modified under the terms of the + GNU General Public License, as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + JUCE is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with JUCE; if not, visit www.gnu.org/licenses or write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------------ + + If you'd like to release a closed-source product which uses JUCE, commercial + licenses are also available: visit www.rawmaterialsoftware.com/juce for + more information. + + ============================================================================== +*/ + +#include "../../../juce_core/basics/juce_StandardHeader.h" + +BEGIN_JUCE_NAMESPACE + +#include "juce_AudioPluginFormat.h" + + +//============================================================================== +AudioPluginFormat::AudioPluginFormat() throw() +{ +} + +AudioPluginFormat::~AudioPluginFormat() throw() +{ +} + + +END_JUCE_NAMESPACE diff --git a/src/juce_appframework/audio/plugins/juce_AudioPluginFormat.h b/src/juce_appframework/audio/plugins/juce_AudioPluginFormat.h new file mode 100644 index 0000000000..5be0ad90b6 --- /dev/null +++ b/src/juce_appframework/audio/plugins/juce_AudioPluginFormat.h @@ -0,0 +1,107 @@ +/* + ============================================================================== + + This file is part of the JUCE library - "Jules' Utility Class Extensions" + Copyright 2004-7 by Raw Material Software ltd. + + ------------------------------------------------------------------------------ + + JUCE can be redistributed and/or modified under the terms of the + GNU General Public License, as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + JUCE is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with JUCE; if not, visit www.gnu.org/licenses or write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------------ + + If you'd like to release a closed-source product which uses JUCE, commercial + licenses are also available: visit www.rawmaterialsoftware.com/juce for + more information. + + ============================================================================== +*/ + +#ifndef __JUCE_AUDIOPLUGINFORMAT_JUCEHEADER__ +#define __JUCE_AUDIOPLUGINFORMAT_JUCEHEADER__ + +#include "juce_AudioPluginInstance.h" +#include "../../../juce_core/io/files/juce_FileSearchPath.h" +class PluginDescription; + + +//============================================================================== +/** + The base class for a type of plugin format, such as VST, AudioUnit, LADSPA, etc. + + Use the static getNumFormats() and getFormat() calls to find the types + of format that are available. +*/ +class JUCE_API AudioPluginFormat +{ +public: + //============================================================================== + /** Destructor. */ + virtual ~AudioPluginFormat(); + + //============================================================================== + /** Returns the format name. + + E.g. "VST", "AudioUnit", etc. + */ + virtual const String getName() const = 0; + + /** This tries to create descriptions for all the plugin types available in + a binary module file. + + The file will be some kind of DLL or bundle. + + Normally there will only be one type returned, but some plugins + (e.g. VST shells) can use a single DLL to create a set of different plugin + subtypes, so in that case, each subtype is returned as a separate object. + */ + virtual void findAllTypesForFile (OwnedArray & results, + const File& file) = 0; + + /** Tries to recreate a type from a previously generated PluginDescription. + + @see PluginDescription::createInstance + */ + virtual AudioPluginInstance* createInstanceFromDescription (const PluginDescription& desc) = 0; + + /** Should do a quick check to see if this file or directory might be a plugin of + this format. + + This is for searching for potential files, so it shouldn't actually try to + load the plugin or do anything time-consuming. + */ + virtual bool fileMightContainThisPluginType (const File& file) = 0; + + /** Returns the typical places to look for this kind of plugin. + + Note that if this returns no paths, it means that the format can't be scanned-for + (i.e. it's an internal format that doesn't live in files) + */ + virtual const FileSearchPath getDefaultLocationsToSearch() = 0; + + + //============================================================================== + juce_UseDebuggingNewOperator + + +protected: + AudioPluginFormat() throw(); + + AudioPluginFormat (const AudioPluginFormat&); + const AudioPluginFormat& operator= (const AudioPluginFormat&); +}; + + +#endif // __JUCE_AUDIOPLUGINFORMAT_JUCEHEADER__ diff --git a/src/juce_appframework/audio/plugins/juce_AudioPluginFormatManager.cpp b/src/juce_appframework/audio/plugins/juce_AudioPluginFormatManager.cpp new file mode 100644 index 0000000000..ef3d851fb1 --- /dev/null +++ b/src/juce_appframework/audio/plugins/juce_AudioPluginFormatManager.cpp @@ -0,0 +1,138 @@ +/* + ============================================================================== + + This file is part of the JUCE library - "Jules' Utility Class Extensions" + Copyright 2004-7 by Raw Material Software ltd. + + ------------------------------------------------------------------------------ + + JUCE can be redistributed and/or modified under the terms of the + GNU General Public License, as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + JUCE is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with JUCE; if not, visit www.gnu.org/licenses or write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------------ + + If you'd like to release a closed-source product which uses JUCE, commercial + licenses are also available: visit www.rawmaterialsoftware.com/juce for + more information. + + ============================================================================== +*/ + +#include "../../../juce_core/basics/juce_StandardHeader.h" + +BEGIN_JUCE_NAMESPACE + +#include "juce_AudioPluginFormatManager.h" +#include "juce_PluginDescription.h" +#include "../../../juce_core/text/juce_LocalisedStrings.h" + +#include "formats/juce_VSTPluginFormat.h" +#include "formats/juce_AudioUnitPluginFormat.h" +#include "formats/juce_DirectXPluginFormat.h" +#include "formats/juce_LADSPAPluginFormat.h" + + +//============================================================================== +AudioPluginFormatManager::AudioPluginFormatManager() throw() +{ +} + +AudioPluginFormatManager::~AudioPluginFormatManager() throw() +{ +} + +juce_ImplementSingleton_SingleThreaded (AudioPluginFormatManager); + +//============================================================================== +void AudioPluginFormatManager::addDefaultFormats() +{ +#ifdef JUCE_DEBUG + // you should only call this method once! + for (int i = formats.size(); --i >= 0;) + { + #if JUCE_PLUGINHOST_VST + jassert (dynamic_cast (formats[i]) == 0); + #endif + + #if JUCE_PLUGINHOST_AU && JUCE_MAC + jassert (dynamic_cast (formats[i]) == 0); + #endif + + #if JUCE_PLUGINHOST_DX && JUCE_WIN32 + jassert (dynamic_cast (formats[i]) == 0); + #endif + + #if JUCE_PLUGINHOST_LADSPA && JUCE_LINUX + jassert (dynamic_cast (formats[i]) == 0); + #endif + } +#endif + +#if JUCE_PLUGINHOST_VST + formats.add (new VSTPluginFormat()); +#endif + +#if JUCE_PLUGINHOST_AU && JUCE_MAC + formats.add (new AudioUnitPluginFormat()); +#endif + +#if JUCE_PLUGINHOST_DX && JUCE_WIN32 + formats.add (new DirectXPluginFormat()); +#endif + +#if JUCE_PLUGINHOST_LADSPA && JUCE_LINUX + formats.add (new LADSPAPluginFormat()); +#endif +} + +int AudioPluginFormatManager::getNumFormats() throw() +{ + return formats.size(); +} + +AudioPluginFormat* AudioPluginFormatManager::getFormat (const int index) throw() +{ + return formats [index]; +} + +void AudioPluginFormatManager::addFormat (AudioPluginFormat* const format) throw() +{ + formats.add (format); +} + +AudioPluginInstance* AudioPluginFormatManager::createPluginInstance (const PluginDescription& description, + String& errorMessage) const +{ + AudioPluginInstance* result = 0; + + for (int i = 0; i < formats.size(); ++i) + { + result = formats.getUnchecked(i)->createInstanceFromDescription (description); + + if (result != 0) + break; + } + + if (result == 0) + { + if (description.file != File::nonexistent && ! description.file.exists()) + errorMessage = TRANS ("This plug-in file no longer exists"); + else + errorMessage = TRANS ("This plug-in failed to load correctly"); + } + + return result; +} + +END_JUCE_NAMESPACE diff --git a/src/juce_appframework/audio/plugins/juce_AudioPluginFormatManager.h b/src/juce_appframework/audio/plugins/juce_AudioPluginFormatManager.h new file mode 100644 index 0000000000..bfacbf3d9a --- /dev/null +++ b/src/juce_appframework/audio/plugins/juce_AudioPluginFormatManager.h @@ -0,0 +1,107 @@ +/* + ============================================================================== + + This file is part of the JUCE library - "Jules' Utility Class Extensions" + Copyright 2004-7 by Raw Material Software ltd. + + ------------------------------------------------------------------------------ + + JUCE can be redistributed and/or modified under the terms of the + GNU General Public License, as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + JUCE is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with JUCE; if not, visit www.gnu.org/licenses or write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------------ + + If you'd like to release a closed-source product which uses JUCE, commercial + licenses are also available: visit www.rawmaterialsoftware.com/juce for + more information. + + ============================================================================== +*/ + +#ifndef __JUCE_AUDIOPLUGINFORMATMANAGER_JUCEHEADER__ +#define __JUCE_AUDIOPLUGINFORMATMANAGER_JUCEHEADER__ + +#include "juce_AudioPluginFormat.h" +#include "../../application/juce_DeletedAtShutdown.h" +#include "../../../juce_core/basics/juce_Singleton.h" + + +//============================================================================== +/** + This maintains a list of known AudioPluginFormats. + + @see AudioPluginFormat +*/ +class JUCE_API AudioPluginFormatManager : public DeletedAtShutdown +{ +public: + //============================================================================== + AudioPluginFormatManager() throw(); + + /** Destructor. */ + ~AudioPluginFormatManager() throw(); + + juce_DeclareSingleton_SingleThreaded (AudioPluginFormatManager, false); + + //============================================================================== + /** Adds any formats that it knows about, e.g. VST. + */ + void addDefaultFormats(); + + //============================================================================== + /** Returns the number of types of format that are available. + + Use getFormat() to get one of them. + */ + int getNumFormats() throw(); + + /** Returns one of the available formats. + + @see getNumFormats + */ + AudioPluginFormat* getFormat (const int index) throw(); + + //============================================================================== + /** Adds a format to the list. + + The object passed in will be owned and deleted by the manager. + */ + void addFormat (AudioPluginFormat* const format) throw(); + + + //============================================================================== + /** Tries to load the type for this description, by trying all the formats + that this manager knows about. + + The caller is responsible for deleting the object that is returned. + + If it can't load the plugin, it returns 0 and leaves a message in the + errorMessage string. + */ + AudioPluginInstance* createPluginInstance (const PluginDescription& description, + String& errorMessage) const; + + //============================================================================== + juce_UseDebuggingNewOperator + +private: + OwnedArray formats; + + AudioPluginFormatManager (const AudioPluginFormatManager&); + const AudioPluginFormatManager& operator= (const AudioPluginFormatManager&); +}; + + + +#endif // __JUCE_AUDIOPLUGINFORMATMANAGER_JUCEHEADER__ diff --git a/src/juce_appframework/audio/plugins/juce_AudioPluginInstance.cpp b/src/juce_appframework/audio/plugins/juce_AudioPluginInstance.cpp new file mode 100644 index 0000000000..3f95e8b8c4 --- /dev/null +++ b/src/juce_appframework/audio/plugins/juce_AudioPluginInstance.cpp @@ -0,0 +1,51 @@ +/* + ============================================================================== + + This file is part of the JUCE library - "Jules' Utility Class Extensions" + Copyright 2004-7 by Raw Material Software ltd. + + ------------------------------------------------------------------------------ + + JUCE can be redistributed and/or modified under the terms of the + GNU General Public License, as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + JUCE is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with JUCE; if not, visit www.gnu.org/licenses or write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------------ + + If you'd like to release a closed-source product which uses JUCE, commercial + licenses are also available: visit www.rawmaterialsoftware.com/juce for + more information. + + ============================================================================== +*/ + +#define JUCE_PLUGIN_HOST 1 + +#include "../../../juce_core/basics/juce_StandardHeader.h" + +BEGIN_JUCE_NAMESPACE + +#include "juce_AudioPluginInstance.h" + + +//============================================================================== +AudioPluginInstance::AudioPluginInstance() +{ +} + +AudioPluginInstance::~AudioPluginInstance() +{ +} + + +END_JUCE_NAMESPACE diff --git a/src/juce_appframework/audio/plugins/juce_AudioPluginInstance.h b/src/juce_appframework/audio/plugins/juce_AudioPluginInstance.h new file mode 100644 index 0000000000..78c5838e05 --- /dev/null +++ b/src/juce_appframework/audio/plugins/juce_AudioPluginInstance.h @@ -0,0 +1,76 @@ +/* + ============================================================================== + + This file is part of the JUCE library - "Jules' Utility Class Extensions" + Copyright 2004-7 by Raw Material Software ltd. + + ------------------------------------------------------------------------------ + + JUCE can be redistributed and/or modified under the terms of the + GNU General Public License, as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + JUCE is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with JUCE; if not, visit www.gnu.org/licenses or write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------------ + + If you'd like to release a closed-source product which uses JUCE, commercial + licenses are also available: visit www.rawmaterialsoftware.com/juce for + more information. + + ============================================================================== +*/ + +#ifndef __JUCE_AUDIOPLUGININSTANCE_JUCEHEADER__ +#define __JUCE_AUDIOPLUGININSTANCE_JUCEHEADER__ + +#include "../processors/juce_AudioProcessor.h" +#include "juce_PluginDescription.h" + + +//============================================================================== +/** + Base class for an active instance of a plugin. + + This derives from the AudioProcessor class, and adds some extra functionality + that helps when wrapping dynamically loaded plugins. + + @see AudioProcessor, AudioPluginFormat +*/ +class JUCE_API AudioPluginInstance : public AudioProcessor +{ +public: + //============================================================================== + /** Destructor. + + Make sure that you delete any UI components that belong to this plugin before + deleting the plugin. + */ + virtual ~AudioPluginInstance(); + + //============================================================================== + /** Fills-in the appropriate parts of this plugin description object. + */ + virtual void fillInPluginDescription (PluginDescription& description) const = 0; + + + //============================================================================== + juce_UseDebuggingNewOperator + +protected: + AudioPluginInstance(); + + AudioPluginInstance (const AudioPluginInstance&); + const AudioPluginInstance& operator= (const AudioPluginInstance&); +}; + + +#endif // __JUCE_AUDIOPLUGININSTANCE_JUCEHEADER__ diff --git a/src/juce_appframework/audio/plugins/juce_KnownPluginList.cpp b/src/juce_appframework/audio/plugins/juce_KnownPluginList.cpp new file mode 100644 index 0000000000..8b3308cf3a --- /dev/null +++ b/src/juce_appframework/audio/plugins/juce_KnownPluginList.cpp @@ -0,0 +1,431 @@ +/* + ============================================================================== + + This file is part of the JUCE library - "Jules' Utility Class Extensions" + Copyright 2004-7 by Raw Material Software ltd. + + ------------------------------------------------------------------------------ + + JUCE can be redistributed and/or modified under the terms of the + GNU General Public License, as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + JUCE is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with JUCE; if not, visit www.gnu.org/licenses or write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------------ + + If you'd like to release a closed-source product which uses JUCE, commercial + licenses are also available: visit www.rawmaterialsoftware.com/juce for + more information. + + ============================================================================== +*/ + +#include "../../../juce_core/basics/juce_StandardHeader.h" + +BEGIN_JUCE_NAMESPACE + +#include "juce_KnownPluginList.h" +#include "juce_AudioPluginFormatManager.h" + + +//============================================================================== +KnownPluginList::KnownPluginList() +{ +} + +KnownPluginList::~KnownPluginList() +{ +} + +void KnownPluginList::clear() +{ + if (types.size() > 0) + { + types.clear(); + sendChangeMessage (this); + } +} + +PluginDescription* KnownPluginList::getTypeForFile (const File& file) const throw() +{ + for (int i = 0; i < types.size(); ++i) + if (types.getUnchecked(i)->file == file) + return types.getUnchecked(i); + + return 0; +} + +PluginDescription* KnownPluginList::getTypeForIdentifierString (const String& identifierString) const throw() +{ + for (int i = 0; i < types.size(); ++i) + if (types.getUnchecked(i)->createIdentifierString() == identifierString) + return types.getUnchecked(i); + + return 0; +} + +bool KnownPluginList::addType (const PluginDescription& type) +{ + for (int i = types.size(); --i >= 0;) + { + if (types.getUnchecked(i)->isDuplicateOf (type)) + { + // strange - found a duplicate plugin with different info.. + jassert (types.getUnchecked(i)->name == type.name); + jassert (types.getUnchecked(i)->isInstrument == type.isInstrument); + + *types.getUnchecked(i) = type; + return false; + } + } + + types.add (new PluginDescription (type)); + sendChangeMessage (this); + return true; +} + +void KnownPluginList::removeType (const int index) throw() +{ + types.remove (index); + sendChangeMessage (this); +} + +bool KnownPluginList::isListingUpToDate (const File& possiblePluginFile) const throw() +{ + if (getTypeForFile (possiblePluginFile) == 0) + return false; + + for (int i = types.size(); --i >= 0;) + { + const PluginDescription* const d = types.getUnchecked(i); + + if (d->file == possiblePluginFile + && d->lastFileModTime != possiblePluginFile.getLastModificationTime()) + { + return false; + } + } + + return true; +} + +bool KnownPluginList::scanAndAddFile (const File& possiblePluginFile, + const bool dontRescanIfAlreadyInList, + OwnedArray & typesFound) +{ + bool addedOne = false; + + if (dontRescanIfAlreadyInList + && getTypeForFile (possiblePluginFile) != 0) + { + bool needsRescanning = false; + + for (int i = types.size(); --i >= 0;) + { + const PluginDescription* const d = types.getUnchecked(i); + + if (d->file == possiblePluginFile) + { + if (d->lastFileModTime != possiblePluginFile.getLastModificationTime()) + needsRescanning = true; + else + typesFound.add (new PluginDescription (*d)); + } + } + + if (! needsRescanning) + return false; + } + + for (int i = 0; i < AudioPluginFormatManager::getInstance()->getNumFormats(); ++i) + { + AudioPluginFormat* const format = AudioPluginFormatManager::getInstance()->getFormat (i); + + OwnedArray found; + format->findAllTypesForFile (found, possiblePluginFile); + + for (int i = 0; i < found.size(); ++i) + { + PluginDescription* const desc = found.getUnchecked(i); + jassert (desc != 0); + + if (addType (*desc)) + addedOne = true; + + typesFound.add (new PluginDescription (*desc)); + } + } + + return addedOne; +} + +void KnownPluginList::scanAndAddDragAndDroppedFiles (const StringArray& files, + OwnedArray & typesFound) +{ + for (int i = 0; i < files.size(); ++i) + { + const File f (files [i]); + + if (! scanAndAddFile (f, true, typesFound)) + { + if (f.isDirectory()) + { + StringArray s; + + { + OwnedArray subFiles; + f.findChildFiles (subFiles, File::findFilesAndDirectories, false); + + for (int j = 0; j < subFiles.size(); ++j) + s.add (subFiles.getUnchecked (j)->getFullPathName()); + } + + scanAndAddDragAndDroppedFiles (s, typesFound); + } + } + } +} + +//============================================================================== +class PluginSorter +{ +public: + KnownPluginList::SortMethod method; + + PluginSorter() throw() {} + + int compareElements (const PluginDescription* const first, + const PluginDescription* const second) const throw() + { + int diff = 0; + + if (method == KnownPluginList::sortByCategory) + diff = first->category.compareLexicographically (second->category); + else if (method == KnownPluginList::sortByManufacturer) + diff = first->manufacturerName.compareLexicographically (second->manufacturerName); + else if (method == KnownPluginList::sortByFileSystemLocation) + diff = first->file.getParentDirectory().getFullPathName().compare (second->file.getParentDirectory().getFullPathName()); + + if (diff == 0) + diff = first->name.compareLexicographically (second->name); + + return diff; + } +}; + +void KnownPluginList::sort (const SortMethod method) +{ + if (method != defaultOrder) + { + PluginSorter sorter; + sorter.method = method; + types.sort (sorter, true); + + sendChangeMessage (this); + } +} + +//============================================================================== +XmlElement* KnownPluginList::createXml() const +{ + XmlElement* const e = new XmlElement (T("KNOWNPLUGINS")); + + for (int i = 0; i < types.size(); ++i) + e->addChildElement (types.getUnchecked(i)->createXml()); + + return e; +} + +void KnownPluginList::recreateFromXml (const XmlElement& xml) +{ + clear(); + + if (xml.hasTagName (T("KNOWNPLUGINS"))) + { + forEachXmlChildElement (xml, e) + { + PluginDescription info; + + if (info.loadFromXml (*e)) + addType (info); + } + } +} + +//============================================================================== +const int menuIdBase = 0x324503f4; + +// This is used to turn a bunch of paths into a nested menu structure. +struct PluginFilesystemTree +{ +private: + String folder; + OwnedArray subFolders; + Array plugins; + + void addPlugin (PluginDescription* const pd, const String& path) + { + if (path.isEmpty()) + { + plugins.add (pd); + } + else + { + const String firstSubFolder (path.upToFirstOccurrenceOf (T("/"), false, false)); + const String remainingPath (path.fromFirstOccurrenceOf (T("/"), false, false)); + + for (int i = subFolders.size(); --i >= 0;) + { + if (subFolders.getUnchecked(i)->folder.equalsIgnoreCase (firstSubFolder)) + { + subFolders.getUnchecked(i)->addPlugin (pd, remainingPath); + return; + } + } + + PluginFilesystemTree* const newFolder = new PluginFilesystemTree(); + newFolder->folder = firstSubFolder; + subFolders.add (newFolder); + + newFolder->addPlugin (pd, remainingPath); + } + } + + // removes any deeply nested folders that don't contain any actual plugins + void optimise() + { + for (int i = subFolders.size(); --i >= 0;) + { + PluginFilesystemTree* const sub = subFolders.getUnchecked(i); + + sub->optimise(); + + if (sub->plugins.size() == 0) + { + for (int j = 0; j < sub->subFolders.size(); ++j) + subFolders.add (sub->subFolders.getUnchecked(j)); + + sub->subFolders.clear (false); + subFolders.remove (i); + } + } + } + +public: + void buildTree (const Array & allPlugins) + { + for (int i = 0; i < allPlugins.size(); ++i) + { + String path (allPlugins.getUnchecked(i)->file.getParentDirectory().getFullPathName()); + + if (path.substring (1, 2) == T(":")) + path = path.substring (2); + + path = path.replaceCharacter (T('\\'), T('/')); + + addPlugin (allPlugins.getUnchecked(i), path); + } + + optimise(); + } + + void addToMenu (PopupMenu& m, const OwnedArray & allPlugins) const + { + int i; + for (i = 0; i < subFolders.size(); ++i) + { + const PluginFilesystemTree* const sub = subFolders.getUnchecked(i); + + PopupMenu subMenu; + sub->addToMenu (subMenu, allPlugins); + m.addSubMenu (sub->folder, subMenu); + } + + for (i = 0; i < plugins.size(); ++i) + { + PluginDescription* const plugin = plugins.getUnchecked(i); + + m.addItem (allPlugins.indexOf (plugin) + menuIdBase, + plugin->name, true, false); + } + } +}; + +//============================================================================== +void KnownPluginList::addToMenu (PopupMenu& menu, const SortMethod sortMethod) const +{ + Array sorted; + + { + PluginSorter sorter; + sorter.method = sortMethod; + + for (int i = 0; i < types.size(); ++i) + sorted.addSorted (sorter, types.getUnchecked(i)); + } + + if (sortMethod == sortByCategory + || sortMethod == sortByManufacturer) + { + String lastSubMenuName; + PopupMenu sub; + + for (int i = 0; i < sorted.size(); ++i) + { + const PluginDescription* const pd = sorted.getUnchecked(i); + String thisSubMenuName (sortMethod == sortByCategory ? pd->category + : pd->manufacturerName); + + if (thisSubMenuName.trim().isEmpty()) + thisSubMenuName = T("Other"); + + if (thisSubMenuName != lastSubMenuName) + { + if (sub.getNumItems() > 0) + { + menu.addSubMenu (lastSubMenuName, sub); + sub.clear(); + } + + lastSubMenuName = thisSubMenuName; + } + + sub.addItem (types.indexOf (pd) + menuIdBase, pd->name, true, false); + } + + if (sub.getNumItems() > 0) + menu.addSubMenu (lastSubMenuName, sub); + } + else if (sortMethod == sortByFileSystemLocation) + { + PluginFilesystemTree root; + root.buildTree (sorted); + root.addToMenu (menu, types); + } + else + { + for (int i = 0; i < sorted.size(); ++i) + { + const PluginDescription* const pd = sorted.getUnchecked(i); + menu.addItem (types.indexOf (pd) + menuIdBase, pd->name, true, false); + } + } +} + +int KnownPluginList::getIndexChosenByMenu (const int menuResultCode) const +{ + const int i = menuResultCode - menuIdBase; + + return (((unsigned int) i) < (unsigned int) types.size()) ? i : -1; +} + +END_JUCE_NAMESPACE diff --git a/src/juce_appframework/audio/plugins/juce_KnownPluginList.h b/src/juce_appframework/audio/plugins/juce_KnownPluginList.h new file mode 100644 index 0000000000..b4c11c8453 --- /dev/null +++ b/src/juce_appframework/audio/plugins/juce_KnownPluginList.h @@ -0,0 +1,176 @@ +/* + ============================================================================== + + This file is part of the JUCE library - "Jules' Utility Class Extensions" + Copyright 2004-7 by Raw Material Software ltd. + + ------------------------------------------------------------------------------ + + JUCE can be redistributed and/or modified under the terms of the + GNU General Public License, as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + JUCE is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with JUCE; if not, visit www.gnu.org/licenses or write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------------ + + If you'd like to release a closed-source product which uses JUCE, commercial + licenses are also available: visit www.rawmaterialsoftware.com/juce for + more information. + + ============================================================================== +*/ + +#ifndef __JUCE_KNOWNPLUGINLIST_JUCEHEADER__ +#define __JUCE_KNOWNPLUGINLIST_JUCEHEADER__ + +#include "juce_PluginDescription.h" +#include "juce_AudioPluginFormat.h" +#include "../../events/juce_ChangeBroadcaster.h" +#include "../../gui/components/menus/juce_PopupMenu.h" + + +//============================================================================== +/** + Manages a list of plugin types. + + This can be easily edited, saved and loaded, and used to create instances of + the plugin types in it. + + @see PluginListComponent +*/ +class JUCE_API KnownPluginList : public ChangeBroadcaster +{ +public: + //============================================================================== + /** Creates an empty list. + */ + KnownPluginList(); + + /** Destructor. */ + ~KnownPluginList(); + + //============================================================================== + /** Clears the list. */ + void clear(); + + /** Returns the number of types currently in the list. + @see getType + */ + int getNumTypes() const throw() { return types.size(); } + + /** Returns one of the types. + @see getNumTypes + */ + PluginDescription* getType (const int index) const throw() { return types [index]; } + + /** Looks for a type in the list which comes from this file. + */ + PluginDescription* getTypeForFile (const File& file) const throw(); + + /** Looks for a type in the list which matches a plugin type ID. + + The identifierString parameter must have been created by + PluginDescription::createIdentifierString(). + */ + PluginDescription* getTypeForIdentifierString (const String& identifierString) const throw(); + + /** Adds a type manually from its description. */ + bool addType (const PluginDescription& type); + + /** Removes a type. */ + void removeType (const int index) throw(); + + /** Looks for all types that can be loaded from a given file, and adds them + to the list. + + If dontRescanIfAlreadyInList is true, then the file will only be loaded and + re-tested if it's not already in the list, or if the file's modification + time has changed since the list was created. If dontRescanIfAlreadyInList is + false, the file will always be reloaded and tested. + + Returns true if any new types were added, and all the types found in this + file (even if it was already known and hasn't been re-scanned) get returned + in the array. + */ + bool scanAndAddFile (const File& possiblePluginFile, + const bool dontRescanIfAlreadyInList, + OwnedArray & typesFound); + + /** Returns true if the specified file is already known about and if it + hasn't been modified since our entry was created. + */ + bool isListingUpToDate (const File& possiblePluginFile) const throw(); + + /** Scans and adds a bunch of files that might have been dragged-and-dropped. + + If any types are found in the files, their descriptions are returned in the array. + */ + void scanAndAddDragAndDroppedFiles (const StringArray& filenames, + OwnedArray & typesFound); + + //============================================================================== + /** Sort methods used to change the order of the plugins in the list. + */ + enum SortMethod + { + defaultOrder = 0, + sortAlphabetically, + sortByCategory, + sortByManufacturer, + sortByFileSystemLocation + }; + + //============================================================================== + /** Adds all the plugin types to a popup menu so that the user can select one. + + Depending on the sort method, it may add sub-menus for categories, + manufacturers, etc. + + Use getIndexChosenByMenu() to find out the type that was chosen. + */ + void addToMenu (PopupMenu& menu, + const SortMethod sortMethod) const; + + /** Converts a menu item index that has been chosen into its index in this list. + + Returns -1 if it's not an ID that was used. + + @see addToMenu + */ + int getIndexChosenByMenu (const int menuResultCode) const; + + //============================================================================== + /** Sorts the list. */ + void sort (const SortMethod method); + + //============================================================================== + /** Creates some XML that can be used to store the state of this list. + */ + XmlElement* createXml() const; + + /** Recreates the state of this list from its stored XML format. + */ + void recreateFromXml (const XmlElement& xml); + + + //============================================================================== + juce_UseDebuggingNewOperator + +private: + OwnedArray types; + + KnownPluginList (const KnownPluginList&); + const KnownPluginList& operator= (const KnownPluginList&); +}; + + +#endif // __JUCE_KNOWNPLUGINLIST_JUCEHEADER__ diff --git a/src/juce_appframework/audio/plugins/juce_PluginDescription.cpp b/src/juce_appframework/audio/plugins/juce_PluginDescription.cpp new file mode 100644 index 0000000000..1873557239 --- /dev/null +++ b/src/juce_appframework/audio/plugins/juce_PluginDescription.cpp @@ -0,0 +1,139 @@ +/* + ============================================================================== + + This file is part of the JUCE library - "Jules' Utility Class Extensions" + Copyright 2004-7 by Raw Material Software ltd. + + ------------------------------------------------------------------------------ + + JUCE can be redistributed and/or modified under the terms of the + GNU General Public License, as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + JUCE is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with JUCE; if not, visit www.gnu.org/licenses or write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------------ + + If you'd like to release a closed-source product which uses JUCE, commercial + licenses are also available: visit www.rawmaterialsoftware.com/juce for + more information. + + ============================================================================== +*/ + +#include "../../../juce_core/basics/juce_StandardHeader.h" + +BEGIN_JUCE_NAMESPACE + +#include "juce_PluginDescription.h" +#include "juce_AudioPluginFormat.h" + + +//============================================================================== +PluginDescription::PluginDescription() throw() + : uid (0), + isInstrument (false), + numInputChannels (0), + numOutputChannels (0) +{ +} + +PluginDescription::~PluginDescription() throw() +{ +} + +PluginDescription::PluginDescription (const PluginDescription& other) throw() + : name (other.name), + pluginFormatName (other.pluginFormatName), + category (other.category), + manufacturerName (other.manufacturerName), + version (other.version), + file (other.file), + uid (other.uid), + isInstrument (other.isInstrument), + lastFileModTime (other.lastFileModTime), + numInputChannels (other.numInputChannels), + numOutputChannels (other.numOutputChannels) +{ +} + +const PluginDescription& PluginDescription::operator= (const PluginDescription& other) throw() +{ + name = other.name; + pluginFormatName = other.pluginFormatName; + category = other.category; + manufacturerName = other.manufacturerName; + version = other.version; + file = other.file; + uid = other.uid; + isInstrument = other.isInstrument; + lastFileModTime = other.lastFileModTime; + numInputChannels = other.numInputChannels; + numOutputChannels = other.numOutputChannels; + + return *this; +} + +bool PluginDescription::isDuplicateOf (const PluginDescription& other) const +{ + return file == other.file + && uid == other.uid; +} + +const String PluginDescription::createIdentifierString() const throw() +{ + return pluginFormatName + + T("-") + name + + T("-") + String::toHexString (file.getFileName().hashCode()) + + T("-") + String::toHexString (uid); +} + +XmlElement* PluginDescription::createXml() const +{ + XmlElement* const e = new XmlElement (T("PLUGIN")); + e->setAttribute (T("name"), name); + e->setAttribute (T("format"), pluginFormatName); + e->setAttribute (T("category"), category); + e->setAttribute (T("manufacturer"), manufacturerName); + e->setAttribute (T("version"), version); + e->setAttribute (T("file"), file.getFullPathName()); + e->setAttribute (T("uid"), String::toHexString (uid)); + e->setAttribute (T("isInstrument"), isInstrument); + e->setAttribute (T("fileTime"), String::toHexString (lastFileModTime.toMilliseconds())); + e->setAttribute (T("numInputs"), numInputChannels); + e->setAttribute (T("numOutputs"), numOutputChannels); + + return e; +} + +bool PluginDescription::loadFromXml (const XmlElement& xml) +{ + if (xml.hasTagName (T("PLUGIN"))) + { + name = xml.getStringAttribute (T("name")); + pluginFormatName = xml.getStringAttribute (T("format")); + category = xml.getStringAttribute (T("category")); + manufacturerName = xml.getStringAttribute (T("manufacturer")); + version = xml.getStringAttribute (T("version")); + file = File (xml.getStringAttribute (T("file"))); + uid = xml.getStringAttribute (T("uid")).getHexValue32(); + isInstrument = xml.getBoolAttribute (T("isInstrument"), false); + lastFileModTime = Time (xml.getStringAttribute (T("fileTime")).getHexValue64()); + numInputChannels = xml.getIntAttribute (T("numInputs")); + numOutputChannels = xml.getIntAttribute (T("numOutputs")); + + return true; + } + + return false; +} + +END_JUCE_NAMESPACE diff --git a/src/juce_appframework/audio/plugins/juce_PluginDescription.h b/src/juce_appframework/audio/plugins/juce_PluginDescription.h new file mode 100644 index 0000000000..9af83fa35c --- /dev/null +++ b/src/juce_appframework/audio/plugins/juce_PluginDescription.h @@ -0,0 +1,140 @@ +/* + ============================================================================== + + This file is part of the JUCE library - "Jules' Utility Class Extensions" + Copyright 2004-7 by Raw Material Software ltd. + + ------------------------------------------------------------------------------ + + JUCE can be redistributed and/or modified under the terms of the + GNU General Public License, as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + JUCE is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with JUCE; if not, visit www.gnu.org/licenses or write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------------ + + If you'd like to release a closed-source product which uses JUCE, commercial + licenses are also available: visit www.rawmaterialsoftware.com/juce for + more information. + + ============================================================================== +*/ + +#ifndef __JUCE_PLUGINDESCRIPTION_JUCEHEADER__ +#define __JUCE_PLUGINDESCRIPTION_JUCEHEADER__ + +#include "../../../juce_core/io/files/juce_File.h" +#include "../../../juce_core/text/juce_XmlElement.h" + + +//============================================================================== +/** + A small class to represent some facts about a particular type of plugin. + + This class is for storing and managing the details about a plugin without + actually having to load an instance of it. + + A KnownPluginList contains a list of PluginDescription objects. + + @see KnownPluginList +*/ +class JUCE_API PluginDescription +{ +public: + //============================================================================== + PluginDescription() throw(); + PluginDescription (const PluginDescription& other) throw(); + const PluginDescription& operator= (const PluginDescription& other) throw(); + ~PluginDescription() throw(); + + //============================================================================== + /** The name of the plugin. */ + String name; + + /** The plugin format, e.g. "VST", "AudioUnit", etc. + */ + String pluginFormatName; + + /** A category, such as "Dynamics", "Reverbs", etc. + */ + String category; + + /** The manufacturer. */ + String manufacturerName; + + /** The version. This string doesn't have any particular format. */ + String version; + + /** The binary module file containing the plugin. */ + File file; + + /** The last time the plugin file was changed. + This is handy when scanning for new or changed plugins. + */ + Time lastFileModTime; + + /** A unique ID for the plugin. + + Note that this might not be unique between formats, e.g. a VST and some + other format might actually have the same id. + + @see createIdentifierString + */ + int uid; + + /** True if the plugin identifies itself as a synthesiser. */ + bool isInstrument; + + /** The number of inputs. */ + int numInputChannels; + + /** The number of outputs. */ + int numOutputChannels; + + /** Returns true if the two descriptions refer the the same plugin. + + This isn't quite as simple as them just having the same file (because of + shell plugins). + */ + bool isDuplicateOf (const PluginDescription& other) const; + + //============================================================================== + /** Returns a string that can be saved and used to uniquely identify the + plugin again. + + This contains less info than the XML encoding, and is independent of the + plugin's file location, so can be used to store a plugin ID for use + across different machines. + */ + const String createIdentifierString() const throw(); + + //============================================================================== + /** Creates an XML object containing these details. + + @see loadFromXml + */ + XmlElement* createXml() const; + + /** Reloads the info in this structure from an XML record that was previously + saved with createXML(). + + Returns true if the XML was a valid plugin description. + */ + bool loadFromXml (const XmlElement& xml); + + + //============================================================================== + juce_UseDebuggingNewOperator +}; + + +#endif // __JUCE_PLUGINDESCRIPTION_JUCEHEADER__ diff --git a/src/juce_appframework/audio/plugins/juce_PluginDirectoryScanner.cpp b/src/juce_appframework/audio/plugins/juce_PluginDirectoryScanner.cpp new file mode 100644 index 0000000000..476d5cf124 --- /dev/null +++ b/src/juce_appframework/audio/plugins/juce_PluginDirectoryScanner.cpp @@ -0,0 +1,164 @@ +/* + ============================================================================== + + This file is part of the JUCE library - "Jules' Utility Class Extensions" + Copyright 2004-7 by Raw Material Software ltd. + + ------------------------------------------------------------------------------ + + JUCE can be redistributed and/or modified under the terms of the + GNU General Public License, as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + JUCE is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with JUCE; if not, visit www.gnu.org/licenses or write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------------ + + If you'd like to release a closed-source product which uses JUCE, commercial + licenses are also available: visit www.rawmaterialsoftware.com/juce for + more information. + + ============================================================================== +*/ + +#include "../../../juce_core/basics/juce_StandardHeader.h" + +BEGIN_JUCE_NAMESPACE + +#include "juce_PluginDirectoryScanner.h" +#include "juce_AudioPluginFormat.h" +#include "../../../juce_core/io/files/juce_DirectoryIterator.h" + + +//============================================================================== +PluginDirectoryScanner::PluginDirectoryScanner (KnownPluginList& listToAddTo, + AudioPluginFormat& formatToLookFor, + FileSearchPath directoriesToSearch, + const bool recursive, + const File& deadMansPedalFile_) + : list (listToAddTo), + format (formatToLookFor), + deadMansPedalFile (deadMansPedalFile_), + nextIndex (0), + progress (0) +{ + directoriesToSearch.removeRedundantPaths(); + + for (int j = 0; j < directoriesToSearch.getNumPaths(); ++j) + recursiveFileSearch (directoriesToSearch [j], recursive); + + // If any plugins have crashed recently when being loaded, move them to the + // end of the list to give the others a chance to load correctly.. + const StringArray crashedPlugins (getDeadMansPedalFile()); + + for (int i = 0; i < crashedPlugins.size(); ++i) + { + const File f (crashedPlugins[i]); + + for (int j = filesToScan.size(); --j >= 0;) + if (f == *filesToScan.getUnchecked (j)) + filesToScan.move (j, -1); + } +} + +void PluginDirectoryScanner::recursiveFileSearch (const File& dir, const bool recursive) +{ + // avoid allowing the dir iterator to be recursive, because we want to avoid letting it delve inside + // .component or .vst directories. + DirectoryIterator iter (dir, false, "*", File::findFilesAndDirectories); + + while (iter.next()) + { + const File f (iter.getFile()); + bool isPlugin = false; + + if (format.fileMightContainThisPluginType (f)) + { + isPlugin = true; + filesToScan.add (new File (f)); + } + + if (recursive && (! isPlugin) && f.isDirectory()) + recursiveFileSearch (f, true); + } +} + +PluginDirectoryScanner::~PluginDirectoryScanner() +{ +} + +//============================================================================== +const File PluginDirectoryScanner::getNextPluginFileThatWillBeScanned() const throw() +{ + File* const file = filesToScan [nextIndex]; + + if (file != 0) + return *file; + + return File::nonexistent; +} + +bool PluginDirectoryScanner::scanNextFile (const bool dontRescanIfAlreadyInList) +{ + File* const file = filesToScan [nextIndex]; + + if (file != 0) + { + if (! list.isListingUpToDate (*file)) + { + OwnedArray typesFound; + + // Add this plugin to the end of the dead-man's pedal list in case it crashes... + StringArray crashedPlugins (getDeadMansPedalFile()); + crashedPlugins.removeString (file->getFullPathName()); + crashedPlugins.add (file->getFullPathName()); + setDeadMansPedalFile (crashedPlugins); + + list.scanAndAddFile (*file, + dontRescanIfAlreadyInList, + typesFound); + + // Managed to load without crashing, so remove it from the dead-man's-pedal.. + crashedPlugins.removeString (file->getFullPathName()); + setDeadMansPedalFile (crashedPlugins); + + if (typesFound.size() == 0) + failedFiles.add (file->getFullPathName()); + } + + ++nextIndex; + progress = nextIndex / (float) filesToScan.size(); + } + + return nextIndex < filesToScan.size(); +} + +const StringArray PluginDirectoryScanner::getDeadMansPedalFile() throw() +{ + StringArray lines; + + if (deadMansPedalFile != File::nonexistent) + { + lines.addLines (deadMansPedalFile.loadFileAsString()); + lines.removeEmptyStrings(); + } + + return lines; +} + +void PluginDirectoryScanner::setDeadMansPedalFile (const StringArray& newContents) throw() +{ + if (deadMansPedalFile != File::nonexistent) + deadMansPedalFile.replaceWithText (newContents.joinIntoString ("\n"), true, true); +} + + +END_JUCE_NAMESPACE diff --git a/src/juce_appframework/audio/plugins/juce_PluginDirectoryScanner.h b/src/juce_appframework/audio/plugins/juce_PluginDirectoryScanner.h new file mode 100644 index 0000000000..d559707a7c --- /dev/null +++ b/src/juce_appframework/audio/plugins/juce_PluginDirectoryScanner.h @@ -0,0 +1,129 @@ +/* + ============================================================================== + + This file is part of the JUCE library - "Jules' Utility Class Extensions" + Copyright 2004-7 by Raw Material Software ltd. + + ------------------------------------------------------------------------------ + + JUCE can be redistributed and/or modified under the terms of the + GNU General Public License, as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + JUCE is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with JUCE; if not, visit www.gnu.org/licenses or write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------------ + + If you'd like to release a closed-source product which uses JUCE, commercial + licenses are also available: visit www.rawmaterialsoftware.com/juce for + more information. + + ============================================================================== +*/ + +#ifndef __JUCE_PLUGINDIRECTORYSCANNER_JUCEHEADER__ +#define __JUCE_PLUGINDIRECTORYSCANNER_JUCEHEADER__ + +#include "juce_KnownPluginList.h" +#include "juce_AudioPluginFormatManager.h" + + +//============================================================================== +/** + Scans a directory for plugins, and adds them to a KnownPluginList. + + To use one of these, create it and call scanNextFile() repeatedly, until + it returns false. +*/ +class JUCE_API PluginDirectoryScanner +{ +public: + //============================================================================== + /** + Creates a scanner. + + @param listToAddResultsTo this will get the new types added to it. + @param formatToLookFor this is the type of format that you want to look for + @param directoriesToSearch the path to search + @param searchRecursively true to search recursively + @param deadMansPedalFile if this isn't File::nonexistent, then it will + be used as a file to store the names of any plugins + that crash during initialisation. If there are + any plugins listed in it, then these will always + be scanned after all other possible files have + been tried - in this way, even if there's a few + dodgy plugins in your path, then a couple of rescans + will still manage to find all the proper plugins. + It's probably best to choose a file in the user's + application data directory (alongside your app's + settings file) for this. The file format it uses + is just a list of filenames of the modules that + failed. + */ + PluginDirectoryScanner (KnownPluginList& listToAddResultsTo, + AudioPluginFormat& formatToLookFor, + FileSearchPath directoriesToSearch, + const bool searchRecursively, + const File& deadMansPedalFile); + + /** Destructor. */ + ~PluginDirectoryScanner(); + + //============================================================================== + /** Tries the next likely-looking file. + + If dontRescanIfAlreadyInList is true, then the file will only be loaded and + re-tested if it's not already in the list, or if the file's modification + time has changed since the list was created. If dontRescanIfAlreadyInList is + false, the file will always be reloaded and tested. + + Returns false when there are no more files to try. + */ + bool scanNextFile (const bool dontRescanIfAlreadyInList); + + /** Returns the file that will be scanned during the next call to scanNextFile(). + + This is handy if you want to show the user which file is currently getting + scanned. + */ + const File getNextPluginFileThatWillBeScanned() const throw(); + + /** Returns the estimated progress, between 0 and 1. + */ + float getProgress() const { return progress; } + + /** This returns a list of all the filenames of things that looked like being + a plugin file, but which failed to open for some reason. + */ + const StringArray& getFailedFiles() const throw() { return failedFiles; } + + //============================================================================== + juce_UseDebuggingNewOperator + +private: + KnownPluginList& list; + AudioPluginFormat& format; + OwnedArray filesToScan; + File deadMansPedalFile; + StringArray failedFiles; + int nextIndex; + float progress; + + void recursiveFileSearch (const File& dir, const bool recursive); + const StringArray getDeadMansPedalFile() throw(); + void setDeadMansPedalFile (const StringArray& newContents) throw(); + + PluginDirectoryScanner (const PluginDirectoryScanner&); + const PluginDirectoryScanner& operator= (const PluginDirectoryScanner&); +}; + + +#endif // __JUCE_PLUGINDIRECTORYSCANNER_JUCEHEADER__ diff --git a/src/juce_appframework/audio/plugins/juce_PluginListComponent.cpp b/src/juce_appframework/audio/plugins/juce_PluginListComponent.cpp new file mode 100644 index 0000000000..24e2c42025 --- /dev/null +++ b/src/juce_appframework/audio/plugins/juce_PluginListComponent.cpp @@ -0,0 +1,308 @@ +/* + ============================================================================== + + This file is part of the JUCE library - "Jules' Utility Class Extensions" + Copyright 2004-7 by Raw Material Software ltd. + + ------------------------------------------------------------------------------ + + JUCE can be redistributed and/or modified under the terms of the + GNU General Public License, as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + JUCE is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with JUCE; if not, visit www.gnu.org/licenses or write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------------ + + If you'd like to release a closed-source product which uses JUCE, commercial + licenses are also available: visit www.rawmaterialsoftware.com/juce for + more information. + + ============================================================================== +*/ + +#include "../../../juce_core/basics/juce_StandardHeader.h" + +BEGIN_JUCE_NAMESPACE + +#include "juce_PluginListComponent.h" +#include "juce_PluginDirectoryScanner.h" +#include "../../gui/components/controls/juce_TextEditor.h" +#include "../../gui/components/windows/juce_AlertWindow.h" +#include "../../gui/graphics/fonts/juce_GlyphArrangement.h" +#include "../../gui/components/filebrowser/juce_FileSearchPathListComponent.h" +#include "../../../juce_core/text/juce_LocalisedStrings.h" +#include "../../events/juce_MessageManager.h" + + +//============================================================================== +PluginListComponent::PluginListComponent (KnownPluginList& listToEdit, + const File& deadMansPedalFile_, + PropertiesFile* const propertiesToUse_) + : list (listToEdit), + deadMansPedalFile (deadMansPedalFile_), + propertiesToUse (propertiesToUse_) +{ + addAndMakeVisible (listBox = new ListBox (String::empty, this)); + + addAndMakeVisible (optionsButton = new TextButton ("Options...")); + optionsButton->addButtonListener (this); + optionsButton->setTriggeredOnMouseDown (true); + + setSize (400, 600); + list.addChangeListener (this); +} + +PluginListComponent::~PluginListComponent() +{ + list.removeChangeListener (this); + deleteAllChildren(); +} + +void PluginListComponent::resized() +{ + listBox->setBounds (0, 0, getWidth(), getHeight() - 30); + optionsButton->changeWidthToFitText (24); + optionsButton->setTopLeftPosition (8, getHeight() - 28); +} + +void PluginListComponent::changeListenerCallback (void*) +{ + listBox->updateContent(); + listBox->repaint(); +} + +int PluginListComponent::getNumRows() +{ + return list.getNumTypes(); +} + +void PluginListComponent::paintListBoxItem (int row, + Graphics& g, + int width, int height, + bool rowIsSelected) +{ + if (rowIsSelected) + g.fillAll (findColour (TextEditor::highlightColourId)); + + const PluginDescription* const pd = list.getType (row); + + if (pd != 0) + { + GlyphArrangement ga; + ga.addCurtailedLineOfText (Font (height * 0.7f, Font::bold), pd->name, 8.0f, height * 0.8f, width - 10.0f, true); + + g.setColour (Colours::black); + ga.draw (g); + + float x, y, r, b; + ga.getBoundingBox (0, -1, x, y, r, b, false); + + String desc; + desc << pd->pluginFormatName + << (pd->isInstrument ? " instrument" : " effect") + << " - " + << pd->numInputChannels << (pd->numInputChannels == 1 ? " in" : " ins") + << " / " + << pd->numOutputChannels << (pd->numOutputChannels == 1 ? " out" : " outs"); + + if (pd->manufacturerName.isNotEmpty()) + desc << " - " << pd->manufacturerName; + + if (pd->version.isNotEmpty()) + desc << " - " << pd->version; + + if (pd->category.isNotEmpty()) + desc << " - category: '" << pd->category << '\''; + + g.setColour (Colours::grey); + + ga.clear(); + ga.addCurtailedLineOfText (Font (height * 0.6f), desc, r + 10.0f, height * 0.8f, width - r - 12.0f, true); + ga.draw (g); + } +} + +void PluginListComponent::deleteKeyPressed (int lastRowSelected) +{ + list.removeType (lastRowSelected); +} + +void PluginListComponent::buttonClicked (Button* b) +{ + if (optionsButton == b) + { + PopupMenu menu; + menu.addItem (1, TRANS("Clear list")); + menu.addItem (5, TRANS("Remove selected plugin from list"), listBox->getNumSelectedRows() > 0); + menu.addItem (6, TRANS("Show folder containing selected plugin"), listBox->getNumSelectedRows() > 0); + menu.addItem (7, TRANS("Remove any plugins whose files no longer exist")); + menu.addSeparator(); + menu.addItem (2, TRANS("Sort alphabetically")); + menu.addItem (3, TRANS("Sort by category")); + menu.addItem (4, TRANS("Sort by manufacturer")); + menu.addSeparator(); + + for (int i = 0; i < AudioPluginFormatManager::getInstance()->getNumFormats(); ++i) + { + AudioPluginFormat* const format = AudioPluginFormatManager::getInstance()->getFormat (i); + + if (format->getDefaultLocationsToSearch().getNumPaths() > 0) + menu.addItem (10 + i, "Scan for new or updated " + format->getName() + " plugins..."); + } + + const int r = menu.showAt (optionsButton); + + if (r == 1) + { + list.clear(); + } + else if (r == 2) + { + list.sort (KnownPluginList::sortAlphabetically); + } + else if (r == 3) + { + list.sort (KnownPluginList::sortByCategory); + } + else if (r == 4) + { + list.sort (KnownPluginList::sortByManufacturer); + } + else if (r == 5) + { + const SparseSet selected (listBox->getSelectedRows()); + + for (int i = list.getNumTypes(); --i >= 0;) + if (selected.contains (i)) + list.removeType (i); + } + else if (r == 6) + { + const PluginDescription* const desc = list.getType (listBox->getSelectedRow()); + + if (desc != 0) + desc->file.getParentDirectory().startAsProcess(); + } + else if (r == 7) + { + for (int i = list.getNumTypes(); --i >= 0;) + { + if (list.getType (i)->file != File::nonexistent + && ! list.getType (i)->file.exists()) + { + list.removeType (i); + } + } + } + else if (r != 0) + { + typeToScan = r - 10; + startTimer (1); + } + } +} + +void PluginListComponent::timerCallback() +{ + stopTimer(); + scanFor (AudioPluginFormatManager::getInstance()->getFormat (typeToScan)); +} + +bool PluginListComponent::isInterestedInFileDrag (const StringArray& /*files*/) +{ + return true; +} + +void PluginListComponent::filesDropped (const StringArray& files, int, int) +{ + OwnedArray typesFound; + list.scanAndAddDragAndDroppedFiles (files, typesFound); +} + +void PluginListComponent::scanFor (AudioPluginFormat* format) +{ + if (format == 0) + return; + + FileSearchPath path (format->getDefaultLocationsToSearch()); + + if (propertiesToUse != 0) + path = propertiesToUse->getValue ("lastPluginScanPath_" + format->getName(), path.toString()); + + { + AlertWindow aw (TRANS("Select folders to scan..."), String::empty, AlertWindow::NoIcon); + FileSearchPathListComponent pathList; + pathList.setSize (500, 300); + pathList.setPath (path); + + aw.addCustomComponent (&pathList); + aw.addButton (TRANS("Scan"), 1, KeyPress::returnKey); + aw.addButton (TRANS("Cancel"), 0, KeyPress (KeyPress::escapeKey)); + + if (aw.runModalLoop() == 0) + return; + + path = pathList.getPath(); + } + + if (propertiesToUse != 0) + { + propertiesToUse->setValue ("lastPluginScanPath_" + format->getName(), path.toString()); + propertiesToUse->saveIfNeeded(); + } + + double progress = 0.0; + + AlertWindow aw (TRANS("Scanning for plugins..."), + TRANS("Searching for all possible plugin files..."), AlertWindow::NoIcon); + + aw.addButton (TRANS("Cancel"), 0, KeyPress (KeyPress::escapeKey)); + aw.addProgressBarComponent (progress); + + aw.enterModalState(); + + MessageManager::getInstance()->dispatchPendingMessages(); + + PluginDirectoryScanner scanner (list, *format, path, true, deadMansPedalFile); + + for (;;) + { + aw.setMessage (TRANS("Testing:\n\n") + + scanner.getNextPluginFileThatWillBeScanned().getFileName()); + + MessageManager::getInstance()->dispatchPendingMessages (500); + + if (! scanner.scanNextFile (true)) + break; + + if (! aw.isCurrentlyModal()) + break; + + progress = scanner.getProgress(); + } + + if (scanner.getFailedFiles().size() > 0) + { + StringArray shortNames; + + for (int i = 0; i < scanner.getFailedFiles().size(); ++i) + shortNames.add (File (scanner.getFailedFiles()[i]).getFileName()); + + AlertWindow::showMessageBox (AlertWindow::InfoIcon, + TRANS("Scan complete"), + TRANS("Note that the following files appeared to be plugin files, but failed to load correctly:\n\n") + + shortNames.joinIntoString (", ")); + } +} + + +END_JUCE_NAMESPACE diff --git a/src/juce_appframework/audio/plugins/juce_PluginListComponent.h b/src/juce_appframework/audio/plugins/juce_PluginListComponent.h new file mode 100644 index 0000000000..1441e973a0 --- /dev/null +++ b/src/juce_appframework/audio/plugins/juce_PluginListComponent.h @@ -0,0 +1,107 @@ +/* + ============================================================================== + + This file is part of the JUCE library - "Jules' Utility Class Extensions" + Copyright 2004-7 by Raw Material Software ltd. + + ------------------------------------------------------------------------------ + + JUCE can be redistributed and/or modified under the terms of the + GNU General Public License, as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + JUCE is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with JUCE; if not, visit www.gnu.org/licenses or write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------------ + + If you'd like to release a closed-source product which uses JUCE, commercial + licenses are also available: visit www.rawmaterialsoftware.com/juce for + more information. + + ============================================================================== +*/ + +#ifndef __JUCE_PLUGINLISTCOMPONENT_JUCEHEADER__ +#define __JUCE_PLUGINLISTCOMPONENT_JUCEHEADER__ + +#include "juce_KnownPluginList.h" +#include "juce_AudioPluginFormat.h" +#include "../../gui/components/controls/juce_ListBox.h" +#include "../../gui/components/buttons/juce_TextButton.h" +#include "../../application/juce_PropertiesFile.h" + + +//============================================================================== +/** + A component displaying a list of plugins, with options to scan for them, + add, remove and sort them. +*/ +class JUCE_API PluginListComponent : public Component, + public ListBoxModel, + public ChangeListener, + public ButtonListener, + public Timer +{ +public: + //============================================================================== + /** + Creates the list component. + + For info about the deadMansPedalFile, see the PluginDirectoryScanner constructor. + + The properties file, if supplied, is used to store the user's last search paths. + */ + PluginListComponent (KnownPluginList& listToRepresent, + const File& deadMansPedalFile, + PropertiesFile* const propertiesToUse); + + /** Destructor. */ + ~PluginListComponent(); + + //============================================================================== + /** @internal */ + void resized(); + /** @internal */ + bool isInterestedInFileDrag (const StringArray& files); + /** @internal */ + void filesDropped (const StringArray& files, int, int); + /** @internal */ + int getNumRows(); + /** @internal */ + void paintListBoxItem (int row, Graphics& g, int width, int height, bool rowIsSelected); + /** @internal */ + void deleteKeyPressed (int lastRowSelected); + /** @internal */ + void buttonClicked (Button* b); + /** @internal */ + void changeListenerCallback (void*); + /** @internal */ + void timerCallback(); + + //============================================================================== + juce_UseDebuggingNewOperator + +private: + KnownPluginList& list; + File deadMansPedalFile; + ListBox* listBox; + TextButton* optionsButton; + PropertiesFile* propertiesToUse; + int typeToScan; + + void scanFor (AudioPluginFormat* format); + + PluginListComponent (const PluginListComponent&); + const PluginListComponent& operator= (const PluginListComponent&); +}; + + +#endif // __JUCE_PLUGINLISTCOMPONENT_JUCEHEADER__ diff --git a/src/juce_appframework/audio/processors/juce_AudioProcessor.h b/src/juce_appframework/audio/processors/juce_AudioProcessor.h index ab94ad38a6..4af3489f60 100644 --- a/src/juce_appframework/audio/processors/juce_AudioProcessor.h +++ b/src/juce_appframework/audio/processors/juce_AudioProcessor.h @@ -69,6 +69,11 @@ public: /** Destructor. */ virtual ~AudioProcessor(); + //============================================================================== + /** Returns the name of this processor. + */ + virtual const String getName() const = 0; + //============================================================================== /** Called before playback starts, to let the filter prepare itself. @@ -229,6 +234,12 @@ public: */ void setLatencySamples (const int newLatency); + /** Returns true if the processor wants midi messages. */ + virtual bool acceptsMidi() const = 0; + + /** Returns true if the processor produces midi messages. */ + virtual bool producesMidi() const = 0; + //============================================================================== /** This returns a critical section that will automatically be locked while the host is calling the processBlock() method. @@ -535,6 +546,9 @@ private: #ifdef JUCE_DEBUG BitArray changingParams; #endif + + AudioProcessor (const AudioProcessor&); + const AudioProcessor& operator= (const AudioProcessor&); }; diff --git a/src/juce_appframework/audio/processors/juce_AudioProcessorGraph.cpp b/src/juce_appframework/audio/processors/juce_AudioProcessorGraph.cpp new file mode 100644 index 0000000000..9db2742b12 --- /dev/null +++ b/src/juce_appframework/audio/processors/juce_AudioProcessorGraph.cpp @@ -0,0 +1,1340 @@ +/* + ============================================================================== + + This file is part of the JUCE library - "Jules' Utility Class Extensions" + Copyright 2004-7 by Raw Material Software ltd. + + ------------------------------------------------------------------------------ + + JUCE can be redistributed and/or modified under the terms of the + GNU General Public License, as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + JUCE is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with JUCE; if not, visit www.gnu.org/licenses or write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------------ + + If you'd like to release a closed-source product which uses JUCE, commercial + licenses are also available: visit www.rawmaterialsoftware.com/juce for + more information. + + ============================================================================== +*/ + +#include "../../../juce_core/basics/juce_StandardHeader.h" + +BEGIN_JUCE_NAMESPACE + +#include "juce_AudioProcessorGraph.h" +#include "../../events/juce_MessageManager.h" + + +const int AudioProcessorGraph::midiChannelIndex = 0x1000; + +//============================================================================== +AudioProcessorGraph::Node::Node (const uint32 id_, + AudioProcessor* const processor_) throw() + : id (id_), + processor (processor_), + isPrepared (false) +{ + jassert (processor_ != 0); +} + +AudioProcessorGraph::Node::~Node() +{ + delete processor; +} + +void AudioProcessorGraph::Node::prepare (const double sampleRate, const int blockSize, + AudioProcessorGraph* const graph) +{ + if (! isPrepared) + { + isPrepared = true; + + AudioProcessorGraph::AudioGraphIOProcessor* const ioProc + = dynamic_cast (processor); + + if (ioProc != 0) + ioProc->setParentGraph (graph); + + processor->setPlayConfigDetails (processor->getNumInputChannels(), + processor->getNumOutputChannels(), + sampleRate, blockSize); + + processor->prepareToPlay (sampleRate, blockSize); + } +} + +void AudioProcessorGraph::Node::unprepare() +{ + if (isPrepared) + { + isPrepared = false; + processor->releaseResources(); + } +} + +//============================================================================== +AudioProcessorGraph::AudioProcessorGraph() + : lastNodeId (0), + renderingBuffers (1, 1) +{ +} + +AudioProcessorGraph::~AudioProcessorGraph() +{ + clearRenderingSequence(); + clear(); +} + +const String AudioProcessorGraph::getName() const +{ + return "Audio Graph"; +} + +//============================================================================== +void AudioProcessorGraph::clear() +{ + nodes.clear(); + connections.clear(); + triggerAsyncUpdate(); +} + +AudioProcessorGraph::Node* AudioProcessorGraph::getNodeForId (const uint32 nodeId) const throw() +{ + for (int i = nodes.size(); --i >= 0;) + if (nodes.getUnchecked(i)->id == nodeId) + return nodes.getUnchecked(i); + + return 0; +} + +AudioProcessorGraph::Node* AudioProcessorGraph::addNode (AudioProcessor* const newProcessor, + uint32 nodeId) +{ + if (newProcessor == 0) + { + jassertfalse + return 0; + } + + if (nodeId == 0) + { + nodeId = ++lastNodeId; + } + else + { + // you can't add a node with an id that already exists in the graph.. + jassert (getNodeForId (nodeId) == 0); + removeNode (nodeId); + } + + lastNodeId = nodeId; + + Node* const n = new Node (nodeId, newProcessor); + nodes.add (n); + triggerAsyncUpdate(); + + AudioProcessorGraph::AudioGraphIOProcessor* const ioProc + = dynamic_cast (n->processor); + + if (ioProc != 0) + ioProc->setParentGraph (this); + + return n; +} + +bool AudioProcessorGraph::removeNode (const uint32 nodeId) +{ + disconnectNode (nodeId); + + for (int i = nodes.size(); --i >= 0;) + { + if (nodes.getUnchecked(i)->id == nodeId) + { + AudioProcessorGraph::AudioGraphIOProcessor* const ioProc + = dynamic_cast (nodes.getUnchecked(i)->processor); + + if (ioProc != 0) + ioProc->setParentGraph (0); + + nodes.remove (i); + triggerAsyncUpdate(); + + return true; + } + } + + return false; +} + +//============================================================================== +const AudioProcessorGraph::Connection* AudioProcessorGraph::getConnectionBetween (const uint32 sourceNodeId, + const int sourceChannelIndex, + const uint32 destNodeId, + const int destChannelIndex) const throw() +{ + for (int i = connections.size(); --i >= 0;) + { + const Connection* const c = connections.getUnchecked(i); + + if (c->sourceNodeId == sourceNodeId + && c->destNodeId == destNodeId + && c->sourceChannelIndex == sourceChannelIndex + && c->destChannelIndex == destChannelIndex) + { + return c; + } + } + + return 0; +} + +bool AudioProcessorGraph::isConnected (const uint32 possibleSourceNodeId, + const uint32 possibleDestNodeId) const throw() +{ + for (int i = connections.size(); --i >= 0;) + { + const Connection* const c = connections.getUnchecked(i); + + if (c->sourceNodeId == possibleSourceNodeId + && c->destNodeId == possibleDestNodeId) + { + return true; + } + } + + return false; +} + +bool AudioProcessorGraph::canConnect (const uint32 sourceNodeId, + const int sourceChannelIndex, + const uint32 destNodeId, + const int destChannelIndex) const throw() +{ + if (sourceChannelIndex < 0 + || destChannelIndex < 0 + || sourceNodeId == destNodeId + || (destChannelIndex == midiChannelIndex) != (sourceChannelIndex == midiChannelIndex)) + return false; + + const Node* const source = getNodeForId (sourceNodeId); + + if (source == 0 + || (sourceChannelIndex != midiChannelIndex && sourceChannelIndex >= source->processor->getNumOutputChannels()) + || (sourceChannelIndex == midiChannelIndex && ! source->processor->producesMidi())) + return false; + + const Node* const dest = getNodeForId (destNodeId); + + if (dest == 0 + || (destChannelIndex != midiChannelIndex && destChannelIndex >= dest->processor->getNumInputChannels()) + || (destChannelIndex == midiChannelIndex && ! dest->processor->acceptsMidi())) + return false; + + return getConnectionBetween (sourceNodeId, sourceChannelIndex, + destNodeId, destChannelIndex) == 0; +} + +bool AudioProcessorGraph::addConnection (const uint32 sourceNodeId, + const int sourceChannelIndex, + const uint32 destNodeId, + const int destChannelIndex) +{ + if (! canConnect (sourceNodeId, sourceChannelIndex, destNodeId, destChannelIndex)) + return false; + + Connection* const c = new Connection(); + c->sourceNodeId = sourceNodeId; + c->sourceChannelIndex = sourceChannelIndex; + c->destNodeId = destNodeId; + c->destChannelIndex = destChannelIndex; + + connections.add (c); + triggerAsyncUpdate(); + + return true; +} + +void AudioProcessorGraph::removeConnection (const int index) +{ + connections.remove (index); + triggerAsyncUpdate(); +} + +bool AudioProcessorGraph::removeConnection (const uint32 sourceNodeId, const int sourceChannelIndex, + const uint32 destNodeId, const int destChannelIndex) +{ + bool doneAnything = false; + + for (int i = connections.size(); --i >= 0;) + { + const Connection* const c = connections.getUnchecked(i); + + if (c->sourceNodeId == sourceNodeId + && c->destNodeId == destNodeId + && c->sourceChannelIndex == sourceChannelIndex + && c->destChannelIndex == destChannelIndex) + { + removeConnection (i); + doneAnything = true; + triggerAsyncUpdate(); + } + } + + return doneAnything; +} + +bool AudioProcessorGraph::disconnectNode (const uint32 nodeId) +{ + bool doneAnything = false; + + for (int i = connections.size(); --i >= 0;) + { + const Connection* const c = connections.getUnchecked(i); + + if (c->sourceNodeId == nodeId || c->destNodeId == nodeId) + { + removeConnection (i); + doneAnything = true; + triggerAsyncUpdate(); + } + } + + return doneAnything; +} + +bool AudioProcessorGraph::removeIllegalConnections() +{ + bool doneAnything = false; + + for (int i = connections.size(); --i >= 0;) + { + const Connection* const c = connections.getUnchecked(i); + + const Node* const source = getNodeForId (c->sourceNodeId); + const Node* const dest = getNodeForId (c->destNodeId); + + if (source == 0 || dest == 0 + || (c->sourceChannelIndex != midiChannelIndex + && (((unsigned int) c->sourceChannelIndex) >= (unsigned int) source->processor->getNumOutputChannels())) + || (c->sourceChannelIndex == midiChannelIndex + && ! source->processor->producesMidi()) + || (c->destChannelIndex != midiChannelIndex + && (((unsigned int) c->destChannelIndex) >= (unsigned int) dest->processor->getNumInputChannels())) + || (c->destChannelIndex == midiChannelIndex + && ! dest->processor->acceptsMidi())) + { + removeConnection (i); + doneAnything = true; + triggerAsyncUpdate(); + } + } + + return doneAnything; +} + +//============================================================================== +namespace GraphRenderingOps +{ + +//============================================================================== +class AudioGraphRenderingOp +{ +public: + AudioGraphRenderingOp() throw() {} + virtual ~AudioGraphRenderingOp() throw() {} + + virtual void perform (AudioSampleBuffer& sharedBufferChans, + const OwnedArray & sharedMidiBuffers, + const int numSamples) throw() = 0; + + juce_UseDebuggingNewOperator +}; + +//============================================================================== +class ClearChannelOp : public AudioGraphRenderingOp +{ +public: + ClearChannelOp (const int channelNum_) throw() + : channelNum (channelNum_) + {} + + ~ClearChannelOp() throw() {} + + void perform (AudioSampleBuffer& sharedBufferChans, const OwnedArray &, const int numSamples) throw() + { + sharedBufferChans.clear (channelNum, 0, numSamples); + } + +private: + const int channelNum; + + ClearChannelOp (const ClearChannelOp&); + const ClearChannelOp& operator= (const ClearChannelOp&); +}; + +//============================================================================== +class CopyChannelOp : public AudioGraphRenderingOp +{ +public: + CopyChannelOp (const int srcChannelNum_, const int dstChannelNum_) throw() + : srcChannelNum (srcChannelNum_), + dstChannelNum (dstChannelNum_) + {} + + ~CopyChannelOp() throw() {} + + void perform (AudioSampleBuffer& sharedBufferChans, const OwnedArray &, const int numSamples) throw() + { + sharedBufferChans.copyFrom (dstChannelNum, 0, sharedBufferChans, srcChannelNum, 0, numSamples); + } + +private: + const int srcChannelNum, dstChannelNum; + + CopyChannelOp (const CopyChannelOp&); + const CopyChannelOp& operator= (const CopyChannelOp&); +}; + +//============================================================================== +class AddChannelOp : public AudioGraphRenderingOp +{ +public: + AddChannelOp (const int srcChannelNum_, const int dstChannelNum_) throw() + : srcChannelNum (srcChannelNum_), + dstChannelNum (dstChannelNum_) + {} + + ~AddChannelOp() throw() {} + + void perform (AudioSampleBuffer& sharedBufferChans, + const OwnedArray & sharedMidiBuffers, + const int numSamples) throw() + { + if (dstChannelNum != AudioProcessorGraph::midiChannelIndex) + sharedBufferChans.addFrom (dstChannelNum, 0, sharedBufferChans, srcChannelNum, 0, numSamples); + else + sharedMidiBuffers.getUnchecked (dstChannelNum) + ->addEvents (*sharedMidiBuffers.getUnchecked (srcChannelNum), + 0, numSamples, 0); + } + +private: + const int srcChannelNum, dstChannelNum; + + AddChannelOp (const AddChannelOp&); + const AddChannelOp& operator= (const AddChannelOp&); +}; + +//============================================================================== +class ClearMidiBufferOp : public AudioGraphRenderingOp +{ +public: + ClearMidiBufferOp (const int bufferNum_) throw() + : bufferNum (bufferNum_) + {} + + ~ClearMidiBufferOp() throw() {} + + void perform (AudioSampleBuffer&, const OwnedArray & sharedMidiBuffers, const int) throw() + { + sharedMidiBuffers.getUnchecked (bufferNum)->clear(); + } + +private: + const int bufferNum; + + ClearMidiBufferOp (const ClearMidiBufferOp&); + const ClearMidiBufferOp& operator= (const ClearMidiBufferOp&); +}; + +//============================================================================== +class CopyMidiBufferOp : public AudioGraphRenderingOp +{ +public: + CopyMidiBufferOp (const int srcBufferNum_, const int dstBufferNum_) throw() + : srcBufferNum (srcBufferNum_), + dstBufferNum (dstBufferNum_) + {} + + ~CopyMidiBufferOp() throw() {} + + void perform (AudioSampleBuffer&, const OwnedArray & sharedMidiBuffers, const int) throw() + { + *sharedMidiBuffers.getUnchecked (dstBufferNum) = *sharedMidiBuffers.getUnchecked (srcBufferNum); + } + +private: + const int srcBufferNum, dstBufferNum; + + CopyMidiBufferOp (const CopyMidiBufferOp&); + const CopyMidiBufferOp& operator= (const CopyMidiBufferOp&); +}; + +//============================================================================== +class AddMidiBufferOp : public AudioGraphRenderingOp +{ +public: + AddMidiBufferOp (const int srcBufferNum_, const int dstBufferNum_) throw() + : srcBufferNum (srcBufferNum_), + dstBufferNum (dstBufferNum_) + {} + + ~AddMidiBufferOp() throw() {} + + void perform (AudioSampleBuffer&, const OwnedArray & sharedMidiBuffers, const int numSamples) throw() + { + sharedMidiBuffers.getUnchecked (dstBufferNum) + ->addEvents (*sharedMidiBuffers.getUnchecked (srcBufferNum), 0, numSamples, 0); + } + +private: + const int srcBufferNum, dstBufferNum; + + AddMidiBufferOp (const AddMidiBufferOp&); + const AddMidiBufferOp& operator= (const AddMidiBufferOp&); +}; + +//============================================================================== +class ProcessBufferOp : public AudioGraphRenderingOp +{ +public: + ProcessBufferOp (const AudioProcessorGraph::Node::Ptr& node_, + const Array & audioChannelsToUse_, + const int totalChans_, + const int midiBufferToUse_) throw() + : node (node_), + processor (node_->processor), + audioChannelsToUse (audioChannelsToUse_), + totalChans (totalChans_), + midiBufferToUse (midiBufferToUse_) + { + channels = (float**) juce_calloc (sizeof (float*) * totalChans_); + } + + ~ProcessBufferOp() throw() + { + juce_free (channels); + } + + void perform (AudioSampleBuffer& sharedBufferChans, const OwnedArray & sharedMidiBuffers, const int numSamples) throw() + { + for (int i = totalChans; --i >= 0;) + channels[i] = sharedBufferChans.getSampleData (audioChannelsToUse.getUnchecked (i), 0); + + AudioSampleBuffer buffer (channels, totalChans, numSamples); + + processor->processBlock (buffer, *sharedMidiBuffers.getUnchecked (midiBufferToUse)); + } + + const AudioProcessorGraph::Node::Ptr node; + AudioProcessor* const processor; + +private: + Array audioChannelsToUse; + float** channels; + int totalChans; + int midiBufferToUse; + + ProcessBufferOp (const ProcessBufferOp&); + const ProcessBufferOp& operator= (const ProcessBufferOp&); +}; + +//============================================================================== +/** Used to calculate the correct sequence of rendering ops needed, based on + the best re-use of shared buffers at each stage. +*/ +class RenderingOpSequenceCalculator +{ +public: + //============================================================================== + RenderingOpSequenceCalculator (AudioProcessorGraph& graph_, + const VoidArray& orderedNodes_, + VoidArray& renderingOps) + : graph (graph_), + orderedNodes (orderedNodes_) + { + nodeIds.add (-2); // first buffer is read-only zeros + channels.add (0); + + midiNodeIds.add (-2); + + for (int i = 0; i < orderedNodes.size(); ++i) + { + createRenderingOpsForNode ((AudioProcessorGraph::Node*) orderedNodes.getUnchecked(i), + renderingOps, i); + + markAnyUnusedBuffersAsFree (i); + } + } + + int getNumBuffersNeeded() const throw() { return nodeIds.size(); } + + int getNumMidiBuffersNeeded() const throw() { return midiNodeIds.size(); } + + + //============================================================================== + juce_UseDebuggingNewOperator + +private: + AudioProcessorGraph& graph; + const VoidArray& orderedNodes; + Array nodeIds, channels, midiNodeIds; + + //============================================================================== + void createRenderingOpsForNode (AudioProcessorGraph::Node* const node, + VoidArray& renderingOps, + const int ourRenderingIndex) + { + const int numIns = node->processor->getNumInputChannels(); + const int numOuts = node->processor->getNumOutputChannels(); + const int totalChans = jmax (numIns, numOuts); + + Array audioChannelsToUse; + int midiBufferToUse = -1; + + for (int inputChan = 0; inputChan < numIns; ++inputChan) + { + // get a list of all the inputs to this node + Array sourceNodes, sourceOutputChans; + + for (int i = graph.getNumConnections(); --i >= 0;) + { + const AudioProcessorGraph::Connection* const c = graph.getConnection (i); + + if (c->destNodeId == node->id && c->destChannelIndex == inputChan) + { + sourceNodes.add (c->sourceNodeId); + sourceOutputChans.add (c->sourceChannelIndex); + } + } + + int bufIndex = -1; + + if (sourceNodes.size() == 0) + { + // unconnected input channel + + if (inputChan >= numOuts) + { + bufIndex = getReadOnlyEmptyBuffer(); + jassert (bufIndex >= 0); + } + else + { + bufIndex = getFreeBuffer (false); + renderingOps.add (new ClearChannelOp (bufIndex)); + } + } + else if (sourceNodes.size() == 1) + { + // channel with a straightforward single input.. + const int srcNode = sourceNodes.getUnchecked(0); + const int srcChan = sourceOutputChans.getUnchecked(0); + + bufIndex = getBufferContaining (srcNode, srcChan); + jassert (bufIndex >= 0); + + if (inputChan < numOuts + && isBufferNeededLater (ourRenderingIndex, + inputChan, + srcNode, srcChan)) + { + // can't mess up this channel because it's needed later by another node, so we + // need to use a copy of it.. + const int newFreeBuffer = getFreeBuffer (false); + + renderingOps.add (new CopyChannelOp (bufIndex, newFreeBuffer)); + + bufIndex = newFreeBuffer; + } + } + else + { + // channel with a mix of several inputs.. + + // try to find a re-usable channel from our inputs.. + int reusableInputIndex = -1; + + for (int i = 0; i < sourceNodes.size(); ++i) + { + const int sourceBufIndex = getBufferContaining (sourceNodes.getUnchecked(i), + sourceOutputChans.getUnchecked(i)); + + jassert (sourceBufIndex >= 0); + + if (! isBufferNeededLater (ourRenderingIndex, + inputChan, + sourceNodes.getUnchecked(i), + sourceOutputChans.getUnchecked(i))) + { + // we've found one of our input chans that can be re-used.. + reusableInputIndex = i; + bufIndex = sourceBufIndex; + break; + } + } + + if (reusableInputIndex < 0) + { + // can't re-use any of our input chans, so get a new one and copy everything into it.. + bufIndex = getFreeBuffer (false); + jassert (bufIndex != 0); + + const int srcIndex = getBufferContaining (sourceNodes.getUnchecked (0), + sourceOutputChans.getUnchecked (0)); + jassert (srcIndex >= 0); + + renderingOps.add (new CopyChannelOp (srcIndex, bufIndex)); + + reusableInputIndex = 0; + } + + for (int j = 0; j < sourceNodes.size(); ++j) + { + if (j != reusableInputIndex) + { + const int srcIndex = getBufferContaining (sourceNodes.getUnchecked(j), + sourceOutputChans.getUnchecked(j)); + jassert (srcIndex >= 0); + + renderingOps.add (new AddChannelOp (srcIndex, bufIndex)); + } + } + } + + jassert (bufIndex >= 0); + audioChannelsToUse.add (bufIndex); + + if (inputChan < numOuts) + markBufferAsContaining (bufIndex, node->id, inputChan); + } + + for (int outputChan = numIns; outputChan < numOuts; ++outputChan) + { + const int bufIndex = getFreeBuffer (false); + jassert (bufIndex != 0); + audioChannelsToUse.add (bufIndex); + + markBufferAsContaining (bufIndex, node->id, outputChan); + } + + // Now the same thing for midi.. + Array midiSourceNodes; + + for (int i = graph.getNumConnections(); --i >= 0;) + { + const AudioProcessorGraph::Connection* const c = graph.getConnection (i); + + if (c->destNodeId == node->id && c->destChannelIndex == AudioProcessorGraph::midiChannelIndex) + midiSourceNodes.add (c->sourceNodeId); + } + + if (midiSourceNodes.size() == 0) + { + // No midi inputs.. + midiBufferToUse = getFreeBuffer (true); // need to pick a buffer even if the processor doesn't use midi + + if (node->processor->acceptsMidi() || node->processor->producesMidi()) + renderingOps.add (new ClearMidiBufferOp (midiBufferToUse)); + } + else if (midiSourceNodes.size() == 1) + { + // One midi input.. + midiBufferToUse = getBufferContaining (midiSourceNodes.getUnchecked(0), + AudioProcessorGraph::midiChannelIndex); + + jassert (midiBufferToUse >= 0); + + if (midiBufferToUse >= 0) + { + if (isBufferNeededLater (ourRenderingIndex, + AudioProcessorGraph::midiChannelIndex, + midiSourceNodes.getUnchecked(0), + AudioProcessorGraph::midiChannelIndex)) + { + // can't mess up this channel because it's needed later by another node, so we + // need to use a copy of it.. + const int newFreeBuffer = getFreeBuffer (true); + renderingOps.add (new CopyMidiBufferOp (midiBufferToUse, newFreeBuffer)); + midiBufferToUse = newFreeBuffer; + } + } + } + else + { + // More than one midi input being mixed.. + int reusableInputIndex = -1; + + for (int i = 0; i < midiSourceNodes.size(); ++i) + { + const int sourceBufIndex = getBufferContaining (midiSourceNodes.getUnchecked(i), + AudioProcessorGraph::midiChannelIndex); + + jassert (sourceBufIndex >= 0); + + if (! isBufferNeededLater (ourRenderingIndex, + AudioProcessorGraph::midiChannelIndex, + midiSourceNodes.getUnchecked(i), + AudioProcessorGraph::midiChannelIndex)) + { + // we've found one of our input buffers that can be re-used.. + reusableInputIndex = i; + midiBufferToUse = sourceBufIndex; + break; + } + } + + if (reusableInputIndex < 0) + { + // can't re-use any of our input buffers, so get a new one and copy everything into it.. + midiBufferToUse = getFreeBuffer (true); + jassert (midiBufferToUse >= 0); + + const int srcIndex = getBufferContaining (midiSourceNodes.getUnchecked(0), + AudioProcessorGraph::midiChannelIndex); + jassert (srcIndex >= 0); + + renderingOps.add (new CopyMidiBufferOp (srcIndex, midiBufferToUse)); + + reusableInputIndex = 0; + } + + for (int j = 0; j < midiSourceNodes.size(); ++j) + { + if (j != reusableInputIndex) + { + const int srcIndex = getBufferContaining (midiSourceNodes.getUnchecked(j), + AudioProcessorGraph::midiChannelIndex); + jassert (srcIndex >= 0); + + renderingOps.add (new AddMidiBufferOp (srcIndex, midiBufferToUse)); + } + } + } + + if (node->processor->producesMidi()) + markBufferAsContaining (midiBufferToUse, node->id, + AudioProcessorGraph::midiChannelIndex); + + renderingOps.add (new ProcessBufferOp (node, audioChannelsToUse, + totalChans, midiBufferToUse)); + } + + //============================================================================== + int getFreeBuffer (const bool forMidi) + { + if (forMidi) + { + for (int i = 1; i < midiNodeIds.size(); ++i) + if (midiNodeIds.getUnchecked(i) < 0) + return i; + + midiNodeIds.add (-1); + return midiNodeIds.size() - 1; + } + else + { + for (int i = 1; i < nodeIds.size(); ++i) + if (nodeIds.getUnchecked(i) < 0) + return i; + + nodeIds.add (-1); + channels.add (0); + return nodeIds.size() - 1; + } + } + + int getReadOnlyEmptyBuffer() const throw() + { + return 0; + } + + int getBufferContaining (const int nodeId, const int outputChannel) const throw() + { + if (outputChannel == AudioProcessorGraph::midiChannelIndex) + { + for (int i = midiNodeIds.size(); --i >= 0;) + if (midiNodeIds.getUnchecked(i) == nodeId) + return i; + } + else + { + for (int i = nodeIds.size(); --i >= 0;) + if (nodeIds.getUnchecked(i) == nodeId + && channels.getUnchecked(i) == outputChannel) + return i; + } + + return -1; + } + + void markAnyUnusedBuffersAsFree (const int stepIndex) + { + int i; + for (i = 0; i < nodeIds.size(); ++i) + { + if (nodeIds.getUnchecked(i) >= 0 + && ! isBufferNeededLater (stepIndex, -1, + nodeIds.getUnchecked(i), + channels.getUnchecked(i))) + { + nodeIds.set (i, -1); + } + } + + for (i = 0; i < midiNodeIds.size(); ++i) + { + if (midiNodeIds.getUnchecked(i) >= 0 + && ! isBufferNeededLater (stepIndex, -1, + midiNodeIds.getUnchecked(i), + AudioProcessorGraph::midiChannelIndex)) + { + midiNodeIds.set (i, -1); + } + } + } + + bool isBufferNeededLater (int stepIndexToSearchFrom, + int inputChannelOfIndexToIgnore, + const int nodeId, + const int outputChanIndex) const throw() + { + while (stepIndexToSearchFrom < orderedNodes.size()) + { + const AudioProcessorGraph::Node* const node = (const AudioProcessorGraph::Node*) orderedNodes.getUnchecked (stepIndexToSearchFrom); + + if (outputChanIndex == AudioProcessorGraph::midiChannelIndex) + { + if (inputChannelOfIndexToIgnore != AudioProcessorGraph::midiChannelIndex + && graph.getConnectionBetween (nodeId, AudioProcessorGraph::midiChannelIndex, + node->id, AudioProcessorGraph::midiChannelIndex) != 0) + return true; + } + else + { + for (int i = 0; i < node->processor->getNumInputChannels(); ++i) + if (i != inputChannelOfIndexToIgnore + && graph.getConnectionBetween (nodeId, outputChanIndex, + node->id, i) != 0) + return true; + } + + inputChannelOfIndexToIgnore = -1; + ++stepIndexToSearchFrom; + } + + return false; + } + + void markBufferAsContaining (int bufferNum, int nodeId, int outputIndex) + { + if (outputIndex == AudioProcessorGraph::midiChannelIndex) + { + jassert (bufferNum > 0 && bufferNum < midiNodeIds.size()); + + midiNodeIds.set (bufferNum, nodeId); + } + else + { + jassert (bufferNum > 0 && bufferNum < nodeIds.size()); + + nodeIds.set (bufferNum, nodeId); + channels.set (bufferNum, outputIndex); + } + } + + RenderingOpSequenceCalculator (const RenderingOpSequenceCalculator&); + const RenderingOpSequenceCalculator& operator= (const RenderingOpSequenceCalculator&); +}; + +} + +//============================================================================== +void AudioProcessorGraph::clearRenderingSequence() +{ + const ScopedLock sl (renderLock); + + for (int i = renderingOps.size(); --i >= 0;) + { + GraphRenderingOps::AudioGraphRenderingOp* const r + = (GraphRenderingOps::AudioGraphRenderingOp*) renderingOps.getUnchecked(i); + + renderingOps.remove (i); + delete r; + } +} + +bool AudioProcessorGraph::isAnInputTo (const uint32 possibleInputId, + const uint32 possibleDestinationId, + const int recursionCheck) const throw() +{ + if (recursionCheck > 0) + { + for (int i = connections.size(); --i >= 0;) + { + const AudioProcessorGraph::Connection* const c = connections.getUnchecked (i); + + if (c->destNodeId == possibleDestinationId + && (c->sourceNodeId == possibleInputId + || isAnInputTo (possibleInputId, c->sourceNodeId, recursionCheck - 1))) + return true; + } + } + + return false; +} + +void AudioProcessorGraph::buildRenderingSequence() +{ + VoidArray newRenderingOps; + int numRenderingBuffersNeeded = 2; + int numMidiBuffersNeeded = 1; + + { + MessageManagerLock mml; + + VoidArray orderedNodes; + + int i; + for (i = 0; i < nodes.size(); ++i) + { + Node* const node = nodes.getUnchecked(i); + + node->prepare (getSampleRate(), getBlockSize(), this); + + int j = 0; + for (; j < orderedNodes.size(); ++j) + if (isAnInputTo (node->id, + ((Node*) orderedNodes.getUnchecked (j))->id, + nodes.size() + 1)) + break; + + orderedNodes.insert (j, node); + } + + GraphRenderingOps::RenderingOpSequenceCalculator calculator (*this, orderedNodes, newRenderingOps); + + numRenderingBuffersNeeded = calculator.getNumBuffersNeeded(); + numMidiBuffersNeeded = calculator.getNumMidiBuffersNeeded(); + } + + VoidArray oldRenderingOps (renderingOps); + + { + // swap over to the new set of rendering sequence.. + const ScopedLock sl (renderLock); + + renderingBuffers.setSize (numRenderingBuffersNeeded, getBlockSize()); + renderingBuffers.clear(); + + for (int i = midiBuffers.size(); --i >= 0;) + midiBuffers.getUnchecked(i)->clear(); + + while (midiBuffers.size() < numMidiBuffersNeeded) + midiBuffers.add (new MidiBuffer()); + + renderingOps = newRenderingOps; + } + + for (int i = oldRenderingOps.size(); --i >= 0;) + delete (GraphRenderingOps::AudioGraphRenderingOp*) oldRenderingOps.getUnchecked(i); +} + +void AudioProcessorGraph::handleAsyncUpdate() +{ + buildRenderingSequence(); +} + +//============================================================================== +void AudioProcessorGraph::prepareToPlay (double /*sampleRate*/, int /*estimatedSamplesPerBlock*/) +{ + currentAudioIOBuffer = 0; + currentMidiIOBuffer = 0; + + clearRenderingSequence(); + buildRenderingSequence(); +} + +void AudioProcessorGraph::releaseResources() +{ + for (int i = 0; i < nodes.size(); ++i) + nodes.getUnchecked(i)->unprepare(); + + renderingBuffers.setSize (1, 1); + midiBuffers.clear(); + + currentAudioIOBuffer = 0; + currentMidiIOBuffer = 0; +} + +void AudioProcessorGraph::processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages) +{ + const ScopedLock sl (renderLock); + + currentAudioIOBuffer = &buffer; + currentMidiIOBuffer = &midiMessages; + + const int numSamples = buffer.getNumSamples(); + + for (int i = 0; i < renderingOps.size(); ++i) + { + GraphRenderingOps::AudioGraphRenderingOp* const op + = (GraphRenderingOps::AudioGraphRenderingOp*) renderingOps.getUnchecked(i); + + op->perform (renderingBuffers, midiBuffers, numSamples); + } +} + +const String AudioProcessorGraph::getInputChannelName (const int channelIndex) const +{ + return "Input " + String (channelIndex + 1); +} + +const String AudioProcessorGraph::getOutputChannelName (const int channelIndex) const +{ + return "Output " + String (channelIndex + 1); +} + +bool AudioProcessorGraph::isInputChannelStereoPair (int /*index*/) const +{ + return true; +} + +bool AudioProcessorGraph::isOutputChannelStereoPair (int /*index*/) const +{ + return true; +} + +bool AudioProcessorGraph::acceptsMidi() const +{ + return true; +} + +bool AudioProcessorGraph::producesMidi() const +{ + return true; +} + +void AudioProcessorGraph::getStateInformation (JUCE_NAMESPACE::MemoryBlock& /*destData*/) +{ +} + +void AudioProcessorGraph::setStateInformation (const void* /*data*/, int /*sizeInBytes*/) +{ +} + + +//============================================================================== +AudioProcessorGraph::AudioGraphIOProcessor::AudioGraphIOProcessor (const IODeviceType type_) + : type (type_), + graph (0) +{ +} + +AudioProcessorGraph::AudioGraphIOProcessor::~AudioGraphIOProcessor() +{ +} + +const String AudioProcessorGraph::AudioGraphIOProcessor::getName() const +{ + switch (type) + { + case audioOutputNode: + return "Audio Output"; + case audioInputNode: + return "Audio Input"; + case midiOutputNode: + return "Midi Output"; + case midiInputNode: + return "Midi Input"; + default: + break; + } + + return String::empty; +} + +void AudioProcessorGraph::AudioGraphIOProcessor::fillInPluginDescription (PluginDescription& d) const +{ + d.name = getName(); + d.uid = d.name.hashCode(); + d.category = "I/O devices"; + d.pluginFormatName = "Internal"; + d.manufacturerName = "Raw Material Software"; + d.version = "1.0"; + d.isInstrument = false; + + d.numInputChannels = getNumInputChannels(); + if (type == audioOutputNode && graph != 0) + d.numInputChannels = graph->getNumInputChannels(); + + d.numOutputChannels = getNumOutputChannels(); + if (type == audioInputNode && graph != 0) + d.numOutputChannels = graph->getNumOutputChannels(); +} + +void AudioProcessorGraph::AudioGraphIOProcessor::prepareToPlay (double, int) +{ + jassert (graph != 0); +} + +void AudioProcessorGraph::AudioGraphIOProcessor::releaseResources() +{ +} + +void AudioProcessorGraph::AudioGraphIOProcessor::processBlock (AudioSampleBuffer& buffer, + MidiBuffer& midiMessages) +{ + jassert (graph != 0); + + switch (type) + { + case audioOutputNode: + { + for (int i = jmin (graph->currentAudioIOBuffer->getNumChannels(), + buffer.getNumChannels()); --i >= 0;) + { + graph->currentAudioIOBuffer->addFrom (i, 0, buffer, i, 0, buffer.getNumSamples()); + } + + break; + } + + case audioInputNode: + for (int i = jmin (graph->currentAudioIOBuffer->getNumChannels(), + buffer.getNumChannels()); --i >= 0;) + { + buffer.addFrom (i, 0, *graph->currentAudioIOBuffer, i, 0, buffer.getNumSamples()); + } + + break; + + case midiOutputNode: + graph->currentMidiIOBuffer->addEvents (midiMessages, 0, buffer.getNumSamples(), 0); + break; + + case midiInputNode: + midiMessages.addEvents (*graph->currentMidiIOBuffer, 0, buffer.getNumSamples(), 0); + break; + + default: + break; + } +} + +bool AudioProcessorGraph::AudioGraphIOProcessor::acceptsMidi() const +{ + return type == midiOutputNode; +} + +bool AudioProcessorGraph::AudioGraphIOProcessor::producesMidi() const +{ + return type == midiInputNode; +} + +const String AudioProcessorGraph::AudioGraphIOProcessor::getInputChannelName (const int channelIndex) const +{ + switch (type) + { + case audioOutputNode: + return "Output " + String (channelIndex + 1); + case midiOutputNode: + return "Midi Output"; + default: + break; + } + + return String::empty; +} + +const String AudioProcessorGraph::AudioGraphIOProcessor::getOutputChannelName (const int channelIndex) const +{ + switch (type) + { + case audioInputNode: + return "Input " + String (channelIndex + 1); + case midiInputNode: + return "Midi Input"; + default: + break; + } + + return String::empty; +} + +bool AudioProcessorGraph::AudioGraphIOProcessor::isInputChannelStereoPair (int /*index*/) const +{ + return type == audioInputNode || type == audioOutputNode; +} + +bool AudioProcessorGraph::AudioGraphIOProcessor::isOutputChannelStereoPair (int index) const +{ + return isInputChannelStereoPair (index); +} + +bool AudioProcessorGraph::AudioGraphIOProcessor::isInput() const throw() +{ + return type == audioInputNode || type == midiInputNode; +} + +bool AudioProcessorGraph::AudioGraphIOProcessor::isOutput() const throw() +{ + return type == audioOutputNode || type == midiOutputNode; +} + +AudioProcessorEditor* AudioProcessorGraph::AudioGraphIOProcessor::createEditor() +{ + return 0; +} + +int AudioProcessorGraph::AudioGraphIOProcessor::getNumParameters() { return 0; } +const String AudioProcessorGraph::AudioGraphIOProcessor::getParameterName (int) { return String::empty; } + +float AudioProcessorGraph::AudioGraphIOProcessor::getParameter (int) { return 0.0f; } +const String AudioProcessorGraph::AudioGraphIOProcessor::getParameterText (int) { return String::empty; } +void AudioProcessorGraph::AudioGraphIOProcessor::setParameter (int, float) { } + +int AudioProcessorGraph::AudioGraphIOProcessor::getNumPrograms() { return 0; } +int AudioProcessorGraph::AudioGraphIOProcessor::getCurrentProgram() { return 0; } +void AudioProcessorGraph::AudioGraphIOProcessor::setCurrentProgram (int) { } + +const String AudioProcessorGraph::AudioGraphIOProcessor::getProgramName (int) { return String::empty; } +void AudioProcessorGraph::AudioGraphIOProcessor::changeProgramName (int, const String&) { } + +void AudioProcessorGraph::AudioGraphIOProcessor::getStateInformation (JUCE_NAMESPACE::MemoryBlock&) +{ +} + +void AudioProcessorGraph::AudioGraphIOProcessor::setStateInformation (const void*, int) +{ +} + +void AudioProcessorGraph::AudioGraphIOProcessor::setParentGraph (AudioProcessorGraph* const newGraph) throw() +{ + graph = newGraph; + + if (graph != 0) + { + setPlayConfigDetails (type == audioOutputNode ? graph->getNumInputChannels() : 0, + type == audioInputNode ? graph->getNumOutputChannels() : 0, + getSampleRate(), + getBlockSize()); + + updateHostDisplay(); + } +} + + +END_JUCE_NAMESPACE diff --git a/src/juce_appframework/audio/processors/juce_AudioProcessorGraph.h b/src/juce_appframework/audio/processors/juce_AudioProcessorGraph.h new file mode 100644 index 0000000000..20c12fdbbc --- /dev/null +++ b/src/juce_appframework/audio/processors/juce_AudioProcessorGraph.h @@ -0,0 +1,440 @@ +/* + ============================================================================== + + This file is part of the JUCE library - "Jules' Utility Class Extensions" + Copyright 2004-7 by Raw Material Software ltd. + + ------------------------------------------------------------------------------ + + JUCE can be redistributed and/or modified under the terms of the + GNU General Public License, as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + JUCE is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with JUCE; if not, visit www.gnu.org/licenses or write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------------ + + If you'd like to release a closed-source product which uses JUCE, commercial + licenses are also available: visit www.rawmaterialsoftware.com/juce for + more information. + + ============================================================================== +*/ + +#ifndef __JUCE_AUDIOPROCESSORGRAPH_JUCEHEADER__ +#define __JUCE_AUDIOPROCESSORGRAPH_JUCEHEADER__ + +#include "juce_AudioProcessor.h" +#include "../plugins/juce_AudioPluginFormatManager.h" +#include "../plugins/juce_KnownPluginList.h" +#include "../../../juce_core/containers/juce_ReferenceCountedArray.h" + + +//============================================================================== +/** + A type of AudioProcessor which plays back a graph of other AudioProcessors. + + Use one of these objects if you want to wire-up a set of AudioProcessors + and play back the result. + + Processors can be added to the graph as "nodes" using addNode(), and once + added, you can connect any of their input or output channels to other + nodes using addConnection(). + + To play back a graph through an audio device, you might want to use an + AudioProcessorPlayer object. +*/ +class JUCE_API AudioProcessorGraph : public AudioProcessor, + public AsyncUpdater +{ +public: + //============================================================================== + /** Creates an empty graph. + */ + AudioProcessorGraph(); + + /** Destructor. + + Any processor objects that have been added to the graph will also be deleted. + */ + ~AudioProcessorGraph(); + + //============================================================================== + /** Represents one of the nodes, or processors, in an AudioProcessorGraph. + + To create a node, call AudioProcessorGraph::addNode(). + */ + class Node : public ReferenceCountedObject + { + public: + /** Destructor. + */ + ~Node(); + + //============================================================================== + /** The ID number assigned to this node. + + This is assigned by the graph that owns it, and can't be changed. + */ + const uint32 id; + + /** The actual processor object that this node represents. + */ + AudioProcessor* const processor; + + /** A set of user-definable properties that are associated with this node. + + This can be used to attach values to the node for whatever purpose seems + useful. For example, you might store an x and y position if your application + is displaying the nodes on-screen. + */ + PropertySet properties; + + //============================================================================== + /** A convenient typedef for referring to a pointer to a node object. + */ + typedef ReferenceCountedObjectPtr Ptr; + + //============================================================================== + juce_UseDebuggingNewOperator + + private: + friend class AudioProcessorGraph; + + bool isPrepared; + + Node (const uint32 id, AudioProcessor* const processor) throw(); + + void prepare (const double sampleRate, const int blockSize, AudioProcessorGraph* const graph); + void unprepare(); + + Node (const Node&); + const Node& operator= (const Node&); + }; + + //============================================================================== + /** Represents a connection between two channels of two nodes in an AudioProcessorGraph. + + To create a connection, use AudioProcessorGraph::addConnection(). + */ + struct Connection + { + public: + //============================================================================== + /** The ID number of the node which is the input source for this connection. + @see AudioProcessorGraph::getNodeForId + */ + uint32 sourceNodeId; + + /** The index of the output channel of the source node from which this + connection takes its data. + + If this value is the special number AudioProcessorGraph::midiChannelIndex, then + it is referring to the source node's midi output. Otherwise, it is the zero-based + index of an audio output channel in the source node. + */ + int sourceChannelIndex; + + /** The ID number of the node which is the destination for this connection. + @see AudioProcessorGraph::getNodeForId + */ + uint32 destNodeId; + + /** The index of the input channel of the destination node to which this + connection delivers its data. + + If this value is the special number AudioProcessorGraph::midiChannelIndex, then + it is referring to the destination node's midi input. Otherwise, it is the zero-based + index of an audio input channel in the destination node. + */ + int destChannelIndex; + + //============================================================================== + juce_UseDebuggingNewOperator + + private: + }; + + //============================================================================== + /** Deletes all nodes and connections from this graph. + + Any processor objects in the graph will be deleted. + */ + void clear(); + + /** Returns the number of nodes in the graph. */ + int getNumNodes() const throw() { return nodes.size(); } + + /** Returns a pointer to one of the nodes in the graph. + + This will return 0 if the index is out of range. + @see getNodeForId + */ + Node* getNode (const int index) const throw() { return nodes [index]; } + + /** Searches the graph for a node with the given ID number and returns it. + + If no such node was found, this returns 0. + @see getNode + */ + Node* getNodeForId (const uint32 nodeId) const throw(); + + /** Adds a node to the graph. + + This creates a new node in the graph, for the specified processor. Once you have + added a processor to the graph, the graph owns it and will delete it later when + it is no longer needed. + + The optional nodeId parameter lets you specify an ID to use for the node, but + if the value is already in use, this new node will overwrite the old one. + + If this succeeds, it returns a pointer to the newly-created node. + */ + Node* addNode (AudioProcessor* const newProcessor, + uint32 nodeId = 0); + + /** Deletes a node within the graph which has the specified ID. + + This will also delete any connections that are attached to this node. + */ + bool removeNode (const uint32 nodeId); + + //============================================================================== + /** Returns the number of connections in the graph. */ + int getNumConnections() const throw() { return connections.size(); } + + /** Returns a pointer to one of the connections in the graph. */ + const Connection* getConnection (const int index) const throw() { return connections [index]; } + + /** Searches for a connection between some specified channels. + + If no such connection is found, this returns 0. + */ + const Connection* getConnectionBetween (const uint32 sourceNodeId, + const int sourceChannelIndex, + const uint32 destNodeId, + const int destChannelIndex) const throw(); + + /** Returns true if there is a connection between any of the channels of + two specified nodes. + */ + bool isConnected (const uint32 possibleSourceNodeId, + const uint32 possibleDestNodeId) const throw(); + + /** Returns true if it would be legal to connect the specified points. + */ + bool canConnect (const uint32 sourceNodeId, const int sourceChannelIndex, + const uint32 destNodeId, const int destChannelIndex) const throw(); + + /** Attempts to connect two specified channels of two nodes. + + If this isn't allowed (e.g. because you're trying to connect a midi channel + to an audio one or other such nonsense), then it'll return 0. + */ + bool addConnection (const uint32 sourceNodeId, const int sourceChannelIndex, + const uint32 destNodeId, const int destChannelIndex); + + /** Deletes the connection with the specified index. + + Returns true if a connection was actually deleted. + */ + void removeConnection (const int index); + + /** Deletes any connection between two specified points. + + Returns true if a connection was actually deleted. + */ + bool removeConnection (const uint32 sourceNodeId, const int sourceChannelIndex, + const uint32 destNodeId, const int destChannelIndex); + + /** Removes all connections from the specified node. + */ + bool disconnectNode (const uint32 nodeId); + + /** Performs a sanity checks of all the connections. + + This might be useful if some of the processors are doing things like changing + their channel counts, which could render some connections obsolete. + */ + bool removeIllegalConnections(); + + //============================================================================== + /** A special number that represents the midi channel of a node. + + This is used as a channel index value if you want to refer to the midi input + or output instead of an audio channel. + */ + static const int midiChannelIndex; + + + //============================================================================== + /** A special type of AudioProcessor that can live inside an AudioProcessorGraph + in order to use the audio that comes into and out of the graph itself. + + If you create an AudioGraphIOProcessor in "input" mode, it will act as a + node in the graph which delivers the audio that is coming into the parent + graph. This allows you to stream the data to other nodes and process the + incoming audio. + + Likewise, one of these in "output" mode can be sent data which it will add to + the sum of data being sent to the graph's output. + + @see AudioProcessorGraph + */ + class AudioGraphIOProcessor : public AudioPluginInstance + { + public: + /** Specifies the mode in which this processor will operate. + */ + enum IODeviceType + { + audioInputNode, /**< In this mode, the processor has output channels + representing all the audio input channels that are + coming into its parent audio graph. */ + audioOutputNode, /**< In this mode, the processor has input channels + representing all the audio output channels that are + going out of its parent audio graph. */ + midiInputNode, /**< In this mode, the processor has a midi output which + delivers the same midi data that is arriving at its + parent graph. */ + midiOutputNode /**< In this mode, the processor has a midi input and + any data sent to it will be passed out of the parent + graph. */ + }; + + //============================================================================== + /** Returns the mode of this processor. */ + IODeviceType getType() const throw() { return type; } + + /** Returns the parent graph to which this processor belongs, or 0 if it + hasn't yet been added to one. */ + AudioProcessorGraph* getParentGraph() const throw() { return graph; } + + /** True if this is an audio or midi input. */ + bool isInput() const throw(); + /** True if this is an audio or midi output. */ + bool isOutput() const throw(); + + //============================================================================== + AudioGraphIOProcessor (const IODeviceType type); + ~AudioGraphIOProcessor(); + + const String getName() const; + void fillInPluginDescription (PluginDescription& d) const; + + void prepareToPlay (double sampleRate, int estimatedSamplesPerBlock); + void releaseResources(); + void processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages); + + const String getInputChannelName (const int channelIndex) const; + const String getOutputChannelName (const int channelIndex) const; + bool isInputChannelStereoPair (int index) const; + bool isOutputChannelStereoPair (int index) const; + bool acceptsMidi() const; + bool producesMidi() const; + + AudioProcessorEditor* createEditor(); + + int getNumParameters(); + const String getParameterName (int); + float getParameter (int); + const String getParameterText (int); + void setParameter (int, float); + + int getNumPrograms(); + int getCurrentProgram(); + void setCurrentProgram (int); + const String getProgramName (int); + void changeProgramName (int, const String&); + + void getStateInformation (JUCE_NAMESPACE::MemoryBlock& destData); + void setStateInformation (const void* data, int sizeInBytes); + + /** @internal */ + void setParentGraph (AudioProcessorGraph* const graph) throw(); + + juce_UseDebuggingNewOperator + + private: + const IODeviceType type; + AudioProcessorGraph* graph; + + AudioGraphIOProcessor (const AudioGraphIOProcessor&); + const AudioGraphIOProcessor& operator= (const AudioGraphIOProcessor&); + }; + + //============================================================================== + // AudioProcessor methods: + + const String getName() const; + + void prepareToPlay (double sampleRate, int estimatedSamplesPerBlock); + void releaseResources(); + void processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages); + + const String getInputChannelName (const int channelIndex) const; + const String getOutputChannelName (const int channelIndex) const; + bool isInputChannelStereoPair (int index) const; + bool isOutputChannelStereoPair (int index) const; + + bool acceptsMidi() const; + bool producesMidi() const; + + AudioProcessorEditor* createEditor() { return 0; } + + int getNumParameters() { return 0; } + const String getParameterName (int) { return String::empty; } + float getParameter (int) { return 0; } + const String getParameterText (int) { return String::empty; } + void setParameter (int, float) { } + + int getNumPrograms() { return 0; } + int getCurrentProgram() { return 0; } + void setCurrentProgram (int) { } + const String getProgramName (int) { return String::empty; } + void changeProgramName (int, const String&) { } + + void getStateInformation (JUCE_NAMESPACE::MemoryBlock& destData); + void setStateInformation (const void* data, int sizeInBytes); + + /** @internal */ + void handleAsyncUpdate(); + + //============================================================================== + juce_UseDebuggingNewOperator + +private: + ReferenceCountedArray nodes; + OwnedArray connections; + int lastNodeId; + AudioSampleBuffer renderingBuffers; + OwnedArray midiBuffers; + + CriticalSection renderLock; + VoidArray renderingOps; + + friend class AudioGraphIOProcessor; + AudioSampleBuffer* currentAudioIOBuffer; + MidiBuffer* currentMidiIOBuffer; + + void clearRenderingSequence(); + void buildRenderingSequence(); + + bool isAnInputTo (const uint32 possibleInputId, + const uint32 possibleDestinationId, + const int recursionCheck) const throw(); + + AudioProcessorGraph (const AudioProcessorGraph&); + const AudioProcessorGraph& operator= (const AudioProcessorGraph&); +}; + + +#endif // __JUCE_AUDIOPROCESSORGRAPH_JUCEHEADER__ diff --git a/src/juce_appframework/audio/processors/juce_AudioProcessorPlayer.cpp b/src/juce_appframework/audio/processors/juce_AudioProcessorPlayer.cpp new file mode 100644 index 0000000000..254c762860 --- /dev/null +++ b/src/juce_appframework/audio/processors/juce_AudioProcessorPlayer.cpp @@ -0,0 +1,207 @@ +/* + ============================================================================== + + This file is part of the JUCE library - "Jules' Utility Class Extensions" + Copyright 2004-7 by Raw Material Software ltd. + + ------------------------------------------------------------------------------ + + JUCE can be redistributed and/or modified under the terms of the + GNU General Public License, as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + JUCE is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with JUCE; if not, visit www.gnu.org/licenses or write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------------ + + If you'd like to release a closed-source product which uses JUCE, commercial + licenses are also available: visit www.rawmaterialsoftware.com/juce for + more information. + + ============================================================================== +*/ + +#include "../../../juce_core/basics/juce_StandardHeader.h" + +BEGIN_JUCE_NAMESPACE + +#include "juce_AudioProcessorPlayer.h" +#include "../../../juce_core/threads/juce_ScopedLock.h" + + +//============================================================================== +AudioProcessorPlayer::AudioProcessorPlayer() + : processor (0), + sampleRate (0), + blockSize (0), + isPrepared (false), + numInputChans (0), + numOutputChans (0), + tempBuffer (1, 1) +{ +} + +AudioProcessorPlayer::~AudioProcessorPlayer() +{ + setProcessor (0); +} + +//============================================================================== +void AudioProcessorPlayer::setProcessor (AudioProcessor* const processorToPlay) +{ + if (processor != processorToPlay) + { + if (processorToPlay != 0 && sampleRate > 0 && blockSize > 0) + { + processorToPlay->setPlayConfigDetails (numInputChans, numOutputChans, + sampleRate, blockSize); + + processorToPlay->prepareToPlay (sampleRate, blockSize); + } + + lock.enter(); + AudioProcessor* const oldOne = isPrepared ? processor : 0; + processor = processorToPlay; + isPrepared = true; + lock.exit(); + + if (oldOne != 0) + oldOne->releaseResources(); + } +} + +//============================================================================== +void AudioProcessorPlayer::audioDeviceIOCallback (const float** inputChannelData, + int totalNumInputChannels, + float** outputChannelData, + int totalNumOutputChannels, + int numSamples) +{ + // these should have been prepared by audioDeviceAboutToStart()... + jassert (sampleRate > 0 && blockSize > 0); + + incomingMidi.clear(); + messageCollector.removeNextBlockOfMessages (incomingMidi, numSamples); + + int i, numActiveChans = 0, numInputs = 0, numOutputs = 0; + + // messy stuff needed to compact the channels down into an array + // of non-zero pointers.. + for (i = 0; i < totalNumInputChannels; ++i) + { + if (inputChannelData[i] != 0) + { + inputChans [numInputs++] = inputChannelData[i]; + if (numInputs >= numElementsInArray (inputChans)) + break; + } + } + + for (i = 0; i < totalNumOutputChannels; ++i) + { + if (outputChannelData[i] != 0) + { + outputChans [numOutputs++] = outputChannelData[i]; + if (numOutputs >= numElementsInArray (outputChans)) + break; + } + } + + if (numInputs > numOutputs) + { + // if there aren't enough output channels for the number of + // inputs, we need to create some temporary extra ones (can't + // use the input data in case it gets written to) + tempBuffer.setSize (numInputs - numOutputs, numSamples, + false, false, true); + + for (i = 0; i < numOutputs; ++i) + { + channels[numActiveChans] = outputChans[i]; + memcpy (channels[numActiveChans], inputChans[i], sizeof (float) * numSamples); + ++numActiveChans; + } + + for (i = numOutputs; i < numInputs; ++i) + { + channels[numActiveChans] = tempBuffer.getSampleData (i - numOutputs, 0); + memcpy (channels[numActiveChans], inputChans[i], sizeof (float) * numSamples); + ++numActiveChans; + } + } + else + { + for (i = 0; i < numInputs; ++i) + { + channels[numActiveChans] = outputChans[i]; + memcpy (channels[numActiveChans], inputChans[i], sizeof (float) * numSamples); + ++numActiveChans; + } + + for (i = numInputs; i < numOutputs; ++i) + { + channels[numActiveChans] = outputChans[i]; + zeromem (channels[numActiveChans], sizeof (float) * numSamples); + ++numActiveChans; + } + } + + AudioSampleBuffer buffer (channels, numActiveChans, numSamples); + + const ScopedLock sl (lock); + + if (processor != 0) + processor->processBlock (buffer, incomingMidi); +} + +void AudioProcessorPlayer::audioDeviceAboutToStart (AudioIODevice* device) +{ + const ScopedLock sl (lock); + + sampleRate = device->getCurrentSampleRate(); + blockSize = device->getCurrentBufferSizeSamples(); + numInputChans = device->getActiveInputChannels().countNumberOfSetBits(); + numOutputChans = device->getActiveOutputChannels().countNumberOfSetBits(); + + messageCollector.reset (sampleRate); + zeromem (channels, sizeof (channels)); + + if (processor != 0) + { + if (isPrepared) + processor->releaseResources(); + + AudioProcessor* const oldProcessor = processor; + setProcessor (0); + setProcessor (oldProcessor); + } +} + +void AudioProcessorPlayer::audioDeviceStopped() +{ + const ScopedLock sl (lock); + + if (processor != 0 && isPrepared) + processor->releaseResources(); + + sampleRate = 0.0; + blockSize = 0; + isPrepared = false; + tempBuffer.setSize (1, 1); +} + +void AudioProcessorPlayer::handleIncomingMidiMessage (MidiInput*, const MidiMessage& message) +{ + messageCollector.addMessageToQueue (message); +} + + +END_JUCE_NAMESPACE diff --git a/src/juce_appframework/audio/processors/juce_AudioProcessorPlayer.h b/src/juce_appframework/audio/processors/juce_AudioProcessorPlayer.h new file mode 100644 index 0000000000..cad3a5168b --- /dev/null +++ b/src/juce_appframework/audio/processors/juce_AudioProcessorPlayer.h @@ -0,0 +1,121 @@ +/* + ============================================================================== + + This file is part of the JUCE library - "Jules' Utility Class Extensions" + Copyright 2004-7 by Raw Material Software ltd. + + ------------------------------------------------------------------------------ + + JUCE can be redistributed and/or modified under the terms of the + GNU General Public License, as published by the Free Software Foundation; + either version 2 of the License, or (at your option) any later version. + + JUCE is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with JUCE; if not, visit www.gnu.org/licenses or write to the + Free Software Foundation, Inc., 59 Temple Place, Suite 330, + Boston, MA 02111-1307 USA + + ------------------------------------------------------------------------------ + + If you'd like to release a closed-source product which uses JUCE, commercial + licenses are also available: visit www.rawmaterialsoftware.com/juce for + more information. + + ============================================================================== +*/ + +#ifndef __JUCE_AUDIOPROCESSORPLAYER_JUCEHEADER__ +#define __JUCE_AUDIOPROCESSORPLAYER_JUCEHEADER__ + +#include "juce_AudioProcessor.h" +#include "../devices/juce_AudioIODevice.h" +#include "../devices/juce_MidiInput.h" +#include "../midi/juce_MidiMessageCollector.h" + + +//============================================================================== +/** + An AudioIODeviceCallback object which streams audio through an AudioProcessor. + + To use one of these, just make it the callback used by your AudioIODevice, and + give it a processor to use by calling setProcessor(). + + It's also a MidiInputCallback, so you can connect it to both an audio and midi + input to send both streams through the processor. + + @see AudioProcessor, AudioProcessorGraph +*/ +class JUCE_API AudioProcessorPlayer : public AudioIODeviceCallback, + public MidiInputCallback +{ +public: + //============================================================================== + /** + */ + AudioProcessorPlayer(); + + /** Destructor. */ + virtual ~AudioProcessorPlayer(); + + //============================================================================== + /** Sets the processor that should be played. + + The processor that is passed in will not be deleted or owned by this object. + To stop anything playing, pass in 0 to this method. + */ + void setProcessor (AudioProcessor* const processorToPlay); + + /** Returns the current audio processor that is being played. + */ + AudioProcessor* getCurrentProcessor() const throw() { return processor; } + + /** Returns a midi message collector that you can pass midi messages to if you + want them to be injected into the midi stream that is being sent to the + processor. + */ + MidiMessageCollector& getMidiMessageCollector() throw() { return messageCollector; } + + //============================================================================== + /** @internal */ + void audioDeviceIOCallback (const float** inputChannelData, + int totalNumInputChannels, + float** outputChannelData, + int totalNumOutputChannels, + int numSamples); + /** @internal */ + void audioDeviceAboutToStart (AudioIODevice* device); + /** @internal */ + void audioDeviceStopped(); + /** @internal */ + void handleIncomingMidiMessage (MidiInput* source, const MidiMessage& message); + + //============================================================================== + juce_UseDebuggingNewOperator + +private: + AudioProcessor* processor; + CriticalSection lock; + double sampleRate; + int blockSize; + bool isPrepared; + + int numInputChans, numOutputChans; + float* channels [128]; + float* outputChans [128]; + const float* inputChans [128]; + AudioSampleBuffer tempBuffer; + + MidiBuffer incomingMidi; + MidiMessageCollector messageCollector; + + AudioProcessorPlayer (const AudioProcessorPlayer&); + const AudioProcessorPlayer& operator= (const AudioProcessorPlayer&); +}; + + +#endif // __JUCE_AUDIOPROCESSORPLAYER_JUCEHEADER__