diff --git a/Builds/MacOSX/Dexed.xcodeproj/project.pbxproj b/Builds/MacOSX/Dexed.xcodeproj/project.pbxproj index d6d8d51..ba6efd2 100644 --- a/Builds/MacOSX/Dexed.xcodeproj/project.pbxproj +++ b/Builds/MacOSX/Dexed.xcodeproj/project.pbxproj @@ -86,6 +86,7 @@ 0707C87B401DC983E3FF4263 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "juce_MathsFunctions.h"; path = "../../JuceLibraryCode/modules/juce_core/maths/juce_MathsFunctions.h"; sourceTree = "SOURCE_ROOT"; }; 0733471B6DA02299D2C9590A = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "juce_StringArray.cpp"; path = "../../JuceLibraryCode/modules/juce_core/text/juce_StringArray.cpp"; sourceTree = "SOURCE_ROOT"; }; 075BB641199B15A84856DE6E = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "juce_SliderPropertyComponent.cpp"; path = "../../JuceLibraryCode/modules/juce_gui_basics/properties/juce_SliderPropertyComponent.cpp"; sourceTree = "SOURCE_ROOT"; }; + 076C4F22CCC47AFEAC2D0C68 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "juce_LuaCodeTokeniser.h"; path = "../../JuceLibraryCode/modules/juce_gui_extra/code_editor/juce_LuaCodeTokeniser.h"; sourceTree = "SOURCE_ROOT"; }; 07CB27E064E2A82C2B1D3832 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "juce_AbstractFifo.h"; path = "../../JuceLibraryCode/modules/juce_core/containers/juce_AbstractFifo.h"; sourceTree = "SOURCE_ROOT"; }; 0A3CCDBDD4238A7F9CB559BD = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "juce_FilenameComponent.h"; path = "../../JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FilenameComponent.h"; sourceTree = "SOURCE_ROOT"; }; 0A55490419140DC6D2FBB1DA = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "juce_ComboBox.cpp"; path = "../../JuceLibraryCode/modules/juce_gui_basics/widgets/juce_ComboBox.cpp"; sourceTree = "SOURCE_ROOT"; }; @@ -129,6 +130,7 @@ 17C3024C2A1EE03BC0ED9C96 = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = "juce_mac_Windowing.mm"; path = "../../JuceLibraryCode/modules/juce_gui_basics/native/juce_mac_Windowing.mm"; sourceTree = "SOURCE_ROOT"; }; 17ED00953353016B7E7492B0 = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "juce_ApplicationCommandInfo.cpp"; path = "../../JuceLibraryCode/modules/juce_gui_basics/commands/juce_ApplicationCommandInfo.cpp"; sourceTree = "SOURCE_ROOT"; }; 18AD1662D33E576F96C30A6A = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "juce_TopLevelWindow.cpp"; path = "../../JuceLibraryCode/modules/juce_gui_basics/windows/juce_TopLevelWindow.cpp"; sourceTree = "SOURCE_ROOT"; }; + 18D52C793029AFCC92C77A75 = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "juce_LiveConstantEditor.cpp"; path = "../../JuceLibraryCode/modules/juce_gui_extra/misc/juce_LiveConstantEditor.cpp"; sourceTree = "SOURCE_ROOT"; }; 1914E45DEAEBD1592581F1FD = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "juce_AudioPluginFormatManager.h"; path = "../../JuceLibraryCode/modules/juce_audio_processors/format/juce_AudioPluginFormatManager.h"; sourceTree = "SOURCE_ROOT"; }; 195B617C1FF754108A97F645 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "juce_KeyPressMappingSet.h"; path = "../../JuceLibraryCode/modules/juce_gui_basics/commands/juce_KeyPressMappingSet.h"; sourceTree = "SOURCE_ROOT"; }; 19B56FF22ED0090A8D7A3E04 = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = "juce_gui_basics.mm"; path = "../../JuceLibraryCode/modules/juce_gui_basics/juce_gui_basics.mm"; sourceTree = "SOURCE_ROOT"; }; @@ -294,6 +296,7 @@ 45F6575A7BDB9134EAEA7633 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "juce_ActiveXControlComponent.h"; path = "../../JuceLibraryCode/modules/juce_gui_extra/embedding/juce_ActiveXControlComponent.h"; sourceTree = "SOURCE_ROOT"; }; 462B768DFC2129F54233D51D = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "juce_StretchableLayoutResizerBar.h"; path = "../../JuceLibraryCode/modules/juce_gui_basics/layout/juce_StretchableLayoutResizerBar.h"; sourceTree = "SOURCE_ROOT"; }; 46908987EEFC3623A53A95C2 = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "juce_LagrangeInterpolator.cpp"; path = "../../JuceLibraryCode/modules/juce_audio_basics/effects/juce_LagrangeInterpolator.cpp"; sourceTree = "SOURCE_ROOT"; }; + 46969E6B78BC89383358DCDA = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "juce_XMLCodeTokeniser.h"; path = "../../JuceLibraryCode/modules/juce_gui_extra/code_editor/juce_XMLCodeTokeniser.h"; sourceTree = "SOURCE_ROOT"; }; 46B8FB88F2949700DD70A821 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "juce_WildcardFileFilter.h"; path = "../../JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_WildcardFileFilter.h"; sourceTree = "SOURCE_ROOT"; }; 46C20298CCB469481F5C8D36 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "juce_MouseInactivityDetector.h"; path = "../../JuceLibraryCode/modules/juce_gui_basics/mouse/juce_MouseInactivityDetector.h"; sourceTree = "SOURCE_ROOT"; }; 4710CA869326390AF0CE2A0B = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "juce_ResizableEdgeComponent.h"; path = "../../JuceLibraryCode/modules/juce_gui_basics/layout/juce_ResizableEdgeComponent.h"; sourceTree = "SOURCE_ROOT"; }; @@ -398,6 +401,7 @@ 6D256DC39965C982ADAAFA81 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "juce_FillType.h"; path = "../../JuceLibraryCode/modules/juce_graphics/colour/juce_FillType.h"; sourceTree = "SOURCE_ROOT"; }; 6DC26B61BA1638AFAF142A6C = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = "juce_gui_extra.mm"; path = "../../JuceLibraryCode/modules/juce_gui_extra/juce_gui_extra.mm"; sourceTree = "SOURCE_ROOT"; }; 6DCB546B40A962FCA3C74DB9 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "juce_ColourGradient.h"; path = "../../JuceLibraryCode/modules/juce_graphics/colour/juce_ColourGradient.h"; sourceTree = "SOURCE_ROOT"; }; + 6DEFC761C7F27A8ED88790B4 = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "juce_XMLCodeTokeniser.cpp"; path = "../../JuceLibraryCode/modules/juce_gui_extra/code_editor/juce_XMLCodeTokeniser.cpp"; sourceTree = "SOURCE_ROOT"; }; 6E2A781F28B3F735F4FAB2A2 = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "juce_KeyMappingEditorComponent.cpp"; path = "../../JuceLibraryCode/modules/juce_gui_extra/misc/juce_KeyMappingEditorComponent.cpp"; sourceTree = "SOURCE_ROOT"; }; 6E84E300D2ECD62ED1ED3478 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "juce_FileInputSource.h"; path = "../../JuceLibraryCode/modules/juce_core/streams/juce_FileInputSource.h"; sourceTree = "SOURCE_ROOT"; }; 6E8FC799E88893F8CD15BB71 = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "juce_LADSPAPluginFormat.cpp"; path = "../../JuceLibraryCode/modules/juce_audio_processors/format_types/juce_LADSPAPluginFormat.cpp"; sourceTree = "SOURCE_ROOT"; }; @@ -838,6 +842,7 @@ F957420DFF4D2354671B4116 = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "juce_RectanglePlacement.cpp"; path = "../../JuceLibraryCode/modules/juce_graphics/placement/juce_RectanglePlacement.cpp"; sourceTree = "SOURCE_ROOT"; }; F9B29C9F01195D5A979AB5C9 = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "juce_SVGParser.cpp"; path = "../../JuceLibraryCode/modules/juce_gui_basics/drawables/juce_SVGParser.cpp"; sourceTree = "SOURCE_ROOT"; }; F9FA29A5FF9C9921D785A1C0 = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "juce_LookAndFeel_V1.cpp"; path = "../../JuceLibraryCode/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V1.cpp"; sourceTree = "SOURCE_ROOT"; }; + FA0406B777C1CB9C71F86BC1 = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "juce_LuaCodeTokeniser.cpp"; path = "../../JuceLibraryCode/modules/juce_gui_extra/code_editor/juce_LuaCodeTokeniser.cpp"; sourceTree = "SOURCE_ROOT"; }; FA726CE9275EF0E84BBEA666 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "juce_LinkedListPointer.h"; path = "../../JuceLibraryCode/modules/juce_core/containers/juce_LinkedListPointer.h"; sourceTree = "SOURCE_ROOT"; }; FA98BC06299525310A31107B = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "juce_Label.h"; path = "../../JuceLibraryCode/modules/juce_gui_basics/widgets/juce_Label.h"; sourceTree = "SOURCE_ROOT"; }; FAC5045BEAA6C0B1AC904BED = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "juce_TextDragAndDropTarget.h"; path = "../../JuceLibraryCode/modules/juce_gui_basics/mouse/juce_TextDragAndDropTarget.h"; sourceTree = "SOURCE_ROOT"; }; @@ -847,6 +852,7 @@ FC452B781AEE181BEF7F948E = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "juce_ComponentPeer.cpp"; path = "../../JuceLibraryCode/modules/juce_gui_basics/windows/juce_ComponentPeer.cpp"; sourceTree = "SOURCE_ROOT"; }; FC856709502EE15E8D3F448B = { isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOKit.framework; path = System/Library/Frameworks/IOKit.framework; sourceTree = SDKROOT; }; FCABD85F0480D4972896F379 = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = "juce_ReverbAudioSource.cpp"; path = "../../JuceLibraryCode/modules/juce_audio_basics/sources/juce_ReverbAudioSource.cpp"; sourceTree = "SOURCE_ROOT"; }; + FCBA692E842A80D9618CA467 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "juce_LiveConstantEditor.h"; path = "../../JuceLibraryCode/modules/juce_gui_extra/misc/juce_LiveConstantEditor.h"; sourceTree = "SOURCE_ROOT"; }; FD2285710D78FDBC856ADF13 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "juce_AudioProcessorPlayer.h"; path = "../../JuceLibraryCode/modules/juce_audio_utils/players/juce_AudioProcessorPlayer.h"; sourceTree = "SOURCE_ROOT"; }; FD333147C1339A81B846EC52 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "juce_RecentlyOpenedFilesList.h"; path = "../../JuceLibraryCode/modules/juce_gui_extra/misc/juce_RecentlyOpenedFilesList.h"; sourceTree = "SOURCE_ROOT"; }; FD799268DEA0EC4CDFD4DA10 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "juce_NewLine.h"; path = "../../JuceLibraryCode/modules/juce_core/text/juce_NewLine.h"; sourceTree = "SOURCE_ROOT"; }; @@ -1741,7 +1747,11 @@ 5DE3DC6998A92F718C9683FA, 71DFB6F3C44390C0FD109073, 7E68BB771E88E0A2A323D365, - 588255FD68989F1A03FDF31C ); name = "code_editor"; sourceTree = ""; }; + 588255FD68989F1A03FDF31C, + FA0406B777C1CB9C71F86BC1, + 076C4F22CCC47AFEAC2D0C68, + 6DEFC761C7F27A8ED88790B4, + 46969E6B78BC89383358DCDA ); name = "code_editor"; sourceTree = ""; }; 3BAE6D34CB0FFE32A18C6008 = { isa = PBXGroup; children = ( BAA44451865610A98B3A69AE, 346937AF08405CC63D570161 ); name = documents; sourceTree = ""; }; @@ -1757,6 +1767,8 @@ 4F977F1C295B0D355391AAD3, 6E2A781F28B3F735F4FAB2A2, A00DC4E59356AF5F1D9C02D1, + 18D52C793029AFCC92C77A75, + FCBA692E842A80D9618CA467, 5BFC8D75FFE4E8DEE50B3B1A, A062855D9DD17397012BC224, AE8321756C03700EB12FF98A, diff --git a/Builds/MacOSX/Dexed.xcodeproj/project.xcworkspace/xcuserdata/asb2m10.xcuserdatad/UserInterfaceState.xcuserstate b/Builds/MacOSX/Dexed.xcodeproj/project.xcworkspace/xcuserdata/asb2m10.xcuserdatad/UserInterfaceState.xcuserstate index c41b940..4bb9c08 100644 Binary files a/Builds/MacOSX/Dexed.xcodeproj/project.xcworkspace/xcuserdata/asb2m10.xcuserdatad/UserInterfaceState.xcuserstate and b/Builds/MacOSX/Dexed.xcodeproj/project.xcworkspace/xcuserdata/asb2m10.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/Builds/VisualStudio2012/Dexed.vcxproj b/Builds/VisualStudio2012/Dexed.vcxproj index 9ccec7a..e01b429 100644 --- a/Builds/VisualStudio2012/Dexed.vcxproj +++ b/Builds/VisualStudio2012/Dexed.vcxproj @@ -1036,6 +1036,12 @@ true + + true + + + true + true @@ -1048,6 +1054,9 @@ true + + true + true @@ -1502,6 +1511,8 @@ + + @@ -1510,6 +1521,7 @@ + diff --git a/Builds/VisualStudio2012/Dexed.vcxproj.filters b/Builds/VisualStudio2012/Dexed.vcxproj.filters index 3569e1b..f2259fd 100644 --- a/Builds/VisualStudio2012/Dexed.vcxproj.filters +++ b/Builds/VisualStudio2012/Dexed.vcxproj.filters @@ -1270,6 +1270,12 @@ Juce Modules\juce_gui_extra\code_editor + + Juce Modules\juce_gui_extra\code_editor + + + Juce Modules\juce_gui_extra\code_editor + Juce Modules\juce_gui_extra\documents @@ -1282,6 +1288,9 @@ Juce Modules\juce_gui_extra\misc + + Juce Modules\juce_gui_extra\misc + Juce Modules\juce_gui_extra\misc @@ -2556,6 +2565,12 @@ Juce Modules\juce_gui_extra\code_editor + + Juce Modules\juce_gui_extra\code_editor + + + Juce Modules\juce_gui_extra\code_editor + Juce Modules\juce_gui_extra\documents @@ -2580,6 +2595,9 @@ Juce Modules\juce_gui_extra\misc + + Juce Modules\juce_gui_extra\misc + Juce Modules\juce_gui_extra\misc diff --git a/Builds/VisualStudio2013/Dexed.vcxproj b/Builds/VisualStudio2013/Dexed.vcxproj index 0dc5f34..c74725b 100644 --- a/Builds/VisualStudio2013/Dexed.vcxproj +++ b/Builds/VisualStudio2013/Dexed.vcxproj @@ -1036,6 +1036,12 @@ true + + true + + + true + true @@ -1048,6 +1054,9 @@ true + + true + true @@ -1502,6 +1511,8 @@ + + @@ -1510,6 +1521,7 @@ + diff --git a/Builds/VisualStudio2013/Dexed.vcxproj.filters b/Builds/VisualStudio2013/Dexed.vcxproj.filters index ce5b2ed..56f2fec 100644 --- a/Builds/VisualStudio2013/Dexed.vcxproj.filters +++ b/Builds/VisualStudio2013/Dexed.vcxproj.filters @@ -1270,6 +1270,12 @@ Juce Modules\juce_gui_extra\code_editor + + Juce Modules\juce_gui_extra\code_editor + + + Juce Modules\juce_gui_extra\code_editor + Juce Modules\juce_gui_extra\documents @@ -1282,6 +1288,9 @@ Juce Modules\juce_gui_extra\misc + + Juce Modules\juce_gui_extra\misc + Juce Modules\juce_gui_extra\misc @@ -2556,6 +2565,12 @@ Juce Modules\juce_gui_extra\code_editor + + Juce Modules\juce_gui_extra\code_editor + + + Juce Modules\juce_gui_extra\code_editor + Juce Modules\juce_gui_extra\documents @@ -2580,6 +2595,9 @@ Juce Modules\juce_gui_extra\misc + + Juce Modules\juce_gui_extra\misc + Juce Modules\juce_gui_extra\misc diff --git a/JuceLibraryCode/AppConfig.h b/JuceLibraryCode/AppConfig.h index efdaf74..635bb80 100644 --- a/JuceLibraryCode/AppConfig.h +++ b/JuceLibraryCode/AppConfig.h @@ -168,6 +168,10 @@ //#define JUCE_WEB_BROWSER #endif +#ifndef JUCE_ENABLE_LIVE_CONSTANT_EDITOR + //#define JUCE_ENABLE_LIVE_CONSTANT_EDITOR +#endif + //============================================================================== // Audio plugin settings.. diff --git a/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.cpp b/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.cpp index 449e9ae..9dda414 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.cpp +++ b/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.cpp @@ -22,10 +22,13 @@ ============================================================================== */ -#if JUCE_USE_SSE_INTRINSICS - namespace FloatVectorHelpers { + + #define JUCE_INCREMENT_SRC_DEST dest += 4; src += 4; + #define JUCE_INCREMENT_DEST dest += 4; + + #if JUCE_USE_SSE_INTRINSICS static bool sse2Present = false; static bool isSSE2Available() noexcept @@ -44,7 +47,6 @@ namespace FloatVectorHelpers static inline float findMinimumOrMaximum (const float* src, int num, const bool isMinimum) noexcept { - #if JUCE_USE_SSE_INTRINSICS const int numLongOps = num / 4; if (numLongOps > 1 && FloatVectorHelpers::isSSE2Available()) @@ -90,66 +92,142 @@ namespace FloatVectorHelpers return localVal; } - #endif return isMinimum ? juce::findMinimum (src, num) : juce::findMaximum (src, num); } -} -#define JUCE_BEGIN_SSE_OP \ - if (FloatVectorHelpers::isSSE2Available()) \ - { \ + #define JUCE_BEGIN_SSE_OP \ + if (FloatVectorHelpers::isSSE2Available()) \ + { \ + const int numLongOps = num / 4; + + #define JUCE_FINISH_SSE_OP(normalOp) \ + num &= 3; \ + if (num == 0) return; \ + } \ + for (int i = 0; i < num; ++i) normalOp; + + #define JUCE_SSE_LOOP(sseOp, srcLoad, dstLoad, dstStore, locals, increment) \ + for (int i = 0; i < numLongOps; ++i) \ + { \ + locals (srcLoad, dstLoad); \ + dstStore (dest, sseOp); \ + increment; \ + } + + #define JUCE_LOAD_NONE(srcLoad, dstLoad) + #define JUCE_LOAD_DEST(srcLoad, dstLoad) const __m128 d = dstLoad (dest); + #define JUCE_LOAD_SRC(srcLoad, dstLoad) const __m128 s = srcLoad (src); + #define JUCE_LOAD_SRC_DEST(srcLoad, dstLoad) const __m128 d = dstLoad (dest); const __m128 s = srcLoad (src); + + #define JUCE_PERFORM_SSE_OP_DEST(normalOp, sseOp, locals) \ + JUCE_BEGIN_SSE_OP \ + if (FloatVectorHelpers::isAligned (dest)) JUCE_SSE_LOOP (sseOp, dummy, _mm_load_ps, _mm_store_ps, locals, JUCE_INCREMENT_DEST) \ + else JUCE_SSE_LOOP (sseOp, dummy, _mm_loadu_ps, _mm_storeu_ps, locals, JUCE_INCREMENT_DEST) \ + JUCE_FINISH_SSE_OP (normalOp) + + #define JUCE_PERFORM_SSE_OP_SRC_DEST(normalOp, sseOp, locals, increment) \ + JUCE_BEGIN_SSE_OP \ + if (FloatVectorHelpers::isAligned (dest)) \ + { \ + if (FloatVectorHelpers::isAligned (src)) JUCE_SSE_LOOP (sseOp, _mm_load_ps, _mm_load_ps, _mm_store_ps, locals, increment) \ + else JUCE_SSE_LOOP (sseOp, _mm_loadu_ps, _mm_load_ps, _mm_store_ps, locals, increment) \ + }\ + else \ + { \ + if (FloatVectorHelpers::isAligned (src)) JUCE_SSE_LOOP (sseOp, _mm_load_ps, _mm_loadu_ps, _mm_storeu_ps, locals, increment) \ + else JUCE_SSE_LOOP (sseOp, _mm_loadu_ps, _mm_loadu_ps, _mm_storeu_ps, locals, increment) \ + } \ + JUCE_FINISH_SSE_OP (normalOp) + + + //============================================================================== + #elif JUCE_USE_ARM_NEON + + static inline float findMinimumOrMaximum (const float* src, int num, const bool isMinimum) noexcept + { + const int numLongOps = num / 4; + + if (numLongOps > 1) + { + float32x4_t val; + + #define JUCE_MINIMUMMAXIMUM_NEON_LOOP(loadOp, minMaxOp) \ + val = loadOp (src); \ + src += 4; \ + for (int i = 1; i < numLongOps; ++i) \ + { \ + const float32x4_t s = loadOp (src); \ + val = minMaxOp (val, s); \ + src += 4; \ + } + + if (isMinimum) { JUCE_MINIMUMMAXIMUM_NEON_LOOP (vld1q_f32, vminq_f32) } + else { JUCE_MINIMUMMAXIMUM_NEON_LOOP (vld1q_f32, vmaxq_f32) } + + float localVal; + + { + float vals[4]; + vst1q_f32 (vals, val); + + localVal = isMinimum ? jmin (vals[0], vals[1], vals[2], vals[3]) + : jmax (vals[0], vals[1], vals[2], vals[3]); + } + + num &= 3; + + for (int i = 0; i < num; ++i) + localVal = isMinimum ? jmin (localVal, src[i]) + : jmax (localVal, src[i]); + + return localVal; + } + + return isMinimum ? juce::findMinimum (src, num) + : juce::findMaximum (src, num); + } + + #define JUCE_BEGIN_NEON_OP \ const int numLongOps = num / 4; -#define JUCE_FINISH_SSE_OP(normalOp) \ + #define JUCE_FINISH_NEON_OP(normalOp) \ num &= 3; \ if (num == 0) return; \ - } \ - for (int i = 0; i < num; ++i) normalOp; - -#define JUCE_SSE_LOOP(sseOp, srcLoad, dstLoad, dstStore, locals, increment) \ - for (int i = 0; i < numLongOps; ++i) \ - { \ - locals (srcLoad, dstLoad); \ - dstStore (dest, sseOp); \ - increment; \ - } + for (int i = 0; i < num; ++i) normalOp; + + #define JUCE_NEON_LOOP(neonOp, srcLoad, dstLoad, dstStore, locals, increment) \ + for (int i = 0; i < numLongOps; ++i) \ + { \ + locals (srcLoad, dstLoad); \ + dstStore (dest, neonOp); \ + increment; \ + } -#define JUCE_INCREMENT_SRC_DEST dest += 4; src += 4; -#define JUCE_INCREMENT_DEST dest += 4; - -#define JUCE_LOAD_NONE(srcLoad, dstLoad) -#define JUCE_LOAD_DEST(srcLoad, dstLoad) const __m128 d = dstLoad (dest); -#define JUCE_LOAD_SRC(srcLoad, dstLoad) const __m128 s = srcLoad (src); -#define JUCE_LOAD_SRC_DEST(srcLoad, dstLoad) const __m128 d = dstLoad (dest); const __m128 s = srcLoad (src); - -#define JUCE_PERFORM_SSE_OP_DEST(normalOp, sseOp, locals) \ - JUCE_BEGIN_SSE_OP \ - if (FloatVectorHelpers::isAligned (dest)) JUCE_SSE_LOOP (sseOp, dummy, _mm_load_ps, _mm_store_ps, locals, JUCE_INCREMENT_DEST) \ - else JUCE_SSE_LOOP (sseOp, dummy, _mm_loadu_ps, _mm_storeu_ps, locals, JUCE_INCREMENT_DEST) \ - JUCE_FINISH_SSE_OP (normalOp) - -#define JUCE_PERFORM_SSE_OP_SRC_DEST(normalOp, sseOp, locals, increment) \ - JUCE_BEGIN_SSE_OP \ - if (FloatVectorHelpers::isAligned (dest)) \ - { \ - if (FloatVectorHelpers::isAligned (src)) JUCE_SSE_LOOP (sseOp, _mm_load_ps, _mm_load_ps, _mm_store_ps, locals, increment) \ - else JUCE_SSE_LOOP (sseOp, _mm_loadu_ps, _mm_load_ps, _mm_store_ps, locals, increment) \ - }\ - else \ - { \ - if (FloatVectorHelpers::isAligned (src)) JUCE_SSE_LOOP (sseOp, _mm_load_ps, _mm_loadu_ps, _mm_storeu_ps, locals, increment) \ - else JUCE_SSE_LOOP (sseOp, _mm_loadu_ps, _mm_loadu_ps, _mm_storeu_ps, locals, increment) \ - } \ - JUCE_FINISH_SSE_OP (normalOp) - - -#else - #define JUCE_PERFORM_SSE_OP_DEST(normalOp, unused1, unused2) for (int i = 0; i < num; ++i) normalOp; - #define JUCE_PERFORM_SSE_OP_SRC_DEST(normalOp, sseOp, locals, increment) for (int i = 0; i < num; ++i) normalOp; -#endif + #define JUCE_LOAD_NONE(srcLoad, dstLoad) + #define JUCE_LOAD_DEST(srcLoad, dstLoad) const float32x4_t d = dstLoad (dest); + #define JUCE_LOAD_SRC(srcLoad, dstLoad) const float32x4_t s = srcLoad (src); + #define JUCE_LOAD_SRC_DEST(srcLoad, dstLoad) const float32x4_t d = dstLoad (dest); const float32x4_t s = srcLoad (src); + + #define JUCE_PERFORM_NEON_OP_DEST(normalOp, neonOp, locals) \ + JUCE_BEGIN_NEON_OP \ + JUCE_NEON_LOOP (neonOp, dummy, vld1q_f32, vst1q_f32, locals, JUCE_INCREMENT_DEST) \ + JUCE_FINISH_NEON_OP (normalOp) + + #define JUCE_PERFORM_NEON_OP_SRC_DEST(normalOp, neonOp, locals) \ + JUCE_BEGIN_NEON_OP \ + JUCE_NEON_LOOP (neonOp, vld1q_f32, vld1q_f32, vst1q_f32, locals, JUCE_INCREMENT_SRC_DEST) \ + JUCE_FINISH_NEON_OP (normalOp) + + //============================================================================== + #else + #define JUCE_PERFORM_SSE_OP_DEST(normalOp, unused1, unused2) for (int i = 0; i < num; ++i) normalOp; + #define JUCE_PERFORM_SSE_OP_SRC_DEST(normalOp, sseOp, locals, increment) for (int i = 0; i < num; ++i) normalOp; + #endif +} +//============================================================================== void JUCE_CALLTYPE FloatVectorOperations::clear (float* dest, int num) noexcept { #if JUCE_USE_VDSP_FRAMEWORK @@ -163,11 +241,13 @@ void JUCE_CALLTYPE FloatVectorOperations::fill (float* dest, float valueToFill, { #if JUCE_USE_VDSP_FRAMEWORK vDSP_vfill (&valueToFill, dest, 1, (size_t) num); + #elif JUCE_USE_ARM_NEON + const float32x4_t val = vld1q_dup_f32 (&valueToFill); + JUCE_PERFORM_NEON_OP_DEST (dest[i] = valueToFill, val, JUCE_LOAD_NONE) #else #if JUCE_USE_SSE_INTRINSICS const __m128 val = _mm_load1_ps (&valueToFill); #endif - JUCE_PERFORM_SSE_OP_DEST (dest[i] = valueToFill, val, JUCE_LOAD_NONE) #endif } @@ -181,58 +261,78 @@ void JUCE_CALLTYPE FloatVectorOperations::copyWithMultiply (float* dest, const f { #if JUCE_USE_VDSP_FRAMEWORK vDSP_vsmul (src, 1, &multiplier, dest, 1, num); + #elif JUCE_USE_ARM_NEON + JUCE_PERFORM_NEON_OP_SRC_DEST (dest[i] += src[i], vmulq_n_f32(s, multiplier), JUCE_LOAD_SRC) #else #if JUCE_USE_SSE_INTRINSICS const __m128 mult = _mm_load1_ps (&multiplier); #endif - - JUCE_PERFORM_SSE_OP_SRC_DEST (dest[i] = src[i] * multiplier, - _mm_mul_ps (mult, s), + JUCE_PERFORM_SSE_OP_SRC_DEST (dest[i] = src[i] * multiplier, _mm_mul_ps (mult, s), JUCE_LOAD_SRC, JUCE_INCREMENT_SRC_DEST) #endif } +void JUCE_CALLTYPE FloatVectorOperations::add (float* dest, float amount, int num) noexcept +{ + #if JUCE_USE_ARM_NEON + const float32x4_t amountToAdd = vld1q_dup_f32(&amount); + JUCE_PERFORM_NEON_OP_DEST (dest[i] += amount, vaddq_f32 (d, amountToAdd), JUCE_LOAD_DEST) + #else + #if JUCE_USE_SSE_INTRINSICS + const __m128 amountToAdd = _mm_load1_ps (&amount); + #endif + JUCE_PERFORM_SSE_OP_DEST (dest[i] += amount, _mm_add_ps (d, amountToAdd), JUCE_LOAD_DEST) + #endif +} + void JUCE_CALLTYPE FloatVectorOperations::add (float* dest, const float* src, int num) noexcept { #if JUCE_USE_VDSP_FRAMEWORK vDSP_vadd (src, 1, dest, 1, dest, 1, num); + #elif JUCE_USE_ARM_NEON + JUCE_PERFORM_NEON_OP_SRC_DEST (dest[i] += src[i], vaddq_f32 (d, s), JUCE_LOAD_SRC_DEST) #else - JUCE_PERFORM_SSE_OP_SRC_DEST (dest[i] += src[i], - _mm_add_ps (d, s), - JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST) + JUCE_PERFORM_SSE_OP_SRC_DEST (dest[i] += src[i], _mm_add_ps (d, s), JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST) #endif } -void JUCE_CALLTYPE FloatVectorOperations::add (float* dest, float amount, int num) noexcept +void JUCE_CALLTYPE FloatVectorOperations::subtract (float* dest, const float* src, int num) noexcept { - #if JUCE_USE_SSE_INTRINSICS - const __m128 amountToAdd = _mm_load1_ps (&amount); + #if JUCE_USE_VDSP_FRAMEWORK + vDSP_vsub (src, 1, dest, 1, dest, 1, num); + #elif JUCE_USE_ARM_NEON + JUCE_PERFORM_NEON_OP_SRC_DEST (dest[i] -= src[i], vsubq_f32 (d, s), JUCE_LOAD_SRC_DEST) + #else + JUCE_PERFORM_SSE_OP_SRC_DEST (dest[i] -= src[i], _mm_sub_ps (d, s), JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST) #endif - - JUCE_PERFORM_SSE_OP_DEST (dest[i] += amount, - _mm_add_ps (d, amountToAdd), - JUCE_LOAD_DEST) } void JUCE_CALLTYPE FloatVectorOperations::addWithMultiply (float* dest, const float* src, float multiplier, int num) noexcept { - #if JUCE_USE_SSE_INTRINSICS - const __m128 mult = _mm_load1_ps (&multiplier); + #if JUCE_USE_ARM_NEON + JUCE_PERFORM_NEON_OP_SRC_DEST (dest[i] += src[i] * multiplier, + vmlaq_n_f32 (d, s, multiplier), + JUCE_LOAD_SRC_DEST) + #else + #if JUCE_USE_SSE_INTRINSICS + const __m128 mult = _mm_load1_ps (&multiplier); + #endif + + JUCE_PERFORM_SSE_OP_SRC_DEST (dest[i] += src[i] * multiplier, + _mm_add_ps (d, _mm_mul_ps (mult, s)), + JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST) #endif - JUCE_PERFORM_SSE_OP_SRC_DEST (dest[i] += src[i] * multiplier, - _mm_add_ps (d, _mm_mul_ps (mult, s)), - JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST) } void JUCE_CALLTYPE FloatVectorOperations::multiply (float* dest, const float* src, int num) noexcept { #if JUCE_USE_VDSP_FRAMEWORK vDSP_vmul (src, 1, dest, 1, dest, 1, num); + #elif JUCE_USE_ARM_NEON + JUCE_PERFORM_NEON_OP_SRC_DEST (dest[i] *= src[i], vmulq_f32 (d, s), JUCE_LOAD_SRC_DEST) #else - JUCE_PERFORM_SSE_OP_SRC_DEST (dest[i] *= src[i], - _mm_mul_ps (d, s), - JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST) + JUCE_PERFORM_SSE_OP_SRC_DEST (dest[i] *= src[i], _mm_mul_ps (d, s), JUCE_LOAD_SRC_DEST, JUCE_INCREMENT_SRC_DEST) #endif } @@ -240,14 +340,13 @@ void JUCE_CALLTYPE FloatVectorOperations::multiply (float* dest, float multiplie { #if JUCE_USE_VDSP_FRAMEWORK vDSP_vsmul (dest, 1, &multiplier, dest, 1, num); + #elif JUCE_USE_ARM_NEON + JUCE_PERFORM_NEON_OP_DEST (dest[i] *= multiplier, vmulq_n_f32 (d, multiplier), JUCE_LOAD_DEST) #else #if JUCE_USE_SSE_INTRINSICS const __m128 mult = _mm_load1_ps (&multiplier); #endif - - JUCE_PERFORM_SSE_OP_DEST (dest[i] *= multiplier, - _mm_mul_ps (d, mult), - JUCE_LOAD_DEST) + JUCE_PERFORM_SSE_OP_DEST (dest[i] *= multiplier, _mm_mul_ps (d, mult), JUCE_LOAD_DEST) #endif } @@ -262,13 +361,19 @@ void FloatVectorOperations::negate (float* dest, const float* src, int num) noex void JUCE_CALLTYPE FloatVectorOperations::convertFixedToFloat (float* dest, const int* src, float multiplier, int num) noexcept { - #if JUCE_USE_SSE_INTRINSICS - const __m128 mult = _mm_load1_ps (&multiplier); - #endif + #if JUCE_USE_ARM_NEON + JUCE_PERFORM_NEON_OP_SRC_DEST (dest[i] = src[i] * multiplier, + vmulq_n_f32 (vcvtq_f32_s32 (vld1q_s32 (src)), multiplier), + JUCE_LOAD_NONE) + #else + #if JUCE_USE_SSE_INTRINSICS + const __m128 mult = _mm_load1_ps (&multiplier); + #endif - JUCE_PERFORM_SSE_OP_SRC_DEST (dest[i] = src[i] * multiplier, - _mm_mul_ps (mult, _mm_cvtepi32_ps (_mm_loadu_si128 ((const __m128i*) src))), - JUCE_LOAD_NONE, JUCE_INCREMENT_SRC_DEST) + JUCE_PERFORM_SSE_OP_SRC_DEST (dest[i] = src[i] * multiplier, + _mm_mul_ps (mult, _mm_cvtepi32_ps (_mm_loadu_si128 ((const __m128i*) src))), + JUCE_LOAD_NONE, JUCE_INCREMENT_SRC_DEST) + #endif } void JUCE_CALLTYPE FloatVectorOperations::findMinAndMax (const float* src, int num, float& minResult, float& maxResult) noexcept @@ -315,6 +420,51 @@ void JUCE_CALLTYPE FloatVectorOperations::findMinAndMax (const float* src, int n localMax = jmax (localMax, s); } + minResult = localMin; + maxResult = localMax; + return; + } + #elif JUCE_USE_ARM_NEON + const int numLongOps = num / 4; + + if (numLongOps > 1) + { + float32x4_t mn, mx; + + #define JUCE_MINMAX_NEON_LOOP(loadOp) \ + mn = loadOp (src); \ + mx = mn; \ + src += 4; \ + for (int i = 1; i < numLongOps; ++i) \ + { \ + const float32x4_t s = loadOp (src); \ + mn = vminq_f32 (mn, s); \ + mx = vmaxq_f32 (mx, s); \ + src += 4; \ + } + + JUCE_MINMAX_NEON_LOOP (vld1q_f32); + + float localMin, localMax; + + { + float mns[4], mxs[4]; + vst1q_f32 (mns, mn); + vst1q_f32 (mxs, mx); + + localMin = jmin (mns[0], mns[1], mns[2], mns[3]); + localMax = jmax (mxs[0], mxs[1], mxs[2], mxs[3]); + } + + num &= 3; + + for (int i = 0; i < num; ++i) + { + const float s = src[i]; + localMin = jmin (localMin, s); + localMax = jmax (localMax, s); + } + minResult = localMin; maxResult = localMax; return; @@ -326,7 +476,7 @@ void JUCE_CALLTYPE FloatVectorOperations::findMinAndMax (const float* src, int n float JUCE_CALLTYPE FloatVectorOperations::findMinimum (const float* src, int num) noexcept { - #if JUCE_USE_SSE_INTRINSICS + #if JUCE_USE_SSE_INTRINSICS || JUCE_USE_ARM_NEON return FloatVectorHelpers::findMinimumOrMaximum (src, num, true); #else return juce::findMinimum (src, num); @@ -335,7 +485,7 @@ float JUCE_CALLTYPE FloatVectorOperations::findMinimum (const float* src, int nu float JUCE_CALLTYPE FloatVectorOperations::findMaximum (const float* src, int num) noexcept { - #if JUCE_USE_SSE_INTRINSICS + #if JUCE_USE_SSE_INTRINSICS || JUCE_USE_ARM_NEON return FloatVectorHelpers::findMinimumOrMaximum (src, num, false); #else return juce::findMaximum (src, num); @@ -350,3 +500,129 @@ void JUCE_CALLTYPE FloatVectorOperations::enableFlushToZeroMode (bool shouldEnab #endif (void) shouldEnable; } + +//============================================================================== +//============================================================================== +#if JUCE_UNIT_TESTS + +class FloatVectorOperationsTests : public UnitTest +{ +public: + FloatVectorOperationsTests() : UnitTest ("FloatVectorOperations") {} + + void runTest() + { + beginTest ("FloatVectorOperations"); + + for (int i = 100; --i >= 0;) + { + const int num = getRandom().nextInt (500) + 1; + + HeapBlock buffer1 (num + 16), buffer2 (num + 16); + HeapBlock buffer3 (num + 16); + + #if JUCE_ARM + float* const data1 = buffer1; + float* const data2 = buffer2; + int* const int1 = buffer3; + #else + float* const data1 = addBytesToPointer (buffer1.getData(), getRandom().nextInt (16)); + float* const data2 = addBytesToPointer (buffer2.getData(), getRandom().nextInt (16)); + int* const int1 = addBytesToPointer (buffer3.getData(), getRandom().nextInt (16)); + #endif + + fillRandomly (data1, num); + fillRandomly (data2, num); + + float mn1, mx1, mn2, mx2; + FloatVectorOperations::findMinAndMax (data1, num, mn1, mx1); + juce::findMinAndMax (data1, num, mn2, mx2); + expect (mn1 == mn2); + expect (mx1 == mx2); + + expect (FloatVectorOperations::findMinimum (data1, num) == juce::findMinimum (data1, num)); + expect (FloatVectorOperations::findMaximum (data1, num) == juce::findMaximum (data1, num)); + + expect (FloatVectorOperations::findMinimum (data2, num) == juce::findMinimum (data2, num)); + expect (FloatVectorOperations::findMaximum (data2, num) == juce::findMaximum (data2, num)); + + FloatVectorOperations::clear (data1, num); + expect (areAllValuesEqual (data1, num, 0)); + + FloatVectorOperations::fill (data1, 2.0f, num); + expect (areAllValuesEqual (data1, num, 2.0f)); + + FloatVectorOperations::add (data1, 2.0f, num); + expect (areAllValuesEqual (data1, num, 4.0f)); + + FloatVectorOperations::copy (data2, data1, num); + expect (areAllValuesEqual (data2, num, 4.0f)); + + FloatVectorOperations::add (data2, data1, num); + expect (areAllValuesEqual (data2, num, 8.0f)); + + FloatVectorOperations::copyWithMultiply (data2, data1, 4.0f, num); + expect (areAllValuesEqual (data2, num, 16.0f)); + + FloatVectorOperations::addWithMultiply (data2, data1, 4.0f, num); + expect (areAllValuesEqual (data2, num, 32.0f)); + + FloatVectorOperations::multiply (data1, 2.0f, num); + expect (areAllValuesEqual (data1, num, 8.0f)); + + FloatVectorOperations::multiply (data1, data2, num); + expect (areAllValuesEqual (data1, num, 256.0f)); + + FloatVectorOperations::negate (data2, data1, num); + expect (areAllValuesEqual (data2, num, -256.0f)); + + FloatVectorOperations::subtract (data1, data2, num); + expect (areAllValuesEqual (data1, num, 512.0f)); + + fillRandomly (int1, num); + FloatVectorOperations::convertFixedToFloat (data1, int1, 2.0f, num); + convertFixed (data2, int1, 2.0f, num); + expect (buffersMatch (data1, data2, num)); + } + } + + void fillRandomly (float* d, int num) + { + while (--num >= 0) + *d++ = getRandom().nextFloat() * 1000.0f; + } + + void fillRandomly (int* d, int num) + { + while (--num >= 0) + *d++ = getRandom().nextInt(); + } + + static void convertFixed (float* d, const int* s, float multiplier, int num) + { + while (--num >= 0) + *d++ = *s++ * multiplier; + } + + static bool areAllValuesEqual (const float* d, int num, float target) + { + while (--num >= 0) + if (*d++ != target) + return false; + + return true; + } + + static bool buffersMatch (const float* d1, const float* d2, int num) + { + while (--num >= 0) + if (std::abs (*d1++ - *d2++) > std::numeric_limits::epsilon()) + return false; + + return true; + } +}; + +static FloatVectorOperationsTests vectorOpTests; + +#endif diff --git a/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.h b/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.h index 6268ab9..6ffcfab 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.h +++ b/JuceLibraryCode/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.h @@ -47,11 +47,14 @@ public: /** Copies a vector of floats, multiplying each value by a given multiplier */ static void JUCE_CALLTYPE copyWithMultiply (float* dest, const float* src, float multiplier, int numValues) noexcept; + /** Adds a fixed value to the destination values. */ + static void JUCE_CALLTYPE add (float* dest, float amount, int numValues) noexcept; + /** Adds the source values to the destination values. */ static void JUCE_CALLTYPE add (float* dest, const float* src, int numValues) noexcept; - /** Adds a fixed value to the destination values. */ - static void JUCE_CALLTYPE add (float* dest, float amount, int numValues) noexcept; + /** Subtracts the source values from the destination values. */ + static void JUCE_CALLTYPE subtract (float* dest, const float* src, int numValues) noexcept; /** Multiplies each source value by the given multiplier, then adds it to the destination value. */ static void JUCE_CALLTYPE addWithMultiply (float* dest, const float* src, float multiplier, int numValues) noexcept; diff --git a/JuceLibraryCode/modules/juce_audio_basics/juce_audio_basics.cpp b/JuceLibraryCode/modules/juce_audio_basics/juce_audio_basics.cpp index 06ed681..ee7d3cf 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/juce_audio_basics.cpp +++ b/JuceLibraryCode/modules/juce_audio_basics/juce_audio_basics.cpp @@ -58,6 +58,11 @@ #undef JUCE_USE_VDSP_FRAMEWORK #endif +#if __ARM_NEON__ && ! (JUCE_USE_VDSP_FRAMEWORK || defined (JUCE_USE_ARM_NEON)) + #define JUCE_USE_ARM_NEON 1 + #include +#endif + namespace juce { diff --git a/JuceLibraryCode/modules/juce_audio_basics/juce_module_info b/JuceLibraryCode/modules/juce_audio_basics/juce_module_info index 455a820..acdd765 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/juce_module_info +++ b/JuceLibraryCode/modules/juce_audio_basics/juce_module_info @@ -1,7 +1,7 @@ { "id": "juce_audio_basics", "name": "JUCE audio and midi data classes", - "version": "3.0.0", + "version": "3.0.1", "description": "Classes for audio buffer manipulation, midi message handling, synthesis, etc", "website": "http://www.juce.com/juce", "license": "GPL/Commercial", diff --git a/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiMessage.cpp b/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiMessage.cpp index aa866ee..c111404 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiMessage.cpp +++ b/JuceLibraryCode/modules/juce_audio_basics/midi/juce_MidiMessage.cpp @@ -945,29 +945,38 @@ const char* MidiMessage::getGMInstrumentName (const int n) { static const char* names[] = { - "Acoustic Grand Piano", "Bright Acoustic Piano", "Electric Grand Piano", "Honky-tonk Piano", - "Electric Piano 1", "Electric Piano 2", "Harpsichord", "Clavinet", "Celesta", "Glockenspiel", - "Music Box", "Vibraphone", "Marimba", "Xylophone", "Tubular Bells", "Dulcimer", "Drawbar Organ", - "Percussive Organ", "Rock Organ", "Church Organ", "Reed Organ", "Accordion", "Harmonica", - "Tango Accordion", "Acoustic Guitar (nylon)", "Acoustic Guitar (steel)", "Electric Guitar (jazz)", - "Electric Guitar (clean)", "Electric Guitar (mute)", "Overdriven Guitar", "Distortion Guitar", - "Guitar Harmonics", "Acoustic Bass", "Electric Bass (finger)", "Electric Bass (pick)", - "Fretless Bass", "Slap Bass 1", "Slap Bass 2", "Synth Bass 1", "Synth Bass 2", "Violin", - "Viola", "Cello", "Contrabass", "Tremolo Strings", "Pizzicato Strings", "Orchestral Harp", - "Timpani", "String Ensemble 1", "String Ensemble 2", "SynthStrings 1", "SynthStrings 2", - "Choir Aahs", "Voice Oohs", "Synth Voice", "Orchestra Hit", "Trumpet", "Trombone", "Tuba", - "Muted Trumpet", "French Horn", "Brass Section", "SynthBrass 1", "SynthBrass 2", "Soprano Sax", - "Alto Sax", "Tenor Sax", "Baritone Sax", "Oboe", "English Horn", "Bassoon", "Clarinet", - "Piccolo", "Flute", "Recorder", "Pan Flute", "Blown Bottle", "Shakuhachi", "Whistle", - "Ocarina", "Lead 1 (square)", "Lead 2 (sawtooth)", "Lead 3 (calliope)", "Lead 4 (chiff)", - "Lead 5 (charang)", "Lead 6 (voice)", "Lead 7 (fifths)", "Lead 8 (bass+lead)", "Pad 1 (new age)", - "Pad 2 (warm)", "Pad 3 (polysynth)", "Pad 4 (choir)", "Pad 5 (bowed)", "Pad 6 (metallic)", - "Pad 7 (halo)", "Pad 8 (sweep)", "FX 1 (rain)", "FX 2 (soundtrack)", "FX 3 (crystal)", - "FX 4 (atmosphere)", "FX 5 (brightness)", "FX 6 (goblins)", "FX 7 (echoes)", "FX 8 (sci-fi)", - "Sitar", "Banjo", "Shamisen", "Koto", "Kalimba", "Bag pipe", "Fiddle", "Shanai", "Tinkle Bell", - "Agogo", "Steel Drums", "Woodblock", "Taiko Drum", "Melodic Tom", "Synth Drum", "Reverse Cymbal", - "Guitar Fret Noise", "Breath Noise", "Seashore", "Bird Tweet", "Telephone Ring", "Helicopter", - "Applause", "Gunshot" + NEEDS_TRANS("Acoustic Grand Piano"), NEEDS_TRANS("Bright Acoustic Piano"), NEEDS_TRANS("Electric Grand Piano"), NEEDS_TRANS("Honky-tonk Piano"), + NEEDS_TRANS("Electric Piano 1"), NEEDS_TRANS("Electric Piano 2"), NEEDS_TRANS("Harpsichord"), NEEDS_TRANS("Clavinet"), + NEEDS_TRANS("Celesta"), NEEDS_TRANS("Glockenspiel"), NEEDS_TRANS("Music Box"), NEEDS_TRANS("Vibraphone"), + NEEDS_TRANS("Marimba"), NEEDS_TRANS("Xylophone"), NEEDS_TRANS("Tubular Bells"), NEEDS_TRANS("Dulcimer"), + NEEDS_TRANS("Drawbar Organ"), NEEDS_TRANS("Percussive Organ"), NEEDS_TRANS("Rock Organ"), NEEDS_TRANS("Church Organ"), + NEEDS_TRANS("Reed Organ"), NEEDS_TRANS("Accordion"), NEEDS_TRANS("Harmonica"), NEEDS_TRANS("Tango Accordion"), + NEEDS_TRANS("Acoustic Guitar (nylon)"), NEEDS_TRANS("Acoustic Guitar (steel)"), NEEDS_TRANS("Electric Guitar (jazz)"), NEEDS_TRANS("Electric Guitar (clean)"), + NEEDS_TRANS("Electric Guitar (mute)"), NEEDS_TRANS("Overdriven Guitar"), NEEDS_TRANS("Distortion Guitar"), NEEDS_TRANS("Guitar Harmonics"), + NEEDS_TRANS("Acoustic Bass"), NEEDS_TRANS("Electric Bass (finger)"), NEEDS_TRANS("Electric Bass (pick)"), NEEDS_TRANS("Fretless Bass"), + NEEDS_TRANS("Slap Bass 1"), NEEDS_TRANS("Slap Bass 2"), NEEDS_TRANS("Synth Bass 1"), NEEDS_TRANS("Synth Bass 2"), + NEEDS_TRANS("Violin"), NEEDS_TRANS("Viola"), NEEDS_TRANS("Cello"), NEEDS_TRANS("Contrabass"), + NEEDS_TRANS("Tremolo Strings"), NEEDS_TRANS("Pizzicato Strings"), NEEDS_TRANS("Orchestral Harp"), NEEDS_TRANS("Timpani"), + NEEDS_TRANS("String Ensemble 1"), NEEDS_TRANS("String Ensemble 2"), NEEDS_TRANS("SynthStrings 1"), NEEDS_TRANS("SynthStrings 2"), + NEEDS_TRANS("Choir Aahs"), NEEDS_TRANS("Voice Oohs"), NEEDS_TRANS("Synth Voice"), NEEDS_TRANS("Orchestra Hit"), + NEEDS_TRANS("Trumpet"), NEEDS_TRANS("Trombone"), NEEDS_TRANS("Tuba"), NEEDS_TRANS("Muted Trumpet"), + NEEDS_TRANS("French Horn"), NEEDS_TRANS("Brass Section"), NEEDS_TRANS("SynthBrass 1"), NEEDS_TRANS("SynthBrass 2"), + NEEDS_TRANS("Soprano Sax"), NEEDS_TRANS("Alto Sax"), NEEDS_TRANS("Tenor Sax"), NEEDS_TRANS("Baritone Sax"), + NEEDS_TRANS("Oboe"), NEEDS_TRANS("English Horn"), NEEDS_TRANS("Bassoon"), NEEDS_TRANS("Clarinet"), + NEEDS_TRANS("Piccolo"), NEEDS_TRANS("Flute"), NEEDS_TRANS("Recorder"), NEEDS_TRANS("Pan Flute"), + NEEDS_TRANS("Blown Bottle"), NEEDS_TRANS("Shakuhachi"), NEEDS_TRANS("Whistle"), NEEDS_TRANS("Ocarina"), + NEEDS_TRANS("Lead 1 (square)"), NEEDS_TRANS("Lead 2 (sawtooth)"), NEEDS_TRANS("Lead 3 (calliope)"), NEEDS_TRANS("Lead 4 (chiff)"), + NEEDS_TRANS("Lead 5 (charang)"), NEEDS_TRANS("Lead 6 (voice)"), NEEDS_TRANS("Lead 7 (fifths)"), NEEDS_TRANS("Lead 8 (bass+lead)"), + NEEDS_TRANS("Pad 1 (new age)"), NEEDS_TRANS("Pad 2 (warm)"), NEEDS_TRANS("Pad 3 (polysynth)"), NEEDS_TRANS("Pad 4 (choir)"), + NEEDS_TRANS("Pad 5 (bowed)"), NEEDS_TRANS("Pad 6 (metallic)"), NEEDS_TRANS("Pad 7 (halo)"), NEEDS_TRANS("Pad 8 (sweep)"), + NEEDS_TRANS("FX 1 (rain)"), NEEDS_TRANS("FX 2 (soundtrack)"), NEEDS_TRANS("FX 3 (crystal)"), NEEDS_TRANS("FX 4 (atmosphere)"), + NEEDS_TRANS("FX 5 (brightness)"), NEEDS_TRANS("FX 6 (goblins)"), NEEDS_TRANS("FX 7 (echoes)"), NEEDS_TRANS("FX 8 (sci-fi)"), + NEEDS_TRANS("Sitar"), NEEDS_TRANS("Banjo"), NEEDS_TRANS("Shamisen"), NEEDS_TRANS("Koto"), + NEEDS_TRANS("Kalimba"), NEEDS_TRANS("Bag pipe"), NEEDS_TRANS("Fiddle"), NEEDS_TRANS("Shanai"), + NEEDS_TRANS("Tinkle Bell"), NEEDS_TRANS("Agogo"), NEEDS_TRANS("Steel Drums"), NEEDS_TRANS("Woodblock"), + NEEDS_TRANS("Taiko Drum"), NEEDS_TRANS("Melodic Tom"), NEEDS_TRANS("Synth Drum"), NEEDS_TRANS("Reverse Cymbal"), + NEEDS_TRANS("Guitar Fret Noise"), NEEDS_TRANS("Breath Noise"), NEEDS_TRANS("Seashore"), NEEDS_TRANS("Bird Tweet"), + NEEDS_TRANS("Telephone Ring"), NEEDS_TRANS("Helicopter"), NEEDS_TRANS("Applause"), NEEDS_TRANS("Gunshot") }; return isPositiveAndBelow (n, numElementsInArray (names)) ? names[n] : nullptr; @@ -977,10 +986,10 @@ const char* MidiMessage::getGMInstrumentBankName (const int n) { static const char* names[] = { - "Piano", "Chromatic Percussion", "Organ", "Guitar", - "Bass", "Strings", "Ensemble", "Brass", - "Reed", "Pipe", "Synth Lead", "Synth Pad", - "Synth Effects", "Ethnic", "Percussive", "Sound Effects" + NEEDS_TRANS("Piano"), NEEDS_TRANS("Chromatic Percussion"), NEEDS_TRANS("Organ"), NEEDS_TRANS("Guitar"), + NEEDS_TRANS("Bass"), NEEDS_TRANS("Strings"), NEEDS_TRANS("Ensemble"), NEEDS_TRANS("Brass"), + NEEDS_TRANS("Reed"), NEEDS_TRANS("Pipe"), NEEDS_TRANS("Synth Lead"), NEEDS_TRANS("Synth Pad"), + NEEDS_TRANS("Synth Effects"), NEEDS_TRANS("Ethnic"), NEEDS_TRANS("Percussive"), NEEDS_TRANS("Sound Effects") }; return isPositiveAndBelow (n, numElementsInArray (names)) ? names[n] : nullptr; @@ -990,15 +999,18 @@ const char* MidiMessage::getRhythmInstrumentName (const int n) { static const char* names[] = { - "Acoustic Bass Drum", "Bass Drum 1", "Side Stick", "Acoustic Snare", - "Hand Clap", "Electric Snare", "Low Floor Tom", "Closed Hi-Hat", "High Floor Tom", - "Pedal Hi-Hat", "Low Tom", "Open Hi-Hat", "Low-Mid Tom", "Hi-Mid Tom", "Crash Cymbal 1", - "High Tom", "Ride Cymbal 1", "Chinese Cymbal", "Ride Bell", "Tambourine", "Splash Cymbal", - "Cowbell", "Crash Cymbal 2", "Vibraslap", "Ride Cymbal 2", "Hi Bongo", "Low Bongo", - "Mute Hi Conga", "Open Hi Conga", "Low Conga", "High Timbale", "Low Timbale", "High Agogo", - "Low Agogo", "Cabasa", "Maracas", "Short Whistle", "Long Whistle", "Short Guiro", - "Long Guiro", "Claves", "Hi Wood Block", "Low Wood Block", "Mute Cuica", "Open Cuica", - "Mute Triangle", "Open Triangle" + NEEDS_TRANS("Acoustic Bass Drum"), NEEDS_TRANS("Bass Drum 1"), NEEDS_TRANS("Side Stick"), NEEDS_TRANS("Acoustic Snare"), + NEEDS_TRANS("Hand Clap"), NEEDS_TRANS("Electric Snare"), NEEDS_TRANS("Low Floor Tom"), NEEDS_TRANS("Closed Hi-Hat"), + NEEDS_TRANS("High Floor Tom"), NEEDS_TRANS("Pedal Hi-Hat"), NEEDS_TRANS("Low Tom"), NEEDS_TRANS("Open Hi-Hat"), + NEEDS_TRANS("Low-Mid Tom"), NEEDS_TRANS("Hi-Mid Tom"), NEEDS_TRANS("Crash Cymbal 1"), NEEDS_TRANS("High Tom"), + NEEDS_TRANS("Ride Cymbal 1"), NEEDS_TRANS("Chinese Cymbal"), NEEDS_TRANS("Ride Bell"), NEEDS_TRANS("Tambourine"), + NEEDS_TRANS("Splash Cymbal"), NEEDS_TRANS("Cowbell"), NEEDS_TRANS("Crash Cymbal 2"), NEEDS_TRANS("Vibraslap"), + NEEDS_TRANS("Ride Cymbal 2"), NEEDS_TRANS("Hi Bongo"), NEEDS_TRANS("Low Bongo"), NEEDS_TRANS("Mute Hi Conga"), + NEEDS_TRANS("Open Hi Conga"), NEEDS_TRANS("Low Conga"), NEEDS_TRANS("High Timbale"), NEEDS_TRANS("Low Timbale"), + NEEDS_TRANS("High Agogo"), NEEDS_TRANS("Low Agogo"), NEEDS_TRANS("Cabasa"), NEEDS_TRANS("Maracas"), + NEEDS_TRANS("Short Whistle"), NEEDS_TRANS("Long Whistle"), NEEDS_TRANS("Short Guiro"), NEEDS_TRANS("Long Guiro"), + NEEDS_TRANS("Claves"), NEEDS_TRANS("Hi Wood Block"), NEEDS_TRANS("Low Wood Block"), NEEDS_TRANS("Mute Cuica"), + NEEDS_TRANS("Open Cuica"), NEEDS_TRANS("Mute Triangle"), NEEDS_TRANS("Open Triangle") }; return (n >= 35 && n <= 81) ? names [n - 35] : nullptr; @@ -1008,28 +1020,38 @@ const char* MidiMessage::getControllerName (const int n) { static const char* names[] = { - "Bank Select", "Modulation Wheel (coarse)", "Breath controller (coarse)", - 0, "Foot Pedal (coarse)", "Portamento Time (coarse)", - "Data Entry (coarse)", "Volume (coarse)", "Balance (coarse)", - 0, "Pan position (coarse)", "Expression (coarse)", "Effect Control 1 (coarse)", - "Effect Control 2 (coarse)", 0, 0, "General Purpose Slider 1", "General Purpose Slider 2", - "General Purpose Slider 3", "General Purpose Slider 4", 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, "Bank Select (fine)", "Modulation Wheel (fine)", "Breath controller (fine)", - 0, "Foot Pedal (fine)", "Portamento Time (fine)", "Data Entry (fine)", "Volume (fine)", - "Balance (fine)", 0, "Pan position (fine)", "Expression (fine)", "Effect Control 1 (fine)", - "Effect Control 2 (fine)", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - "Hold Pedal (on/off)", "Portamento (on/off)", "Sustenuto Pedal (on/off)", "Soft Pedal (on/off)", - "Legato Pedal (on/off)", "Hold 2 Pedal (on/off)", "Sound Variation", "Sound Timbre", - "Sound Release Time", "Sound Attack Time", "Sound Brightness", "Sound Control 6", - "Sound Control 7", "Sound Control 8", "Sound Control 9", "Sound Control 10", - "General Purpose Button 1 (on/off)", "General Purpose Button 2 (on/off)", - "General Purpose Button 3 (on/off)", "General Purpose Button 4 (on/off)", - 0, 0, 0, 0, 0, 0, 0, "Reverb Level", "Tremolo Level", "Chorus Level", "Celeste Level", - "Phaser Level", "Data Button increment", "Data Button decrement", "Non-registered Parameter (fine)", - "Non-registered Parameter (coarse)", "Registered Parameter (fine)", "Registered Parameter (coarse)", - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "All Sound Off", "All Controllers Off", - "Local Keyboard (on/off)", "All Notes Off", "Omni Mode Off", "Omni Mode On", "Mono Operation", - "Poly Operation" + NEEDS_TRANS("Bank Select"), NEEDS_TRANS("Modulation Wheel (coarse)"), NEEDS_TRANS("Breath controller (coarse)"), + nullptr, + NEEDS_TRANS("Foot Pedal (coarse)"), NEEDS_TRANS("Portamento Time (coarse)"), NEEDS_TRANS("Data Entry (coarse)"), + NEEDS_TRANS("Volume (coarse)"), NEEDS_TRANS("Balance (coarse)"), + nullptr, + NEEDS_TRANS("Pan position (coarse)"), NEEDS_TRANS("Expression (coarse)"), NEEDS_TRANS("Effect Control 1 (coarse)"), + NEEDS_TRANS("Effect Control 2 (coarse)"), + nullptr, nullptr, + NEEDS_TRANS("General Purpose Slider 1"), NEEDS_TRANS("General Purpose Slider 2"), + NEEDS_TRANS("General Purpose Slider 3"), NEEDS_TRANS("General Purpose Slider 4"), + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, + NEEDS_TRANS("Bank Select (fine)"), NEEDS_TRANS("Modulation Wheel (fine)"), NEEDS_TRANS("Breath controller (fine)"), + nullptr, + NEEDS_TRANS("Foot Pedal (fine)"), NEEDS_TRANS("Portamento Time (fine)"), NEEDS_TRANS("Data Entry (fine)"), NEEDS_TRANS("Volume (fine)"), + NEEDS_TRANS("Balance (fine)"), nullptr, NEEDS_TRANS("Pan position (fine)"), NEEDS_TRANS("Expression (fine)"), + NEEDS_TRANS("Effect Control 1 (fine)"), NEEDS_TRANS("Effect Control 2 (fine)"), + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, + NEEDS_TRANS("Hold Pedal (on/off)"), NEEDS_TRANS("Portamento (on/off)"), NEEDS_TRANS("Sustenuto Pedal (on/off)"), NEEDS_TRANS("Soft Pedal (on/off)"), + NEEDS_TRANS("Legato Pedal (on/off)"), NEEDS_TRANS("Hold 2 Pedal (on/off)"), NEEDS_TRANS("Sound Variation"), NEEDS_TRANS("Sound Timbre"), + NEEDS_TRANS("Sound Release Time"), NEEDS_TRANS("Sound Attack Time"), NEEDS_TRANS("Sound Brightness"), NEEDS_TRANS("Sound Control 6"), + NEEDS_TRANS("Sound Control 7"), NEEDS_TRANS("Sound Control 8"), NEEDS_TRANS("Sound Control 9"), NEEDS_TRANS("Sound Control 10"), + NEEDS_TRANS("General Purpose Button 1 (on/off)"), NEEDS_TRANS("General Purpose Button 2 (on/off)"), + NEEDS_TRANS("General Purpose Button 3 (on/off)"), NEEDS_TRANS("General Purpose Button 4 (on/off)"), + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, + NEEDS_TRANS("Reverb Level"), NEEDS_TRANS("Tremolo Level"), NEEDS_TRANS("Chorus Level"), NEEDS_TRANS("Celeste Level"), + NEEDS_TRANS("Phaser Level"), NEEDS_TRANS("Data Button increment"), NEEDS_TRANS("Data Button decrement"), NEEDS_TRANS("Non-registered Parameter (fine)"), + NEEDS_TRANS("Non-registered Parameter (coarse)"), NEEDS_TRANS("Registered Parameter (fine)"), NEEDS_TRANS("Registered Parameter (coarse)"), + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, + NEEDS_TRANS("All Sound Off"), NEEDS_TRANS("All Controllers Off"), NEEDS_TRANS("Local Keyboard (on/off)"), NEEDS_TRANS("All Notes Off"), + NEEDS_TRANS("Omni Mode Off"), NEEDS_TRANS("Omni Mode On"), NEEDS_TRANS("Mono Operation"), NEEDS_TRANS("Poly Operation") }; return isPositiveAndBelow (n, numElementsInArray (names)) ? names[n] : nullptr; diff --git a/JuceLibraryCode/modules/juce_audio_basics/synthesisers/juce_Synthesiser.cpp b/JuceLibraryCode/modules/juce_audio_basics/synthesisers/juce_Synthesiser.cpp index 6af687e..1aa248b 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/synthesisers/juce_Synthesiser.cpp +++ b/JuceLibraryCode/modules/juce_audio_basics/synthesisers/juce_Synthesiser.cpp @@ -56,6 +56,8 @@ void SynthesiserVoice::clearCurrentNote() currentlyPlayingSound = nullptr; } +void SynthesiserVoice::aftertouchChanged (int) {} + //============================================================================== Synthesiser::Synthesiser() : sampleRate (0), @@ -191,6 +193,10 @@ void Synthesiser::handleMidiEvent (const MidiMessage& m) handlePitchWheel (channel, wheelPos); } + else if (m.isAftertouch()) + { + handleAftertouch (m.getChannel(), m.getNoteNumber(), m.getAfterTouchValue()); + } else if (m.isController()) { handleController (m.getChannel(), m.getControllerNumber(), m.getControllerValue()); @@ -338,6 +344,20 @@ void Synthesiser::handleController (const int midiChannel, } } +void Synthesiser::handleAftertouch (int midiChannel, int midiNoteNumber, int aftertouchValue) +{ + const ScopedLock sl (lock); + + for (int i = voices.size(); --i >= 0;) + { + SynthesiserVoice* const voice = voices.getUnchecked (i); + + if (voice->getCurrentlyPlayingNote() == midiNoteNumber + && (midiChannel <= 0 || voice->isPlayingChannel (midiChannel))) + voice->aftertouchChanged (aftertouchValue); + } +} + void Synthesiser::handleSustainPedal (int midiChannel, bool isDown) { jassert (midiChannel > 0 && midiChannel <= 16); diff --git a/JuceLibraryCode/modules/juce_audio_basics/synthesisers/juce_Synthesiser.h b/JuceLibraryCode/modules/juce_audio_basics/synthesisers/juce_Synthesiser.h index eb443cf..7202b4b 100644 --- a/JuceLibraryCode/modules/juce_audio_basics/synthesisers/juce_Synthesiser.h +++ b/JuceLibraryCode/modules/juce_audio_basics/synthesisers/juce_Synthesiser.h @@ -116,7 +116,6 @@ public: virtual bool canPlaySound (SynthesiserSound*) = 0; /** Called to start a new note. - This will be called during the rendering callback, so must be fast and thread-safe. */ virtual void startNote (int midiNoteNumber, @@ -142,12 +141,17 @@ public: /** Called to let the voice know that the pitch wheel has been moved. This will be called during the rendering callback, so must be fast and thread-safe. */ - virtual void pitchWheelMoved (int newValue) = 0; + virtual void pitchWheelMoved (int newPitchWheelValue) = 0; /** Called to let the voice know that a midi controller has been moved. This will be called during the rendering callback, so must be fast and thread-safe. */ - virtual void controllerMoved (int controllerNumber, int newValue) = 0; + virtual void controllerMoved (int controllerNumber, int newControllerValue) = 0; + + /** Called to let the voice know that the aftertouch has changed. + This will be called during the rendering callback, so must be fast and thread-safe. + */ + virtual void aftertouchChanged (int newAftertouchValue); //============================================================================== /** Renders the next block of data for this voice. @@ -186,6 +190,14 @@ public: */ void setCurrentPlaybackSampleRate (double newRate); + /** Returns true if the key that triggered this voice is still held down. + Note that the voice may still be playing after the key was released (e.g because the + sostenuto pedal is down). + */ + bool isKeyDown() const noexcept { return keyIsDown; } + + /** Returns true if the sostenuto pedal is currently active for this voice. */ + bool isSostenutoPedalDown() const noexcept { return sostenutoPedalDown; } protected: //============================================================================== @@ -218,8 +230,7 @@ private: int currentlyPlayingNote; uint32 noteOnTime; SynthesiserSound::Ptr currentlyPlayingSound; - bool keyIsDown; // the voice may still be playing when the key is not down (i.e. sustain pedal) - bool sostenutoPedalDown; + bool keyIsDown, sostenutoPedalDown; JUCE_LEAK_DETECTOR (SynthesiserVoice) }; @@ -400,6 +411,21 @@ public: int controllerNumber, int controllerValue); + /** Sends an aftertouch message. + + This will send an aftertouch message to any voices that are playing sounds on + the given midi channel and note number. + + This method will be called automatically according to the midi data passed into + renderNextBlock(), but may be called explicitly too. + + @param midiChannel the midi channel, from 1 to 16 inclusive + @param midiNoteNumber the midi note number, 0 to 127 + @param aftertouchValue the aftertouch value, between 0 and 127, + as returned by MidiMessage::getAftertouchValue() + */ + virtual void handleAftertouch (int midiChannel, int midiNoteNumber, int aftertouchValue); + /** Handles a sustain pedal event. */ virtual void handleSustainPedal (int midiChannel, bool isDown); diff --git a/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.cpp b/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.cpp index 9d2041d..aeff3c6 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.cpp +++ b/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.cpp @@ -361,8 +361,8 @@ void AudioDeviceManager::getAudioDeviceSetup (AudioDeviceSetup& setup) void AudioDeviceManager::deleteCurrentDevice() { currentAudioDevice = nullptr; - currentSetup.inputDeviceName = String::empty; - currentSetup.outputDeviceName = String::empty; + currentSetup.inputDeviceName.clear(); + currentSetup.outputDeviceName.clear(); } void AudioDeviceManager::setCurrentAudioDeviceType (const String& type, @@ -408,15 +408,15 @@ String AudioDeviceManager::setAudioDeviceSetup (const AudioDeviceSetup& newSetup jassert (&newSetup != ¤tSetup); // this will have no effect if (newSetup == currentSetup && currentAudioDevice != nullptr) - return String::empty; + return String(); if (! (newSetup == currentSetup)) sendChangeMessage(); stopDevice(); - const String newInputDeviceName (numInputChansNeeded == 0 ? String::empty : newSetup.inputDeviceName); - const String newOutputDeviceName (numOutputChansNeeded == 0 ? String::empty : newSetup.outputDeviceName); + const String newInputDeviceName (numInputChansNeeded == 0 ? String() : newSetup.inputDeviceName); + const String newOutputDeviceName (numOutputChansNeeded == 0 ? String() : newSetup.outputDeviceName); String error; AudioIODeviceType* type = getCurrentDeviceTypeObject(); @@ -428,7 +428,7 @@ String AudioDeviceManager::setAudioDeviceSetup (const AudioDeviceSetup& newSetup if (treatAsChosenDevice) updateXml(); - return String::empty; + return String(); } if (currentSetup.inputDeviceName != newInputDeviceName @@ -524,16 +524,16 @@ double AudioDeviceManager::chooseBestSampleRate (double rate) const { jassert (currentAudioDevice != nullptr); - if (rate > 0) - for (int i = currentAudioDevice->getNumSampleRates(); --i >= 0;) - if (currentAudioDevice->getSampleRate (i) == rate) - return rate; + const Array rates (currentAudioDevice->getAvailableSampleRates()); + + if (rate > 0 && rates.contains (rate)) + return rate; double lowestAbove44 = 0.0; - for (int i = currentAudioDevice->getNumSampleRates(); --i >= 0;) + for (int i = rates.size(); --i >= 0;) { - const double sr = currentAudioDevice->getSampleRate (i); + const double sr = rates[i]; if (sr >= 44100.0 && (lowestAbove44 < 1.0 || sr < lowestAbove44)) lowestAbove44 = sr; @@ -542,17 +542,15 @@ double AudioDeviceManager::chooseBestSampleRate (double rate) const if (lowestAbove44 > 0.0) return lowestAbove44; - return currentAudioDevice->getSampleRate (0); + return rates[0]; } int AudioDeviceManager::chooseBestBufferSize (int bufferSize) const { jassert (currentAudioDevice != nullptr); - if (bufferSize > 0) - for (int i = currentAudioDevice->getNumBufferSizesAvailable(); --i >= 0;) - if (currentAudioDevice->getBufferSizeSamples(i) == bufferSize) - return bufferSize; + if (bufferSize > 0 && currentAudioDevice->getAvailableBufferSizes().contains (bufferSize)) + return bufferSize; return currentAudioDevice->getDefaultBufferSize(); } diff --git a/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.h b/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.h index 14432bd..d821593 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.h +++ b/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.h @@ -190,7 +190,7 @@ public: int numOutputChannelsNeeded, const XmlElement* savedState, bool selectDefaultDeviceOnFailure, - const String& preferredDefaultDeviceName = String::empty, + const String& preferredDefaultDeviceName = String(), const AudioDeviceSetup* preferredSetupOptions = 0); /** Returns some XML representing the current state of the manager. diff --git a/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioIODevice.h b/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioIODevice.h index 7028072..6817ae6 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioIODevice.h +++ b/JuceLibraryCode/modules/juce_audio_devices/audio_io/juce_AudioIODevice.h @@ -157,47 +157,19 @@ public: virtual StringArray getInputChannelNames() = 0; //============================================================================== - /** Returns the number of sample-rates this device supports. - - To find out which rates are available on this device, use this method to - find out how many there are, and getSampleRate() to get the rates. - - @see getSampleRate - */ - virtual int getNumSampleRates() = 0; - - /** Returns one of the sample-rates this device supports. - - To find out which rates are available on this device, use getNumSampleRates() to - find out how many there are, and getSampleRate() to get the individual rates. - - The sample rate is set by the open() method. - - (Note that for DirectSound some rates might not work, depending on combinations - of i/o channels that are being opened). - - @see getNumSampleRates + /** Returns the set of sample-rates this device supports. + @see getCurrentSampleRate */ - virtual double getSampleRate (int index) = 0; - - /** Returns the number of sizes of buffer that are available. + virtual Array getAvailableSampleRates() = 0; - @see getBufferSizeSamples, getDefaultBufferSize + /** Returns the set of buffer sizes that are available. + @see getCurrentBufferSizeSamples, getDefaultBufferSize */ - virtual int getNumBufferSizesAvailable() = 0; - - /** Returns one of the possible buffer-sizes. - - @param index the index of the buffer-size to use, from 0 to getNumBufferSizesAvailable() - 1 - @returns a number of samples - @see getNumBufferSizesAvailable, getDefaultBufferSize - */ - virtual int getBufferSizeSamples (int index) = 0; + virtual Array getAvailableBufferSizes() = 0; /** Returns the default buffer-size to use. - @returns a number of samples - @see getNumBufferSizesAvailable, getBufferSizeSamples + @see getAvailableBufferSizes */ virtual int getDefaultBufferSize() = 0; @@ -209,9 +181,9 @@ public: @param outputChannels a BigInteger in which a set bit indicates that the corresponding output channel should be enabled @param sampleRate the sample rate to try to use - to find out which rates are - available, see getNumSampleRates() and getSampleRate() + available, see getAvailableSampleRates() @param bufferSizeSamples the size of i/o buffer to use - to find out the available buffer - sizes, see getNumBufferSizesAvailable() and getBufferSizeSamples() + sizes, see getAvailableBufferSizes() @returns an error description if there's a problem, or an empty string if it succeeds in opening the device @see close diff --git a/JuceLibraryCode/modules/juce_audio_devices/juce_module_info b/JuceLibraryCode/modules/juce_audio_devices/juce_module_info index bb5e193..5afbb4e 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/juce_module_info +++ b/JuceLibraryCode/modules/juce_audio_devices/juce_module_info @@ -1,7 +1,7 @@ { "id": "juce_audio_devices", "name": "JUCE audio and midi I/O device classes", - "version": "3.0.0", + "version": "3.0.1", "description": "Classes to play and record from audio and midi i/o devices.", "website": "http://www.juce.com/juce", "license": "GPL/Commercial", diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/juce_android_Audio.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/juce_android_Audio.cpp index e21f946..2e3cd7e 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/native/juce_android_Audio.cpp +++ b/JuceLibraryCode/modules/juce_audio_devices/native/juce_android_Audio.cpp @@ -105,7 +105,7 @@ public: close(); } - StringArray getOutputChannelNames() + StringArray getOutputChannelNames() override { StringArray s; s.add ("Left"); @@ -113,7 +113,7 @@ public: return s; } - StringArray getInputChannelNames() + StringArray getInputChannelNames() override { StringArray s; @@ -130,36 +130,43 @@ public: return s; } - int getNumSampleRates() { return 1;} - double getSampleRate (int index) { return sampleRate; } - - int getDefaultBufferSize() { return 2048; } - int getNumBufferSizesAvailable() { return 50; } + Array getAvailableSampleRates() override + { + Array r; + r.add ((double) sampleRate); + return r; + } - int getBufferSizeSamples (int index) + Array getAvailableBufferSizes() override { + Array b; int n = 16; - for (int i = 0; i < index; ++i) + + for (int i = 0; i < 50; ++i) + { + b.add (n); n += n < 64 ? 16 : (n < 512 ? 32 : (n < 1024 ? 64 : (n < 2048 ? 128 : 256))); + } - return n; + return b; } + int getDefaultBufferSize() override { return 2048; } String open (const BigInteger& inputChannels, const BigInteger& outputChannels, double requestedSampleRate, - int bufferSize) + int bufferSize) override { close(); if (sampleRate != (int) requestedSampleRate) return "Sample rate not allowed"; - lastError = String::empty; + lastError.clear(); int preferredBufferSize = (bufferSize <= 0) ? getDefaultBufferSize() : bufferSize; numDeviceInputChannels = 0; @@ -227,7 +234,7 @@ public: return lastError; } - void close() + void close() override { if (isRunning) { @@ -237,18 +244,18 @@ public: } } - int getOutputLatencyInSamples() { return (minBufferSizeOut * 3) / 4; } - int getInputLatencyInSamples() { return (minBufferSizeIn * 3) / 4; } - bool isOpen() { return isRunning; } - int getCurrentBufferSizeSamples() { return actualBufferSize; } - int getCurrentBitDepth() { return 16; } - double getCurrentSampleRate() { return sampleRate; } - BigInteger getActiveOutputChannels() const { return activeOutputChans; } - BigInteger getActiveInputChannels() const { return activeInputChans; } - String getLastError() { return lastError; } - bool isPlaying() { return isRunning && callback != 0; } - - void start (AudioIODeviceCallback* newCallback) + int getOutputLatencyInSamples() override { return (minBufferSizeOut * 3) / 4; } + int getInputLatencyInSamples() override { return (minBufferSizeIn * 3) / 4; } + bool isOpen() override { return isRunning; } + int getCurrentBufferSizeSamples() override { return actualBufferSize; } + int getCurrentBitDepth() override { return 16; } + double getCurrentSampleRate() override { return sampleRate; } + BigInteger getActiveOutputChannels() const override { return activeOutputChans; } + BigInteger getActiveInputChannels() const override { return activeInputChans; } + String getLastError() override { return lastError; } + bool isPlaying() override { return isRunning && callback != 0; } + + void start (AudioIODeviceCallback* newCallback) override { if (isRunning && callback != newCallback) { @@ -260,7 +267,7 @@ public: } } - void stop() + void stop() override { if (isRunning) { diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/juce_android_OpenSL.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/juce_android_OpenSL.cpp index d15629b..eb60dcc 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/native/juce_android_OpenSL.cpp +++ b/JuceLibraryCode/modules/juce_audio_devices/native/juce_android_OpenSL.cpp @@ -30,9 +30,6 @@ bool isOpenSLAvailable() return library.open ("libOpenSLES.so"); } -const unsigned short openSLRates[] = { 8000, 16000, 32000, 44100, 48000 }; -const unsigned short openSLBufferSizes[] = { 256, 512, 768, 1024, 1280, 1600 }; // must all be multiples of the block size - //============================================================================== class OpenSLAudioIODevice : public AudioIODevice, public Thread @@ -66,7 +63,7 @@ public: bool openedOk() const { return engine.outputMixObject != nullptr; } - StringArray getOutputChannelNames() + StringArray getOutputChannelNames() override { StringArray s; s.add ("Left"); @@ -74,38 +71,33 @@ public: return s; } - StringArray getInputChannelNames() + StringArray getInputChannelNames() override { StringArray s; s.add ("Audio Input"); return s; } - int getNumSampleRates() { return numElementsInArray (openSLRates); } - - double getSampleRate (int index) + Array getAvailableSampleRates() override { - jassert (index >= 0 && index < getNumSampleRates()); - return (int) openSLRates [index]; + static const double rates[] = { 8000.0, 16000.0, 32000.0, 44100.0, 48000.0 }; + return Array (rates, numElementsInArray (rates)); } - int getDefaultBufferSize() { return 1024; } - int getNumBufferSizesAvailable() { return numElementsInArray (openSLBufferSizes); } - - int getBufferSizeSamples (int index) + Array getAvailableBufferSizes() override { - jassert (index >= 0 && index < getNumBufferSizesAvailable()); - return (int) openSLBufferSizes [index]; + static const int sizes[] = { 256, 512, 768, 1024, 1280, 1600 }; // must all be multiples of the block size + return Array (sizes, numElementsInArray (sizes)); } String open (const BigInteger& inputChannels, const BigInteger& outputChannels, double requestedSampleRate, - int bufferSize) + int bufferSize) override { close(); - lastError = String::empty; + lastError.clear(); sampleRate = (int) requestedSampleRate; int preferredBufferSize = (bufferSize <= 0) ? getDefaultBufferSize() : bufferSize; @@ -133,7 +125,7 @@ public: return lastError; } - void close() + void close() override { stop(); stopThread (6000); @@ -142,18 +134,19 @@ public: player = nullptr; } - int getOutputLatencyInSamples() { return outputLatency; } - int getInputLatencyInSamples() { return inputLatency; } - bool isOpen() { return deviceOpen; } - int getCurrentBufferSizeSamples() { return actualBufferSize; } - int getCurrentBitDepth() { return 16; } - double getCurrentSampleRate() { return sampleRate; } - BigInteger getActiveOutputChannels() const { return activeOutputChans; } - BigInteger getActiveInputChannels() const { return activeInputChans; } - String getLastError() { return lastError; } - bool isPlaying() { return callback != nullptr; } - - void start (AudioIODeviceCallback* newCallback) + int getDefaultBufferSize() override { return 1024; } + int getOutputLatencyInSamples() override { return outputLatency; } + int getInputLatencyInSamples() override { return inputLatency; } + bool isOpen() override { return deviceOpen; } + int getCurrentBufferSizeSamples() override { return actualBufferSize; } + int getCurrentBitDepth() override { return 16; } + double getCurrentSampleRate() override { return sampleRate; } + BigInteger getActiveOutputChannels() const override { return activeOutputChans; } + BigInteger getActiveInputChannels() const override { return activeInputChans; } + String getLastError() override { return lastError; } + bool isPlaying() override { return callback != nullptr; } + + void start (AudioIODeviceCallback* newCallback) override { stop(); @@ -166,7 +159,7 @@ public: } } - void stop() + void stop() override { if (AudioIODeviceCallback* const oldCallback = setCallback (nullptr)) oldCallback->audioDeviceStopped(); diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/juce_ios_Audio.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/juce_ios_Audio.cpp index adbb1dc..a5a1b31 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/native/juce_ios_Audio.cpp +++ b/JuceLibraryCode/modules/juce_audio_devices/native/juce_ios_Audio.cpp @@ -48,7 +48,7 @@ public: close(); } - StringArray getOutputChannelNames() + StringArray getOutputChannelNames() override { StringArray s; s.add ("Left"); @@ -56,7 +56,7 @@ public: return s; } - StringArray getInputChannelNames() + StringArray getInputChannelNames() override { StringArray s; if (audioInputIsAvailable) @@ -67,20 +67,27 @@ public: return s; } - int getNumSampleRates() { return jmax (1, sampleRates.size()); } - double getSampleRate (int index) { return sampleRates.size() > 0 ? sampleRates [index] : sampleRate; } + Array getAvailableSampleRates() override { return sampleRates; } - int getNumBufferSizesAvailable() { return 6; } - int getBufferSizeSamples (int index) { return 1 << (jlimit (0, 5, index) + 6); } - int getDefaultBufferSize() { return 1024; } + Array getAvailableBufferSizes() override + { + Array r; + + for (int i = 6; i < 12; ++i) + r.add (1 << i); + + return r; + } + + int getDefaultBufferSize() override { return 1024; } String open (const BigInteger& inputChannelsWanted, const BigInteger& outputChannelsWanted, - double targetSampleRate, int bufferSize) + double targetSampleRate, int bufferSize) override { close(); - lastError = String::empty; + lastError.clear(); preferredBufferSize = (bufferSize <= 0) ? getDefaultBufferSize() : bufferSize; // xxx set up channel mapping @@ -127,7 +134,7 @@ public: return lastError; } - void close() + void close() override { if (isRunning) { @@ -146,19 +153,19 @@ public: } } - bool isOpen() { return isRunning; } + bool isOpen() override { return isRunning; } - int getCurrentBufferSizeSamples() { return actualBufferSize; } - double getCurrentSampleRate() { return sampleRate; } - int getCurrentBitDepth() { return 16; } + int getCurrentBufferSizeSamples() override { return actualBufferSize; } + double getCurrentSampleRate() override { return sampleRate; } + int getCurrentBitDepth() override { return 16; } - BigInteger getActiveOutputChannels() const { return activeOutputChans; } - BigInteger getActiveInputChannels() const { return activeInputChans; } + BigInteger getActiveOutputChannels() const override { return activeOutputChans; } + BigInteger getActiveInputChannels() const override { return activeInputChans; } - int getOutputLatencyInSamples() { return 0; } //xxx - int getInputLatencyInSamples() { return 0; } //xxx + int getOutputLatencyInSamples() override { return 0; } //xxx + int getInputLatencyInSamples() override { return 0; } //xxx - void start (AudioIODeviceCallback* newCallback) + void start (AudioIODeviceCallback* newCallback) override { if (isRunning && callback != newCallback) { @@ -170,7 +177,7 @@ public: } } - void stop() + void stop() override { if (isRunning) { @@ -187,8 +194,8 @@ public: } } - bool isPlaying() { return isRunning && callback != nullptr; } - String getLastError() { return lastError; } + bool isPlaying() override { return isRunning && callback != nullptr; } + String getLastError() override { return lastError; } private: //================================================================================================== @@ -436,6 +443,11 @@ private: isRunning = true; AudioSessionSetActive (true); AudioOutputUnitStart (audioUnit); + + const ScopedLock sl (callbackLock); + + if (callback != nullptr) + callback->audioDeviceError ("iOS audio session resumed"); } } diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/juce_linux_ALSA.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/juce_linux_ALSA.cpp index 7d47f05..9bc9a26 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/native/juce_linux_ALSA.cpp +++ b/JuceLibraryCode/modules/juce_audio_devices/native/juce_linux_ALSA.cpp @@ -47,7 +47,7 @@ namespace #define JUCE_ALSA_FAILED(x) failed (x) -static void getDeviceSampleRates (snd_pcm_t* handle, Array & rates) +static void getDeviceSampleRates (snd_pcm_t* handle, Array& rates) { const int ratesToTry[] = { 22050, 32000, 44100, 48000, 88200, 96000, 176400, 192000, 0 }; @@ -59,7 +59,7 @@ static void getDeviceSampleRates (snd_pcm_t* handle, Array & rates) if (snd_pcm_hw_params_any (handle, hwParams) >= 0 && snd_pcm_hw_params_test_rate (handle, hwParams, ratesToTry[i], 0) == 0) { - rates.addIfNotAlreadyThere (ratesToTry[i]); + rates.addIfNotAlreadyThere ((double) ratesToTry[i]); } } } @@ -91,7 +91,7 @@ static void getDeviceProperties (const String& deviceID, unsigned int& maxChansOut, unsigned int& minChansIn, unsigned int& maxChansIn, - Array & rates, + Array& rates, bool testOutput, bool testInput) { @@ -482,7 +482,7 @@ public: { close(); - error = String::empty; + error.clear(); sampleRate = newSampleRate; bufferSize = newBufferSize; @@ -722,7 +722,7 @@ public: int bufferSize, outputLatency, inputLatency; BigInteger currentInputChans, currentOutputChans; - Array sampleRates; + Array sampleRates; StringArray channelNamesOut, channelNamesIn; AudioIODeviceCallback* callback; @@ -798,31 +798,34 @@ public: close(); } - StringArray getOutputChannelNames() { return internal.channelNamesOut; } - StringArray getInputChannelNames() { return internal.channelNamesIn; } + StringArray getOutputChannelNames() override { return internal.channelNamesOut; } + StringArray getInputChannelNames() override { return internal.channelNamesIn; } - int getNumSampleRates() { return internal.sampleRates.size(); } - double getSampleRate (int index) { return internal.sampleRates [index]; } + Array getAvailableSampleRates() override { return internal.sampleRates; } - int getDefaultBufferSize() { return 512; } - int getNumBufferSizesAvailable() { return 50; } - - int getBufferSizeSamples (int index) + Array getAvailableBufferSizes() override { + Array r; int n = 16; - for (int i = 0; i < index; ++i) + + for (int i = 0; i < 50; ++i) + { + r.add (n); n += n < 64 ? 16 : (n < 512 ? 32 : (n < 1024 ? 64 : (n < 2048 ? 128 : 256))); + } - return n; + return r; } + int getDefaultBufferSize() override { return 512; } + String open (const BigInteger& inputChannels, const BigInteger& outputChannels, double sampleRate, - int bufferSizeSamples) + int bufferSizeSamples) override { close(); @@ -831,11 +834,13 @@ public: if (sampleRate <= 0) { - for (int i = 0; i < getNumSampleRates(); ++i) + for (int i = 0; i < internal.sampleRates.size(); ++i) { - if (getSampleRate (i) >= 44100) + double rate = internal.sampleRates[i]; + + if (rate >= 44100) { - sampleRate = getSampleRate (i); + sampleRate = rate; break; } } @@ -848,28 +853,28 @@ public: return internal.error; } - void close() + void close() override { stop(); internal.close(); isOpen_ = false; } - bool isOpen() { return isOpen_; } - bool isPlaying() { return isStarted && internal.error.isEmpty(); } - String getLastError() { return internal.error; } + bool isOpen() override { return isOpen_; } + bool isPlaying() override { return isStarted && internal.error.isEmpty(); } + String getLastError() override { return internal.error; } - int getCurrentBufferSizeSamples() { return internal.bufferSize; } - double getCurrentSampleRate() { return internal.sampleRate; } - int getCurrentBitDepth() { return internal.getBitDepth(); } + int getCurrentBufferSizeSamples() override { return internal.bufferSize; } + double getCurrentSampleRate() override { return internal.sampleRate; } + int getCurrentBitDepth() override { return internal.getBitDepth(); } - BigInteger getActiveOutputChannels() const { return internal.currentOutputChans; } - BigInteger getActiveInputChannels() const { return internal.currentInputChans; } + BigInteger getActiveOutputChannels() const override { return internal.currentOutputChans; } + BigInteger getActiveInputChannels() const override { return internal.currentInputChans; } - int getOutputLatencyInSamples() { return internal.outputLatency; } - int getInputLatencyInSamples() { return internal.inputLatency; } + int getOutputLatencyInSamples() override { return internal.outputLatency; } + int getInputLatencyInSamples() override { return internal.inputLatency; } - void start (AudioIODeviceCallback* callback) + void start (AudioIODeviceCallback* callback) override { if (! isOpen_) callback = nullptr; @@ -882,7 +887,7 @@ public: isStarted = (callback != nullptr); } - void stop() + void stop() override { AudioIODeviceCallback* const oldCallback = internal.callback; @@ -1002,7 +1007,7 @@ private: { unsigned int minChansOut = 0, maxChansOut = 0; unsigned int minChansIn = 0, maxChansIn = 0; - Array rates; + Array rates; bool isInput = inputName.isNotEmpty(), isOutput = outputName.isNotEmpty(); getDeviceProperties (id, minChansOut, maxChansOut, minChansIn, maxChansIn, rates, isOutput, isInput); diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/juce_linux_JackAudio.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/juce_linux_JackAudio.cpp index 08962a0..2e57d10 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/native/juce_linux_JackAudio.cpp +++ b/JuceLibraryCode/modules/juce_audio_devices/native/juce_linux_JackAudio.cpp @@ -218,16 +218,36 @@ public: return names; } - StringArray getOutputChannelNames() { return getChannelNames (false); } - StringArray getInputChannelNames() { return getChannelNames (true); } - int getNumSampleRates() { return client != nullptr ? 1 : 0; } - double getSampleRate (int /*index*/) { return client != nullptr ? juce::jack_get_sample_rate (client) : 0; } - int getNumBufferSizesAvailable() { return client != nullptr ? 1 : 0; } - int getBufferSizeSamples (int /*index*/) { return getDefaultBufferSize(); } - int getDefaultBufferSize() { return client != nullptr ? juce::jack_get_buffer_size (client) : 0; } + StringArray getOutputChannelNames() override { return getChannelNames (false); } + StringArray getInputChannelNames() override { return getChannelNames (true); } + + Array getAvailableSampleRates() override + { + Array rates; + + if (client != nullptr) + rates.add (juce::jack_get_sample_rate (client)); + + return rates; + } + + Array getAvailableBufferSizes() override + { + Array sizes; + + if (client != nullptr) + sizes.add (juce::jack_get_buffer_size (client)); + + return sizes; + } + + int getDefaultBufferSize() override { return getCurrentBufferSizeSamples(); } + int getCurrentBufferSizeSamples() override { return client != nullptr ? juce::jack_get_buffer_size (client) : 0; } + double getCurrentSampleRate() override { return client != nullptr ? juce::jack_get_sample_rate (client) : 0; } + String open (const BigInteger& inputChannels, const BigInteger& outputChannels, - double /* sampleRate */, int /* bufferSizeSamples */) + double /* sampleRate */, int /* bufferSizeSamples */) override { if (client == nullptr) { @@ -235,7 +255,7 @@ public: return lastError; } - lastError = String::empty; + lastError.clear(); close(); juce::jack_set_process_callback (client, processCallback, this); @@ -273,7 +293,7 @@ public: return lastError; } - void close() + void close() override { stop(); @@ -288,7 +308,7 @@ public: deviceIsOpen = false; } - void start (AudioIODeviceCallback* newCallback) + void start (AudioIODeviceCallback* newCallback) override { if (deviceIsOpen && newCallback != callback) { @@ -307,22 +327,20 @@ public: } } - void stop() + void stop() override { start (nullptr); } - bool isOpen() { return deviceIsOpen; } - bool isPlaying() { return callback != nullptr; } - int getCurrentBufferSizeSamples() { return getBufferSizeSamples (0); } - double getCurrentSampleRate() { return getSampleRate (0); } - int getCurrentBitDepth() { return 32; } - String getLastError() { return lastError; } + bool isOpen() override { return deviceIsOpen; } + bool isPlaying() override { return callback != nullptr; } + int getCurrentBitDepth() override { return 32; } + String getLastError() override { return lastError; } - BigInteger getActiveOutputChannels() const { return activeOutputChannels; } - BigInteger getActiveInputChannels() const { return activeInputChannels; } + BigInteger getActiveOutputChannels() const override { return activeOutputChannels; } + BigInteger getActiveInputChannels() const override { return activeInputChannels; } - int getOutputLatencyInSamples() + int getOutputLatencyInSamples() override { int latency = 0; @@ -332,7 +350,7 @@ public: return latency; } - int getInputLatencyInSamples() + int getInputLatencyInSamples() override { int latency = 0; diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/juce_mac_CoreAudio.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/juce_mac_CoreAudio.cpp index 60b82b7..799207c 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/native/juce_mac_CoreAudio.cpp +++ b/JuceLibraryCode/modules/juce_audio_devices/native/juce_mac_CoreAudio.cpp @@ -23,7 +23,7 @@ */ #if JUCE_COREAUDIO_LOGGING_ENABLED - #define JUCE_COREAUDIOLOG(a) Logger::writeToLog (a) + #define JUCE_COREAUDIOLOG(a) { String camsg ("CoreAudio: "); camsg << a; Logger::writeToLog (camsg); } #else #define JUCE_COREAUDIOLOG(a) #endif @@ -140,7 +140,7 @@ class CoreAudioIODevice; class CoreAudioInternal : private Timer { public: - CoreAudioInternal (CoreAudioIODevice& d, AudioDeviceID id, bool isSlave) + CoreAudioInternal (CoreAudioIODevice& d, AudioDeviceID id) : owner (d), inputLatency (0), outputLatency (0), @@ -148,7 +148,6 @@ public: #if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 audioProcID (0), #endif - isSlaveDevice (isSlave), deviceID (id), started (false), sampleRate (0), @@ -216,12 +215,12 @@ public: pa.mScope = input ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput; pa.mElement = kAudioObjectPropertyElementMaster; - if (OK (AudioObjectGetPropertyDataSize (deviceID, &pa, 0, 0, &size))) + if (OK (AudioObjectGetPropertyDataSize (deviceID, &pa, 0, nullptr, &size))) { HeapBlock bufList; bufList.calloc (size, 1); - if (OK (AudioObjectGetPropertyData (deviceID, &pa, 0, 0, &size, bufList))) + if (OK (AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, bufList))) { const int numStreams = (int) bufList->mNumberBuffers; @@ -232,17 +231,27 @@ public: for (unsigned int j = 0; j < b.mNumberChannels; ++j) { String name; + NSString* nameNSString = nil; + size = sizeof (nameNSString); + + #if JUCE_CLANG + // Very irritating that AudioDeviceGetProperty is marked as deprecated, since + // there seems to be no replacement way of getting the channel names. + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wdeprecated-declarations" + #endif + if (AudioDeviceGetProperty (deviceID, chanNum + 1, input, kAudioDevicePropertyChannelNameCFString, + &size, &nameNSString) == noErr) { - char channelName [256] = { 0 }; - UInt32 nameSize = sizeof (channelName); - UInt32 channelNum = (UInt32) chanNum + 1; - pa.mSelector = kAudioDevicePropertyChannelName; - - if (AudioObjectGetPropertyData (deviceID, &pa, sizeof (channelNum), &channelNum, &nameSize, channelName) == noErr) - name = String::fromUTF8 (channelName, (int) nameSize); + name = nsStringToJuce (nameNSString); + [nameNSString release]; } + #if JUCE_CLANG + #pragma clang diagnostic pop + #endif + if ((input ? activeInputChans : activeOutputChans) [chanNum]) { CallbackDetailsForChannel info = { i, (int) j, (int) b.mNumberChannels }; @@ -265,7 +274,6 @@ public: Array getSampleRatesFromDevice() const { Array newSampleRates; - String rates; AudioObjectPropertyAddress pa; pa.mScope = kAudioObjectPropertyScopeWildcard; @@ -273,12 +281,12 @@ public: pa.mSelector = kAudioDevicePropertyAvailableNominalSampleRates; UInt32 size = 0; - if (OK (AudioObjectGetPropertyDataSize (deviceID, &pa, 0, 0, &size))) + if (OK (AudioObjectGetPropertyDataSize (deviceID, &pa, 0, nullptr, &size))) { HeapBlock ranges; ranges.calloc (size, 1); - if (OK (AudioObjectGetPropertyData (deviceID, &pa, 0, 0, &size, ranges))) + if (OK (AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, ranges))) { static const double possibleRates[] = { 44100.0, 48000.0, 88200.0, 96000.0, 176400.0, 192000.0 }; @@ -289,7 +297,6 @@ public: if (possibleRates[i] >= ranges[j].mMinimum - 2 && possibleRates[i] <= ranges[j].mMaximum + 2) { newSampleRates.add (possibleRates[i]); - rates << possibleRates[i] << ' '; break; } } @@ -298,12 +305,8 @@ public: } if (newSampleRates.size() == 0 && sampleRate > 0) - { newSampleRates.add (sampleRate); - rates << sampleRate; - } - JUCE_COREAUDIOLOG ("rates: " + rates); return newSampleRates; } @@ -317,12 +320,12 @@ public: pa.mSelector = kAudioDevicePropertyBufferFrameSizeRange; UInt32 size = 0; - if (OK (AudioObjectGetPropertyDataSize (deviceID, &pa, 0, 0, &size))) + if (OK (AudioObjectGetPropertyDataSize (deviceID, &pa, 0, nullptr, &size))) { HeapBlock ranges; ranges.calloc (size, 1); - if (OK (AudioObjectGetPropertyData (deviceID, &pa, 0, 0, &size, ranges))) + if (OK (AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, ranges))) { newBufferSizes.add ((int) (ranges[0].mMinimum + 15) & ~15); @@ -357,7 +360,7 @@ public: pa.mElement = kAudioObjectPropertyElementMaster; pa.mSelector = kAudioDevicePropertyLatency; pa.mScope = scope; - AudioObjectGetPropertyData (deviceID, &pa, 0, 0, &size, &lat); + AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, &lat); return (int) lat; } @@ -377,26 +380,25 @@ public: UInt32 isAlive; UInt32 size = sizeof (isAlive); pa.mSelector = kAudioDevicePropertyDeviceIsAlive; - if (OK (AudioObjectGetPropertyData (deviceID, &pa, 0, 0, &size, &isAlive)) && isAlive == 0) + if (OK (AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, &isAlive)) && isAlive == 0) return; Float64 sr; size = sizeof (sr); pa.mSelector = kAudioDevicePropertyNominalSampleRate; - if (OK (AudioObjectGetPropertyData (deviceID, &pa, 0, 0, &size, &sr))) + if (OK (AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, &sr))) sampleRate = sr; UInt32 framesPerBuf = bufferSize; size = sizeof (framesPerBuf); pa.mSelector = kAudioDevicePropertyBufferFrameSize; - AudioObjectGetPropertyData (deviceID, &pa, 0, 0, &size, &framesPerBuf); + AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, &framesPerBuf); Array newBufferSizes (getBufferSizesFromDevice()); Array newSampleRates (getSampleRatesFromDevice()); inputLatency = getLatencyFromDevice (kAudioDevicePropertyScopeInput); outputLatency = getLatencyFromDevice (kAudioDevicePropertyScopeOutput); - JUCE_COREAUDIOLOG ("lat: " + String (inputLatency) + " " + String (outputLatency)); Array newInChans, newOutChans; StringArray newInNames (getChannelInfo (true, newInChans)); @@ -442,11 +444,8 @@ public: pa.mScope = input ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput; pa.mElement = kAudioObjectPropertyElementMaster; - if (OK (AudioObjectGetPropertyData (deviceID, &pa, 0, 0, &transSize, &avt))) - { - DBG (buffer); + if (OK (AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &transSize, &avt))) s.add (buffer); - } } return s; @@ -465,7 +464,7 @@ public: if (deviceID != 0) { - if (OK (AudioObjectGetPropertyData (deviceID, &pa, 0, 0, &size, ¤tSourceID))) + if (OK (AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, ¤tSourceID))) { HeapBlock types; const int num = getAllDataSourcesForDevice (deviceID, types); @@ -508,11 +507,9 @@ public: //============================================================================== String reopen (const BigInteger& inputChannels, const BigInteger& outputChannels, - double newSampleRate, - int bufferSizeSamples) + double newSampleRate, int bufferSizeSamples) { String error; - JUCE_COREAUDIOLOG ("CoreAudio reopen"); callbacksAllowed = false; stopTimer(); @@ -566,11 +563,6 @@ public: error = "Device has no available sample-rates"; else if (bufferSizes.size() == 0) error = "Device has no available buffer-sizes"; - else if (inputDevice != 0) - error = inputDevice->reopen (inputChannels, - outputChannels, - newSampleRate, - bufferSizeSamples); } } @@ -578,7 +570,7 @@ public: return error; } - bool start (AudioIODeviceCallback* cb) + bool start() { if (! started) { @@ -609,13 +601,13 @@ public: } } - if (started) - { - const ScopedLock sl (callbackLock); - callback = cb; - } + return started; + } - return started && (inputDevice == nullptr || inputDevice->start (cb)); + void setCallback (AudioIODeviceCallback* cb) + { + const ScopedLock sl (callbackLock); + callback = cb; } void stop (bool leaveInterruptRunning) @@ -655,7 +647,7 @@ public: pa.mScope = kAudioObjectPropertyScopeWildcard; pa.mElement = kAudioObjectPropertyElementMaster; - OK (AudioObjectGetPropertyData (deviceID, &pa, 0, 0, &size, &running)); + OK (AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, &running)); if (running == 0) break; @@ -663,9 +655,6 @@ public: const ScopedLock sl (callbackLock); } - - if (inputDevice != nullptr) - inputDevice->stop (leaveInterruptRunning); } double getSampleRate() const { return sampleRate; } @@ -678,77 +667,34 @@ public: if (callback != nullptr) { - if (inputDevice == 0) + for (int i = numInputChans; --i >= 0;) { - for (int i = numInputChans; --i >= 0;) - { - const CallbackDetailsForChannel& info = inputChannelInfo.getReference(i); - float* dest = tempInputBuffers [i]; - const float* src = ((const float*) inInputData->mBuffers[info.streamNum].mData) - + info.dataOffsetSamples; - const int stride = info.dataStrideSamples; + const CallbackDetailsForChannel& info = inputChannelInfo.getReference(i); + float* dest = tempInputBuffers [i]; + const float* src = ((const float*) inInputData->mBuffers[info.streamNum].mData) + + info.dataOffsetSamples; + const int stride = info.dataStrideSamples; - if (stride != 0) // if this is zero, info is invalid + if (stride != 0) // if this is zero, info is invalid + { + for (int j = bufferSize; --j >= 0;) { - for (int j = bufferSize; --j >= 0;) - { - *dest++ = *src; - src += stride; - } + *dest++ = *src; + src += stride; } } } - if (! isSlaveDevice) - { - if (inputDevice == 0) - { - callback->audioDeviceIOCallback (const_cast (tempInputBuffers.getData()), - numInputChans, - tempOutputBuffers, - numOutputChans, - bufferSize); - } - else - { - jassert (inputDevice->bufferSize == bufferSize); - - // Sometimes the two linked devices seem to get their callbacks in - // parallel, so we need to lock both devices to stop the input data being - // changed while inside our callback.. - const ScopedLock sl2 (inputDevice->callbackLock); - - callback->audioDeviceIOCallback (const_cast (inputDevice->tempInputBuffers.getData()), - inputDevice->numInputChans, - tempOutputBuffers, - numOutputChans, - bufferSize); - } + callback->audioDeviceIOCallback (const_cast (tempInputBuffers.getData()), + numInputChans, + tempOutputBuffers, + numOutputChans, + bufferSize); - for (int i = numOutputChans; --i >= 0;) - { - const CallbackDetailsForChannel& info = outputChannelInfo.getReference(i); - const float* src = tempOutputBuffers [i]; - float* dest = ((float*) outOutputData->mBuffers[info.streamNum].mData) - + info.dataOffsetSamples; - const int stride = info.dataStrideSamples; - - if (stride != 0) // if this is zero, info is invalid - { - for (int j = bufferSize; --j >= 0;) - { - *dest = *src++; - dest += stride; - } - } - } - } - } - else - { - for (int i = jmin (numOutputChans, outputChannelInfo.size()); --i >= 0;) + for (int i = numOutputChans; --i >= 0;) { const CallbackDetailsForChannel& info = outputChannelInfo.getReference(i); + const float* src = tempOutputBuffers [i]; float* dest = ((float*) outOutputData->mBuffers[info.streamNum].mData) + info.dataOffsetSamples; const int stride = info.dataStrideSamples; @@ -757,12 +703,18 @@ public: { for (int j = bufferSize; --j >= 0;) { - *dest = 0.0f; + *dest = *src++; dest += stride; } } } } + else + { + for (UInt32 i = 0; i < outOutputData->mNumberBuffers; ++i) + zeromem (outOutputData->mBuffers[i].mData, + outOutputData->mBuffers[i].mDataByteSize); + } } // called by callbacks @@ -774,9 +726,9 @@ public: void timerCallback() override { - stopTimer(); - JUCE_COREAUDIOLOG ("CoreAudio device changed callback"); + JUCE_COREAUDIOLOG ("Device changed"); + stopTimer(); const double oldSampleRate = sampleRate; const int oldBufferSize = bufferSize; updateDetailsFromDevice(); @@ -790,28 +742,25 @@ public: int inputLatency, outputLatency; BigInteger activeInputChans, activeOutputChans; StringArray inChanNames, outChanNames; - Array sampleRates; - Array bufferSizes; + Array sampleRates; + Array bufferSizes; AudioIODeviceCallback* callback; #if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 AudioDeviceIOProcID audioProcID; #endif - ScopedPointer inputDevice; - bool isSlaveDevice; - private: CriticalSection callbackLock; AudioDeviceID deviceID; bool started; double sampleRate; int bufferSize; - HeapBlock audioBuffer; + HeapBlock audioBuffer; int numInputChans, numOutputChans; bool callbacksAllowed; Array inputChannelInfo, outputChannelInfo; - HeapBlock tempInputBuffers, tempOutputBuffers; + HeapBlock tempInputBuffers, tempOutputBuffers; //============================================================================== static OSStatus audioIOProc (AudioDeviceID /*inDevice*/, @@ -822,13 +771,13 @@ private: const AudioTimeStamp* /*inOutputTime*/, void* device) { - static_cast (device)->audioCallback (inInputData, outOutputData); + static_cast (device)->audioCallback (inInputData, outOutputData); return noErr; } static OSStatus deviceListenerProc (AudioDeviceID /*inDevice*/, UInt32 /*inLine*/, const AudioObjectPropertyAddress* pa, void* inClientData) { - CoreAudioInternal* const intern = static_cast (inClientData); + CoreAudioInternal* const intern = static_cast (inClientData); switch (pa->mSelector) { @@ -854,7 +803,7 @@ private: } //============================================================================== - static int getAllDataSourcesForDevice (AudioDeviceID deviceID, HeapBlock & types) + static int getAllDataSourcesForDevice (AudioDeviceID deviceID, HeapBlock& types) { AudioObjectPropertyAddress pa; pa.mSelector = kAudioDevicePropertyDataSources; @@ -863,11 +812,11 @@ private: UInt32 size = 0; if (deviceID != 0 - && AudioObjectGetPropertyDataSize (deviceID, &pa, 0, 0, &size) == noErr) + && AudioObjectGetPropertyDataSize (deviceID, &pa, 0, nullptr, &size) == noErr) { types.calloc (size, 1); - if (AudioObjectGetPropertyData (deviceID, &pa, 0, 0, &size, types) == noErr) + if (AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, types) == noErr) return size / (int) sizeof (OSType); } @@ -897,10 +846,8 @@ class CoreAudioIODevice : public AudioIODevice { public: CoreAudioIODevice (const String& deviceName, - AudioDeviceID inputDeviceId, - const int inputIndex_, - AudioDeviceID outputDeviceId, - const int outputIndex_) + AudioDeviceID inputDeviceId, const int inputIndex_, + AudioDeviceID outputDeviceId, const int outputIndex_) : AudioIODevice (deviceName, "CoreAudio"), inputIndex (inputIndex_), outputIndex (outputIndex_), @@ -912,15 +859,11 @@ public: if (outputDeviceId == 0 || outputDeviceId == inputDeviceId) { jassert (inputDeviceId != 0); - - device = new CoreAudioInternal (*this, inputDeviceId, false); + device = new CoreAudioInternal (*this, inputDeviceId); } else { - device = new CoreAudioInternal (*this, outputDeviceId, false); - - if (inputDeviceId != 0) - device->inputDevice = new CoreAudioInternal (*this, inputDeviceId, true); + device = new CoreAudioInternal (*this, outputDeviceId); } internal = device; @@ -928,8 +871,8 @@ public: AudioObjectPropertyAddress pa; pa.mSelector = kAudioObjectPropertySelectorWildcard; - pa.mScope = kAudioObjectPropertyScopeWildcard; - pa.mElement = kAudioObjectPropertyElementWildcard; + pa.mScope = kAudioObjectPropertyScopeWildcard; + pa.mElement = kAudioObjectPropertyElementWildcard; AudioObjectAddPropertyListener (kAudioObjectSystemObject, &pa, hardwareListenerProc, internal); } @@ -946,37 +889,24 @@ public: AudioObjectRemovePropertyListener (kAudioObjectSystemObject, &pa, hardwareListenerProc, internal); } - StringArray getOutputChannelNames() - { - return internal->outChanNames; - } - - StringArray getInputChannelNames() - { - if (internal->inputDevice != 0) - return internal->inputDevice->inChanNames; - - return internal->inChanNames; - } - - bool isOpen() { return isOpen_; } + StringArray getOutputChannelNames() override { return internal->outChanNames; } + StringArray getInputChannelNames() override { return internal->inChanNames; } - int getNumSampleRates() { return internal->sampleRates.size(); } - double getSampleRate (int index) { return internal->sampleRates [index]; } - double getCurrentSampleRate() { return internal->getSampleRate(); } + bool isOpen() override { return isOpen_; } - int getCurrentBitDepth() { return 32; } // no way to find out, so just assume it's high.. + Array getAvailableSampleRates() override { return internal->sampleRates; } + Array getAvailableBufferSizes() override { return internal->bufferSizes; } - int getNumBufferSizesAvailable() { return internal->bufferSizes.size(); } - int getBufferSizeSamples (int index) { return internal->bufferSizes [index]; } - int getCurrentBufferSizeSamples() { return internal->getBufferSize(); } + double getCurrentSampleRate() override { return internal->getSampleRate(); } + int getCurrentBitDepth() override { return 32; } // no way to find out, so just assume it's high.. + int getCurrentBufferSizeSamples() override { return internal->getBufferSize(); } - int getDefaultBufferSize() + int getDefaultBufferSize() override { int best = 0; - for (int i = 0; best < 512 && i < getNumBufferSizesAvailable(); ++i) - best = getBufferSizeSamples(i); + for (int i = 0; best < 512 && i < internal->bufferSizes.size(); ++i) + best = internal->bufferSizes.getUnchecked(i); if (best == 0) best = 512; @@ -986,8 +916,7 @@ public: String open (const BigInteger& inputChannels, const BigInteger& outputChannels, - double sampleRate, - int bufferSizeSamples) + double sampleRate, int bufferSizeSamples) override { isOpen_ = true; @@ -995,11 +924,15 @@ public: bufferSizeSamples = getDefaultBufferSize(); lastError = internal->reopen (inputChannels, outputChannels, sampleRate, bufferSizeSamples); + + JUCE_COREAUDIOLOG ("Opened: " << getName()); + JUCE_COREAUDIOLOG ("Latencies: " << getInputLatencyInSamples() << ' ' << getOutputLatencyInSamples()); + isOpen_ = lastError.isEmpty(); return lastError; } - void close() + void close() override { isOpen_ = false; internal->stop (false); @@ -1007,54 +940,43 @@ public: void restart() { + JUCE_COREAUDIOLOG ("Restarting"); AudioIODeviceCallback* oldCallback = internal->callback; stop(); - - if (oldCallback != nullptr) - start (oldCallback); - } - - BigInteger getActiveOutputChannels() const - { - return internal->activeOutputChans; + start (oldCallback); } - BigInteger getActiveInputChannels() const - { - BigInteger chans (internal->activeInputChans); - - if (internal->inputDevice != nullptr) - chans |= internal->inputDevice->activeInputChans; + BigInteger getActiveOutputChannels() const override { return internal->activeOutputChans; } + BigInteger getActiveInputChannels() const override { return internal->activeInputChans; } - return chans; - } - - int getOutputLatencyInSamples() + int getOutputLatencyInSamples() override { // this seems like a good guess at getting the latency right - comparing // this with a round-trip measurement, it gets it to within a few millisecs // for the built-in mac soundcard - return internal->outputLatency + internal->getBufferSize() * 2; + return internal->outputLatency; } - int getInputLatencyInSamples() + int getInputLatencyInSamples() override { - return internal->inputLatency + internal->getBufferSize() * 2; + return internal->inputLatency; } - void start (AudioIODeviceCallback* callback) + void start (AudioIODeviceCallback* callback) override { if (! isStarted) { if (callback != nullptr) callback->audioDeviceAboutToStart (this); - isStarted = true; - internal->start (callback); + isStarted = internal->start(); + + if (isStarted) + internal->setCallback (callback); } } - void stop() + void stop() override { if (isStarted) { @@ -1068,7 +990,7 @@ public: } } - bool isPlaying() + bool isPlaying() override { if (internal->callback == nullptr) isStarted = false; @@ -1076,7 +998,7 @@ public: return isStarted; } - String getLastError() + String getLastError() override { return lastError; } @@ -1090,12 +1012,10 @@ private: static OSStatus hardwareListenerProc (AudioDeviceID /*inDevice*/, UInt32 /*inLine*/, const AudioObjectPropertyAddress* pa, void* inClientData) { - CoreAudioInternal* const intern = static_cast (inClientData); - switch (pa->mSelector) { case kAudioHardwarePropertyDevices: - intern->deviceDetailsChanged(); + static_cast (inClientData)->deviceDetailsChanged(); break; case kAudioHardwarePropertyDefaultOutputDevice: @@ -1111,10 +1031,646 @@ private: }; //============================================================================== -class CoreAudioIODeviceType : public AudioIODeviceType +class AudioIODeviceCombiner : public AudioIODevice, + private Thread { public: + AudioIODeviceCombiner (const String& deviceName) + : AudioIODevice (deviceName, "CoreAudio"), + Thread (deviceName), callback (nullptr), + currentSampleRate (0), currentBufferSize (0), active (false), + fifos (1, 1) + { + } + + ~AudioIODeviceCombiner() + { + close(); + devices.clear(); + } + + void addDevice (AudioIODevice* device) + { + jassert (device != nullptr); + jassert (! isOpen()); + jassert (! device->isOpen()); + devices.add (new DeviceWrapper (*this, device)); + } + + Array getDevices() const + { + Array devs; + + for (int i = 0; i < devices.size(); ++i) + devs.add (devices.getUnchecked(i)->device); + + return devs; + } + + StringArray getOutputChannelNames() override + { + StringArray names; + + for (int i = 0; i < devices.size(); ++i) + names.addArray (devices.getUnchecked(i)->device->getOutputChannelNames()); + + + names.appendNumbersToDuplicates (false, true); + return names; + } + + StringArray getInputChannelNames() override + { + StringArray names; + + for (int i = 0; i < devices.size(); ++i) + names.addArray (devices.getUnchecked(i)->device->getInputChannelNames()); + + names.appendNumbersToDuplicates (false, true); + return names; + } + + Array getAvailableSampleRates() override + { + Array commonRates; + + for (int i = 0; i < devices.size(); ++i) + { + Array rates (devices.getUnchecked(i)->device->getAvailableSampleRates()); + + if (i == 0) + commonRates = rates; + else + commonRates.removeValuesNotIn (rates); + } + + return commonRates; + } + + Array getAvailableBufferSizes() override + { + Array commonSizes; + + for (int i = 0; i < devices.size(); ++i) + { + Array sizes (devices.getUnchecked(i)->device->getAvailableBufferSizes()); + + if (i == 0) + commonSizes = sizes; + else + commonSizes.removeValuesNotIn (sizes); + } + + return commonSizes; + } + + bool isOpen() override { return active; } + bool isPlaying() override { return callback != nullptr; } + double getCurrentSampleRate() override { return currentSampleRate; } + int getCurrentBufferSizeSamples() override { return currentBufferSize; } + + int getCurrentBitDepth() override + { + int depth = 32; + + for (int i = 0; i < devices.size(); ++i) + depth = jmin (depth, devices.getUnchecked(i)->device->getCurrentBitDepth()); + + return depth; + } + + int getDefaultBufferSize() override + { + int size = 0; + + for (int i = 0; i < devices.size(); ++i) + size = jmax (size, devices.getUnchecked(i)->device->getDefaultBufferSize()); + + return size; + } + + String open (const BigInteger& inputChannels, + const BigInteger& outputChannels, + double sampleRate, int bufferSize) override + { + close(); + active = true; + + if (bufferSize <= 0) + bufferSize = getDefaultBufferSize(); + + if (sampleRate <= 0) + { + Array rates (getAvailableSampleRates()); + + for (int i = 0; i < rates.size() && sampleRate < 44100.0; ++i) + sampleRate = rates.getUnchecked(i); + } + + currentSampleRate = sampleRate; + currentBufferSize = bufferSize; + + const int fifoSize = bufferSize * 3 + 1; + int totalInputChanIndex = 0, totalOutputChanIndex = 0; + int chanIndex = 0; + + for (int i = 0; i < devices.size(); ++i) + { + DeviceWrapper& d = *devices.getUnchecked(i); + + BigInteger ins (inputChannels >> totalInputChanIndex); + BigInteger outs (outputChannels >> totalOutputChanIndex); + + int numIns = d.device->getInputChannelNames().size(); + int numOuts = d.device->getOutputChannelNames().size(); + + totalInputChanIndex += numIns; + totalOutputChanIndex += numOuts; + + String err = d.open (ins, outs, sampleRate, bufferSize, + chanIndex, fifoSize); + + if (err.isNotEmpty()) + { + close(); + lastError = err; + return err; + } + + chanIndex += d.numInputChans + d.numOutputChans; + } + + fifos.setSize (chanIndex, fifoSize); + fifos.clear(); + startThread (9); + + return String(); + } + + void close() override + { + stop(); + stopThread (10000); + fifos.clear(); + active = false; + + for (int i = 0; i < devices.size(); ++i) + devices.getUnchecked(i)->close(); + } + + BigInteger getActiveOutputChannels() const override + { + BigInteger chans; + int start = 0; + + for (int i = 0; i < devices.size(); ++i) + { + const int numChans = devices.getUnchecked(i)->device->getOutputChannelNames().size(); + chans |= (devices.getUnchecked(i)->device->getActiveOutputChannels() << start); + start += numChans; + } + + return chans; + } + + BigInteger getActiveInputChannels() const override + { + BigInteger chans; + int start = 0; + + for (int i = 0; i < devices.size(); ++i) + { + const int numChans = devices.getUnchecked(i)->device->getInputChannelNames().size(); + chans |= (devices.getUnchecked(i)->device->getActiveInputChannels() << start); + start += numChans; + } + + return chans; + } + + int getOutputLatencyInSamples() override + { + int lat = 0; + + for (int i = 0; i < devices.size(); ++i) + lat = jmax (lat, devices.getUnchecked(i)->device->getOutputLatencyInSamples()); + + return lat + currentBufferSize * 2; + } + + int getInputLatencyInSamples() override + { + int lat = 0; + + for (int i = 0; i < devices.size(); ++i) + lat = jmax (lat, devices.getUnchecked(i)->device->getInputLatencyInSamples()); + + return lat + currentBufferSize * 2; + } + + void start (AudioIODeviceCallback* newCallback) override + { + if (callback != newCallback) + { + stop(); + fifos.clear(); + + for (int i = 0; i < devices.size(); ++i) + devices.getUnchecked(i)->start(); + + if (newCallback != nullptr) + newCallback->audioDeviceAboutToStart (this); + + const ScopedLock sl (callbackLock); + callback = newCallback; + } + } + + void stop() override + { + AudioIODeviceCallback* lastCallback = nullptr; + + { + const ScopedLock sl (callbackLock); + std::swap (callback, lastCallback); + } + + for (int i = 0; i < devices.size(); ++i) + devices.getUnchecked(i)->device->stop(); + + if (lastCallback != nullptr) + lastCallback->audioDeviceStopped(); + } + + String getLastError() override + { + return lastError; + } + +private: + CriticalSection callbackLock; + AudioIODeviceCallback* callback; + double currentSampleRate; + int currentBufferSize; + bool active; + String lastError; + + AudioSampleBuffer fifos; + + void run() override + { + const int numSamples = currentBufferSize; + + AudioSampleBuffer buffer (fifos.getNumChannels(), numSamples); + buffer.clear(); + + Array inputChans, outputChans; + + for (int i = 0; i < devices.size(); ++i) + { + DeviceWrapper& d = *devices.getUnchecked(i); + + for (int j = 0; j < d.numInputChans; ++j) inputChans.add (buffer.getSampleData (d.inputIndex + j)); + for (int j = 0; j < d.numOutputChans; ++j) outputChans.add (buffer.getSampleData (d.outputIndex + j)); + } + + const int numInputChans = inputChans.size(); + const int numOutputChans = outputChans.size(); + + inputChans.add (nullptr); + outputChans.add (nullptr); + + const int blockSizeMs = jmax (1, (int) (1000 * numSamples / currentSampleRate)); + + jassert (numInputChans + numOutputChans == buffer.getNumChannels()); + + while (! threadShouldExit()) + { + readInput (buffer, numSamples, blockSizeMs); + + bool didCallback = true; + + { + const ScopedLock sl (callbackLock); + + if (callback != nullptr) + callback->audioDeviceIOCallback ((const float**) inputChans.getRawDataPointer(), numInputChans, + outputChans.getRawDataPointer(), numOutputChans, numSamples); + else + didCallback = false; + } + + if (didCallback) + { + pushOutputData (buffer, numSamples, blockSizeMs); + } + else + { + for (int i = 0; i < numOutputChans; ++i) + FloatVectorOperations::clear (outputChans[i], numSamples); + + reset(); + } + } + } + + void reset() + { + for (int i = 0; i < devices.size(); ++i) + devices.getUnchecked(i)->reset(); + } + + void underrun() + { + } + + void readInput (AudioSampleBuffer& buffer, const int numSamples, const int blockSizeMs) + { + for (int i = 0; i < devices.size(); ++i) + { + DeviceWrapper& d = *devices.getUnchecked(i); + d.done = (d.numInputChans == 0); + } + + for (int tries = 3;;) + { + bool anyRemaining = false; + + for (int i = 0; i < devices.size(); ++i) + { + DeviceWrapper& d = *devices.getUnchecked(i); + + if (! d.done) + { + if (d.isInputReady (numSamples)) + { + d.readInput (buffer, numSamples); + d.done = true; + } + else + anyRemaining = true; + } + } + + if (! anyRemaining) + return; + + if (--tries == 0) + break; + + wait (blockSizeMs); + } + + for (int j = 0; j < devices.size(); ++j) + { + DeviceWrapper& d = *devices.getUnchecked(j); + + if (! d.done) + for (int i = 0; i < d.numInputChans; ++i) + buffer.clear (d.inputIndex + i, 0, numSamples); + } + } + + void pushOutputData (AudioSampleBuffer& buffer, const int numSamples, const int blockSizeMs) + { + for (int i = 0; i < devices.size(); ++i) + { + DeviceWrapper& d = *devices.getUnchecked(i); + d.done = (d.numOutputChans == 0); + } + + for (int tries = 3;;) + { + bool anyRemaining = false; + + for (int i = 0; i < devices.size(); ++i) + { + DeviceWrapper& d = *devices.getUnchecked(i); + + if (! d.done) + { + if (d.isOutputReady (numSamples)) + { + d.pushOutputData (buffer, numSamples); + d.done = true; + } + else + anyRemaining = true; + } + } + + if ((! anyRemaining) || --tries == 0) + return; + + wait (blockSizeMs); + } + } + //============================================================================== + struct DeviceWrapper : private AudioIODeviceCallback + { + DeviceWrapper (AudioIODeviceCombiner& cd, AudioIODevice* d) + : owner (cd), device (d), inputIndex (0), outputIndex (0), + inputFifo (32), outputFifo (32), done (false) + { + } + + ~DeviceWrapper() + { + close(); + } + + String open (const BigInteger& inputChannels, const BigInteger& outputChannels, + double sampleRate, int bufferSize, + int channelIndex, + int fifoSize) + { + inputFifo.setTotalSize (fifoSize); + outputFifo.setTotalSize (fifoSize); + inputFifo.reset(); + outputFifo.reset(); + + String err (device->open (inputChannels, outputChannels, sampleRate, bufferSize)); + + numInputChans = device->getActiveInputChannels().countNumberOfSetBits(); + numOutputChans = device->getActiveOutputChannels().countNumberOfSetBits(); + + inputIndex = channelIndex; + outputIndex = channelIndex + numInputChans; + + return err; + } + + void close() + { + device->close(); + } + + void start() + { + reset(); + device->start (this); + } + + void reset() + { + inputFifo.reset(); + outputFifo.reset(); + } + + bool isInputReady (int numSamples) const noexcept + { + return numInputChans == 0 || inputFifo.getNumReady() >= numSamples; + } + + void readInput (AudioSampleBuffer& destBuffer, int numSamples) + { + if (numInputChans == 0) + return; + + int start1, size1, start2, size2; + inputFifo.prepareToRead (numSamples, start1, size1, start2, size2); + + for (int i = 0; i < numInputChans; ++i) + { + const int index = inputIndex + i; + float* const dest = destBuffer.getSampleData (index); + const float* const src = owner.fifos.getSampleData (index); + + if (size1 > 0) FloatVectorOperations::copy (dest, src + start1, size1); + if (size2 > 0) FloatVectorOperations::copy (dest + size1, src + start2, size2); + } + + inputFifo.finishedRead (size1 + size2); + } + + bool isOutputReady (int numSamples) const noexcept + { + return numOutputChans == 0 || outputFifo.getFreeSpace() >= numSamples; + } + + void pushOutputData (AudioSampleBuffer& srcBuffer, int numSamples) + { + if (numOutputChans == 0) + return; + + int start1, size1, start2, size2; + outputFifo.prepareToWrite (numSamples, start1, size1, start2, size2); + + for (int i = 0; i < numOutputChans; ++i) + { + const int index = outputIndex + i; + float* const dest = owner.fifos.getSampleData (index); + const float* const src = srcBuffer.getSampleData (index); + + if (size1 > 0) FloatVectorOperations::copy (dest + start1, src, size1); + if (size2 > 0) FloatVectorOperations::copy (dest + start2, src + size1, size2); + } + + outputFifo.finishedWrite (size1 + size2); + } + + void audioDeviceIOCallback (const float** inputChannelData, int numInputChannels, + float** outputChannelData, int numOutputChannels, + int numSamples) override + { + AudioSampleBuffer& buf = owner.fifos; + + if (numInputChannels > 0) + { + int start1, size1, start2, size2; + inputFifo.prepareToWrite (numSamples, start1, size1, start2, size2); + + if (size1 + size2 < numSamples) + { + inputFifo.reset(); + inputFifo.prepareToWrite (numSamples, start1, size1, start2, size2); + } + + for (int i = 0; i < numInputChannels; ++i) + { + float* const dest = buf.getSampleData (inputIndex + i); + const float* const src = inputChannelData[i]; + + if (size1 > 0) FloatVectorOperations::copy (dest + start1, src, size1); + if (size2 > 0) FloatVectorOperations::copy (dest + start2, src + size1, size2); + } + + inputFifo.finishedWrite (size1 + size2); + + if (numSamples > size1 + size2) + { + for (int i = 0; i < numInputChans; ++i) + buf.clear (inputIndex + i, size1 + size2, numSamples - (size1 + size2)); + + owner.underrun(); + } + } + + if (numOutputChannels > 0) + { + int start1, size1, start2, size2; + outputFifo.prepareToRead (numSamples, start1, size1, start2, size2); + + if (size1 + size2 < numSamples) + { + Thread::sleep (1); + outputFifo.prepareToRead (numSamples, start1, size1, start2, size2); + } + + for (int i = 0; i < numOutputChannels; ++i) + { + float* const dest = outputChannelData[i]; + const float* const src = buf.getSampleData (outputIndex + i); + + if (size1 > 0) FloatVectorOperations::copy (dest, src + start1, size1); + if (size2 > 0) FloatVectorOperations::copy (dest + size1, src + start2, size2); + } + + outputFifo.finishedRead (size1 + size2); + + if (numSamples > size1 + size2) + { + for (int i = 0; i < numOutputChannels; ++i) + FloatVectorOperations::clear (outputChannelData[i] + (size1 + size2), numSamples - (size1 + size2)); + + owner.underrun(); + } + } + + owner.notify(); + } + + void audioDeviceAboutToStart (AudioIODevice*) override {} + void audioDeviceStopped() override {} + + void audioDeviceError (const String& errorMessage) override + { + const ScopedLock sl (owner.callbackLock); + + if (owner.callback != nullptr) + owner.callback->audioDeviceError (errorMessage); + } + + AudioIODeviceCombiner& owner; + ScopedPointer device; + int inputIndex, numInputChans, outputIndex, numOutputChans; + AbstractFifo inputFifo, outputFifo; + bool done; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DeviceWrapper) + }; + + OwnedArray devices; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioIODeviceCombiner) +}; + + +//============================================================================== +class CoreAudioIODeviceType : public AudioIODeviceType +{ +public: CoreAudioIODeviceType() : AudioIODeviceType ("CoreAudio"), hasScanned (false) @@ -1154,12 +1710,12 @@ public: pa.mScope = kAudioObjectPropertyScopeWildcard; pa.mElement = kAudioObjectPropertyElementMaster; - if (AudioObjectGetPropertyDataSize (kAudioObjectSystemObject, &pa, 0, 0, &size) == noErr) + if (AudioObjectGetPropertyDataSize (kAudioObjectSystemObject, &pa, 0, nullptr, &size) == noErr) { - HeapBlock devs; + HeapBlock devs; devs.calloc (size, 1); - if (AudioObjectGetPropertyData (kAudioObjectSystemObject, &pa, 0, 0, &size, devs) == noErr) + if (AudioObjectGetPropertyData (kAudioObjectSystemObject, &pa, 0, nullptr, &size, devs) == noErr) { const int num = size / (int) sizeof (AudioDeviceID); for (int i = 0; i < num; ++i) @@ -1168,7 +1724,7 @@ public: size = sizeof (name); pa.mSelector = kAudioDevicePropertyDeviceName; - if (AudioObjectGetPropertyData (devs[i], &pa, 0, 0, &size, name) == noErr) + if (AudioObjectGetPropertyData (devs[i], &pa, 0, nullptr, &size, name) == noErr) { const String nameString (String::fromUTF8 (name, (int) strlen (name))); const int numIns = getNumChannels (devs[i], true); @@ -1213,11 +1769,12 @@ public: // get the built-in mic rather than the built-in output with no inputs.. AudioObjectPropertyAddress pa; - pa.mSelector = forInput ? kAudioHardwarePropertyDefaultInputDevice : kAudioHardwarePropertyDefaultOutputDevice; - pa.mScope = kAudioObjectPropertyScopeWildcard; - pa.mElement = kAudioObjectPropertyElementMaster; + pa.mSelector = forInput ? kAudioHardwarePropertyDefaultInputDevice + : kAudioHardwarePropertyDefaultOutputDevice; + pa.mScope = kAudioObjectPropertyScopeWildcard; + pa.mElement = kAudioObjectPropertyElementMaster; - if (AudioObjectGetPropertyData (kAudioObjectSystemObject, &pa, 0, 0, &size, &deviceID) == noErr) + if (AudioObjectGetPropertyData (kAudioObjectSystemObject, &pa, 0, nullptr, &size, &deviceID) == noErr) { if (forInput) { @@ -1240,12 +1797,24 @@ public: { jassert (hasScanned); // need to call scanForDevices() before doing this - CoreAudioIODevice* const d = dynamic_cast (device); - if (d == nullptr) - return -1; + if (CoreAudioIODevice* const d = dynamic_cast (device)) + return asInput ? d->inputIndex + : d->outputIndex; - return asInput ? d->inputIndex - : d->outputIndex; + if (AudioIODeviceCombiner* const d = dynamic_cast (device)) + { + const Array devs (d->getDevices()); + + for (int i = 0; i < devs.size(); ++i) + { + const int index = getIndexOfDevice (devs.getUnchecked(i), asInput); + + if (index >= 0) + return index; + } + } + + return -1; } bool hasSeparateInputsAndOutputs() const { return true; } @@ -1255,27 +1824,41 @@ public: { jassert (hasScanned); // need to call scanForDevices() before doing this - const int inputIndex = inputDeviceNames.indexOf (inputDeviceName); + const int inputIndex = inputDeviceNames.indexOf (inputDeviceName); const int outputIndex = outputDeviceNames.indexOf (outputDeviceName); - String deviceName (outputDeviceName); - if (deviceName.isEmpty()) - deviceName = inputDeviceName; + AudioDeviceID inputDeviceID = inputIds [inputIndex]; + AudioDeviceID outputDeviceID = outputIds [outputIndex]; + + if (inputDeviceID == 0 && outputDeviceID == 0) + return nullptr; + + String combinedName (outputDeviceName.isEmpty() ? inputDeviceName : outputDeviceName); + + if (inputDeviceID == outputDeviceID) + return new CoreAudioIODevice (combinedName, inputDeviceID, inputIndex, outputDeviceID, outputIndex); + + ScopedPointer in, out; + + if (inputDeviceID != 0) + in = new CoreAudioIODevice (inputDeviceName, inputDeviceID, inputIndex, 0, -1); + + if (outputDeviceID != 0) + out = new CoreAudioIODevice (outputDeviceName, 0, -1, outputDeviceID, outputIndex); - if (index >= 0) - return new CoreAudioIODevice (deviceName, - inputIds [inputIndex], - inputIndex, - outputIds [outputIndex], - outputIndex); + if (in == nullptr) return out.release(); + if (out == nullptr) return in.release(); - return nullptr; + ScopedPointer combo (new AudioIODeviceCombiner (combinedName)); + combo->addDevice (in.release()); + combo->addDevice (out.release()); + return combo.release(); } //============================================================================== private: StringArray inputDeviceNames, outputDeviceNames; - Array inputIds, outputIds; + Array inputIds, outputIds; bool hasScanned; @@ -1289,12 +1872,12 @@ private: pa.mScope = input ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput; pa.mElement = kAudioObjectPropertyElementMaster; - if (AudioObjectGetPropertyDataSize (deviceID, &pa, 0, 0, &size) == noErr) + if (AudioObjectGetPropertyDataSize (deviceID, &pa, 0, nullptr, &size) == noErr) { HeapBlock bufList; bufList.calloc (size, 1); - if (AudioObjectGetPropertyData (deviceID, &pa, 0, 0, &size, bufList) == noErr) + if (AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, bufList) == noErr) { const int numStreams = (int) bufList->mNumberBuffers; @@ -1317,7 +1900,7 @@ private: static OSStatus hardwareListenerProc (AudioDeviceID, UInt32, const AudioObjectPropertyAddress*, void* clientData) { - static_cast (clientData)->audioDeviceListChanged(); + static_cast (clientData)->audioDeviceListChanged(); return noErr; } diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_ASIO.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_ASIO.cpp index dc46c69..1b520dd 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_ASIO.cpp +++ b/JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_ASIO.cpp @@ -366,13 +366,13 @@ public: { // find a list of sample rates.. const int possibleSampleRates[] = { 44100, 48000, 88200, 96000, 176400, 192000 }; - Array newRates; + Array newRates; if (asioObject != nullptr) { for (int index = 0; index < numElementsInArray (possibleSampleRates); ++index) if (asioObject->canSampleRate ((double) possibleSampleRates[index]) == 0) - newRates.add (possibleSampleRates[index]); + newRates.add ((double) possibleSampleRates[index]); } if (newRates.size() == 0) @@ -398,19 +398,16 @@ public: } } - StringArray getOutputChannelNames() { return outputChannelNames; } - StringArray getInputChannelNames() { return inputChannelNames; } + StringArray getOutputChannelNames() override { return outputChannelNames; } + StringArray getInputChannelNames() override { return inputChannelNames; } - int getNumSampleRates() { return sampleRates.size(); } - double getSampleRate (int index) { return sampleRates [index]; } - - int getNumBufferSizesAvailable() { return bufferSizes.size(); } - int getBufferSizeSamples (int index) { return bufferSizes [index]; } - int getDefaultBufferSize() { return preferredSize; } + Array getAvailableSampleRates() override { return sampleRates; } + Array getAvailableBufferSizes() override { return bufferSizes; } + int getDefaultBufferSize() override { return preferredSize; } String open (const BigInteger& inputChannels, const BigInteger& outputChannels, - double sr, int bufferSizeSamples) + double sr, int bufferSizeSamples) override { if (isOpen()) close(); @@ -436,7 +433,7 @@ public: bufferSizeSamples = readBufferSizes (bufferSizeSamples); - int sampleRate = roundToInt (sr); + double sampleRate = sr; currentSampleRate = sampleRate; currentBlockSizeSamples = bufferSizeSamples; currentChansOut.clear(); @@ -451,12 +448,12 @@ public: jassert (sampleRate != 0); if (sampleRate == 0) - sampleRate = 44100; + sampleRate = 44100.0; updateClockSources(); currentSampleRate = getSampleRate(); - error = String::empty; + error.clear(); buffersCreated = false; setSampleRate (sampleRate); @@ -615,9 +612,9 @@ public: return error; } - void close() + void close() override { - error = String::empty; + error.clear(); stopTimer(); stop(); @@ -643,20 +640,20 @@ public: } } - bool isOpen() { return deviceIsOpen || insideControlPanelModalLoop; } - bool isPlaying() { return asioObject != nullptr && currentCallback != nullptr; } + bool isOpen() override { return deviceIsOpen || insideControlPanelModalLoop; } + bool isPlaying() override { return asioObject != nullptr && currentCallback != nullptr; } - int getCurrentBufferSizeSamples() { return currentBlockSizeSamples; } - double getCurrentSampleRate() { return currentSampleRate; } - int getCurrentBitDepth() { return currentBitDepth; } + int getCurrentBufferSizeSamples() override { return currentBlockSizeSamples; } + double getCurrentSampleRate() override { return currentSampleRate; } + int getCurrentBitDepth() override { return currentBitDepth; } - BigInteger getActiveOutputChannels() const { return currentChansOut; } - BigInteger getActiveInputChannels() const { return currentChansIn; } + BigInteger getActiveOutputChannels() const override { return currentChansOut; } + BigInteger getActiveInputChannels() const override { return currentChansIn; } - int getOutputLatencyInSamples() { return outputLatency + currentBlockSizeSamples / 4; } - int getInputLatencyInSamples() { return inputLatency + currentBlockSizeSamples / 4; } + int getOutputLatencyInSamples() override { return outputLatency + currentBlockSizeSamples / 4; } + int getInputLatencyInSamples() override { return inputLatency + currentBlockSizeSamples / 4; } - void start (AudioIODeviceCallback* callback) + void start (AudioIODeviceCallback* callback) override { if (callback != nullptr) { @@ -667,7 +664,7 @@ public: } } - void stop() + void stop() override { AudioIODeviceCallback* const lastCallback = currentCallback; @@ -764,7 +761,8 @@ private: long totalNumInputChans, totalNumOutputChans; StringArray inputChannelNames, outputChannelNames; - Array sampleRates, bufferSizes; + Array sampleRates; + Array bufferSizes; long inputLatency, outputLatency; long minSize, maxSize, preferredSize, granularity; ASIOClockSource clocks[32]; @@ -951,7 +949,7 @@ private: return cr; } - void setSampleRate (int newRate) + void setSampleRate (double newRate) { if (currentSampleRate != newRate) { @@ -1181,7 +1179,7 @@ private: numActiveOutputChans = 0; currentCallback = nullptr; - error = String::empty; + error.clear(); if (getName().isEmpty()) return error; diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_DirectSound.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_DirectSound.cpp index 2546f99..3eb0757 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_DirectSound.cpp +++ b/JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_DirectSound.cpp @@ -755,7 +755,7 @@ public: String open (const BigInteger& inputChannels, const BigInteger& outputChannels, - double sampleRate, int bufferSizeSamples) + double sampleRate, int bufferSizeSamples) override { lastError = openDevice (inputChannels, outputChannels, sampleRate, bufferSizeSamples); isOpen_ = lastError.isEmpty(); @@ -763,7 +763,7 @@ public: return lastError; } - void close() + void close() override { stop(); @@ -774,38 +774,41 @@ public: } } - bool isOpen() { return isOpen_ && isThreadRunning(); } - int getCurrentBufferSizeSamples() { return bufferSizeSamples; } - double getCurrentSampleRate() { return sampleRate; } - BigInteger getActiveOutputChannels() const { return enabledOutputs; } - BigInteger getActiveInputChannels() const { return enabledInputs; } - int getOutputLatencyInSamples() { return (int) (getCurrentBufferSizeSamples() * 1.5); } - int getInputLatencyInSamples() { return getOutputLatencyInSamples(); } - StringArray getOutputChannelNames() { return outChannels; } - StringArray getInputChannelNames() { return inChannels; } - - int getNumSampleRates() { return 4; } - int getDefaultBufferSize() { return 2560; } - int getNumBufferSizesAvailable() { return 50; } - - double getSampleRate (int index) + bool isOpen() override { return isOpen_ && isThreadRunning(); } + int getCurrentBufferSizeSamples() override { return bufferSizeSamples; } + double getCurrentSampleRate() override { return sampleRate; } + BigInteger getActiveOutputChannels() const override { return enabledOutputs; } + BigInteger getActiveInputChannels() const override { return enabledInputs; } + int getOutputLatencyInSamples() override { return (int) (getCurrentBufferSizeSamples() * 1.5); } + int getInputLatencyInSamples() override { return getOutputLatencyInSamples(); } + StringArray getOutputChannelNames() override { return outChannels; } + StringArray getInputChannelNames() override { return inChannels; } + + Array getAvailableSampleRates() override { - const double samps[] = { 44100.0, 48000.0, 88200.0, 96000.0 }; - return samps [jlimit (0, 3, index)]; + static const double rates[] = { 44100.0, 48000.0, 88200.0, 96000.0 }; + return Array (rates, numElementsInArray (rates)); } - int getBufferSizeSamples (int index) + Array getAvailableBufferSizes() override { + Array r; int n = 64; - for (int i = 0; i < index; ++i) + + for (int i = 0; i < 50; ++i) + { + r.add (n); n += (n < 512) ? 32 : ((n < 1024) ? 64 : ((n < 2048) ? 128 : 256)); + } - return n; + return r; } - int getCurrentBitDepth() + int getDefaultBufferSize() override { return 2560; } + + int getCurrentBitDepth() override { int bits = 256; @@ -821,7 +824,7 @@ public: return bits; } - void start (AudioIODeviceCallback* call) + void start (AudioIODeviceCallback* call) override { if (isOpen_ && call != nullptr && ! isStarted) { @@ -840,7 +843,7 @@ public: } } - void stop() + void stop() override { if (isStarted) { @@ -856,8 +859,8 @@ public: } } - bool isPlaying() { return isStarted && isOpen_ && isThreadRunning(); } - String getLastError() { return lastError; } + bool isPlaying() override { return isStarted && isOpen_ && isThreadRunning(); } + String getLastError() override { return lastError; } //============================================================================== StringArray inChannels, outChannels; diff --git a/JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_WASAPI.cpp b/JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_WASAPI.cpp index 7482ec1..b60cdd4 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_WASAPI.cpp +++ b/JuceLibraryCode/modules/juce_audio_devices/native/juce_win32_WASAPI.cpp @@ -882,7 +882,7 @@ public: return false; } - StringArray getOutputChannelNames() + StringArray getOutputChannelNames() override { StringArray outChannels; @@ -893,7 +893,7 @@ public: return outChannels; } - StringArray getInputChannelNames() + StringArray getInputChannelNames() override { StringArray inChannels; @@ -904,31 +904,29 @@ public: return inChannels; } - int getNumSampleRates() { return sampleRates.size(); } - double getSampleRate (int index) { return sampleRates [index]; } - int getNumBufferSizesAvailable() { return bufferSizes.size(); } - int getBufferSizeSamples (int index) { return bufferSizes [index]; } - int getDefaultBufferSize() { return defaultBufferSize; } + Array getAvailableSampleRates() override { return sampleRates; } + Array getAvailableBufferSizes() override { return bufferSizes; } + int getDefaultBufferSize() override { return defaultBufferSize; } - int getCurrentBufferSizeSamples() { return currentBufferSizeSamples; } - double getCurrentSampleRate() { return currentSampleRate; } - int getCurrentBitDepth() { return 32; } - int getOutputLatencyInSamples() { return latencyOut; } - int getInputLatencyInSamples() { return latencyIn; } - BigInteger getActiveOutputChannels() const { return outputDevice != nullptr ? outputDevice->channels : BigInteger(); } - BigInteger getActiveInputChannels() const { return inputDevice != nullptr ? inputDevice->channels : BigInteger(); } - String getLastError() { return lastError; } + int getCurrentBufferSizeSamples() override { return currentBufferSizeSamples; } + double getCurrentSampleRate() override { return currentSampleRate; } + int getCurrentBitDepth() override { return 32; } + int getOutputLatencyInSamples() override { return latencyOut; } + int getInputLatencyInSamples() override { return latencyIn; } + BigInteger getActiveOutputChannels() const override { return outputDevice != nullptr ? outputDevice->channels : BigInteger(); } + BigInteger getActiveInputChannels() const override { return inputDevice != nullptr ? inputDevice->channels : BigInteger(); } + String getLastError() override { return lastError; } String open (const BigInteger& inputChannels, const BigInteger& outputChannels, - double sampleRate, int bufferSizeSamples) + double sampleRate, int bufferSizeSamples) override { close(); - lastError = String::empty; + lastError.clear(); if (sampleRates.size() == 0 && inputDevice != nullptr && outputDevice != nullptr) { - lastError = "The input and output devices don't share a common sample rate!"; + lastError = TRANS("The input and output devices don't share a common sample rate!"); return lastError; } @@ -939,14 +937,14 @@ public: if (inputDevice != nullptr && ! inputDevice->open (currentSampleRate, inputChannels)) { - lastError = "Couldn't open the input device!"; + lastError = TRANS("Couldn't open the input device!"); return lastError; } if (outputDevice != nullptr && ! outputDevice->open (currentSampleRate, outputChannels)) { close(); - lastError = "Couldn't open the output device!"; + lastError = TRANS("Couldn't open the output device!"); return lastError; } @@ -963,7 +961,7 @@ public: if (! check (inputDevice->client->Start())) { close(); - lastError = "Couldn't start the input device!"; + lastError = TRANS("Couldn't start the input device!"); return lastError; } } @@ -975,7 +973,7 @@ public: if (! check (outputDevice->client->Start())) { close(); - lastError = "Couldn't start the output device!"; + lastError = TRANS("Couldn't start the output device!"); return lastError; } } @@ -984,7 +982,7 @@ public: return lastError; } - void close() + void close() override { stop(); signalThreadShouldExit(); @@ -1000,10 +998,10 @@ public: isOpen_ = false; } - bool isOpen() { return isOpen_ && isThreadRunning(); } - bool isPlaying() { return isStarted && isOpen_ && isThreadRunning(); } + bool isOpen() override { return isOpen_ && isThreadRunning(); } + bool isPlaying() override { return isStarted && isOpen_ && isThreadRunning(); } - void start (AudioIODeviceCallback* call) + void start (AudioIODeviceCallback* call) override { if (isOpen_ && call != nullptr && ! isStarted) { @@ -1022,7 +1020,7 @@ public: } } - void stop() + void stop() override { if (isStarted) { diff --git a/JuceLibraryCode/modules/juce_audio_devices/sources/juce_AudioSourcePlayer.h b/JuceLibraryCode/modules/juce_audio_devices/sources/juce_AudioSourcePlayer.h index badd651..0a3751c 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/sources/juce_AudioSourcePlayer.h +++ b/JuceLibraryCode/modules/juce_audio_devices/sources/juce_AudioSourcePlayer.h @@ -65,8 +65,7 @@ public: void setSource (AudioSource* newSource); /** Returns the source that's playing. - - May return 0 if there's no source. + May return nullptr if there's no source. */ AudioSource* getCurrentSource() const noexcept { return source; } diff --git a/JuceLibraryCode/modules/juce_audio_devices/sources/juce_AudioTransportSource.cpp b/JuceLibraryCode/modules/juce_audio_devices/sources/juce_AudioTransportSource.cpp index 624e9a6..e923b46 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/sources/juce_AudioTransportSource.cpp +++ b/JuceLibraryCode/modules/juce_audio_devices/sources/juce_AudioTransportSource.cpp @@ -44,15 +44,12 @@ AudioTransportSource::AudioTransportSource() AudioTransportSource::~AudioTransportSource() { setSource (nullptr); - releaseMasterResources(); } void AudioTransportSource::setSource (PositionableAudioSource* const newSource, - int readAheadBufferSize_, - TimeSliceThread* readAheadThread, - double sourceSampleRateToCorrectFor, - int maxNumChannels) + int readAheadSize, TimeSliceThread* readAheadThread, + double sourceSampleRateToCorrectFor, int maxNumChannels) { if (source == newSource) { @@ -62,7 +59,7 @@ void AudioTransportSource::setSource (PositionableAudioSource* const newSource, setSource (nullptr, 0, nullptr); // deselect and reselect to avoid releasing resources wrongly } - readAheadBufferSize = readAheadBufferSize_; + readAheadBufferSize = readAheadSize; sourceSampleRate = sourceSampleRateToCorrectFor; ResamplingAudioSource* newResamplerSource = nullptr; @@ -70,15 +67,15 @@ void AudioTransportSource::setSource (PositionableAudioSource* const newSource, PositionableAudioSource* newPositionableSource = nullptr; AudioSource* newMasterSource = nullptr; - ScopedPointer oldResamplerSource (resamplerSource); - ScopedPointer oldBufferingSource (bufferingSource); + ScopedPointer oldResamplerSource (resamplerSource); + ScopedPointer oldBufferingSource (bufferingSource); AudioSource* oldMasterSource = masterSource; if (newSource != nullptr) { newPositionableSource = newSource; - if (readAheadBufferSize_ > 0) + if (readAheadSize > 0) { // If you want to use a read-ahead buffer, you must also provide a TimeSliceThread // for it to use! @@ -86,7 +83,7 @@ void AudioTransportSource::setSource (PositionableAudioSource* const newSource, newPositionableSource = newBufferingSource = new BufferingAudioSource (newPositionableSource, *readAheadThread, - false, readAheadBufferSize_, maxNumChannels); + false, readAheadSize, maxNumChannels); } newPositionableSource->setNextReadPosition (0); @@ -115,6 +112,7 @@ void AudioTransportSource::setSource (PositionableAudioSource* const newSource, masterSource = newMasterSource; positionableSource = newPositionableSource; + inputStreamEOF = false; playing = false; } @@ -170,7 +168,10 @@ double AudioTransportSource::getCurrentPosition() const double AudioTransportSource::getLengthInSeconds() const { - return getTotalLength() / sampleRate; + if (sampleRate > 0.0) + return getTotalLength() / sampleRate; + + return 0.0; } void AudioTransportSource::setNextReadPosition (int64 newPosition) @@ -181,6 +182,7 @@ void AudioTransportSource::setNextReadPosition (int64 newPosition) newPosition = (int64) (newPosition * sourceSampleRate / sampleRate); positionableSource->setNextReadPosition (newPosition); + inputStreamEOF = false; } } @@ -189,7 +191,6 @@ int64 AudioTransportSource::getNextReadPosition() const if (positionableSource != nullptr) { const double ratio = (sampleRate > 0 && sourceSampleRate > 0) ? sampleRate / sourceSampleRate : 1.0; - return (int64) (positionableSource->getNextReadPosition() * ratio); } @@ -203,7 +204,6 @@ int64 AudioTransportSource::getTotalLength() const if (positionableSource != nullptr) { const double ratio = (sampleRate > 0 && sourceSampleRate > 0) ? sampleRate / sourceSampleRate : 1.0; - return (int64) (positionableSource->getTotalLength() * ratio); } @@ -213,9 +213,7 @@ int64 AudioTransportSource::getTotalLength() const bool AudioTransportSource::isLooping() const { const ScopedLock sl (callbackLock); - - return positionableSource != nullptr - && positionableSource->isLooping(); + return positionableSource != nullptr && positionableSource->isLooping(); } void AudioTransportSource::setGain (const float newGain) noexcept @@ -236,6 +234,7 @@ void AudioTransportSource::prepareToPlay (int samplesPerBlockExpected, double ne if (resamplerSource != nullptr && sourceSampleRate > 0) resamplerSource->setResamplingRatio (sourceSampleRate / sampleRate); + inputStreamEOF = false; isPrepared = true; } @@ -258,8 +257,6 @@ void AudioTransportSource::getNextAudioBlock (const AudioSourceChannelInfo& info { const ScopedLock sl (callbackLock); - inputStreamEOF = false; - if (masterSource != nullptr && ! stopped) { masterSource->getNextAudioBlock (info); @@ -275,7 +272,7 @@ void AudioTransportSource::getNextAudioBlock (const AudioSourceChannelInfo& info } if (positionableSource->getNextReadPosition() > positionableSource->getTotalLength() + 1 - && ! positionableSource->isLooping()) + && ! positionableSource->isLooping()) { playing = false; inputStreamEOF = true; @@ -285,10 +282,7 @@ void AudioTransportSource::getNextAudioBlock (const AudioSourceChannelInfo& info stopped = ! playing; for (int i = info.buffer->getNumChannels(); --i >= 0;) - { - info.buffer->applyGainRamp (i, info.startSample, info.numSamples, - lastGain, gain); - } + info.buffer->applyGainRamp (i, info.startSample, info.numSamples, lastGain, gain); } else { diff --git a/JuceLibraryCode/modules/juce_audio_devices/sources/juce_AudioTransportSource.h b/JuceLibraryCode/modules/juce_audio_devices/sources/juce_AudioTransportSource.h index e63fa0d..e60dee0 100644 --- a/JuceLibraryCode/modules/juce_audio_devices/sources/juce_AudioTransportSource.h +++ b/JuceLibraryCode/modules/juce_audio_devices/sources/juce_AudioTransportSource.h @@ -45,7 +45,6 @@ class JUCE_API AudioTransportSource : public PositionableAudioSource, public: //============================================================================== /** Creates an AudioTransportSource. - After creating one of these, use the setSource() method to select an input source. */ AudioTransportSource(); @@ -94,7 +93,6 @@ public: void setPosition (double newPosition); /** Returns the position that the next data block will be read from - This is a time in seconds. */ double getCurrentPosition() const; @@ -102,8 +100,7 @@ public: /** Returns the stream's length in seconds. */ double getLengthInSeconds() const; - /** Returns true if the player has stopped because its input stream ran out of data. - */ + /** Returns true if the player has stopped because its input stream ran out of data. */ bool hasStreamFinished() const noexcept { return inputStreamEOF; } //============================================================================== @@ -126,19 +123,16 @@ public: //============================================================================== /** Changes the gain to apply to the output. - @param newGain a factor by which to multiply the outgoing samples, so 1.0 = 0dB, 0.5 = -6dB, 2.0 = 6dB, etc. */ void setGain (float newGain) noexcept; /** Returns the current gain setting. - @see setGain */ float getGain() const noexcept { return gain; } - //============================================================================== /** Implementation of the AudioSource method. */ void prepareToPlay (int samplesPerBlockExpected, double sampleRate) override; @@ -175,7 +169,7 @@ private: bool volatile playing, stopped; double sampleRate, sourceSampleRate; int blockSize, readAheadBufferSize; - bool isPrepared, inputStreamEOF; + bool volatile isPrepared, inputStreamEOF; void releaseMasterResources(); diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/stream_decoder.c b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/stream_decoder.c index b268fe0..04e1766 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/stream_decoder.c +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/stream_decoder.c @@ -1348,12 +1348,12 @@ FLAC__bool has_id_filtered_(FLAC__StreamDecoder *decoder, FLAC__byte *id) FLAC__bool find_metadata_(FLAC__StreamDecoder *decoder) { FLAC__uint32 x; - unsigned i, id; + unsigned i, id_; FLAC__bool first = true; FLAC__ASSERT(FLAC__bitreader_is_consumed_byte_aligned(decoder->private_->input)); - for(i = id = 0; i < 4; ) { + for(i = id_ = 0; i < 4; ) { if(decoder->private_->cached) { x = (FLAC__uint32)decoder->private_->lookahead; decoder->private_->cached = false; @@ -1365,19 +1365,19 @@ FLAC__bool find_metadata_(FLAC__StreamDecoder *decoder) if(x == FLAC__STREAM_SYNC_STRING[i]) { first = true; i++; - id = 0; + id_ = 0; continue; } - if(x == ID3V2_TAG_[id]) { - id++; + if(x == ID3V2_TAG_[id_]) { + id_++; i = 0; - if(id == 3) { + if(id_ == 3) { if(!skip_id3v2_tag_(decoder)) return false; /* skip_id3v2_tag_ sets the state for us */ } continue; } - id = 0; + id_ = 0; if(x == 0xff) { /* MAGIC NUMBER for the first 8 frame sync bits */ decoder->private_->header_warmup[0] = (FLAC__byte)x; if(!FLAC__bitreader_read_raw_uint32(decoder->private_->input, &x, 8)) @@ -3369,4 +3369,4 @@ FLAC__bool file_eof_callback_(const FLAC__StreamDecoder *decoder, void *client_d return feof(decoder->private_->file)? true : false; } -#endif \ No newline at end of file +#endif diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/stream_encoder.c b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/stream_encoder.c index f2507ca..20e98da 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/stream_encoder.c +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/flac/libFLAC/stream_encoder.c @@ -4333,4 +4333,4 @@ FILE *get_binary_stdout_(void) return stdout; } -#endif \ No newline at end of file +#endif diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_AiffAudioFormat.cpp b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_AiffAudioFormat.cpp index 86f9976..d7edd45 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_AiffAudioFormat.cpp +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_AiffAudioFormat.cpp @@ -343,7 +343,7 @@ namespace AiffFileHelpers out.writeIntBigEndian (values.getValue (prefix + "TimeStamp", "0").getIntValue()); out.writeShortBigEndian ((short) values.getValue (prefix + "Identifier", "0").getIntValue()); - const String comment (values.getValue (prefix + "Text", String::empty)); + const String comment (values.getValue (prefix + "Text", String())); const size_t commentLength = jmin (comment.getNumBytesAsUTF8(), (size_t) 65534); out.writeShortBigEndian ((short) commentLength + 1); diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_LAMEEncoderAudioFormat.cpp b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_LAMEEncoderAudioFormat.cpp index c5c337f..5d30d44 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_LAMEEncoderAudioFormat.cpp +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_LAMEEncoderAudioFormat.cpp @@ -28,7 +28,7 @@ class LAMEEncoderAudioFormat::Writer : public AudioFormatWriter { public: Writer (OutputStream* destStream, const String& formatName, - const File& lameApp, int vbr, int cbr, + const File& appFile, int vbr, int cbr, double sampleRate, unsigned int numberOfChannels, unsigned int bitsPerSample, const StringPairArray& metadata) : AudioFormatWriter (destStream, formatName, sampleRate, @@ -43,7 +43,7 @@ public: writer = wavFormat.createWriterFor (out, sampleRate, numChannels, bitsPerSample, metadata, 0); - args.add (lameApp.getFullPathName()); + args.add (appFile.getFullPathName()); args.add ("--quiet"); @@ -72,7 +72,7 @@ public: void addMetadataArg (const StringPairArray& metadata, const char* key, const char* lameFlag) { - const String value (metadata.getValue (key, String::empty)); + const String value (metadata.getValue (key, String())); if (value.isNotEmpty()) { @@ -103,11 +103,11 @@ private: ScopedPointer writer; StringArray args; - bool runLameChildProcess (const TemporaryFile& tempMP3, const StringArray& args) const + bool runLameChildProcess (const TemporaryFile& tempMP3, const StringArray& processArgs) const { ChildProcess cp; - if (cp.start (args)) + if (cp.start (processArgs)) { const String childOutput (cp.readAllProcessOutput()); DBG (childOutput); (void) childOutput; diff --git a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_MP3AudioFormat.cpp b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_MP3AudioFormat.cpp index 226b374..8a7033a 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_MP3AudioFormat.cpp +++ b/JuceLibraryCode/modules/juce_audio_formats/codecs/juce_MP3AudioFormat.cpp @@ -3095,7 +3095,14 @@ private: const int64 streamSize = stream.stream.getTotalLength(); if (streamSize > 0) - numFrames = (streamSize - streamStartPos) / (stream.frame.frameSize); + { + const int bytesPerFrame = stream.frame.frameSize + 4; + + if (bytesPerFrame == 417 || bytesPerFrame == 418) + numFrames = roundToInt ((streamSize - streamStartPos) / 417.95918); // more accurate for 128k + else + numFrames = (streamSize - streamStartPos) / bytesPerFrame; + } } return numFrames * 1152; diff --git a/JuceLibraryCode/modules/juce_audio_formats/juce_module_info b/JuceLibraryCode/modules/juce_audio_formats/juce_module_info index e749b5c..b37705e 100644 --- a/JuceLibraryCode/modules/juce_audio_formats/juce_module_info +++ b/JuceLibraryCode/modules/juce_audio_formats/juce_module_info @@ -1,7 +1,7 @@ { "id": "juce_audio_formats", "name": "JUCE audio file format codecs", - "version": "3.0.0", + "version": "3.0.1", "description": "Classes for reading and writing various audio file formats.", "website": "http://www.juce.com/juce", "license": "GPL/Commercial", diff --git a/JuceLibraryCode/modules/juce_audio_plugin_client/AU/juce_AU_Wrapper.mm b/JuceLibraryCode/modules/juce_audio_plugin_client/AU/juce_AU_Wrapper.mm index 0ebcf9e..b1d9ec2 100644 --- a/JuceLibraryCode/modules/juce_audio_plugin_client/AU/juce_AU_Wrapper.mm +++ b/JuceLibraryCode/modules/juce_audio_plugin_client/AU/juce_AU_Wrapper.mm @@ -945,9 +945,10 @@ public: (juce::uint8) inData2 }; incomingEvents.addEvent (data, 3, (int) inStartFrame); - #endif - return noErr; + #else + return kAudioUnitErr_PropertyNotInUse; + #endif } OSStatus HandleSysEx (const UInt8* inData, UInt32 inLength) override @@ -955,8 +956,10 @@ public: #if JucePlugin_WantsMidiInput const ScopedLock sl (incomingMidiLock); incomingEvents.addEvent (inData, (int) inLength, 0); - #endif return noErr; + #else + return kAudioUnitErr_PropertyNotInUse; + #endif } //============================================================================== @@ -1369,7 +1372,7 @@ private: JUCE_AUTORELEASEPOOL { jassert (ed != nullptr); - addAndMakeVisible (&editor); + addAndMakeVisible (editor); setOpaque (true); setVisible (true); setBroughtToFrontOnMouseClick (true); diff --git a/JuceLibraryCode/modules/juce_audio_plugin_client/RTAS/juce_RTAS_Wrapper.cpp b/JuceLibraryCode/modules/juce_audio_plugin_client/RTAS/juce_RTAS_Wrapper.cpp index 04077c1..10dcb68 100644 --- a/JuceLibraryCode/modules/juce_audio_plugin_client/RTAS/juce_RTAS_Wrapper.cpp +++ b/JuceLibraryCode/modules/juce_audio_plugin_client/RTAS/juce_RTAS_Wrapper.cpp @@ -861,7 +861,7 @@ private: void GetNameOfLength (char* name, int maxLength, OSType inControllerType) const { // Pro-tools expects all your parameters to have valid names! - jassert (juceFilter->getParameterName (index).isNotEmpty()); + jassert (juceFilter->getParameterName (index, maxLength).isNotEmpty()); juceFilter->getParameterName (index, maxLength).copyToUTF8 (name, (size_t) maxLength + 1); } diff --git a/JuceLibraryCode/modules/juce_audio_plugin_client/Standalone/juce_StandaloneFilterWindow.h b/JuceLibraryCode/modules/juce_audio_plugin_client/Standalone/juce_StandaloneFilterWindow.h index d0613cc..6dfcf87 100644 --- a/JuceLibraryCode/modules/juce_audio_plugin_client/Standalone/juce_StandaloneFilterWindow.h +++ b/JuceLibraryCode/modules/juce_audio_plugin_client/Standalone/juce_StandaloneFilterWindow.h @@ -54,7 +54,7 @@ public: { setTitleBarButtonsRequired (DocumentWindow::minimiseButton | DocumentWindow::closeButton, false); - Component::addAndMakeVisible (&optionsButton); + Component::addAndMakeVisible (optionsButton); optionsButton.addListener (this); optionsButton.setTriggeredOnMouseDown (true); diff --git a/JuceLibraryCode/modules/juce_audio_plugin_client/VST/juce_VST_Wrapper.cpp b/JuceLibraryCode/modules/juce_audio_plugin_client/VST/juce_VST_Wrapper.cpp index 3f0012f..b38206d 100644 --- a/JuceLibraryCode/modules/juce_audio_plugin_client/VST/juce_VST_Wrapper.cpp +++ b/JuceLibraryCode/modules/juce_audio_plugin_client/VST/juce_VST_Wrapper.cpp @@ -40,13 +40,6 @@ #undef STRICT #define STRICT 1 #include - - #ifdef __MINGW32__ - struct MOUSEHOOKSTRUCTEX : public MOUSEHOOKSTRUCT - { - DWORD mouseData; - }; - #endif #elif defined (LINUX) #include #include @@ -62,12 +55,6 @@ #endif //============================================================================== -/* These files come with the Steinberg VST SDK - to get them, you'll need to - visit the Steinberg website and jump through some hoops to sign up as a - VST developer. - - Then, you'll need to make sure your include path contains your "vstsdk2.4" directory. -*/ #ifndef _MSC_VER #define __cdecl #endif @@ -80,7 +67,15 @@ #pragma clang diagnostic ignored "-Wdeprecated-writable-strings" #endif -// VSTSDK V2.4 includes.. +/* These files come with the Steinberg VST SDK - to get them, you'll need to + visit the Steinberg website and agree to whatever is currently required to + get them. The best version to get is the VST3 SDK, which also contains + the older VST2.4 files. + + Then, you'll need to make sure your include path contains your "VST SDK3" + directory (or whatever you've named it on your machine). The introjucer has + a special box for setting this path. +*/ #include #include #include @@ -181,7 +176,10 @@ namespace { if (nCode >= 0 && wParam == WM_MOUSEWHEEL) { - const MOUSEHOOKSTRUCTEX& hs = *(MOUSEHOOKSTRUCTEX*) lParam; + // using a local copy of this struct to support old mingw libraries + struct MOUSEHOOKSTRUCTEX_ : public MOUSEHOOKSTRUCT { DWORD mouseData; }; + + const MOUSEHOOKSTRUCTEX_& hs = *(MOUSEHOOKSTRUCTEX_*) lParam; if (Component* const comp = Desktop::getInstance().findComponentAt (Point (hs.pt.x, hs.pt.y))) if (comp->getWindowHandle() != 0) @@ -835,7 +833,7 @@ public: if (filter != nullptr) { jassert (isPositiveAndBelow (index, filter->getNumParameters())); - filter->getParameterText (index).copyToUTF8 (text, 24); // length should technically be kVstMaxParamStrLen, which is 8, but hosts will normally allow a bit more. + filter->getParameterText (index, 24).copyToUTF8 (text, 24); // length should technically be kVstMaxParamStrLen, which is 8, but hosts will normally allow a bit more. } } @@ -844,7 +842,7 @@ public: if (filter != nullptr) { jassert (isPositiveAndBelow (index, filter->getNumParameters())); - filter->getParameterName (index).copyToUTF8 (text, 16); // length should technically be kVstMaxParamStrLen, which is 8, but hosts will normally allow a bit more. + filter->getParameterName (index, 16).copyToUTF8 (text, 16); // length should technically be kVstMaxParamStrLen, which is 8, but hosts will normally allow a bit more. } } diff --git a/JuceLibraryCode/modules/juce_audio_plugin_client/juce_module_info b/JuceLibraryCode/modules/juce_audio_plugin_client/juce_module_info index adcf0d3..12bd247 100644 --- a/JuceLibraryCode/modules/juce_audio_plugin_client/juce_module_info +++ b/JuceLibraryCode/modules/juce_audio_plugin_client/juce_module_info @@ -1,7 +1,7 @@ { "id": "juce_audio_plugin_client", "name": "JUCE audio plugin wrapper classes", - "version": "3.0.0", + "version": "3.0.1", "description": "Classes for building VST, RTAS and AU plugins.", "website": "http://www.juce.com/juce", "license": "GPL/Commercial", diff --git a/JuceLibraryCode/modules/juce_audio_plugin_client/utility/juce_PluginUtilities.cpp b/JuceLibraryCode/modules/juce_audio_plugin_client/utility/juce_PluginUtilities.cpp index e4684d6..82e7e47 100644 --- a/JuceLibraryCode/modules/juce_audio_plugin_client/utility/juce_PluginUtilities.cpp +++ b/JuceLibraryCode/modules/juce_audio_plugin_client/utility/juce_PluginUtilities.cpp @@ -22,7 +22,7 @@ ============================================================================== */ -#if _MSC_VER +#if _MSC_VER || JUCE_MINGW #include #endif @@ -33,7 +33,7 @@ #include "../utility/juce_CheckSettingMacros.h" #include "juce_IncludeModuleHeaders.h" -#if _MSC_VER +#if _MSC_VER || JUCE_MINGW #if JucePlugin_Build_RTAS extern "C" BOOL WINAPI DllMainRTAS (HINSTANCE, DWORD, LPVOID); diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_AudioUnitPluginFormat.mm b/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_AudioUnitPluginFormat.mm index b6f849f..375b2e7 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_AudioUnitPluginFormat.mm +++ b/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_AudioUnitPluginFormat.mm @@ -58,7 +58,9 @@ namespace juce namespace AudioUnitFormatHelpers { - static int insideCallback = 0; + #if JUCE_DEBUG + static ThreadLocalValue insideCallback; + #endif String osTypeToString (OSType type) { @@ -136,7 +138,7 @@ namespace AudioUnitFormatHelpers fileOrIdentifier.lastIndexOfChar ('/')) + 1)); StringArray tokens; - tokens.addTokens (s, ",", String::empty); + tokens.addTokens (s, ",", String()); tokens.removeEmptyStrings(); if (tokens.size() == 3) @@ -290,7 +292,9 @@ public: { using namespace AudioUnitFormatHelpers; - ++insideCallback; + #if JUCE_DEBUG + ++*insideCallback; + #endif JUCE_AU_LOG ("Opening AU: " + fileOrIdentifier); @@ -306,14 +310,20 @@ public: } } - --insideCallback; + #if JUCE_DEBUG + --*insideCallback; + #endif } ~AudioUnitPluginInstance() { const ScopedLock sl (lock); - jassert (AudioUnitFormatHelpers::insideCallback == 0); + #if JUCE_DEBUG + // this indicates that some kind of recursive call is getting triggered that's + // deleting this plugin while it's still under construction. + jassert (AudioUnitFormatHelpers::insideCallback.get() == 0); + #endif if (eventListenerRef != 0) { @@ -570,7 +580,7 @@ public: if (isPositiveAndBelow (index, getNumInputChannels())) return "Input " + String (index + 1); - return String::empty; + return String(); } const String getOutputChannelName (int index) const override @@ -578,7 +588,7 @@ public: if (isPositiveAndBelow (index, getNumOutputChannels())) return "Output " + String (index + 1); - return String::empty; + return String(); } bool isInputChannelStereoPair (int index) const override { return isPositiveAndBelow (index, getNumInputChannels()); } @@ -652,7 +662,7 @@ public: if (const ParamInfo* p = parameters[index]) return p->name; - return String::empty; + return String(); } const String getParameterText (int index) override { return String (getParameter (index)); } @@ -1293,7 +1303,7 @@ public: : AudioProcessorEditor (&p), plugin (p) { - addAndMakeVisible (&wrapper); + addAndMakeVisible (wrapper); setOpaque (true); setVisible (true); diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_LADSPAPluginFormat.cpp b/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_LADSPAPluginFormat.cpp index c7f794b..d528a27 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_LADSPAPluginFormat.cpp +++ b/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_LADSPAPluginFormat.cpp @@ -222,7 +222,7 @@ public: desc.lastFileModTime = module->file.getLastModificationTime(); desc.pluginFormatName = "LADSPA"; desc.category = getCategory(); - desc.manufacturerName = plugin != nullptr ? String (plugin->Maker) : String::empty; + desc.manufacturerName = plugin != nullptr ? String (plugin->Maker) : String(); desc.version = getVersion(); desc.numInputChannels = getNumInputChannels(); desc.numOutputChannels = getNumOutputChannels(); @@ -338,7 +338,7 @@ public: if (isPositiveAndBelow (index, getNumInputChannels())) return String (plugin->PortNames [inputs [index]]).trim(); - return String::empty; + return String(); } const String getOutputChannelName (const int index) const @@ -346,7 +346,7 @@ public: if (isPositiveAndBelow (index, getNumInputChannels())) return String (plugin->PortNames [outputs [index]]).trim(); - return String::empty; + return String(); } //============================================================================== @@ -390,7 +390,7 @@ public: return String (plugin->PortNames [parameters [index]]).trim(); } - return String::empty; + return String(); } const String getParameterText (int index) @@ -407,7 +407,7 @@ public: return String (parameterValues[index].scaled, 4); } - return String::empty; + return String(); } //============================================================================== @@ -424,7 +424,7 @@ public: const String getProgramName (int index) { // XXX - return String::empty; + return String(); } void changeProgramName (int index, const String& newName) diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp b/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp index 572f423..d162906 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp +++ b/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.cpp @@ -40,11 +40,17 @@ #pragma clang diagnostic ignored "-Wunused-parameter" #pragma clang diagnostic ignored "-Wconversion" #pragma clang diagnostic ignored "-Woverloaded-virtual" + #pragma clang diagnostic ignored "-Wshadow" #endif -// Got an include error here? If so, you'll need to install the VST3 SDK somewhere, -// and use the introjucer or your IDE to add it to your include path. The introjucer -// has a special box for specifying this path for each export target. +/* These files come with the Steinberg VST3 SDK - to get them, you'll need to + visit the Steinberg website and agree to whatever is currently required to + get them. + + Then, you'll need to make sure your include path contains your "VST SDK3" + directory (or whatever you've named it on your machine). The introjucer has + a special box for setting this path. +*/ #include #include #include @@ -85,11 +91,12 @@ #undef DBPRT3 #undef DBPRT4 #undef DBPRT5 +#undef calloc #undef free #undef malloc +#undef realloc #undef NEW #undef NEWVEC -#undef realloc #undef VERIFY #undef VERIFY_IS #undef VERIFY_NOT @@ -98,6 +105,7 @@ #undef SINGLE_CREATE_FUNC #undef _META_CLASS #undef _META_CLASS_IFACE +#undef _META_CLASS_SINGLE #undef META_CLASS #undef META_CLASS_IFACE #undef META_CLASS_SINGLE @@ -306,8 +314,8 @@ static void activateAllBussesOfType (Vst::IComponent* component, } //============================================================================== -/** Assigns an AudioSampleBuffer's channels to an AudioBusBuffers' */ -static void associateBufferTo (Vst::AudioBusBuffers& vstBuffers, const AudioSampleBuffer& buffer) noexcept +/** Assigns a complete AudioSampleBuffer's channels to an AudioBusBuffers' */ +static void associateWholeBufferTo (Vst::AudioBusBuffers& vstBuffers, const AudioSampleBuffer& buffer) noexcept { vstBuffers.channelBuffers32 = buffer.getArrayOfChannels(); vstBuffers.numChannels = buffer.getNumChannels(); @@ -566,30 +574,36 @@ public: for (Steinberg::int32 i = 0; i < eventList.getEventCount(); ++i) { - Event event; + Event e; - if (eventList.getEvent (i, event) == kResultOk) + if (eventList.getEvent (i, e) == kResultOk) { - switch (event.type) + switch (e.type) { case Event::kNoteOnEvent: - result.addEvent (MidiMessage::noteOn (event.noteOn.channel + 1, event.noteOn.pitch, (uint8) (event.noteOn.velocity * 127.0f)), - event.sampleOffset); + result.addEvent (MidiMessage::noteOn (createSafeChannel (e.noteOn.channel), + createSafeNote (e.noteOn.pitch), + (uint8) denormaliseToMidiValue (e.noteOn.velocity)), + e.sampleOffset); break; case Event::kNoteOffEvent: - result.addEvent (MidiMessage::noteOff (event.noteOff.channel + 1, event.noteOff.pitch, (uint8) (event.noteOff.velocity * 127.0f)), - event.sampleOffset); + result.addEvent (MidiMessage::noteOff (createSafeChannel (e.noteOff.channel), + createSafeNote (e.noteOff.pitch), + (uint8) denormaliseToMidiValue (e.noteOff.velocity)), + e.sampleOffset); break; case Event::kPolyPressureEvent: - result.addEvent (MidiMessage::aftertouchChange (event.polyPressure.channel + 1, event.polyPressure.pitch, (int) (event.polyPressure.pressure * 127.0f)), - event.sampleOffset); + result.addEvent (MidiMessage::aftertouchChange (createSafeChannel (e.polyPressure.channel), + createSafeNote (e.polyPressure.pitch), + denormaliseToMidiValue (e.polyPressure.pressure)), + e.sampleOffset); break; case Event::kDataEvent: - result.addEvent (MidiMessage::createSysExMessage (event.data.bytes, event.data.size), - event.sampleOffset); + result.addEvent (MidiMessage::createSysExMessage (e.data.bytes, e.data.size), + e.sampleOffset); break; default: @@ -607,42 +621,58 @@ public: MidiMessage msg; int midiEventPosition = 0; + enum { maxNumEvents = 2048 }; // Steinberg's Host Checker states no more than 2048 events are allowed at once + int numEvents = 0; + while (iterator.getNextEvent (msg, midiEventPosition)) { - Event event = { 0 }; + if (++numEvents > maxNumEvents) + break; + + Event e = { 0 }; if (msg.isNoteOn()) { - event.type = Event::kNoteOnEvent; - event.noteOn.channel = (Steinberg::int16) msg.getChannel() - 1; - event.noteOn.pitch = (Steinberg::int16) msg.getNoteNumber(); - event.noteOn.velocity = msg.getVelocity() / 127.0f; + e.type = Event::kNoteOnEvent; + e.noteOn.channel = createSafeChannel (msg.getChannel()); + e.noteOn.pitch = createSafeNote (msg.getNoteNumber()); + e.noteOn.velocity = normaliseMidiValue (msg.getVelocity()); + e.noteOn.length = 0; + e.noteOn.tuning = 0.0f; + e.noteOn.noteId = -1; } else if (msg.isNoteOff()) { - event.type = Event::kNoteOffEvent; - event.noteOff.channel = (Steinberg::int16) msg.getChannel() - 1; - event.noteOff.pitch = (Steinberg::int16) msg.getNoteNumber(); - event.noteOff.velocity = msg.getVelocity() / 127.0f; + e.type = Event::kNoteOffEvent; + e.noteOff.channel = createSafeChannel (msg.getChannel()); + e.noteOff.pitch = createSafeNote (msg.getNoteNumber()); + e.noteOff.velocity = normaliseMidiValue (msg.getVelocity()); + e.noteOff.tuning = 0.0f; + e.noteOff.noteId = -1; } else if (msg.isSysEx()) { - event.type = Event::kDataEvent; - event.data.bytes = msg.getSysExData(); - event.data.size = msg.getSysExDataSize(); + e.type = Event::kDataEvent; + e.data.bytes = msg.getSysExData(); + e.data.size = msg.getSysExDataSize(); + e.data.type = DataEvent::kMidiSysEx; } else if (msg.isAftertouch()) { - event.type = Event::kPolyPressureEvent; - event.polyPressure.channel = (Steinberg::int16) msg.getChannel() - 1; - event.polyPressure.pitch = (Steinberg::int16) msg.getNoteNumber(); - event.polyPressure.pressure = msg.getAfterTouchValue() / 127.0f; + e.type = Event::kPolyPressureEvent; + e.polyPressure.channel = createSafeChannel (msg.getChannel()); + e.polyPressure.pitch = createSafeNote (msg.getNoteNumber()); + e.polyPressure.pressure = normaliseMidiValue (msg.getAfterTouchValue()); + } + else + { + continue; } - event.busIndex = 0; - event.sampleOffset = midiEventPosition; + e.busIndex = 0; + e.sampleOffset = midiEventPosition; - result.addEvent (event); + result.addEvent (e); } } @@ -650,6 +680,15 @@ private: Array events; Atomic refCount; + static Steinberg::int16 createSafeChannel (int channel) noexcept { return (Steinberg::int16) jlimit (0, 15, channel - 1); } + static int createSafeChannel (Steinberg::int16 channel) noexcept { return (int) jlimit (1, 16, channel + 1); } + + static Steinberg::int16 createSafeNote (int note) noexcept { return (Steinberg::int16) jlimit (0, 127, note); } + static int createSafeNote (Steinberg::int16 note) noexcept { return jlimit (0, 127, (int) note); } + + static float normaliseMidiValue (int value) noexcept { return jlimit (0.0f, 1.0f, (float) value / 127.0f); } + static int denormaliseToMidiValue (float value) noexcept { return roundToInt (jlimit (0.0f, 127.0f, value * 127.0f)); } + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiEventList) }; @@ -1448,16 +1487,16 @@ private: } //============================================================================== - bool open (const File& file, const PluginDescription& description) + bool open (const File& f, const PluginDescription& description) { - dllHandle = new DLLHandle (file.getFullPathName()); + dllHandle = new DLLHandle (f.getFullPathName()); ComSmartPtr pluginFactory (dllHandle->getPluginFactory()); ComSmartPtr host (new VST3HostContext (nullptr)); MatchingDescriptionFinder finder (host, pluginFactory, description); - const Result result (finder.findDescriptionsAndPerform (file)); + const Result result (finder.findDescriptionsAndPerform (f)); if (result.getErrorMessage() == MatchingDescriptionFinder::getSuccessString()) { @@ -1506,14 +1545,8 @@ public: #endif } - Steinberg::uint32 PLUGIN_API addRef() override { return 1; } - Steinberg::uint32 PLUGIN_API release() override { return 1; } - - tresult PLUGIN_API queryInterface (const TUID, void** obj) override - { - *obj = nullptr; - return kNotImplemented; - } + JUCE_DECLARE_VST3_COM_REF_METHODS + JUCE_DECLARE_VST3_COM_QUERY_METHODS void paint (Graphics& g) override { @@ -1602,13 +1635,15 @@ public: private: //============================================================================== + Atomic refCount; ComSmartPtr view; #if JUCE_WINDOWS - struct ChildComponent : public Component + class ChildComponent : public Component { + public: ChildComponent() {} - void paint (Graphics& g) { g.fillAll (Colours::cornflowerblue); } + void paint (Graphics& g) override { g.fillAll (Colours::cornflowerblue); } using Component::createNewPeer; @@ -1652,7 +1687,7 @@ private: pluginHandle = (HandleFormat) peer->getNativeHandle(); #elif JUCE_MAC dummyComponent.setBounds (getBounds().withZeroOrigin()); - addAndMakeVisible (&dummyComponent); + addAndMakeVisible (dummyComponent); pluginHandle = [[NSView alloc] init]; dummyComponent.setView (pluginHandle); #endif @@ -1676,9 +1711,9 @@ class VST3PluginInstance : public AudioPluginInstance public: VST3PluginInstance (const VST3ModuleHandle::Ptr& handle) : module (handle), - result (1, 1), numInputAudioBusses (0), numOutputAudioBusses (0), + resultBuffer (1, 1), inputParameterChanges (new ParameterChangeList()), outputParameterChanges (new ParameterChangeList()), midiInputs (new MidiEventList()), @@ -1761,6 +1796,62 @@ public: return module != nullptr ? module->name : String::empty; } + typedef Array > BusMap; + + /** Assigns a series of AudioSampleBuffer's channels to an AudioBusBuffers' + + @warning For speed, does not check the channel count and offsets + according to the AudioSampleBuffer + */ + void associateBufferTo (Vst::AudioBusBuffers& vstBuffers, + BusMap& busMap, + const AudioSampleBuffer& buffer, + int numChannels, int channelStartOffset, + int sampleOffset = 0) noexcept + { + const int channelEnd = numChannels + channelStartOffset; + jassert (channelEnd >= 0 && channelEnd <= buffer.getNumChannels()); + + busMap.add (Array()); + Array& chans = busMap.getReference (busMap.size() - 1); + + for (int i = channelStartOffset; i < channelEnd; ++i) + chans.add (buffer.getSampleData (i, sampleOffset)); + + vstBuffers.channelBuffers32 = chans.getRawDataPointer(); + vstBuffers.numChannels = numChannels; + vstBuffers.silenceFlags = 0; + } + + void mapAudioSampleBufferToBusses (Array& result, + AudioSampleBuffer& source, + int numBusses, bool isInput) + { + result.clearQuick(); + + BusMap& busMapToUse = isInput ? inputBusMap : outputBusMap; + busMapToUse.clearQuick(); + + int channelIndexOffset = 0; + + for (int i = 0; i < numBusses; ++i) + { + Vst::SpeakerArrangement arrangement = 0; + processor->getBusArrangement (isInput ? Vst::kInput : Vst::kOutput, + (Steinberg::int32) i, arrangement); + + const int numChansForBus = BigInteger ((int64) arrangement).countNumberOfSetBits(); + + result.add (Vst::AudioBusBuffers()); + + associateBufferTo (result.getReference (i), busMapToUse, source, + BigInteger ((int64) arrangement).countNumberOfSetBits(), + channelIndexOffset); + + channelIndexOffset += numChansForBus; + } + } + void prepareToPlay (double sampleRate, int estimatedSamplesPerBlock) override { using namespace Vst; @@ -1777,6 +1868,8 @@ public: setup.sampleRate = sampleRate; setup.processMode = isNonRealtime() ? kOffline : kRealtime; + resultBuffer.setSize (numOutputs, estimatedSamplesPerBlock, false, true, true); + warnOnFailure (processor->setupProcessing (setup)); if (! isComponentInitialised) @@ -1787,28 +1880,28 @@ public: warnOnFailure (component->setActive (true)); warnOnFailure (processor->setProcessing (true)); - result.setSize (numOutputs, estimatedSamplesPerBlock, false, true, true); - Array inArrangements, outArrangements; fillWithCorrespondingSpeakerArrangements (inArrangements, numInputs); fillWithCorrespondingSpeakerArrangements (outArrangements, numOutputs); - warnOnFailure (processor->setBusArrangements (inArrangements.getRawDataPointer(), - getNumSingleDirectionBussesFor (component, true, true), - outArrangements.getRawDataPointer(), - getNumSingleDirectionBussesFor (component, false, true))); + warnOnFailure (processor->setBusArrangements (inArrangements.getRawDataPointer(), numInputAudioBusses, + outArrangements.getRawDataPointer(), numOutputAudioBusses)); } void releaseResources() override { - result.setSize (1, 1, false, true, true); + JUCE_TRY + { + resultBuffer.setSize (1, 1, false, true, true); - if (processor != nullptr) - processor->setProcessing (false); + if (processor != nullptr) + processor->setProcessing (false); - if (component != nullptr) - component->setActive (false); + if (component != nullptr) + component->setActive (false); + } + JUCE_CATCH_ALL_ASSERT } void processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages) override @@ -1823,8 +1916,8 @@ public: ProcessData data; data.processMode = isNonRealtime() ? kOffline : kRealtime; data.symbolicSampleSize = kSample32; - data.numInputs = 1; // Number of busses, not channels! - data.numOutputs = 1; // Number of busses, not channels! + data.numInputs = numInputAudioBusses; + data.numOutputs = numOutputAudioBusses; data.inputParameterChanges = inputParameterChanges; data.outputParameterChanges = outputParameterChanges; data.numSamples = (Steinberg::int32) numSamples; @@ -2057,14 +2150,17 @@ private: mutable ComSmartPtr view; - AudioSampleBuffer result; - Vst::AudioBusBuffers inputs, outputs; - - // The number of IO busses MUST match that of the plugin's, as very poorly specified by the Steinberg SDK + /** The number of IO busses MUST match that of the plugin, + even if there aren't enough channels to process, + as very poorly specified by the Steinberg SDK + */ int numInputAudioBusses, numOutputAudioBusses; + AudioSampleBuffer resultBuffer; + BusMap inputBusMap, outputBusMap; + Array inputBusses, outputBusses; //============================================================================== - template + template static void appendStateFrom (XmlElement& head, ComSmartPtr& object, const String& identifier) { if (object != nullptr) @@ -2133,10 +2229,10 @@ private: { jassert (numClasses >= 0); // The plugin must provide at least an IComponent and IEditController! - for (Steinberg::int32 i = 0; i < numClasses; ++i) + for (Steinberg::int32 j = 0; j < numClasses; ++j) { info = new PClassInfo(); - factory->getClassInfo (i, info); + factory->getClassInfo (j, info); if (std::strcmp (info->category, kVstAudioEffectClass) != 0) continue; @@ -2153,7 +2249,7 @@ private: if (pf2.loadFrom (factory)) { info2 = new PClassInfo2(); - pf2->getClassInfo2 (i, info2); + pf2->getClassInfo2 (j, info2); } else { @@ -2164,7 +2260,7 @@ private: { pf3->setHostContext (host->getFUnknown()); infoW = new PClassInfoW(); - pf3->getClassInfoUnicode (i, infoW); + pf3->getClassInfoUnicode (j, infoW); } else { @@ -2251,7 +2347,8 @@ private: Steinberg::MemoryStream stream; if (component->getState (&stream) == kResultTrue) - warnOnFailure (editController->setComponentState (&stream)); + if (stream.seek (0, Steinberg::IBStream::kIBSeekSet, nullptr) == kResultTrue) + warnOnFailure (editController->setComponentState (&stream)); } void grabInformationObjects() @@ -2304,6 +2401,7 @@ private: component->getBusInfo (forAudio ? Vst::kAudio : Vst::kEvent, forInput ? Vst::kInput : Vst::kOutput, (Steinberg::int32) index, busInfo); + return busInfo; } @@ -2322,23 +2420,15 @@ private: } //============================================================================== - struct AudioBusBuffersWrapper - { - AudioBusBuffersWrapper() {} - ~AudioBusBuffersWrapper() {} - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioBusBuffersWrapper) - }; - void associateTo (Vst::ProcessData& destination, AudioSampleBuffer& buffer) { - result.clear(); + resultBuffer.clear(); - associateBufferTo (inputs, buffer); - associateBufferTo (outputs, result); + mapAudioSampleBufferToBusses (inputBusses, buffer, numInputAudioBusses, true); + mapAudioSampleBufferToBusses (outputBusses, resultBuffer, numOutputAudioBusses, false); - destination.inputs = &inputs; - destination.outputs = &outputs; + destination.inputs = inputBusses.getRawDataPointer(); + destination.outputs = outputBusses.getRawDataPointer(); } void associateTo (Vst::ProcessData& destination, MidiBuffer& midiBuffer) @@ -2360,22 +2450,22 @@ private: Vst::ParameterInfo getParameterInfoForIndex (int index) const { - Vst::ParameterInfo info = { 0 }; + Vst::ParameterInfo paramInfo = { 0 }; if (processor != nullptr) - editController->getParameterInfo (index, info); + editController->getParameterInfo (index, paramInfo); - return info; + return paramInfo; } Vst::ProgramListInfo getProgramListInfo (int index) const { - Vst::ProgramListInfo info = { 0 }; + Vst::ProgramListInfo paramInfo = { 0 }; if (unitInfo != nullptr) - unitInfo->getProgramListInfo (index, info); + unitInfo->getProgramListInfo (index, paramInfo); - return info; + return paramInfo; } JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (VST3PluginInstance) diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.h b/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.h index c637eba..308ff5a 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.h +++ b/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_VST3PluginFormat.h @@ -68,5 +68,5 @@ private: JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (VST3PluginFormat) }; -#endif //JUCE_PLUGINHOST_VST3 -#endif //JUCE_VST3PLUGINFORMAT_H_INCLUDED \ No newline at end of file +#endif // JUCE_PLUGINHOST_VST3 +#endif // JUCE_VST3PLUGINFORMAT_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_VSTMidiEventList.h b/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_VSTMidiEventList.h index 3337272..f442eab 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_VSTMidiEventList.h +++ b/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_VSTMidiEventList.h @@ -22,12 +22,11 @@ ============================================================================== */ -#ifdef __aeffect__ +#ifdef __aeffect__ // NB: this must come first, *before* the header-guard. #ifndef JUCE_VSTMIDIEVENTLIST_H_INCLUDED #define JUCE_VSTMIDIEVENTLIST_H_INCLUDED - //============================================================================== /** Holds a set of VSTMidiEvent objects and makes it easy to add events to the list. @@ -184,6 +183,5 @@ private: } }; - #endif // JUCE_VSTMIDIEVENTLIST_H_INCLUDED #endif diff --git a/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_VSTPluginFormat.cpp b/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_VSTPluginFormat.cpp index e0cc66c..dec1a37 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_VSTPluginFormat.cpp +++ b/JuceLibraryCode/modules/juce_audio_processors/format_types/juce_VSTPluginFormat.cpp @@ -1831,17 +1831,22 @@ private: int versionBits[32]; int n = 0; - while (v != 0) + for (int vv = v; vv != 0; vv /= 10) + versionBits [n++] = vv % 10; + + if (n > 4) // if the number ends up silly, it's probably encoded as hex instead of decimal.. { - versionBits [n++] = v % 10; - v /= 10; - } + n = 0; - s << 'V'; + for (int vv = v; vv != 0; vv >>= 8) + versionBits [n++] = vv & 255; + } while (n > 1 && versionBits [n - 1] == 0) --n; + s << 'V'; + while (n > 0) { s << versionBits [--n]; diff --git a/JuceLibraryCode/modules/juce_audio_processors/juce_module_info b/JuceLibraryCode/modules/juce_audio_processors/juce_module_info index e598809..34124f6 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/juce_module_info +++ b/JuceLibraryCode/modules/juce_audio_processors/juce_module_info @@ -1,7 +1,7 @@ { "id": "juce_audio_processors", "name": "JUCE audio plugin hosting classes", - "version": "3.0.0", + "version": "3.0.1", "description": "Classes for loading and playing VST, AU, or internally-generated audio processors.", "website": "http://www.juce.com/juce", "license": "GPL/Commercial", diff --git a/JuceLibraryCode/modules/juce_audio_processors/processors/juce_AudioProcessor.cpp b/JuceLibraryCode/modules/juce_audio_processors/processors/juce_AudioProcessor.cpp index 8f4f43a..501fedf 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/processors/juce_AudioProcessor.cpp +++ b/JuceLibraryCode/modules/juce_audio_processors/processors/juce_AudioProcessor.cpp @@ -201,7 +201,7 @@ void AudioProcessor::updateHostDisplay() l->audioProcessorChanged (this); } -String AudioProcessor::getParameterLabel (int) const { return String::empty; } +String AudioProcessor::getParameterLabel (int) const { return String(); } bool AudioProcessor::isParameterAutomatable (int) const { return true; } bool AudioProcessor::isMetaParameter (int) const { return false; } @@ -266,7 +266,7 @@ void AudioProcessor::copyXmlToBinary (const XmlElement& xml, juce::MemoryBlock& MemoryOutputStream out (destData, false); out.writeInt (magicXmlNumber); out.writeInt (0); - xml.writeToStream (out, String::empty, true, false); + xml.writeToStream (out, String(), true, false); out.writeByte (0); } diff --git a/JuceLibraryCode/modules/juce_audio_processors/processors/juce_AudioProcessorGraph.cpp b/JuceLibraryCode/modules/juce_audio_processors/processors/juce_AudioProcessorGraph.cpp index e965b22..c1f1929 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/processors/juce_AudioProcessorGraph.cpp +++ b/JuceLibraryCode/modules/juce_audio_processors/processors/juce_AudioProcessorGraph.cpp @@ -1367,7 +1367,7 @@ const String AudioProcessorGraph::AudioGraphIOProcessor::getName() const default: break; } - return String::empty; + return String(); } void AudioProcessorGraph::AudioGraphIOProcessor::fillInPluginDescription (PluginDescription& d) const @@ -1469,7 +1469,7 @@ const String AudioProcessorGraph::AudioGraphIOProcessor::getInputChannelName (in default: break; } - return String::empty; + return String(); } const String AudioProcessorGraph::AudioGraphIOProcessor::getOutputChannelName (int channelIndex) const @@ -1481,7 +1481,7 @@ const String AudioProcessorGraph::AudioGraphIOProcessor::getOutputChannelName (i default: break; } - return String::empty; + return String(); } bool AudioProcessorGraph::AudioGraphIOProcessor::isInputChannelStereoPair (int /*index*/) const @@ -1501,17 +1501,17 @@ bool AudioProcessorGraph::AudioGraphIOProcessor::hasEditor() const AudioProcessorEditor* AudioProcessorGraph::AudioGraphIOProcessor::createEditor() { return nullptr; } int AudioProcessorGraph::AudioGraphIOProcessor::getNumParameters() { return 0; } -const String AudioProcessorGraph::AudioGraphIOProcessor::getParameterName (int) { return String::empty; } +const String AudioProcessorGraph::AudioGraphIOProcessor::getParameterName (int) { return String(); } float AudioProcessorGraph::AudioGraphIOProcessor::getParameter (int) { return 0.0f; } -const String AudioProcessorGraph::AudioGraphIOProcessor::getParameterText (int) { return String::empty; } +const String AudioProcessorGraph::AudioGraphIOProcessor::getParameterText (int) { return String(); } 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; } +const String AudioProcessorGraph::AudioGraphIOProcessor::getProgramName (int) { return String(); } void AudioProcessorGraph::AudioGraphIOProcessor::changeProgramName (int, const String&) {} void AudioProcessorGraph::AudioGraphIOProcessor::getStateInformation (juce::MemoryBlock&) {} diff --git a/JuceLibraryCode/modules/juce_audio_processors/processors/juce_AudioProcessorGraph.h b/JuceLibraryCode/modules/juce_audio_processors/processors/juce_AudioProcessorGraph.h index bf22670..7deb9b5 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/processors/juce_AudioProcessorGraph.h +++ b/JuceLibraryCode/modules/juce_audio_processors/processors/juce_AudioProcessorGraph.h @@ -372,15 +372,15 @@ public: AudioProcessorEditor* createEditor() { return nullptr; } int getNumParameters() { return 0; } - const String getParameterName (int) { return String::empty; } + const String getParameterName (int) { return String(); } float getParameter (int) { return 0; } - const String getParameterText (int) { return String::empty; } + const String getParameterText (int) { return String(); } void setParameter (int, float) { } int getNumPrograms() { return 0; } int getCurrentProgram() { return 0; } void setCurrentProgram (int) { } - const String getProgramName (int) { return String::empty; } + const String getProgramName (int) { return String(); } void changeProgramName (int, const String&) { } void getStateInformation (juce::MemoryBlock&); diff --git a/JuceLibraryCode/modules/juce_audio_processors/processors/juce_GenericAudioProcessorEditor.cpp b/JuceLibraryCode/modules/juce_audio_processors/processors/juce_GenericAudioProcessorEditor.cpp index 1b584e8..4b9da9b 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/processors/juce_GenericAudioProcessorEditor.cpp +++ b/JuceLibraryCode/modules/juce_audio_processors/processors/juce_GenericAudioProcessorEditor.cpp @@ -35,7 +35,7 @@ public: slider (p, index_) { startTimer (100); - addAndMakeVisible (&slider); + addAndMakeVisible (slider); owner.addListener (this); } @@ -123,7 +123,7 @@ GenericAudioProcessorEditor::GenericAudioProcessorEditor (AudioProcessor* const jassert (p != nullptr); setOpaque (true); - addAndMakeVisible (&panel); + addAndMakeVisible (panel); Array params; diff --git a/JuceLibraryCode/modules/juce_audio_processors/scanning/juce_KnownPluginList.cpp b/JuceLibraryCode/modules/juce_audio_processors/scanning/juce_KnownPluginList.cpp index ed58205..2ea3c98 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/scanning/juce_KnownPluginList.cpp +++ b/JuceLibraryCode/modules/juce_audio_processors/scanning/juce_KnownPluginList.cpp @@ -241,7 +241,8 @@ void KnownPluginList::clearBlacklistedFiles() //============================================================================== struct PluginSorter { - PluginSorter (KnownPluginList::SortMethod sortMethod) noexcept : method (sortMethod) {} + PluginSorter (KnownPluginList::SortMethod sortMethod, bool forwards) noexcept + : method (sortMethod), direction (forwards ? 1 : -1) {} int compareElements (const PluginDescription* const first, const PluginDescription* const second) const @@ -252,6 +253,7 @@ struct PluginSorter { case KnownPluginList::sortByCategory: diff = first->category.compareLexicographically (second->category); break; case KnownPluginList::sortByManufacturer: diff = first->manufacturerName.compareLexicographically (second->manufacturerName); break; + case KnownPluginList::sortByFormat: diff = first->pluginFormatName.compare (second->pluginFormatName); break; case KnownPluginList::sortByFileSystemLocation: diff = lastPathPart (first->fileOrIdentifier).compare (lastPathPart (second->fileOrIdentifier)); break; default: break; } @@ -259,7 +261,7 @@ struct PluginSorter if (diff == 0) diff = first->name.compareLexicographically (second->name); - return diff; + return diff * direction; } private: @@ -268,14 +270,17 @@ private: return path.replaceCharacter ('\\', '/').upToLastOccurrenceOf ("/", false, false); } - KnownPluginList::SortMethod method; + const KnownPluginList::SortMethod method; + const int direction; + + JUCE_DECLARE_NON_COPYABLE (PluginSorter) }; -void KnownPluginList::sort (const SortMethod method) +void KnownPluginList::sort (const SortMethod method, bool forwards) { if (method != defaultOrder) { - PluginSorter sorter (method); + PluginSorter sorter (method, forwards); types.sort (sorter, true); sendChangeMessage(); @@ -477,7 +482,7 @@ KnownPluginList::PluginTree* KnownPluginList::createTree (const SortMethod sortM Array sorted; { - PluginSorter sorter (sortMethod); + PluginSorter sorter (sortMethod, true); for (int i = 0; i < types.size(); ++i) sorted.addSorted (sorter, types.getUnchecked(i)); @@ -485,7 +490,7 @@ KnownPluginList::PluginTree* KnownPluginList::createTree (const SortMethod sortM PluginTree* tree = new PluginTree(); - if (sortMethod == sortByCategory || sortMethod == sortByManufacturer) + if (sortMethod == sortByCategory || sortMethod == sortByManufacturer || sortMethod == sortByFormat) { PluginTreeUtils::buildTreeByCategory (*tree, sorted, sortMethod); } diff --git a/JuceLibraryCode/modules/juce_audio_processors/scanning/juce_KnownPluginList.h b/JuceLibraryCode/modules/juce_audio_processors/scanning/juce_KnownPluginList.h index b66c7d6..5f60643 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/scanning/juce_KnownPluginList.h +++ b/JuceLibraryCode/modules/juce_audio_processors/scanning/juce_KnownPluginList.h @@ -132,6 +132,7 @@ public: sortAlphabetically, sortByCategory, sortByManufacturer, + sortByFormat, sortByFileSystemLocation }; @@ -143,7 +144,7 @@ public: Use getIndexChosenByMenu() to find out the type that was chosen. */ - void addToMenu (PopupMenu& menu, const SortMethod sortMethod) const; + void addToMenu (PopupMenu& menu, 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. @@ -153,7 +154,7 @@ public: //============================================================================== /** Sorts the list. */ - void sort (const SortMethod method); + void sort (SortMethod method, bool forwards); //============================================================================== /** Creates some XML that can be used to store the state of this list. */ @@ -195,7 +196,7 @@ public: private: //============================================================================== - OwnedArray types; + OwnedArray types; StringArray blacklist; ScopedPointer scanner; CriticalSection scanLock; diff --git a/JuceLibraryCode/modules/juce_audio_processors/scanning/juce_PluginDirectoryScanner.cpp b/JuceLibraryCode/modules/juce_audio_processors/scanning/juce_PluginDirectoryScanner.cpp index 24eb6d2..5502f00 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/scanning/juce_PluginDirectoryScanner.cpp +++ b/JuceLibraryCode/modules/juce_audio_processors/scanning/juce_PluginDirectoryScanner.cpp @@ -120,7 +120,7 @@ bool PluginDirectoryScanner::skipNextFile() void PluginDirectoryScanner::setDeadMansPedalFile (const StringArray& newContents) { - if (deadMansPedalFile != File::nonexistent) + if (deadMansPedalFile.getFullPathName().isNotEmpty()) deadMansPedalFile.replaceWithText (newContents.joinIntoString ("\n"), true, true); } diff --git a/JuceLibraryCode/modules/juce_audio_processors/scanning/juce_PluginListComponent.cpp b/JuceLibraryCode/modules/juce_audio_processors/scanning/juce_PluginListComponent.cpp index 570263c..8112277 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/scanning/juce_PluginListComponent.cpp +++ b/JuceLibraryCode/modules/juce_audio_processors/scanning/juce_PluginListComponent.cpp @@ -22,10 +22,116 @@ ============================================================================== */ -PluginListComponent::PluginListComponent (AudioPluginFormatManager& manager, - KnownPluginList& listToEdit, - const File& deadMansPedal, - PropertiesFile* const props) +class PluginListComponent::TableModel : public TableListBoxModel +{ +public: + TableModel (PluginListComponent& c, KnownPluginList& l) : owner (c), list (l) {} + + int getNumRows() override + { + return list.getNumTypes() + list.getBlacklistedFiles().size(); + } + + void paintRowBackground (Graphics& g, int /*rowNumber*/, int /*width*/, int /*height*/, bool rowIsSelected) override + { + if (rowIsSelected) + g.fillAll (owner.findColour (TextEditor::highlightColourId)); + } + + enum + { + nameCol = 1, + typeCol = 2, + categoryCol = 3, + manufacturerCol = 4, + descCol = 5 + }; + + void paintCell (Graphics& g, int row, int columnId, int width, int height, bool /*rowIsSelected*/) override + { + String text; + bool isBlacklisted = row >= list.getNumTypes(); + + if (isBlacklisted) + { + if (columnId == nameCol) + text = list.getBlacklistedFiles() [row - list.getNumTypes()]; + else if (columnId == descCol) + text = TRANS("Deactivated after failing to initialise correctly"); + } + else if (const PluginDescription* const desc = list.getType (row)) + { + switch (columnId) + { + case nameCol: text = desc->name; break; + case typeCol: text = desc->pluginFormatName; break; + case categoryCol: text = desc->category.isNotEmpty() ? desc->category : "-"; break; + case manufacturerCol: text = desc->manufacturerName; break; + case descCol: text = getPluginDescription (*desc); break; + + default: jassertfalse; break; + } + } + + if (text.isNotEmpty()) + { + g.setColour (isBlacklisted ? Colours::red + : columnId == nameCol ? Colours::black + : Colours::grey); + g.setFont (Font (height * 0.7f, Font::bold)); + g.drawFittedText (text, 4, 0, width - 6, height, Justification::centredLeft, 1, 0.9f); + } + } + + void deleteKeyPressed (int lastRowSelected) override + { + removePluginItem (list, lastRowSelected); + } + + void sortOrderChanged (int newSortColumnId, bool isForwards) override + { + switch (newSortColumnId) + { + case nameCol: list.sort (KnownPluginList::sortAlphabetically, isForwards); break; + case typeCol: list.sort (KnownPluginList::sortByFormat, isForwards); break; + case categoryCol: list.sort (KnownPluginList::sortByCategory, isForwards); break; + case manufacturerCol: list.sort (KnownPluginList::sortByManufacturer, isForwards); break; + case descCol: break; + + default: jassertfalse; break; + } + } + + static void removePluginItem (KnownPluginList& list, int index) + { + if (index < list.getNumTypes()) + list.removeType (index); + else + list.removeFromBlacklist (list.getBlacklistedFiles() [index - list.getNumTypes()]); + } + + static String getPluginDescription (const PluginDescription& desc) + { + StringArray items; + + if (desc.descriptiveName != desc.name) + items.add (desc.descriptiveName); + + items.add (desc.version); + + items.removeEmptyStrings(); + return items.joinIntoString (" - "); + } + + PluginListComponent& owner; + KnownPluginList& list; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TableModel) +}; + +//============================================================================== +PluginListComponent::PluginListComponent (AudioPluginFormatManager& manager, KnownPluginList& listToEdit, + const File& deadMansPedal, PropertiesFile* const props) : formatManager (manager), list (listToEdit), deadMansPedalFile (deadMansPedal), @@ -33,10 +139,22 @@ PluginListComponent::PluginListComponent (AudioPluginFormatManager& manager, propertiesToUse (props), numThreads (0) { - listBox.setModel (this); - addAndMakeVisible (&listBox); + tableModel = new TableModel (*this, listToEdit); + + TableHeaderComponent& header = table.getHeader(); + + header.addColumn (TRANS("Name"), TableModel::nameCol, 200, 100, 700, TableHeaderComponent::defaultFlags | TableHeaderComponent::sortedForwards); + header.addColumn (TRANS("Format"), TableModel::typeCol, 80, 80, 80, TableHeaderComponent::notResizable); + header.addColumn (TRANS("Category"), TableModel::categoryCol, 100, 100, 200); + header.addColumn (TRANS("Manufacturer"), TableModel::manufacturerCol, 200, 100, 300); + header.addColumn (TRANS("Description"), TableModel::descCol, 300, 100, 500, TableHeaderComponent::notSortable); + + table.setHeaderHeight (22); + table.setRowHeight (20); + table.setModel (tableModel); + addAndMakeVisible (table); - addAndMakeVisible (&optionsButton); + addAndMakeVisible (optionsButton); optionsButton.addListener (this); optionsButton.setTriggeredOnMouseDown (true); @@ -66,9 +184,13 @@ void PluginListComponent::setNumberOfThreadsForScanning (int num) void PluginListComponent::resized() { - listBox.setBounds (0, 0, getWidth(), getHeight() - 30); + Rectangle r (getLocalBounds().reduced (2)); + + optionsButton.setBounds (r.removeFromBottom (24)); optionsButton.changeWidthToFitText (24); - optionsButton.setTopLeftPosition (0, getHeight() - 28); + + r.removeFromBottom (3); + table.setBounds (r); } void PluginListComponent::changeListenerCallback (ChangeBroadcaster*) @@ -78,89 +200,22 @@ void PluginListComponent::changeListenerCallback (ChangeBroadcaster*) void PluginListComponent::updateList() { - listBox.updateContent(); - listBox.repaint(); -} - -int PluginListComponent::getNumRows() -{ - return list.getNumTypes() + list.getBlacklistedFiles().size(); -} - -void PluginListComponent::paintListBoxItem (int row, Graphics& g, int width, int height, bool rowIsSelected) -{ - if (rowIsSelected) - g.fillAll (findColour (TextEditor::highlightColourId)); - - String name, desc; - bool isBlacklisted = false; - - if (row >= list.getNumTypes()) - { - isBlacklisted = true; - name = list.getBlacklistedFiles() [row - list.getNumTypes()]; - desc = TRANS("Deactivated after failing to initialise correctly"); - } - else if (const PluginDescription* const pd = list.getType (row)) - { - name = pd->name; - - 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 << '\''; - } - - if (name.isNotEmpty()) - { - GlyphArrangement ga; - ga.addCurtailedLineOfText (Font (height * 0.7f, Font::bold), - name, 8.0f, height * 0.8f, width - 10.0f, true); - - g.setColour (isBlacklisted ? Colours::red : Colours::black); - ga.draw (g); - - const Rectangle bb (ga.getBoundingBox (0, -1, false)); - - ga.clear(); - ga.addCurtailedLineOfText (Font (height * 0.6f), desc, - jmax (bb.getRight() + 10.0f, width / 3.0f), height * 0.8f, - width - bb.getRight() - 12.0f, true); - - g.setColour (isBlacklisted ? Colours::red : Colours::grey); - ga.draw (g); - } -} - -static void removePluginItem (KnownPluginList& list, int index) -{ - if (index < list.getNumTypes()) - list.removeType (index); - else - list.removeFromBlacklist (list.getBlacklistedFiles() [index - list.getNumTypes()]); -} - -void PluginListComponent::deleteKeyPressed (int lastRowSelected) -{ - removePluginItem (list, lastRowSelected); + table.updateContent(); + table.repaint(); } void PluginListComponent::removeSelected() { - const SparseSet selected (listBox.getSelectedRows()); + const SparseSet selected (table.getSelectedRows()); for (int i = list.getNumTypes(); --i >= 0;) if (selected.contains (i)) - removePluginItem (list, i); + TableModel::removePluginItem (list, i); } bool PluginListComponent::canShowSelectedFolder() const { - if (const PluginDescription* const desc = list.getType (listBox.getSelectedRow())) + if (const PluginDescription* const desc = list.getType (table.getSelectedRow())) return File::createFileWithoutCheckingPath (desc->fileOrIdentifier).exists(); return false; @@ -169,7 +224,7 @@ bool PluginListComponent::canShowSelectedFolder() const void PluginListComponent::showSelectedFolder() { if (canShowSelectedFolder()) - if (const PluginDescription* const desc = list.getType (listBox.getSelectedRow())) + if (const PluginDescription* const desc = list.getType (table.getSelectedRow())) File (desc->fileOrIdentifier).getParentDirectory().startAsProcess(); } @@ -192,12 +247,9 @@ void PluginListComponent::optionsMenuCallback (int result) { case 0: break; case 1: list.clear(); break; - case 2: list.sort (KnownPluginList::sortAlphabetically); break; - case 3: list.sort (KnownPluginList::sortByCategory); break; - case 4: list.sort (KnownPluginList::sortByManufacturer); break; - case 5: removeSelected(); break; - case 6: showSelectedFolder(); break; - case 7: removeMissingPlugins(); break; + case 2: removeSelected(); break; + case 3: showSelectedFolder(); break; + case 4: removeMissingPlugins(); break; default: if (AudioPluginFormat* format = formatManager.getFormat (result - 10)) @@ -213,13 +265,9 @@ void PluginListComponent::buttonClicked (Button* button) { PopupMenu menu; menu.addItem (1, TRANS("Clear list")); - menu.addItem (5, TRANS("Remove selected plug-in from list"), listBox.getNumSelectedRows() > 0); - menu.addItem (6, TRANS("Show folder containing selected plug-in"), canShowSelectedFolder()); - menu.addItem (7, TRANS("Remove any plug-ins 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.addItem (2, TRANS("Remove selected plug-in from list"), table.getNumSelectedRows() > 0); + menu.addItem (3, TRANS("Show folder containing selected plug-in"), canShowSelectedFolder()); + menu.addItem (4, TRANS("Remove any plug-ins whose files no longer exist")); menu.addSeparator(); for (int i = 0; i < formatManager.getNumFormats(); ++i) @@ -262,10 +310,7 @@ void PluginListComponent::setLastSearchPath (PropertiesFile& properties, AudioPl class PluginListComponent::Scanner : private Timer { public: - Scanner (PluginListComponent& plc, - AudioPluginFormat& format, - PropertiesFile* properties, - int threads) + Scanner (PluginListComponent& plc, AudioPluginFormat& format, PropertiesFile* properties, int threads) : owner (plc), formatToScan (format), propertiesToUse (properties), pathChooserWindow (TRANS("Select folders to scan..."), String::empty, AlertWindow::NoIcon), progressWindow (TRANS("Scanning for plug-ins..."), diff --git a/JuceLibraryCode/modules/juce_audio_processors/scanning/juce_PluginListComponent.h b/JuceLibraryCode/modules/juce_audio_processors/scanning/juce_PluginListComponent.h index a3cf675..97751a8 100644 --- a/JuceLibraryCode/modules/juce_audio_processors/scanning/juce_PluginListComponent.h +++ b/JuceLibraryCode/modules/juce_audio_processors/scanning/juce_PluginListComponent.h @@ -33,7 +33,6 @@ */ class JUCE_API PluginListComponent : public Component, public FileDragAndDropTarget, - private ListBoxModel, private ChangeListener, private ButtonListener // (can't use Button::Listener due to idiotic VC2005 bug) { @@ -53,7 +52,7 @@ public: /** Destructor. */ ~PluginListComponent(); - /** Changes the text in the panel's button. */ + /** Changes the text in the panel's options button. */ void setOptionsButtonText (const String& newText); /** Sets how many threads to simultaneously scan for plugins. @@ -62,42 +61,32 @@ public: void setNumberOfThreadsForScanning (int numThreads); /** Returns the last search path stored in a given properties file for the specified format. */ - static FileSearchPath getLastSearchPath (PropertiesFile& properties, AudioPluginFormat& format); + static FileSearchPath getLastSearchPath (PropertiesFile&, AudioPluginFormat&); /** Stores a search path in a properties file for the given format. */ - static void setLastSearchPath (PropertiesFile& properties, AudioPluginFormat& format, - const FileSearchPath& newPath); + static void setLastSearchPath (PropertiesFile&, AudioPluginFormat&, const FileSearchPath&); /** Triggers an asynchronous scan for the given format. */ - void scanFor (AudioPluginFormat& format); + void scanFor (AudioPluginFormat&); /** Returns true if there's currently a scan in progress. */ bool isScanning() const noexcept; - //============================================================================== - /** @internal */ - void resized() override; - /** @internal */ - bool isInterestedInFileDrag (const StringArray&) override; - /** @internal */ - void filesDropped (const StringArray&, int, int) override; - /** @internal */ - int getNumRows() override; - /** @internal */ - void paintListBoxItem (int row, Graphics&, int width, int height, bool rowIsSelected) override; - /** @internal */ - void deleteKeyPressed (int lastRowSelected) override; - private: //============================================================================== AudioPluginFormatManager& formatManager; KnownPluginList& list; File deadMansPedalFile; - ListBox listBox; + TableListBox table; TextButton optionsButton; PropertiesFile* propertiesToUse; int numThreads; + class TableModel; + friend class TableModel; + friend struct ContainerDeletePolicy; + ScopedPointer tableModel; + class Scanner; friend class Scanner; friend struct ContainerDeletePolicy; @@ -107,11 +96,14 @@ private: static void optionsMenuStaticCallback (int, PluginListComponent*); void optionsMenuCallback (int); void updateList(); - void removeSelected(); void showSelectedFolder(); bool canShowSelectedFolder() const; + void removeSelected(); void removeMissingPlugins(); + void resized() override; + bool isInterestedInFileDrag (const StringArray&) override; + void filesDropped (const StringArray&, int, int) override; void buttonClicked (Button*) override; void changeListenerCallback (ChangeBroadcaster*) override; diff --git a/JuceLibraryCode/modules/juce_audio_utils/gui/juce_AudioDeviceSelectorComponent.cpp b/JuceLibraryCode/modules/juce_audio_utils/gui/juce_AudioDeviceSelectorComponent.cpp index ebff477..db22b5d 100644 --- a/JuceLibraryCode/modules/juce_audio_utils/gui/juce_AudioDeviceSelectorComponent.cpp +++ b/JuceLibraryCode/modules/juce_audio_utils/gui/juce_AudioDeviceSelectorComponent.cpp @@ -352,7 +352,7 @@ public: if (error.isNotEmpty()) AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, - TRANS ("Error when trying to open audio device!"), + TRANS("Error when trying to open audio device!"), error); } @@ -410,7 +410,7 @@ public: addAndMakeVisible (outputChanList = new ChannelSelectorListBox (setup, ChannelSelectorListBox::audioOutputType, TRANS ("(no audio output channels found)"))); - outputChanLabel = new Label (String::empty, TRANS ("active output channels:")); + outputChanLabel = new Label (String::empty, TRANS("Active output channels:")); outputChanLabel->attachToComponent (outputChanList, true); } @@ -429,8 +429,8 @@ public: { addAndMakeVisible (inputChanList = new ChannelSelectorListBox (setup, ChannelSelectorListBox::audioInputType, - TRANS ("(no audio input channels found)"))); - inputChanLabel = new Label (String::empty, TRANS ("active input channels:")); + TRANS("(no audio input channels found)"))); + inputChanLabel = new Label (String::empty, TRANS("Active input channels:")); inputChanLabel->attachToComponent (inputChanList, true); } @@ -525,8 +525,8 @@ private: if (currentDevice != nullptr && currentDevice->hasControlPanel()) { - addAndMakeVisible (showUIButton = new TextButton (TRANS ("show this device's control panel"), - TRANS ("opens the device's own control panel"))); + addAndMakeVisible (showUIButton = new TextButton (TRANS ("Show this device's control panel"), + TRANS ("Opens the device's own control panel"))); showUIButton->addListener (this); } @@ -544,13 +544,13 @@ private: addAndMakeVisible (outputDeviceDropDown); outputDeviceLabel = new Label (String::empty, - type.hasSeparateInputsAndOutputs() ? TRANS ("output:") - : TRANS ("device:")); + type.hasSeparateInputsAndOutputs() ? TRANS("Output:") + : TRANS("Device:")); outputDeviceLabel->attachToComponent (outputDeviceDropDown, true); if (setup.maxNumOutputChannels > 0) { - addAndMakeVisible (testButton = new TextButton (TRANS ("Test"))); + addAndMakeVisible (testButton = new TextButton (TRANS("Test"))); testButton->addListener (this); } } @@ -571,7 +571,7 @@ private: inputDeviceDropDown->addListener (this); addAndMakeVisible (inputDeviceDropDown); - inputDeviceLabel = new Label (String::empty, TRANS ("input:")); + inputDeviceLabel = new Label (String::empty, TRANS("Input:")); inputDeviceLabel->attachToComponent (inputDeviceDropDown, true); addAndMakeVisible (inputLevelMeter @@ -590,7 +590,7 @@ private: { addAndMakeVisible (sampleRateDropDown = new ComboBox (String::empty)); - sampleRateLabel = new Label (String::empty, TRANS ("sample rate:")); + sampleRateLabel = new Label (String::empty, TRANS("Sample rate:")); sampleRateLabel->attachToComponent (sampleRateDropDown, true); } else @@ -599,11 +599,11 @@ private: sampleRateDropDown->removeListener (this); } - const int numRates = currentDevice->getNumSampleRates(); + const Array rates (currentDevice->getAvailableSampleRates()); - for (int i = 0; i < numRates; ++i) + for (int i = 0; i < rates.size(); ++i) { - const int rate = roundToInt (currentDevice->getSampleRate (i)); + const int rate = roundToInt (rates[i]); sampleRateDropDown->addItem (String (rate) + " Hz", rate); } @@ -617,7 +617,7 @@ private: { addAndMakeVisible (bufferSizeDropDown = new ComboBox (String::empty)); - bufferSizeLabel = new Label (String::empty, TRANS ("audio buffer size:")); + bufferSizeLabel = new Label (String::empty, TRANS("Audio buffer size:")); bufferSizeLabel->attachToComponent (bufferSizeDropDown, true); } else @@ -626,19 +626,16 @@ private: bufferSizeDropDown->removeListener (this); } - const int numBufferSizes = currentDevice->getNumBufferSizesAvailable(); + const Array bufferSizes (currentDevice->getAvailableBufferSizes()); + double currentRate = currentDevice->getCurrentSampleRate(); if (currentRate == 0) currentRate = 48000.0; - for (int i = 0; i < numBufferSizes; ++i) + for (int i = 0; i < bufferSizes.size(); ++i) { - const int bs = currentDevice->getBufferSizeSamples (i); - bufferSizeDropDown->addItem (String (bs) - + " samples (" - + String (bs * 1000.0 / currentRate, 1) - + " ms)", - bs); + const int bs = bufferSizes[i]; + bufferSizeDropDown->addItem (String (bs) + " samples (" + String (bs * 1000.0 / currentRate, 1) + " ms)", bs); } bufferSizeDropDown->setSelectedId (currentDevice->getCurrentBufferSizeSamples(), dontSendNotification); @@ -936,7 +933,7 @@ AudioDeviceSelectorComponent::AudioDeviceSelectorComponent (AudioDeviceManager& addAndMakeVisible (deviceTypeDropDown); deviceTypeDropDown->addListener (this); - deviceTypeDropDownLabel = new Label (String::empty, TRANS ("Audio device type:")); + deviceTypeDropDownLabel = new Label (String::empty, TRANS("Audio device type:")); deviceTypeDropDownLabel->setJustificationType (Justification::centredRight); deviceTypeDropDownLabel->attachToComponent (deviceTypeDropDown, true); } diff --git a/JuceLibraryCode/modules/juce_audio_utils/juce_module_info b/JuceLibraryCode/modules/juce_audio_utils/juce_module_info index 383c091..59269d2 100644 --- a/JuceLibraryCode/modules/juce_audio_utils/juce_module_info +++ b/JuceLibraryCode/modules/juce_audio_utils/juce_module_info @@ -1,7 +1,7 @@ { "id": "juce_audio_utils", "name": "JUCE extra audio utility classes", - "version": "3.0.0", + "version": "3.0.1", "description": "Classes for audio-related GUI and miscellaneous tasks.", "website": "http://www.juce.com/juce", "license": "GPL/Commercial", diff --git a/JuceLibraryCode/modules/juce_core/containers/juce_OwnedArray.h b/JuceLibraryCode/modules/juce_core/containers/juce_OwnedArray.h index c68bf46..a116df8 100644 --- a/JuceLibraryCode/modules/juce_core/containers/juce_OwnedArray.h +++ b/JuceLibraryCode/modules/juce_core/containers/juce_OwnedArray.h @@ -505,10 +505,7 @@ public: jassert (numElementsToAdd <= 0 || data.elements != nullptr); while (--numElementsToAdd >= 0) - { - data.elements [numUsed] = new ObjectClass (*arrayToAddFrom.getUnchecked (startIndex++)); - ++numUsed; - } + data.elements [numUsed++] = createCopyIfNotNull (arrayToAddFrom.getUnchecked (startIndex++)); } /** Inserts a new object into the array assuming that the array is sorted. diff --git a/JuceLibraryCode/modules/juce_core/containers/juce_PropertySet.cpp b/JuceLibraryCode/modules/juce_core/containers/juce_PropertySet.cpp index 735dc60..6b02baf 100644 --- a/JuceLibraryCode/modules/juce_core/containers/juce_PropertySet.cpp +++ b/JuceLibraryCode/modules/juce_core/containers/juce_PropertySet.cpp @@ -155,8 +155,8 @@ void PropertySet::removeValue (StringRef keyName) void PropertySet::setValue (const String& keyName, const XmlElement* const xml) { - setValue (keyName, xml == nullptr ? var::null - : var (xml->createDocument (String::empty, true))); + setValue (keyName, xml == nullptr ? var() + : var (xml->createDocument ("", true))); } bool PropertySet::containsKey (StringRef keyName) const noexcept diff --git a/JuceLibraryCode/modules/juce_core/containers/juce_PropertySet.h b/JuceLibraryCode/modules/juce_core/containers/juce_PropertySet.h index 8d3f429..2a4ecb1 100644 --- a/JuceLibraryCode/modules/juce_core/containers/juce_PropertySet.h +++ b/JuceLibraryCode/modules/juce_core/containers/juce_PropertySet.h @@ -69,7 +69,7 @@ public: @param keyName the name of the property to retrieve @param defaultReturnValue a value to return if the named property doesn't actually exist */ - String getValue (StringRef keyName, const String& defaultReturnValue = String::empty) const noexcept; + String getValue (StringRef keyName, const String& defaultReturnValue = String()) const noexcept; /** Returns one of the properties as an integer. @@ -109,8 +109,8 @@ public: /** Returns one of the properties as an XML element. - The result will a new XMLElement object that the caller must delete. If may return 0 if the - key isn't found, or if the entry contains an string that isn't valid XML. + The result will a new XMLElement object that the caller must delete. If may return nullptr + if the key isn't found, or if the entry contains an string that isn't valid XML. If the value isn't found in this set, then this will look for it in a fallback property set (if you've specified one with the setFallbackPropertySet() method), diff --git a/JuceLibraryCode/modules/juce_core/files/juce_File.cpp b/JuceLibraryCode/modules/juce_core/files/juce_File.cpp index b69b5a7..eff4af4 100644 --- a/JuceLibraryCode/modules/juce_core/files/juce_File.cpp +++ b/JuceLibraryCode/modules/juce_core/files/juce_File.cpp @@ -75,7 +75,7 @@ const File File::nonexistent; String File::parseAbsolutePath (const String& p) { if (p.isEmpty()) - return String::empty; + return String(); #if JUCE_WINDOWS // Windows.. @@ -322,7 +322,7 @@ String File::getFileNameWithoutExtension() const bool File::isAChildOf (const File& potentialParent) const { - if (potentialParent == File::nonexistent) + if (potentialParent.fullPath.isEmpty()) return false; const String ourPath (getPathUpToLastSlash()); @@ -482,11 +482,11 @@ bool File::loadFileAsData (MemoryBlock& destBlock) const String File::loadFileAsString() const { if (! existsAsFile()) - return String::empty; + return String(); FileInputStream in (*this); return in.openedOk() ? in.readEntireStreamAsString() - : String::empty; + : String(); } void File::readLines (StringArray& destLines) const @@ -598,7 +598,7 @@ String File::getFileExtension() const if (indexOfDot > fullPath.lastIndexOfChar (separator)) return fullPath.substring (indexOfDot); - return String::empty; + return String(); } bool File::hasFileExtension (StringRef possibleSuffix) const @@ -629,7 +629,7 @@ bool File::hasFileExtension (StringRef possibleSuffix) const File File::withFileExtension (StringRef newExtension) const { if (fullPath.isEmpty()) - return File::nonexistent; + return File(); String filePart (getFileName()); diff --git a/JuceLibraryCode/modules/juce_core/files/juce_File.h b/JuceLibraryCode/modules/juce_core/files/juce_File.h index 6e4e654..cfcba5e 100644 --- a/JuceLibraryCode/modules/juce_core/files/juce_File.h +++ b/JuceLibraryCode/modules/juce_core/files/juce_File.h @@ -741,7 +741,7 @@ public: @see revealToUser */ - bool startAsProcess (const String& parameters = String::empty) const; + bool startAsProcess (const String& parameters = String()) const; /** Opens Finder, Explorer, or whatever the OS uses, to show the user this file's location. @see startAsProcess diff --git a/JuceLibraryCode/modules/juce_core/files/juce_TemporaryFile.cpp b/JuceLibraryCode/modules/juce_core/files/juce_TemporaryFile.cpp index d5789b6..50475d5 100644 --- a/JuceLibraryCode/modules/juce_core/files/juce_TemporaryFile.cpp +++ b/JuceLibraryCode/modules/juce_core/files/juce_TemporaryFile.cpp @@ -50,7 +50,7 @@ TemporaryFile::TemporaryFile (const File& target, const int optionFlags) targetFile (target) { // If you use this constructor, you need to give it a valid target file! - jassert (targetFile != File::nonexistent); + jassert (targetFile != File()); } TemporaryFile::TemporaryFile (const File& target, const File& temporary) @@ -79,7 +79,7 @@ bool TemporaryFile::overwriteTargetFileWithTemporary() const { // This method only works if you created this object with the constructor // that takes a target file! - jassert (targetFile != File::nonexistent); + jassert (targetFile != File()); if (temporaryFile.exists()) { diff --git a/JuceLibraryCode/modules/juce_core/files/juce_TemporaryFile.h b/JuceLibraryCode/modules/juce_core/files/juce_TemporaryFile.h index ef48903..04561a7 100644 --- a/JuceLibraryCode/modules/juce_core/files/juce_TemporaryFile.h +++ b/JuceLibraryCode/modules/juce_core/files/juce_TemporaryFile.h @@ -89,7 +89,7 @@ public: The file will not be created until you write to it. And remember that when this object is deleted, the file will also be deleted! */ - TemporaryFile (const String& suffix = String::empty, + TemporaryFile (const String& suffix = String(), int optionFlags = 0); /** Creates a temporary file in the same directory as a specified file. diff --git a/JuceLibraryCode/modules/juce_core/javascript/juce_JSON.cpp b/JuceLibraryCode/modules/juce_core/javascript/juce_JSON.cpp index 8945fcc..8aa8192 100644 --- a/JuceLibraryCode/modules/juce_core/javascript/juce_JSON.cpp +++ b/JuceLibraryCode/modules/juce_core/javascript/juce_JSON.cpp @@ -35,7 +35,7 @@ public: switch (t.getAndAdvance()) { - case 0: result = var::null; return Result::ok(); + case 0: result = var(); return Result::ok(); case '{': return parseObject (t, result); case '[': return parseArray (t, result); } @@ -148,7 +148,7 @@ private: if (t2.getAndAdvance() == 'u' && t2.getAndAdvance() == 'l' && t2.getAndAdvance() == 'l') { t = t2; - result = var::null; + result = var(); return Result::ok(); } break; @@ -254,7 +254,7 @@ private: if (c2 != ':') return createFail ("Expected ':', but found", &oldT); - resultProperties.set (propertyName, var::null); + resultProperties.set (propertyName, var()); var* propertyValue = resultProperties.getVarPointer (propertyName); Result r2 (parseAny (t, *propertyValue)); @@ -300,7 +300,7 @@ private: return createFail ("Unexpected end-of-input in array declaration"); t = oldT; - destArray->add (var::null); + destArray->add (var()); Result r (parseAny (t, destArray->getReference (destArray->size() - 1))); if (r.failed()) @@ -514,7 +514,7 @@ var JSON::parse (const String& text) var result; if (! JSONParser::parseObjectOrArray (text.getCharPointer(), result)) - result = var::null; + result = var(); return result; } @@ -610,7 +610,7 @@ public: { switch (r.nextInt (depth > 3 ? 6 : 8)) { - case 0: return var::null; + case 0: return var(); case 1: return r.nextInt(); case 2: return r.nextInt64(); case 3: return r.nextBool(); @@ -638,7 +638,7 @@ public: } default: - return var::null; + return var(); } } diff --git a/JuceLibraryCode/modules/juce_core/javascript/juce_Javascript.cpp b/JuceLibraryCode/modules/juce_core/javascript/juce_Javascript.cpp index 1e269d0..00dc44b 100644 --- a/JuceLibraryCode/modules/juce_core/javascript/juce_Javascript.cpp +++ b/JuceLibraryCode/modules/juce_core/javascript/juce_Javascript.cpp @@ -298,11 +298,10 @@ struct JavascriptEngine::RootObject : public DynamicObject if (r == returnWasHit) return r; if (r == breakWasHit) break; - if (r == continueWasHit) continue; iterator->perform (s, nullptr); - if (isDoLoop && ! condition->getResult (s)) + if (isDoLoop && r != continueWasHit && ! condition->getResult (s)) break; } @@ -1244,7 +1243,7 @@ struct JavascriptEngine::RootObject : public DynamicObject if (matchIf (TokenTypes::openParen)) return parseSuffixes (matchCloseParen (parseExpression())); if (matchIf (TokenTypes::true_)) return parseSuffixes (new LiteralValue (location, (int) 1)); if (matchIf (TokenTypes::false_)) return parseSuffixes (new LiteralValue (location, (int) 0)); - if (matchIf (TokenTypes::null_)) return parseSuffixes (new LiteralValue (location, var::null)); + if (matchIf (TokenTypes::null_)) return parseSuffixes (new LiteralValue (location, var())); if (matchIf (TokenTypes::undefined)) return parseSuffixes (new Expression (location)); if (currentType == TokenTypes::literal) diff --git a/JuceLibraryCode/modules/juce_core/javascript/juce_Javascript.h b/JuceLibraryCode/modules/juce_core/javascript/juce_Javascript.h index e080b18..42368a4 100644 --- a/JuceLibraryCode/modules/juce_core/javascript/juce_Javascript.h +++ b/JuceLibraryCode/modules/juce_core/javascript/juce_Javascript.h @@ -26,7 +26,6 @@ ============================================================================== */ - /** A simple javascript interpreter! diff --git a/JuceLibraryCode/modules/juce_core/juce_module_info b/JuceLibraryCode/modules/juce_core/juce_module_info index a3a1ee2..d6cb11d 100644 --- a/JuceLibraryCode/modules/juce_core/juce_module_info +++ b/JuceLibraryCode/modules/juce_core/juce_module_info @@ -1,7 +1,7 @@ { "id": "juce_core", "name": "JUCE core classes", - "version": "3.0.0", + "version": "3.0.1", "description": "The essential set of basic JUCE classes, as required by all the other JUCE modules. Includes text, container, memory, threading and i/o functionality.", "website": "http://www.juce.com/juce", "license": "ISC Permissive", diff --git a/JuceLibraryCode/modules/juce_core/maths/juce_BigInteger.cpp b/JuceLibraryCode/modules/juce_core/maths/juce_BigInteger.cpp index ba37420..ba19414 100644 --- a/JuceLibraryCode/modules/juce_core/maths/juce_BigInteger.cpp +++ b/JuceLibraryCode/modules/juce_core/maths/juce_BigInteger.cpp @@ -951,7 +951,7 @@ String BigInteger::toString (const int base, const int minimumNumCharacters) con else { jassertfalse; // can't do the specified base! - return String::empty; + return String(); } s = s.paddedLeft ('0', minimumNumCharacters); diff --git a/JuceLibraryCode/modules/juce_core/maths/juce_Expression.cpp b/JuceLibraryCode/modules/juce_core/maths/juce_Expression.cpp index 41b144a..9734446 100644 --- a/JuceLibraryCode/modules/juce_core/maths/juce_Expression.cpp +++ b/JuceLibraryCode/modules/juce_core/maths/juce_Expression.cpp @@ -53,7 +53,7 @@ public: virtual String getName() const { jassertfalse; // You shouldn't call this for an expression that's not actually a function! - return String::empty; + return String(); } virtual void renameSymbol (const Symbol& oldSymbol, const String& newName, const Scope& scope, int recursionDepth) @@ -1181,5 +1181,5 @@ void Expression::Scope::visitRelativeScope (const String& scopeName, Visitor&) c String Expression::Scope::getScopeUID() const { - return String::empty; + return String(); } diff --git a/JuceLibraryCode/modules/juce_core/misc/juce_Uuid.cpp b/JuceLibraryCode/modules/juce_core/misc/juce_Uuid.cpp index bffb38d..eb74964 100644 --- a/JuceLibraryCode/modules/juce_core/misc/juce_Uuid.cpp +++ b/JuceLibraryCode/modules/juce_core/misc/juce_Uuid.cpp @@ -26,7 +26,6 @@ ============================================================================== */ - Uuid::Uuid() { Random r; diff --git a/JuceLibraryCode/modules/juce_core/native/juce_android_Files.cpp b/JuceLibraryCode/modules/juce_core/native/juce_android_Files.cpp index 1818043..45903b1 100644 --- a/JuceLibraryCode/modules/juce_core/native/juce_android_Files.cpp +++ b/JuceLibraryCode/modules/juce_core/native/juce_android_Files.cpp @@ -80,7 +80,7 @@ File File::getSpecialLocation (const SpecialLocationType type) break; } - return File::nonexistent; + return File(); } bool File::moveToTrash() const diff --git a/JuceLibraryCode/modules/juce_core/native/juce_linux_Files.cpp b/JuceLibraryCode/modules/juce_core/native/juce_linux_Files.cpp index 436480d..d0dd54d 100644 --- a/JuceLibraryCode/modules/juce_core/native/juce_linux_Files.cpp +++ b/JuceLibraryCode/modules/juce_core/native/juce_linux_Files.cpp @@ -72,7 +72,7 @@ bool File::isOnRemovableDrive() const String File::getVersion() const { - return String::empty; // xxx not yet implemented + return String(); // xxx not yet implemented } //============================================================================== @@ -115,7 +115,7 @@ File File::getSpecialLocation (const SpecialLocationType type) if (struct passwd* const pw = getpwuid (getuid())) return File (CharPointer_UTF8 (pw->pw_dir)); - return File::nonexistent; + return File(); } case userDocumentsDirectory: return resolveXDGFolder ("XDG_DOCUMENTS_DIR", "~"); @@ -163,7 +163,7 @@ File File::getSpecialLocation (const SpecialLocationType type) break; } - return File::nonexistent; + return File(); } //============================================================================== diff --git a/JuceLibraryCode/modules/juce_core/native/juce_linux_Network.cpp b/JuceLibraryCode/modules/juce_core/native/juce_linux_Network.cpp index a946273..a052b2c 100644 --- a/JuceLibraryCode/modules/juce_core/native/juce_linux_Network.cpp +++ b/JuceLibraryCode/modules/juce_core/native/juce_linux_Network.cpp @@ -309,7 +309,7 @@ private: { char c = 0; if (read (&c, 1) != 1) - return String::empty; + return String(); buffer.writeByte (c); @@ -324,7 +324,7 @@ private: if (header.startsWithIgnoreCase ("HTTP/")) return header; - return String::empty; + return String(); } static void writeValueIfNotPresent (MemoryOutputStream& dest, const String& headers, const String& key, const String& value) @@ -432,7 +432,7 @@ private: if (lines[i].startsWithIgnoreCase (itemName)) return lines[i].substring (itemName.length()).trim(); - return String::empty; + return String(); } JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WebInputStream) diff --git a/JuceLibraryCode/modules/juce_core/native/juce_linux_SystemStats.cpp b/JuceLibraryCode/modules/juce_core/native/juce_linux_SystemStats.cpp index 91cecbd..a2575a1 100644 --- a/JuceLibraryCode/modules/juce_core/native/juce_linux_SystemStats.cpp +++ b/JuceLibraryCode/modules/juce_core/native/juce_linux_SystemStats.cpp @@ -44,7 +44,7 @@ String SystemStats::getOperatingSystemName() String SystemStats::getDeviceDescription() { - return String::empty; + return String(); } bool SystemStats::isOperatingSystem64Bit() @@ -69,7 +69,7 @@ namespace LinuxStatsHelpers if (lines[i].startsWithIgnoreCase (key)) return lines[i].fromFirstOccurrenceOf (":", false, false).trim(); - return String::empty; + return String(); } } @@ -107,7 +107,7 @@ String SystemStats::getLogonName() if (struct passwd* const pw = getpwuid (getuid())) return CharPointer_UTF8 (pw->pw_name); - return String::empty; + return String(); } String SystemStats::getFullUserName() @@ -121,7 +121,7 @@ String SystemStats::getComputerName() if (gethostname (name, sizeof (name) - 1) == 0) return name; - return String::empty; + return String(); } static String getLocaleValue (nl_item key) diff --git a/JuceLibraryCode/modules/juce_core/native/juce_mac_Files.mm b/JuceLibraryCode/modules/juce_core/native/juce_mac_Files.mm index e4d4f96..0a00e92 100644 --- a/JuceLibraryCode/modules/juce_core/native/juce_mac_Files.mm +++ b/JuceLibraryCode/modules/juce_core/native/juce_mac_Files.mm @@ -257,7 +257,7 @@ File File::getSpecialLocation (const SpecialLocationType type) return File (resultPath.convertToPrecomposedUnicode()); } - return File::nonexistent; + return File(); } //============================================================================== @@ -271,7 +271,7 @@ String File::getVersion() const return nsStringToJuce (name); } - return String::empty; + return String(); } //============================================================================== diff --git a/JuceLibraryCode/modules/juce_core/native/juce_mac_Strings.mm b/JuceLibraryCode/modules/juce_core/native/juce_mac_Strings.mm index ac81900..862eb53 100644 --- a/JuceLibraryCode/modules/juce_core/native/juce_mac_Strings.mm +++ b/JuceLibraryCode/modules/juce_core/native/juce_mac_Strings.mm @@ -29,7 +29,7 @@ String String::fromCFString (CFStringRef cfString) { if (cfString == 0) - return String::empty; + return String(); CFRange range = { 0, CFStringGetLength (cfString) }; HeapBlock u ((size_t) range.length + 1); diff --git a/JuceLibraryCode/modules/juce_core/native/juce_mac_SystemStats.mm b/JuceLibraryCode/modules/juce_core/native/juce_mac_SystemStats.mm index ecccb2d..45967ee 100644 --- a/JuceLibraryCode/modules/juce_core/native/juce_mac_SystemStats.mm +++ b/JuceLibraryCode/modules/juce_core/native/juce_mac_SystemStats.mm @@ -124,7 +124,7 @@ SystemStats::OperatingSystemType SystemStats::getOperatingSystemType() return iOS; #else StringArray parts; - parts.addTokens (getOSXVersion(), ".", String::empty); + parts.addTokens (getOSXVersion(), ".", String()); jassert (parts[0].getIntValue() == 10); const int major = parts[1].getIntValue(); @@ -148,7 +148,7 @@ String SystemStats::getDeviceDescription() #if JUCE_IOS return nsStringToJuce ([[UIDevice currentDevice] model]); #else - return String::empty; + return String(); #endif } @@ -182,7 +182,7 @@ String SystemStats::getCpuVendor() return String (reinterpret_cast (vendor), 12); #else - return String::empty; + return String(); #endif } @@ -218,7 +218,7 @@ String SystemStats::getComputerName() if (gethostname (name, sizeof (name) - 1) == 0) return String (name).upToLastOccurrenceOf (".local", false, true); - return String::empty; + return String(); } static String getLocaleValue (CFStringRef key) diff --git a/JuceLibraryCode/modules/juce_core/native/juce_posix_SharedCode.h b/JuceLibraryCode/modules/juce_core/native/juce_posix_SharedCode.h index 243a194..46d2382 100644 --- a/JuceLibraryCode/modules/juce_core/native/juce_posix_SharedCode.h +++ b/JuceLibraryCode/modules/juce_core/native/juce_posix_SharedCode.h @@ -636,7 +636,7 @@ String File::getVolumeLabel() const } #endif - return String::empty; + return String(); } int File::getVolumeSerialNumber() const diff --git a/JuceLibraryCode/modules/juce_core/native/juce_win32_Files.cpp b/JuceLibraryCode/modules/juce_core/native/juce_win32_Files.cpp index 866d2d3..6f0b3f4 100644 --- a/JuceLibraryCode/modules/juce_core/native/juce_win32_Files.cpp +++ b/JuceLibraryCode/modules/juce_core/native/juce_win32_Files.cpp @@ -93,7 +93,7 @@ namespace WindowsFileHelpers if (SHGetSpecialFolderPath (0, path, type, FALSE)) return File (String (path)); - return File::nonexistent; + return File(); } File getModuleFileName (HINSTANCE moduleHandle) @@ -542,7 +542,7 @@ File JUCE_CALLTYPE File::getSpecialLocation (const SpecialLocationType type) default: jassertfalse; // unknown type? - return File::nonexistent; + return File(); } return WindowsFileHelpers::getSpecialFolderPath (csidlType); diff --git a/JuceLibraryCode/modules/juce_core/network/juce_IPAddress.cpp b/JuceLibraryCode/modules/juce_core/network/juce_IPAddress.cpp index ced207d..6c4d087 100644 --- a/JuceLibraryCode/modules/juce_core/network/juce_IPAddress.cpp +++ b/JuceLibraryCode/modules/juce_core/network/juce_IPAddress.cpp @@ -55,7 +55,7 @@ IPAddress::IPAddress (uint32 n) noexcept IPAddress::IPAddress (const String& adr) { StringArray tokens; - tokens.addTokens (adr, ".", String::empty); + tokens.addTokens (adr, ".", String()); for (int i = 0; i < 4; ++i) address[i] = (uint8) tokens[i].getIntValue(); diff --git a/JuceLibraryCode/modules/juce_core/network/juce_Socket.cpp b/JuceLibraryCode/modules/juce_core/network/juce_Socket.cpp index c902d41..2d5d1fe 100644 --- a/JuceLibraryCode/modules/juce_core/network/juce_Socket.cpp +++ b/JuceLibraryCode/modules/juce_core/network/juce_Socket.cpp @@ -386,7 +386,7 @@ void StreamingSocket::close() ::close (handle); #endif - hostName = String::empty; + hostName.clear(); portNumber = 0; handle = -1; isListener = false; @@ -506,7 +506,7 @@ void DatagramSocket::close() ::close (handle); #endif - hostName = String::empty; + hostName.clear(); portNumber = 0; handle = -1; } diff --git a/JuceLibraryCode/modules/juce_core/network/juce_Socket.h b/JuceLibraryCode/modules/juce_core/network/juce_Socket.h index 616d2db..73795c9 100644 --- a/JuceLibraryCode/modules/juce_core/network/juce_Socket.h +++ b/JuceLibraryCode/modules/juce_core/network/juce_Socket.h @@ -146,7 +146,7 @@ public: @see waitForNextConnection */ - bool createListener (int portNumber, const String& localHostName = String::empty); + bool createListener (int portNumber, const String& localHostName = String()); /** When in "listener" mode, this waits for a connection and spawns it as a new socket. diff --git a/JuceLibraryCode/modules/juce_core/network/juce_URL.cpp b/JuceLibraryCode/modules/juce_core/network/juce_URL.cpp index b9db52a..cf9f2cf 100644 --- a/JuceLibraryCode/modules/juce_core/network/juce_URL.cpp +++ b/JuceLibraryCode/modules/juce_core/network/juce_URL.cpp @@ -181,7 +181,7 @@ namespace URLHelpers << "\"; filename=\"" << file.getFileName() << "\"\r\n"; const String mimeType (url.getMimeTypesOfUploadFiles() - .getValue (paramName, String::empty)); + .getValue (paramName, String())); if (mimeType.isNotEmpty()) data << "Content-Type: " << mimeType << "\r\n"; @@ -253,7 +253,7 @@ String URL::getSubPath() const { const int startOfPath = URLHelpers::findStartOfPath (url); - return startOfPath <= 0 ? String::empty + return startOfPath <= 0 ? String() : url.substring (startOfPath); } @@ -363,7 +363,7 @@ String URL::readEntireTextStream (const bool usePostCommand) const if (in != nullptr) return in->readEntireStreamAsString(); - return String::empty; + return String(); } XmlElement* URL::readEntireXmlStream (const bool usePostCommand) const @@ -470,5 +470,5 @@ bool URL::launchInDefaultBrowser() const if (u.containsChar ('@') && ! u.containsChar (':')) u = "mailto:" + u; - return Process::openDocument (u, String::empty); + return Process::openDocument (u, String()); } diff --git a/JuceLibraryCode/modules/juce_core/network/juce_URL.h b/JuceLibraryCode/modules/juce_core/network/juce_URL.h index 462ec88..28d3aac 100644 --- a/JuceLibraryCode/modules/juce_core/network/juce_URL.h +++ b/JuceLibraryCode/modules/juce_core/network/juce_URL.h @@ -251,7 +251,7 @@ public: InputStream* createInputStream (bool usePostCommand, OpenStreamProgressCallback* progressCallback = nullptr, void* progressCallbackContext = nullptr, - String extraHeaders = String::empty, + String extraHeaders = String(), int connectionTimeOutMs = 0, StringPairArray* responseHeaders = nullptr) const; diff --git a/JuceLibraryCode/modules/juce_core/system/juce_StandardHeader.h b/JuceLibraryCode/modules/juce_core/system/juce_StandardHeader.h index a899843..12e332a 100644 --- a/JuceLibraryCode/modules/juce_core/system/juce_StandardHeader.h +++ b/JuceLibraryCode/modules/juce_core/system/juce_StandardHeader.h @@ -36,7 +36,7 @@ */ #define JUCE_MAJOR_VERSION 3 #define JUCE_MINOR_VERSION 0 -#define JUCE_BUILDNUMBER 0 +#define JUCE_BUILDNUMBER 1 /** Current Juce version number. diff --git a/JuceLibraryCode/modules/juce_core/text/juce_LocalisedStrings.cpp b/JuceLibraryCode/modules/juce_core/text/juce_LocalisedStrings.cpp index f2098a0..1d70e4e 100644 --- a/JuceLibraryCode/modules/juce_core/text/juce_LocalisedStrings.cpp +++ b/JuceLibraryCode/modules/juce_core/text/juce_LocalisedStrings.cpp @@ -43,11 +43,17 @@ LocalisedStrings::~LocalisedStrings() //============================================================================== String LocalisedStrings::translate (const String& text) const { + if (fallback != nullptr && ! translations.containsKey (text)) + return fallback->translate (text); + return translations.getValue (text, text); } String LocalisedStrings::translate (const String& text, const String& resultIfNotFound) const { + if (fallback != nullptr && ! translations.containsKey (text)) + return fallback->translate (text, resultIfNotFound); + return translations.getValue (text, resultIfNotFound); } @@ -73,7 +79,7 @@ namespace SpinLock currentMappingsLock; ScopedPointer currentMappings; - int findCloseQuote (const String& text, int startPos) + static int findCloseQuote (const String& text, int startPos) { juce_wchar lastChar = 0; String::CharPointerType t (text.getCharPointer() + startPos); @@ -92,7 +98,7 @@ namespace return startPos; } - String unescapeString (const String& s) + static String unescapeString (const String& s) { return s.replace ("\\\"", "\"") .replace ("\\\'", "\'") @@ -141,6 +147,8 @@ void LocalisedStrings::loadFromText (const String& fileContents, bool ignoreCase countryCodes.removeEmptyStrings(); } } + + translations.minimiseStorageOverheads(); } void LocalisedStrings::addStrings (const LocalisedStrings& other) @@ -151,6 +159,11 @@ void LocalisedStrings::addStrings (const LocalisedStrings& other) translations.addArray (other.translations); } +void LocalisedStrings::setFallback (LocalisedStrings* f) +{ + fallback = f; +} + //============================================================================== void LocalisedStrings::setCurrentMappings (LocalisedStrings* newTranslations) { diff --git a/JuceLibraryCode/modules/juce_core/text/juce_LocalisedStrings.h b/JuceLibraryCode/modules/juce_core/text/juce_LocalisedStrings.h index 69e9df9..acf1da8 100644 --- a/JuceLibraryCode/modules/juce_core/text/juce_LocalisedStrings.h +++ b/JuceLibraryCode/modules/juce_core/text/juce_LocalisedStrings.h @@ -183,11 +183,18 @@ public: */ void addStrings (const LocalisedStrings&); + /** Gives this object a set of strings to use as a fallback if a string isn't found. + The object that is passed-in will be owned and deleted by this object + when no longer needed. It can be nullptr to clear the existing fallback object. + */ + void setFallback (LocalisedStrings* fallbackStrings); + private: //============================================================================== String languageName; StringArray countryCodes; StringPairArray translations; + ScopedPointer fallback; void loadFromText (const String&, bool ignoreCase); diff --git a/JuceLibraryCode/modules/juce_core/text/juce_String.cpp b/JuceLibraryCode/modules/juce_core/text/juce_String.cpp index d2137de..d9f8fea 100644 --- a/JuceLibraryCode/modules/juce_core/text/juce_String.cpp +++ b/JuceLibraryCode/modules/juce_core/text/juce_String.cpp @@ -259,6 +259,12 @@ void String::swapWith (String& other) noexcept std::swap (text, other.text); } +void String::clear() noexcept +{ + StringHolder::release (text); + text = &(emptyString.text); +} + String& String::operator= (const String& other) noexcept { StringHolder::retain (other.text); @@ -419,7 +425,8 @@ namespace NumberToStringConverters { explicit StackArrayStream (char* d) { - imbue (std::locale::classic()); + static const std::locale classicLocale (std::locale::classic()); + imbue (classicLocale); setp (d, d + charsNeededForDouble); } diff --git a/JuceLibraryCode/modules/juce_core/text/juce_String.h b/JuceLibraryCode/modules/juce_core/text/juce_String.h index c760239..febe6ee 100644 --- a/JuceLibraryCode/modules/juce_core/text/juce_String.h +++ b/JuceLibraryCode/modules/juce_core/text/juce_String.h @@ -307,6 +307,9 @@ public: */ inline bool isNotEmpty() const noexcept { return text[0] != 0; } + /** Resets this string to be empty. */ + void clear() noexcept; + /** Case-insensitive comparison with another string. */ bool equalsIgnoreCase (const String& other) const noexcept; diff --git a/JuceLibraryCode/modules/juce_core/text/juce_StringArray.cpp b/JuceLibraryCode/modules/juce_core/text/juce_StringArray.cpp index 4925023..e26d38d 100644 --- a/JuceLibraryCode/modules/juce_core/text/juce_StringArray.cpp +++ b/JuceLibraryCode/modules/juce_core/text/juce_StringArray.cpp @@ -293,7 +293,7 @@ String StringArray::joinIntoString (StringRef separator, int start, int numberTo start = 0; if (start >= last) - return String::empty; + return String(); if (start == last - 1) return strings.getReference (start); diff --git a/JuceLibraryCode/modules/juce_core/text/juce_StringPairArray.cpp b/JuceLibraryCode/modules/juce_core/text/juce_StringPairArray.cpp index 7912cd9..3275d2d 100644 --- a/JuceLibraryCode/modules/juce_core/text/juce_StringPairArray.cpp +++ b/JuceLibraryCode/modules/juce_core/text/juce_StringPairArray.cpp @@ -78,6 +78,11 @@ String StringPairArray::getValue (StringRef key, const String& defaultReturnValu return defaultReturnValue; } +bool StringPairArray::containsKey (StringRef key) const noexcept +{ + return keys.contains (key); +} + void StringPairArray::set (const String& key, const String& value) { const int i = keys.indexOf (key, ignoreCase); diff --git a/JuceLibraryCode/modules/juce_core/text/juce_StringPairArray.h b/JuceLibraryCode/modules/juce_core/text/juce_StringPairArray.h index add4a60..e1c774d 100644 --- a/JuceLibraryCode/modules/juce_core/text/juce_StringPairArray.h +++ b/JuceLibraryCode/modules/juce_core/text/juce_StringPairArray.h @@ -85,6 +85,8 @@ public: */ String getValue (StringRef, const String& defaultReturnValue) const; + /** Returns true if the given key exists. */ + bool containsKey (StringRef key) const noexcept; /** Returns a list of all keys in the array. */ const StringArray& getAllKeys() const noexcept { return keys; } diff --git a/JuceLibraryCode/modules/juce_core/text/juce_StringPool.cpp b/JuceLibraryCode/modules/juce_core/text/juce_StringPool.cpp index 88ea9a0..6841c47 100644 --- a/JuceLibraryCode/modules/juce_core/text/juce_StringPool.cpp +++ b/JuceLibraryCode/modules/juce_core/text/juce_StringPool.cpp @@ -81,7 +81,7 @@ namespace StringPoolHelpers String::CharPointerType StringPool::getPooledString (const String& s) { if (s.isEmpty()) - return String::empty.getCharPointer(); + return String().getCharPointer(); return StringPoolHelpers::getPooledStringFromArray (strings, s, lock); } @@ -89,7 +89,7 @@ String::CharPointerType StringPool::getPooledString (const String& s) String::CharPointerType StringPool::getPooledString (const char* const s) { if (s == nullptr || *s == 0) - return String::empty.getCharPointer(); + return String().getCharPointer(); return StringPoolHelpers::getPooledStringFromArray (strings, s, lock); } @@ -97,7 +97,7 @@ String::CharPointerType StringPool::getPooledString (const char* const s) String::CharPointerType StringPool::getPooledString (const wchar_t* const s) { if (s == nullptr || *s == 0) - return String::empty.getCharPointer(); + return String().getCharPointer(); return StringPoolHelpers::getPooledStringFromArray (strings, s, lock); } diff --git a/JuceLibraryCode/modules/juce_core/threads/juce_ThreadPool.h b/JuceLibraryCode/modules/juce_core/threads/juce_ThreadPool.h index c93d436..ed820e8 100644 --- a/JuceLibraryCode/modules/juce_core/threads/juce_ThreadPool.h +++ b/JuceLibraryCode/modules/juce_core/threads/juce_ThreadPool.h @@ -247,7 +247,7 @@ public: /** Returns one of the jobs in the queue. Note that this can be a very volatile list as jobs might be continuously getting shifted - around in the list, and this method may return 0 if the index is currently out-of-range. + around in the list, and this method may return nullptr if the index is currently out-of-range. */ ThreadPoolJob* getJob (int index) const; diff --git a/JuceLibraryCode/modules/juce_core/time/juce_PerformanceCounter.cpp b/JuceLibraryCode/modules/juce_core/time/juce_PerformanceCounter.cpp index a071665..b22d5e4 100644 --- a/JuceLibraryCode/modules/juce_core/time/juce_PerformanceCounter.cpp +++ b/JuceLibraryCode/modules/juce_core/time/juce_PerformanceCounter.cpp @@ -28,7 +28,7 @@ static void appendToFile (const File& f, const String& s) { - if (f != File::nonexistent) + if (f.getFullPathName().isNotEmpty()) { FileOutputStream out (f); diff --git a/JuceLibraryCode/modules/juce_core/time/juce_PerformanceCounter.h b/JuceLibraryCode/modules/juce_core/time/juce_PerformanceCounter.h index 81db856..aac50d2 100644 --- a/JuceLibraryCode/modules/juce_core/time/juce_PerformanceCounter.h +++ b/JuceLibraryCode/modules/juce_core/time/juce_PerformanceCounter.h @@ -65,7 +65,7 @@ public: */ PerformanceCounter (const String& counterName, int runsPerPrintout = 100, - const File& loggingFile = File::nonexistent); + const File& loggingFile = File()); /** Destructor. */ ~PerformanceCounter(); diff --git a/JuceLibraryCode/modules/juce_core/xml/juce_XmlDocument.cpp b/JuceLibraryCode/modules/juce_core/xml/juce_XmlDocument.cpp index b95a8a6..6243d3e 100644 --- a/JuceLibraryCode/modules/juce_core/xml/juce_XmlDocument.cpp +++ b/JuceLibraryCode/modules/juce_core/xml/juce_XmlDocument.cpp @@ -210,7 +210,7 @@ XmlElement* XmlDocument::parseDocumentElement (String::CharPointerType textToPar } else { - lastError = String::empty; + lastError.clear(); ScopedPointer result (readNextElement (! onlyReadOuterDocumentElement)); diff --git a/JuceLibraryCode/modules/juce_core/zip/juce_ZipFile.cpp b/JuceLibraryCode/modules/juce_core/zip/juce_ZipFile.cpp index dba7fe7..0f257e2 100644 --- a/JuceLibraryCode/modules/juce_core/zip/juce_ZipFile.cpp +++ b/JuceLibraryCode/modules/juce_core/zip/juce_ZipFile.cpp @@ -541,7 +541,7 @@ private: void writeFlagsAndSizes (OutputStream& target) const { target.writeShort (10); // version needed - target.writeShort (0); // flags + target.writeShort ((short) (1 << 11)); // this flag indicates UTF-8 filename encoding target.writeShort (compressionLevel > 0 ? (short) 8 : (short) 0); writeTimeAndDate (target, fileTime); target.writeInt ((int) checksum); diff --git a/JuceLibraryCode/modules/juce_data_structures/app_properties/juce_PropertiesFile.cpp b/JuceLibraryCode/modules/juce_data_structures/app_properties/juce_PropertiesFile.cpp index 78c9736..3c1c0d4 100644 --- a/JuceLibraryCode/modules/juce_data_structures/app_properties/juce_PropertiesFile.cpp +++ b/JuceLibraryCode/modules/juce_data_structures/app_properties/juce_PropertiesFile.cpp @@ -90,8 +90,8 @@ File PropertiesFile::Options::getDefaultFile() const File dir (File::getSpecialLocation (commonToAllUsers ? File::commonApplicationDataDirectory : File::userApplicationDataDirectory)); - if (dir == File::nonexistent) - return File::nonexistent; + if (dir == File()) + return File(); dir = dir.getChildFile (folderName.isNotEmpty() ? folderName : applicationName); @@ -165,7 +165,7 @@ bool PropertiesFile::save() stopTimer(); if (options.doNotSave - || file == File::nonexistent + || file == File() || file.isDirectory() || ! file.getParentDirectory().createDirectory()) return false; @@ -195,7 +195,7 @@ bool PropertiesFile::loadAsXml() { getAllProperties().set (name, e->getFirstChildElement() != nullptr - ? e->getFirstChildElement()->createDocument (String::empty, true) + ? e->getFirstChildElement()->createDocument ("", true) : e->getStringAttribute (PropertyFileConstants::valueAttribute)); } } @@ -234,7 +234,7 @@ bool PropertiesFile::saveAsXml() if (pl != nullptr && ! pl->isLocked()) return false; // locking failure.. - if (doc.writeToFile (file, String::empty)) + if (doc.writeToFile (file, String())) { needsWriting = false; return true; diff --git a/JuceLibraryCode/modules/juce_data_structures/app_properties/juce_PropertiesFile.h b/JuceLibraryCode/modules/juce_data_structures/app_properties/juce_PropertiesFile.h index 0e82b13..fd09c8c 100644 --- a/JuceLibraryCode/modules/juce_data_structures/app_properties/juce_PropertiesFile.h +++ b/JuceLibraryCode/modules/juce_data_structures/app_properties/juce_PropertiesFile.h @@ -144,7 +144,7 @@ public: C:\\Documents and Settings\\username\\Application Data\\[folderName]\\[applicationName].[filenameSuffix] On Linux it'll return - ~/.[folderName]/[applicationName].[filenameSuffix] + ~/[folderName]/[applicationName].[filenameSuffix] If the folderName variable is empty, it'll use the app name for this (or omit the folder name on the Mac). diff --git a/JuceLibraryCode/modules/juce_data_structures/juce_module_info b/JuceLibraryCode/modules/juce_data_structures/juce_module_info index bd92088..7ba39c4 100644 --- a/JuceLibraryCode/modules/juce_data_structures/juce_module_info +++ b/JuceLibraryCode/modules/juce_data_structures/juce_module_info @@ -1,7 +1,7 @@ { "id": "juce_data_structures", "name": "JUCE data model helper classes", - "version": "3.0.0", + "version": "3.0.1", "description": "Classes for undo/redo management, and smart data structures.", "website": "http://www.juce.com/juce", "license": "GPL/Commercial", diff --git a/JuceLibraryCode/modules/juce_data_structures/undomanager/juce_UndoManager.cpp b/JuceLibraryCode/modules/juce_data_structures/undomanager/juce_UndoManager.cpp index 328a052..3557b90 100644 --- a/JuceLibraryCode/modules/juce_data_structures/undomanager/juce_UndoManager.cpp +++ b/JuceLibraryCode/modules/juce_data_structures/undomanager/juce_UndoManager.cpp @@ -235,7 +235,7 @@ String UndoManager::getUndoDescription() const if (const ActionSet* const s = getCurrentSet()) return s->name; - return String::empty; + return String(); } String UndoManager::getRedoDescription() const @@ -243,7 +243,7 @@ String UndoManager::getRedoDescription() const if (const ActionSet* const s = getNextSet()) return s->name; - return String::empty; + return String(); } Time UndoManager::getTimeOfUndoTransaction() const diff --git a/JuceLibraryCode/modules/juce_data_structures/undomanager/juce_UndoManager.h b/JuceLibraryCode/modules/juce_data_structures/undomanager/juce_UndoManager.h index 6948263..eee9cbc 100644 --- a/JuceLibraryCode/modules/juce_data_structures/undomanager/juce_UndoManager.h +++ b/JuceLibraryCode/modules/juce_data_structures/undomanager/juce_UndoManager.h @@ -107,7 +107,7 @@ public: @see beginNewTransaction */ bool perform (UndoableAction* action, - const String& actionName = String::empty); + const String& actionName = String()); /** Starts a new group of actions that together will be treated as a single transaction. @@ -118,7 +118,7 @@ public: @param actionName a description of the transaction that is about to be performed */ - void beginNewTransaction (const String& actionName = String::empty); + void beginNewTransaction (const String& actionName = String()); /** Changes the name stored for the current transaction. diff --git a/JuceLibraryCode/modules/juce_data_structures/values/juce_ValueTree.cpp b/JuceLibraryCode/modules/juce_data_structures/values/juce_ValueTree.cpp index 2c4a94c..a9e3893 100644 --- a/JuceLibraryCode/modules/juce_data_structures/values/juce_ValueTree.cpp +++ b/JuceLibraryCode/modules/juce_data_structures/values/juce_ValueTree.cpp @@ -168,7 +168,7 @@ public: } else { - undoManager->perform (new SetPropertyAction (this, name, newValue, var::null, true, false)); + undoManager->perform (new SetPropertyAction (this, name, newValue, var(), true, false)); } } } @@ -188,7 +188,7 @@ public: else { if (properties.contains (name)) - undoManager->perform (new SetPropertyAction (this, name, var::null, properties [name], false, true)); + undoManager->perform (new SetPropertyAction (this, name, var(), properties [name], false, true)); } } @@ -206,7 +206,7 @@ public: else { for (int i = properties.size(); --i >= 0;) - undoManager->perform (new SetPropertyAction (this, properties.getName(i), var::null, + undoManager->perform (new SetPropertyAction (this, properties.getName(i), var(), properties.getValueAt(i), false, true)); } } @@ -230,7 +230,7 @@ public: return ValueTree (s); } - return ValueTree::invalid; + return ValueTree(); } ValueTree getOrCreateChildWithName (const Identifier typeToMatch, UndoManager* undoManager) @@ -257,7 +257,7 @@ public: return ValueTree (s); } - return ValueTree::invalid; + return ValueTree(); } bool isAChildOf (const SharedObject* const possibleParent) const noexcept @@ -444,7 +444,7 @@ public: } else { - output.writeString (String::empty); + output.writeString (String()); output.writeCompressedInt (0); output.writeCompressedInt (0); } @@ -846,17 +846,17 @@ ValueTree ValueTree::getChild (int index) const ValueTree ValueTree::getChildWithName (const Identifier type) const { - return object != nullptr ? object->getChildWithName (type) : ValueTree::invalid; + return object != nullptr ? object->getChildWithName (type) : ValueTree(); } ValueTree ValueTree::getOrCreateChildWithName (const Identifier type, UndoManager* undoManager) { - return object != nullptr ? object->getOrCreateChildWithName (type, undoManager) : ValueTree::invalid; + return object != nullptr ? object->getOrCreateChildWithName (type, undoManager) : ValueTree(); } ValueTree ValueTree::getChildWithProperty (const Identifier propertyName, const var& propertyValue) const { - return object != nullptr ? object->getChildWithProperty (propertyName, propertyValue) : ValueTree::invalid; + return object != nullptr ? object->getChildWithProperty (propertyName, propertyValue) : ValueTree(); } bool ValueTree::isAChildOf (const ValueTree& possibleParent) const @@ -962,7 +962,7 @@ ValueTree ValueTree::fromXml (const XmlElement& xml) String ValueTree::toXmlString() const { const ScopedPointer xml (createXml()); - return xml != nullptr ? xml->createDocument (String::empty) : String::empty; + return xml != nullptr ? xml->createDocument ("") : String(); } //============================================================================== @@ -976,7 +976,7 @@ ValueTree ValueTree::readFromStream (InputStream& input) const String type (input.readString()); if (type.isEmpty()) - return ValueTree::invalid; + return ValueTree(); ValueTree v (type); diff --git a/JuceLibraryCode/modules/juce_data_structures/values/juce_ValueTree.h b/JuceLibraryCode/modules/juce_data_structures/values/juce_ValueTree.h index 44fd13f..311d722 100644 --- a/JuceLibraryCode/modules/juce_data_structures/values/juce_ValueTree.h +++ b/JuceLibraryCode/modules/juce_data_structures/values/juce_ValueTree.h @@ -317,7 +317,7 @@ public: //============================================================================== /** Creates an XmlElement that holds a complete image of this node and all its children. - If this node is invalid, this may return 0. Otherwise, the XML that is produced can + If this node is invalid, this may return nullptr. Otherwise, the XML that is produced can be used to recreate a similar node by calling fromXml() @see fromXml */ diff --git a/JuceLibraryCode/modules/juce_events/interprocess/juce_InterprocessConnection.cpp b/JuceLibraryCode/modules/juce_events/interprocess/juce_InterprocessConnection.cpp index a76ff4d..e22ff5b 100644 --- a/JuceLibraryCode/modules/juce_events/interprocess/juce_InterprocessConnection.cpp +++ b/JuceLibraryCode/modules/juce_events/interprocess/juce_InterprocessConnection.cpp @@ -141,7 +141,7 @@ String InterprocessConnection::getConnectedHostName() const return "localhost"; } - return String::empty; + return String(); } //============================================================================== diff --git a/JuceLibraryCode/modules/juce_events/juce_module_info b/JuceLibraryCode/modules/juce_events/juce_module_info index 3bd4805..cb3c7f2 100644 --- a/JuceLibraryCode/modules/juce_events/juce_module_info +++ b/JuceLibraryCode/modules/juce_events/juce_module_info @@ -1,7 +1,7 @@ { "id": "juce_events", "name": "JUCE message and event handling classes", - "version": "3.0.0", + "version": "3.0.1", "description": "Classes for running an application's main event loop and sending/receiving messages, timers, etc.", "website": "http://www.juce.com/juce", "license": "GPL/Commercial", diff --git a/JuceLibraryCode/modules/juce_events/messages/juce_ApplicationBase.cpp b/JuceLibraryCode/modules/juce_events/messages/juce_ApplicationBase.cpp index a5610fa..507823d 100644 --- a/JuceLibraryCode/modules/juce_events/messages/juce_ApplicationBase.cpp +++ b/JuceLibraryCode/modules/juce_events/messages/juce_ApplicationBase.cpp @@ -134,7 +134,7 @@ struct JUCEApplicationBase::MultipleInstanceHandler {}; #if JUCE_ANDROID StringArray JUCEApplicationBase::getCommandLineParameterArray() { return StringArray(); } -String JUCEApplicationBase::getCommandLineParameters() { return String::empty; } +String JUCEApplicationBase::getCommandLineParameters() { return String(); } #else diff --git a/JuceLibraryCode/modules/juce_events/messages/juce_MessageManager.cpp b/JuceLibraryCode/modules/juce_events/messages/juce_MessageManager.cpp index 925c27a..2f38a1c 100644 --- a/JuceLibraryCode/modules/juce_events/messages/juce_MessageManager.cpp +++ b/JuceLibraryCode/modules/juce_events/messages/juce_MessageManager.cpp @@ -22,21 +22,6 @@ ============================================================================== */ -class MessageManager::QuitMessage : public MessageManager::MessageBase -{ -public: - QuitMessage() {} - - void messageCallback() override - { - if (MessageManager* const mm = MessageManager::instance) - mm->quitMessageReceived = true; - } - - JUCE_DECLARE_NON_COPYABLE (QuitMessage) -}; - -//============================================================================== MessageManager::MessageManager() noexcept : quitMessagePosted (false), quitMessageReceived (false), @@ -96,12 +81,6 @@ void MessageManager::runDispatchLoop() runDispatchLoopUntil (-1); } -void MessageManager::stopDispatchLoop() -{ - (new QuitMessage())->post(); - quitMessagePosted = true; -} - bool MessageManager::runDispatchLoopUntil (int millisecondsToRunFor) { jassert (isThisTheMessageThread()); // must only be called by the message thread @@ -124,6 +103,26 @@ bool MessageManager::runDispatchLoopUntil (int millisecondsToRunFor) return ! quitMessageReceived; } +class MessageManager::QuitMessage : public MessageManager::MessageBase +{ +public: + QuitMessage() {} + + void messageCallback() override + { + if (MessageManager* const mm = MessageManager::instance) + mm->quitMessageReceived = true; + } + + JUCE_DECLARE_NON_COPYABLE (QuitMessage) +}; + +void MessageManager::stopDispatchLoop() +{ + (new QuitMessage())->post(); + quitMessagePosted = true; +} + #endif //============================================================================== diff --git a/JuceLibraryCode/modules/juce_events/native/juce_mac_MessageManager.mm b/JuceLibraryCode/modules/juce_events/native/juce_mac_MessageManager.mm index b8d83af..3aed8a2 100644 --- a/JuceLibraryCode/modules/juce_events/native/juce_mac_MessageManager.mm +++ b/JuceLibraryCode/modules/juce_events/native/juce_mac_MessageManager.mm @@ -237,15 +237,32 @@ void MessageManager::runDispatchLoop() } } -void MessageManager::stopDispatchLoop() +static void shutdownNSApp() { - jassert (isThisTheMessageThread()); // must only be called by the message thread + [NSApp stop: nil]; + [NSApp activateIgnoringOtherApps: YES]; // (if the app is inactive, it sits there and ignores the quit request until the next time it gets activated) + [NSEvent startPeriodicEventsAfterDelay: 0 withPeriod: 0.1]; +} +void MessageManager::stopDispatchLoop() +{ quitMessagePosted = true; + #if ! JUCE_PROJUCER_LIVE_BUILD - [NSApp stop: nil]; - [NSApp activateIgnoringOtherApps: YES]; // (if the app is inactive, it sits there and ignores the quit request until the next time it gets activated) - [NSEvent startPeriodicEventsAfterDelay: 0 withPeriod: 0.1]; + if (isThisTheMessageThread()) + { + shutdownNSApp(); + } + else + { + struct QuitCallback : public CallbackMessage + { + QuitCallback() {} + void messageCallback() override { shutdownNSApp(); } + }; + + (new QuitCallback())->post(); + } #endif } diff --git a/JuceLibraryCode/modules/juce_graphics/contexts/juce_GraphicsContext.cpp b/JuceLibraryCode/modules/juce_graphics/contexts/juce_GraphicsContext.cpp index 71ea342..e43cf79 100644 --- a/JuceLibraryCode/modules/juce_graphics/contexts/juce_GraphicsContext.cpp +++ b/JuceLibraryCode/modules/juce_graphics/contexts/juce_GraphicsContext.cpp @@ -232,17 +232,25 @@ Font Graphics::getCurrentFont() const void Graphics::drawSingleLineText (const String& text, const int startX, const int baselineY, Justification justification) const { - if (text.isNotEmpty() - && startX < context.getClipBounds().getRight()) + if (text.isNotEmpty()) { - GlyphArrangement arr; - arr.addLineOfText (context.getFont(), text, (float) startX, (float) baselineY); - // Don't pass any vertical placement flags to this method - they'll be ignored. jassert (justification.getOnlyVerticalFlags() == 0); const int flags = justification.getOnlyHorizontalFlags(); + if (flags == Justification::right) + { + if (startX < context.getClipBounds().getX()) + return; + } + else if (flags == Justification::left) + if (startX > context.getClipBounds().getRight()) + return; + + GlyphArrangement arr; + arr.addLineOfText (context.getFont(), text, (float) startX, (float) baselineY); + if (flags != Justification::left) { float w = arr.getBoundingBox (0, -1, true).getWidth(); diff --git a/JuceLibraryCode/modules/juce_graphics/fonts/juce_AttributedString.cpp b/JuceLibraryCode/modules/juce_graphics/fonts/juce_AttributedString.cpp index d5999c8..43b6318 100644 --- a/JuceLibraryCode/modules/juce_graphics/fonts/juce_AttributedString.cpp +++ b/JuceLibraryCode/modules/juce_graphics/fonts/juce_AttributedString.cpp @@ -166,7 +166,7 @@ void AttributedString::append (const AttributedString& other) void AttributedString::clear() { - text = String::empty; + text.clear(); attributes.clear(); } diff --git a/JuceLibraryCode/modules/juce_graphics/fonts/juce_CustomTypeface.cpp b/JuceLibraryCode/modules/juce_graphics/fonts/juce_CustomTypeface.cpp index 1ab1591..c9d6e8d 100644 --- a/JuceLibraryCode/modules/juce_graphics/fonts/juce_CustomTypeface.cpp +++ b/JuceLibraryCode/modules/juce_graphics/fonts/juce_CustomTypeface.cpp @@ -99,13 +99,13 @@ namespace CustomTypefaceHelpers //============================================================================== CustomTypeface::CustomTypeface() - : Typeface (String::empty, String::empty) + : Typeface (String(), String()) { clear(); } CustomTypeface::CustomTypeface (InputStream& serialisedTypefaceStream) - : Typeface (String::empty, String::empty) + : Typeface (String(), String()) { clear(); diff --git a/JuceLibraryCode/modules/juce_graphics/fonts/juce_Typeface.h b/JuceLibraryCode/modules/juce_graphics/fonts/juce_Typeface.h index 4560bb3..9c2ae31 100644 --- a/JuceLibraryCode/modules/juce_graphics/fonts/juce_Typeface.h +++ b/JuceLibraryCode/modules/juce_graphics/fonts/juce_Typeface.h @@ -64,6 +64,12 @@ public: /** Creates a new system typeface. */ static Ptr createSystemTypefaceFor (const Font& font); + /** Attempts to create a font from some raw font file data (e.g. a TTF or OTF file image). + The system will take its own internal copy of the data, so you can free the block once + this method has returned. + */ + static Ptr createSystemTypefaceFor (const void* fontFileData, size_t fontFileDataSize); + //============================================================================== /** Destructor. */ virtual ~Typeface(); diff --git a/JuceLibraryCode/modules/juce_graphics/geometry/juce_Rectangle.h b/JuceLibraryCode/modules/juce_graphics/geometry/juce_Rectangle.h index 4c38837..1fc9bd6 100644 --- a/JuceLibraryCode/modules/juce_graphics/geometry/juce_Rectangle.h +++ b/JuceLibraryCode/modules/juce_graphics/geometry/juce_Rectangle.h @@ -190,13 +190,17 @@ public: Rectangle withZeroOrigin() const noexcept { return Rectangle (w, h); } /** Returns a rectangle which has the same position and height as this one, but with a different width. */ - Rectangle withWidth (ValueType newWidth) const noexcept { return Rectangle (pos.x, pos.y, newWidth, h); } + Rectangle withWidth (ValueType newWidth) const noexcept { return Rectangle (pos.x, pos.y, newWidth, h); } /** Returns a rectangle which has the same position and width as this one, but with a different height. */ - Rectangle withHeight (ValueType newHeight) const noexcept { return Rectangle (pos.x, pos.y, w, newHeight); } + Rectangle withHeight (ValueType newHeight) const noexcept { return Rectangle (pos.x, pos.y, w, newHeight); } - /** Returns a rectangle with the same position as this one, but a new size. */ - Rectangle withSize (ValueType newWidth, const ValueType newHeight) const noexcept { return Rectangle (pos.x, pos.y, newWidth, newHeight); } + /** Returns a rectangle with the same top-left position as this one, but a new size. */ + Rectangle withSize (ValueType newWidth, ValueType newHeight) const noexcept { return Rectangle (pos.x, pos.y, newWidth, newHeight); } + + /** Returns a rectangle with the same centre position as this one, but a new size. */ + Rectangle withSizeKeepingCentre (ValueType newWidth, ValueType newHeight) const noexcept { return Rectangle (pos.x + (w - newWidth) / (ValueType) 2, + pos.y + (h - newHeight) / (ValueType) 2, newWidth, newHeight); } /** Moves the x position, adjusting the width so that the right-hand edge remains in the same place. If the x is moved to be on the right of the current right-hand edge, the width will be set to zero. @@ -865,7 +869,7 @@ public: static Rectangle fromString (StringRef stringVersion) { StringArray toks; - toks.addTokens (stringVersion.text.findEndOfWhitespace(), ",; \t\r\n", String::empty); + toks.addTokens (stringVersion.text.findEndOfWhitespace(), ",; \t\r\n", ""); return Rectangle (parseIntAfterSpace (toks[0]), parseIntAfterSpace (toks[1]), diff --git a/JuceLibraryCode/modules/juce_graphics/juce_module_info b/JuceLibraryCode/modules/juce_graphics/juce_module_info index 2b0fcdc..6c6e850 100644 --- a/JuceLibraryCode/modules/juce_graphics/juce_module_info +++ b/JuceLibraryCode/modules/juce_graphics/juce_module_info @@ -1,7 +1,7 @@ { "id": "juce_graphics", "name": "JUCE graphics classes", - "version": "3.0.0", + "version": "3.0.1", "description": "Classes for 2D vector graphics, image loading/saving, font handling, etc.", "website": "http://www.juce.com/juce", "license": "GPL/Commercial", diff --git a/JuceLibraryCode/modules/juce_graphics/native/juce_android_Fonts.cpp b/JuceLibraryCode/modules/juce_graphics/native/juce_android_Fonts.cpp index cbd3119..837dd6b 100644 --- a/JuceLibraryCode/modules/juce_graphics/native/juce_android_Fonts.cpp +++ b/JuceLibraryCode/modules/juce_graphics/native/juce_android_Fonts.cpp @@ -291,7 +291,7 @@ private: file = getFontFile (family, "Regular"); if (! file.exists()) - file = getFontFile (family, String::empty); + file = getFontFile (family, String()); return file; } @@ -315,6 +315,12 @@ Typeface::Ptr Typeface::createSystemTypefaceFor (const Font& font) return new AndroidTypeface (font); } +Typeface::Ptr Typeface::createSystemTypefaceFor (const void*, size_t) +{ + jassertfalse; // not yet implemented! + return nullptr; +} + void Typeface::scanFolderForFonts (const File&) { jassertfalse; // not available unless using FreeType diff --git a/JuceLibraryCode/modules/juce_graphics/native/juce_freetype_Fonts.cpp b/JuceLibraryCode/modules/juce_graphics/native/juce_freetype_Fonts.cpp index 5b3f0f2..43a6436 100644 --- a/JuceLibraryCode/modules/juce_graphics/native/juce_freetype_Fonts.cpp +++ b/JuceLibraryCode/modules/juce_graphics/native/juce_freetype_Fonts.cpp @@ -56,6 +56,14 @@ struct FTFaceWrapper : public ReferenceCountedObject face = 0; } + FTFaceWrapper (const FTLibWrapper::Ptr& ftLib, const void* data, size_t dataSize, int faceIndex) + : face (0), library (ftLib), savedFaceData (data, dataSize) + { + if (FT_New_Memory_Face (ftLib->library, (const FT_Byte*) savedFaceData.getData(), + (FT_Long) savedFaceData.getSize(), faceIndex, &face) != 0) + face = 0; + } + ~FTFaceWrapper() { if (face != 0) @@ -64,8 +72,9 @@ struct FTFaceWrapper : public ReferenceCountedObject FT_Face face; FTLibWrapper::Ptr library; + MemoryBlock savedFaceData; - typedef ReferenceCountedObjectPtr Ptr; + typedef ReferenceCountedObjectPtr Ptr; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FTFaceWrapper) }; @@ -106,24 +115,34 @@ public: }; //============================================================================== + static FTFaceWrapper::Ptr selectUnicodeCharmap (FTFaceWrapper* face) + { + if (face != nullptr) + if (FT_Select_Charmap (face->face, ft_encoding_unicode) != 0) + FT_Set_Charmap (face->face, face->face->charmaps[0]); + + return face; + } + + FTFaceWrapper::Ptr createFace (const void* data, size_t dataSize, int index) + { + return selectUnicodeCharmap (new FTFaceWrapper (library, data, dataSize, index)); + } + + FTFaceWrapper::Ptr createFace (const File& file, int index) + { + return selectUnicodeCharmap (new FTFaceWrapper (library, file, index)); + } + FTFaceWrapper::Ptr createFace (const String& fontName, const String& fontStyle) { const KnownTypeface* ftFace = matchTypeface (fontName, fontStyle); if (ftFace == nullptr) ftFace = matchTypeface (fontName, "Regular"); - if (ftFace == nullptr) ftFace = matchTypeface (fontName, String::empty); + if (ftFace == nullptr) ftFace = matchTypeface (fontName, String()); if (ftFace != nullptr) - { - if (FTFaceWrapper::Ptr face = new FTFaceWrapper (library, ftFace->file, ftFace->faceIndex)) - { - // If there isn't a unicode charmap then select the first one. - if (FT_Select_Charmap (face->face, ft_encoding_unicode) != 0) - FT_Set_Charmap (face->face, face->face->charmaps[0]); - - return face; - } - } + return createFace (ftFace->file, ftFace->faceIndex); return nullptr; } @@ -272,20 +291,27 @@ class FreeTypeTypeface : public CustomTypeface { public: FreeTypeTypeface (const Font& font) - : faceWrapper (FTTypefaceList::getInstance() - ->createFace (font.getTypefaceName(), font.getTypefaceStyle())) + : faceWrapper (FTTypefaceList::getInstance()->createFace (font.getTypefaceName(), + font.getTypefaceStyle())) { if (faceWrapper != nullptr) - { - setCharacteristics (font.getTypefaceName(), - font.getTypefaceStyle(), - faceWrapper->face->ascender / (float) (faceWrapper->face->ascender - faceWrapper->face->descender), - L' '); - } - else - { - DBG ("Failed to create typeface: " << font.toString()); - } + initialiseCharacteristics (font.getTypefaceName(), + font.getTypefaceStyle()); + } + + FreeTypeTypeface (const void* data, size_t dataSize) + : faceWrapper (FTTypefaceList::getInstance()->createFace (data, dataSize, 0)) + { + if (faceWrapper != nullptr) + initialiseCharacteristics (faceWrapper->face->family_name, + faceWrapper->face->style_name); + } + + void initialiseCharacteristics (const String& name, const String& style) + { + setCharacteristics (name, style, + faceWrapper->face->ascender / (float) (faceWrapper->face->ascender - faceWrapper->face->descender), + L' '); } bool loadGlyphIfPossible (const juce_wchar character) @@ -295,7 +321,7 @@ public: FT_Face face = faceWrapper->face; const unsigned int glyphIndex = FT_Get_Char_Index (face, character); - if (FT_Load_Glyph (face, glyphIndex, FT_LOAD_NO_SCALE | FT_LOAD_NO_BITMAP | FT_LOAD_IGNORE_TRANSFORM) == 0 + if (FT_Load_Glyph (face, glyphIndex, FT_LOAD_NO_SCALE | FT_LOAD_NO_BITMAP | FT_LOAD_IGNORE_TRANSFORM | FT_LOAD_NO_HINTING) == 0 && face->glyph->format == ft_glyph_format_outline) { const float scale = 1.0f / (float) (face->ascender - face->descender); diff --git a/JuceLibraryCode/modules/juce_graphics/native/juce_linux_Fonts.cpp b/JuceLibraryCode/modules/juce_graphics/native/juce_linux_Fonts.cpp index cf54d62..0869ec7 100644 --- a/JuceLibraryCode/modules/juce_graphics/native/juce_linux_Fonts.cpp +++ b/JuceLibraryCode/modules/juce_graphics/native/juce_linux_Fonts.cpp @@ -43,7 +43,7 @@ StringArray FTTypefaceList::getDefaultFontDirectories() { if (e->getStringAttribute ("prefix") == "xdg") { - String xdgDataHome (SystemStats::getEnvironmentVariable ("XDG_DATA_HOME", String::empty)); + String xdgDataHome (SystemStats::getEnvironmentVariable ("XDG_DATA_HOME", String())); if (xdgDataHome.trimStart().isEmpty()) xdgDataHome = "~/.local/share"; @@ -69,6 +69,11 @@ Typeface::Ptr Typeface::createSystemTypefaceFor (const Font& font) return new FreeTypeTypeface (font); } +Typeface::Ptr Typeface::createSystemTypefaceFor (const void* data, size_t dataSize) +{ + return new FreeTypeTypeface (data, dataSize); +} + void Typeface::scanFolderForFonts (const File& folder) { FTTypefaceList::getInstance()->scanFontPaths (StringArray (folder.getFullPathName())); diff --git a/JuceLibraryCode/modules/juce_graphics/native/juce_mac_Fonts.mm b/JuceLibraryCode/modules/juce_graphics/native/juce_mac_Fonts.mm index 18eca77..2dfa209 100644 --- a/JuceLibraryCode/modules/juce_graphics/native/juce_mac_Fonts.mm +++ b/JuceLibraryCode/modules/juce_graphics/native/juce_mac_Fonts.mm @@ -496,28 +496,73 @@ public: if (ctFontRef != nullptr) { - const float ctAscent = std::abs ((float) CTFontGetAscent (ctFontRef)); - const float ctDescent = std::abs ((float) CTFontGetDescent (ctFontRef)); - const float ctTotalHeight = ctAscent + ctDescent; + fontRef = CTFontCopyGraphicsFont (ctFontRef, nullptr); + initialiseMetrics(); + } + } - ascent = ctAscent / ctTotalHeight; - unitsToHeightScaleFactor = 1.0f / ctTotalHeight; - pathTransform = AffineTransform::identity.scale (unitsToHeightScaleFactor); + OSXTypeface (const void* data, size_t dataSize) + : Typeface (String(), String()), + fontRef (nullptr), + ctFontRef (nullptr), + fontHeightToPointsFactor (1.0f), + renderingTransform (CGAffineTransformIdentity), + attributedStringAtts (nullptr), + ascent (0.0f), + unitsToHeightScaleFactor (0.0f) + { + CFDataRef cfData = CFDataCreate (kCFAllocatorDefault, (const UInt8*) data, (CFIndex) dataSize); + CGDataProviderRef provider = CGDataProviderCreateWithCFData (cfData); + CFRelease (cfData); - fontRef = CTFontCopyGraphicsFont (ctFontRef, nullptr); - fontHeightToPointsFactor = referenceFontSize / ctTotalHeight; + fontRef = CGFontCreateWithDataProvider (provider); + CGDataProviderRelease (provider); - const short zero = 0; - CFNumberRef numberRef = CFNumberCreate (0, kCFNumberShortType, &zero); + if (fontRef != nullptr) + { + ctFontRef = CTFontCreateWithGraphicsFont (fontRef, referenceFontSize, nullptr, nullptr); - CFStringRef keys[] = { kCTFontAttributeName, kCTLigatureAttributeName }; - CFTypeRef values[] = { ctFontRef, numberRef }; - attributedStringAtts = CFDictionaryCreate (nullptr, (const void**) &keys, (const void**) &values, numElementsInArray (keys), - &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); - CFRelease (numberRef); + if (ctFontRef != nullptr) + { + if (CFStringRef fontName = CTFontCopyName (ctFontRef, kCTFontFamilyNameKey)) + { + name = String::fromCFString (fontName); + CFRelease (fontName); + } + + if (CFStringRef fontStyle = CTFontCopyName (ctFontRef, kCTFontStyleNameKey)) + { + style = String::fromCFString (fontStyle); + CFRelease (fontStyle); + } + + initialiseMetrics(); + } } } + void initialiseMetrics() + { + const float ctAscent = std::abs ((float) CTFontGetAscent (ctFontRef)); + const float ctDescent = std::abs ((float) CTFontGetDescent (ctFontRef)); + const float ctTotalHeight = ctAscent + ctDescent; + + ascent = ctAscent / ctTotalHeight; + unitsToHeightScaleFactor = 1.0f / ctTotalHeight; + pathTransform = AffineTransform::identity.scale (unitsToHeightScaleFactor); + + fontHeightToPointsFactor = referenceFontSize / ctTotalHeight; + + const short zero = 0; + CFNumberRef numberRef = CFNumberCreate (0, kCFNumberShortType, &zero); + + CFStringRef keys[] = { kCTFontAttributeName, kCTLigatureAttributeName }; + CFTypeRef values[] = { ctFontRef, numberRef }; + attributedStringAtts = CFDictionaryCreate (nullptr, (const void**) &keys, (const void**) &values, numElementsInArray (keys), + &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + CFRelease (numberRef); + } + ~OSXTypeface() { if (attributedStringAtts != nullptr) @@ -730,7 +775,7 @@ StringArray Font::findAllTypefaceStyles (const String& family) CTFontDescriptorRef ctFontDescRef = CTFontDescriptorCreateWithAttributes (fontDescAttributes); CFRelease (fontDescAttributes); - CFArrayRef fontFamilyArray = CFArrayCreate(kCFAllocatorDefault, (const void**) &ctFontDescRef, 1, &kCFTypeArrayCallBacks); + CFArrayRef fontFamilyArray = CFArrayCreate (kCFAllocatorDefault, (const void**) &ctFontDescRef, 1, &kCFTypeArrayCallBacks); CFRelease (ctFontDescRef); CTFontCollectionRef fontCollectionRef = CTFontCollectionCreateWithFontDescriptors (fontFamilyArray, nullptr); @@ -1202,6 +1247,11 @@ Typeface::Ptr Typeface::createSystemTypefaceFor (const Font& font) return new OSXTypeface (font); } +Typeface::Ptr Typeface::createSystemTypefaceFor (const void* data, size_t dataSize) +{ + return new OSXTypeface (data, dataSize); +} + void Typeface::scanFolderForFonts (const File&) { jassertfalse; // not implemented on this platform diff --git a/JuceLibraryCode/modules/juce_graphics/native/juce_win32_DirectWriteTypeface.cpp b/JuceLibraryCode/modules/juce_graphics/native/juce_win32_DirectWriteTypeface.cpp index 3d92634..8cd8ddc 100644 --- a/JuceLibraryCode/modules/juce_graphics/native/juce_win32_DirectWriteTypeface.cpp +++ b/JuceLibraryCode/modules/juce_graphics/native/juce_win32_DirectWriteTypeface.cpp @@ -163,30 +163,37 @@ public: break; } + jassert (dwFont != nullptr); hr = dwFont->CreateFontFace (dwFontFace.resetAndGetPointerAddress()); - DWRITE_FONT_METRICS dwFontMetrics; - dwFontFace->GetMetrics (&dwFontMetrics); - - // All Font Metrics are in design units so we need to get designUnitsPerEm value - // to get the metrics into Em/Design Independent Pixels - designUnitsPerEm = dwFontMetrics.designUnitsPerEm; - - ascent = std::abs ((float) dwFontMetrics.ascent); - const float totalSize = ascent + std::abs ((float) dwFontMetrics.descent); - ascent /= totalSize; - unitsToHeightScaleFactor = designUnitsPerEm / totalSize; - - HDC tempDC = GetDC (0); - heightToPointsFactor = (72.0f / GetDeviceCaps (tempDC, LOGPIXELSY)) * unitsToHeightScaleFactor; - ReleaseDC (0, tempDC); - - const float pathAscent = (1024.0f * dwFontMetrics.ascent) / designUnitsPerEm; - const float pathDescent = (1024.0f * dwFontMetrics.descent) / designUnitsPerEm; - const float pathScale = 1.0f / (std::abs (pathAscent) + std::abs (pathDescent)); - pathTransform = AffineTransform::scale (pathScale); + if (dwFontFace != nullptr) + { + DWRITE_FONT_METRICS dwFontMetrics; + dwFontFace->GetMetrics (&dwFontMetrics); + + // All Font Metrics are in design units so we need to get designUnitsPerEm value + // to get the metrics into Em/Design Independent Pixels + designUnitsPerEm = dwFontMetrics.designUnitsPerEm; + + ascent = std::abs ((float) dwFontMetrics.ascent); + const float totalSize = ascent + std::abs ((float) dwFontMetrics.descent); + ascent /= totalSize; + unitsToHeightScaleFactor = designUnitsPerEm / totalSize; + + HDC tempDC = GetDC (0); + float dpi = (GetDeviceCaps (tempDC, LOGPIXELSX) + GetDeviceCaps (tempDC, LOGPIXELSY)) / 2.0f; + heightToPointsFactor = (dpi / GetDeviceCaps (tempDC, LOGPIXELSY)) * unitsToHeightScaleFactor; + ReleaseDC (0, tempDC); + + const float pathAscent = (1024.0f * dwFontMetrics.ascent) / designUnitsPerEm; + const float pathDescent = (1024.0f * dwFontMetrics.descent) / designUnitsPerEm; + const float pathScale = 1.0f / (std::abs (pathAscent) + std::abs (pathDescent)); + pathTransform = AffineTransform::scale (pathScale); + } } + bool loadedOk() const noexcept { return dwFontFace != nullptr; } + float getAscent() const { return ascent; } float getDescent() const { return 1.0f - ascent; } float getHeightToPointsFactor() const { return heightToPointsFactor; } diff --git a/JuceLibraryCode/modules/juce_graphics/native/juce_win32_Fonts.cpp b/JuceLibraryCode/modules/juce_graphics/native/juce_win32_Fonts.cpp index b8455eb..a5448f3 100644 --- a/JuceLibraryCode/modules/juce_graphics/native/juce_win32_Fonts.cpp +++ b/JuceLibraryCode/modules/juce_graphics/native/juce_win32_Fonts.cpp @@ -22,6 +22,115 @@ ============================================================================== */ +/* This is some quick-and-dirty code to extract the typeface name from a lump of TTF file data. + It's needed because although win32 will happily load a TTF file from in-memory data, it won't + tell you the name of the damned font that it just loaded.. and in order to actually use the font, + you need to know its name!! Anyway, this awful hack seems to work for most fonts. +*/ +namespace TTFNameExtractor +{ + struct OffsetTable + { + uint32 version; + uint16 numTables, searchRange, entrySelector, rangeShift; + }; + + struct TableDirectory + { + char tag[4]; + uint32 checkSum, offset, length; + }; + + struct NamingTable + { + uint16 formatSelector; + uint16 numberOfNameRecords; + uint16 offsetStartOfStringStorage; + }; + + struct NameRecord + { + uint16 platformID, encodingID, languageID; + uint16 nameID, stringLength, offsetFromStorageArea; + }; + + static String parseNameRecord (MemoryInputStream& input, const NameRecord& nameRecord, + const int64 directoryOffset, const int64 offsetOfStringStorage) + { + String result; + const int64 oldPos = input.getPosition(); + input.setPosition (directoryOffset + offsetOfStringStorage + ByteOrder::swapIfLittleEndian (nameRecord.offsetFromStorageArea)); + const int stringLength = (int) ByteOrder::swapIfLittleEndian (nameRecord.stringLength); + const int platformID = ByteOrder::swapIfLittleEndian (nameRecord.platformID); + + if (platformID == 0 || platformID == 3) + { + const int numChars = stringLength / 2 + 1; + HeapBlock buffer; + buffer.calloc (numChars + 1); + input.read (buffer, stringLength); + + for (int i = 0; i < numChars; ++i) + buffer[i] = ByteOrder::swapIfLittleEndian (buffer[i]); + + static_jassert (sizeof (CharPointer_UTF16::CharType) == sizeof (uint16)); + result = CharPointer_UTF16 ((CharPointer_UTF16::CharType*) buffer.getData()); + } + else + { + HeapBlock buffer; + buffer.calloc (stringLength + 1); + input.read (buffer, stringLength); + result = CharPointer_UTF8 (buffer.getData()); + } + + input.setPosition (oldPos); + return result; + } + + static String parseNameTable (MemoryInputStream& input, int64 directoryOffset) + { + input.setPosition (directoryOffset); + + NamingTable namingTable = { 0 }; + input.read (&namingTable, sizeof (namingTable)); + + for (int i = 0; i < (int) ByteOrder::swapIfLittleEndian (namingTable.numberOfNameRecords); ++i) + { + NameRecord nameRecord = { 0 }; + input.read (&nameRecord, sizeof (nameRecord)); + + if (ByteOrder::swapIfLittleEndian (nameRecord.nameID) == 4) + { + const String result (parseNameRecord (input, nameRecord, directoryOffset, + ByteOrder::swapIfLittleEndian (namingTable.offsetStartOfStringStorage))); + + if (result.isNotEmpty()) + return result; + } + } + + return String(); + } + + static String getTypefaceNameFromFile (MemoryInputStream& input) + { + OffsetTable offsetTable = { 0 }; + input.read (&offsetTable, sizeof (offsetTable)); + + for (int i = 0; i < (int) ByteOrder::swapIfLittleEndian (offsetTable.numTables); ++i) + { + TableDirectory tableDirectory = { 0 }; + input.read (&tableDirectory, sizeof (tableDirectory)); + + if (String (tableDirectory.tag, sizeof (tableDirectory.tag)).equalsIgnoreCase ("name")) + return parseNameTable (input, ByteOrder::swapIfLittleEndian (tableDirectory.offset)); + } + + return String(); + } +} + namespace FontEnumerators { static int CALLBACK fontEnum2 (ENUMLOGFONTEXW* lpelfe, NEWTEXTMETRICEXW*, int type, LPARAM lParam) @@ -204,23 +313,29 @@ class WindowsTypeface : public Typeface { public: WindowsTypeface (const Font& font) - : Typeface (font.getTypefaceName(), - font.getTypefaceStyle()), - fontH (0), - previousFontH (0), - dc (CreateCompatibleDC (0)), + : Typeface (font.getTypefaceName(), font.getTypefaceStyle()), + fontH (0), previousFontH (0), + dc (CreateCompatibleDC (0)), memoryFont (0), ascent (1.0f), heightToPointsFactor (1.0f), defaultGlyph (-1) { loadFont(); + } - if (GetTextMetrics (dc, &tm)) - { - heightToPointsFactor = (72.0f / GetDeviceCaps (dc, LOGPIXELSY)) * heightInPoints / (float) tm.tmHeight; - ascent = tm.tmAscent / (float) tm.tmHeight; - defaultGlyph = getGlyphForChar (dc, tm.tmDefaultChar); - createKerningPairs (dc, (float) tm.tmHeight); - } + WindowsTypeface (const void* data, size_t dataSize) + : Typeface (String(), String()), + fontH (0), previousFontH (0), + dc (CreateCompatibleDC (0)), memoryFont (0), + ascent (1.0f), heightToPointsFactor (1.0f), + defaultGlyph (-1) + { + DWORD numInstalled = 0; + memoryFont = AddFontMemResourceEx (const_cast (data), (DWORD) dataSize, + nullptr, &numInstalled); + + MemoryInputStream m (data, dataSize, false); + name = TTFNameExtractor::getTypefaceNameFromFile (m); + loadFont(); } ~WindowsTypeface() @@ -230,6 +345,9 @@ public: if (fontH != 0) DeleteObject (fontH); + + if (memoryFont != 0) + RemoveFontMemResourceEx (memoryFont); } float getAscent() const { return ascent; } @@ -353,6 +471,7 @@ private: HGDIOBJ previousFontH; HDC dc; TEXTMETRIC tm; + HANDLE memoryFont; float ascent, heightToPointsFactor; int defaultGlyph, heightInPoints; @@ -411,6 +530,15 @@ private: } } } + + if (GetTextMetrics (dc, &tm)) + { + float dpi = (GetDeviceCaps (dc, LOGPIXELSX) + GetDeviceCaps (dc, LOGPIXELSY)) / 2.0f; + heightToPointsFactor = (dpi / GetDeviceCaps (dc, LOGPIXELSY)) * heightInPoints / (float) tm.tmHeight; + ascent = tm.tmAscent / (float) tm.tmHeight; + defaultGlyph = getGlyphForChar (dc, tm.tmDefaultChar); + createKerningPairs (dc, (float) tm.tmHeight); + } } void createKerningPairs (HDC dc, const float height) @@ -493,12 +621,22 @@ Typeface::Ptr Typeface::createSystemTypefaceFor (const Font& font) const Direct2DFactories& factories = Direct2DFactories::getInstance(); if (factories.systemFonts != nullptr) - return new WindowsDirectWriteTypeface (font, factories.systemFonts); + { + ScopedPointer wtf (new WindowsDirectWriteTypeface (font, factories.systemFonts)); + + if (wtf->loadedOk()) + return wtf.release(); + } #endif return new WindowsTypeface (font); } +Typeface::Ptr Typeface::createSystemTypefaceFor (const void* data, size_t dataSize) +{ + return new WindowsTypeface (data, dataSize); +} + void Typeface::scanFolderForFonts (const File&) { jassertfalse; // not implemented on this platform diff --git a/JuceLibraryCode/modules/juce_gui_basics/application/juce_Application.cpp b/JuceLibraryCode/modules/juce_gui_basics/application/juce_Application.cpp index 93b54be..e96ff68 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/application/juce_Application.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/application/juce_Application.cpp @@ -22,7 +22,6 @@ ============================================================================== */ - JUCEApplication::JUCEApplication() {} JUCEApplication::~JUCEApplication() {} diff --git a/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_Button.cpp b/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_Button.cpp index 502f4a9..ac93013 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_Button.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/buttons/juce_Button.cpp @@ -186,7 +186,7 @@ void Button::setToggleState (const bool shouldBeOn, const NotificationType notif // async callbacks aren't possible here jassert (notification != sendNotificationAsync); - sendClickMessage (ModifierKeys()); + sendClickMessage (ModifierKeys::getCurrentModifiers()); if (deletionWatcher == nullptr) return; diff --git a/JuceLibraryCode/modules/juce_gui_basics/commands/juce_ApplicationCommandManager.cpp b/JuceLibraryCode/modules/juce_gui_basics/commands/juce_ApplicationCommandManager.cpp index ad88cd0..4f4ff46 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/commands/juce_ApplicationCommandManager.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/commands/juce_ApplicationCommandManager.cpp @@ -127,7 +127,7 @@ String ApplicationCommandManager::getNameOfCommand (const CommandID commandID) c if (const ApplicationCommandInfo* const ci = getCommandForID (commandID)) return ci->shortName; - return String::empty; + return String(); } String ApplicationCommandManager::getDescriptionOfCommand (const CommandID commandID) const noexcept @@ -136,7 +136,7 @@ String ApplicationCommandManager::getDescriptionOfCommand (const CommandID comma return ci->description.isNotEmpty() ? ci->description : ci->shortName; - return String::empty; + return String(); } StringArray ApplicationCommandManager::getCommandCategories() const diff --git a/JuceLibraryCode/modules/juce_gui_basics/components/juce_Component.cpp b/JuceLibraryCode/modules/juce_gui_basics/components/juce_Component.cpp index de4244e..414dc36 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/components/juce_Component.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/components/juce_Component.cpp @@ -1443,25 +1443,25 @@ Component* Component::getComponentAt (const int x, const int y) } //============================================================================== -void Component::addChildComponent (Component* const child, int zOrder) +void Component::addChildComponent (Component& child, int zOrder) { // if component methods are being called from threads other than the message // thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe. CHECK_MESSAGE_MANAGER_IS_LOCKED_OR_OFFSCREEN - if (child != nullptr && child->parentComponent != this) + if (child.parentComponent != this) { - if (child->parentComponent != nullptr) - child->parentComponent->removeChildComponent (child); + if (child.parentComponent != nullptr) + child.parentComponent->removeChildComponent (&child); else - child->removeFromDesktop(); + child.removeFromDesktop(); - child->parentComponent = this; + child.parentComponent = this; - if (child->isVisible()) - child->repaintParent(); + if (child.isVisible()) + child.repaintParent(); - if (! child->isAlwaysOnTop()) + if (! child.isAlwaysOnTop()) { if (zOrder < 0 || zOrder > childComponentList.size()) zOrder = childComponentList.size(); @@ -1475,20 +1475,29 @@ void Component::addChildComponent (Component* const child, int zOrder) } } - childComponentList.insert (zOrder, child); + childComponentList.insert (zOrder, &child); - child->internalHierarchyChanged(); + child.internalHierarchyChanged(); internalChildrenChanged(); } } +void Component::addAndMakeVisible (Component& child, int zOrder) +{ + child.setVisible (true); + addChildComponent (child, zOrder); +} + +void Component::addChildComponent (Component* const child, int zOrder) +{ + if (child != nullptr) + addChildComponent (*child, zOrder); +} + void Component::addAndMakeVisible (Component* const child, int zOrder) { if (child != nullptr) - { - child->setVisible (true); - addChildComponent (child, zOrder); - } + addAndMakeVisible (*child, zOrder); } void Component::addChildAndSetID (Component* const child, const String& childID) diff --git a/JuceLibraryCode/modules/juce_gui_basics/components/juce_Component.h b/JuceLibraryCode/modules/juce_gui_basics/components/juce_Component.h index e3e4e31..00ef6ec 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/components/juce_Component.h +++ b/JuceLibraryCode/modules/juce_gui_basics/components/juce_Component.h @@ -178,7 +178,7 @@ public: object that it is using. Otherwise, it will return the window of its top-level parent component. - This may return 0 if there isn't a desktop component. + This may return nullptr if there isn't a desktop component. @see addToDesktop, isOnDesktop */ @@ -703,13 +703,38 @@ public: */ void addChildComponent (Component* child, int zOrder = -1); - /** Adds a child component to this one, and also makes the child visible if it isn't. + /** Adds a child component to this one. + + Adding a child component does not mean that the component will own or delete the child - it's + your responsibility to delete the component. Note that it's safe to delete a component + without first removing it from its parent - doing so will automatically remove it and + send out the appropriate notifications before the deletion completes. + + If the child is already a child of this component, then no action will be taken, and its + z-order will be left unchanged. + + @param child the new component to add. If the component passed-in is already + the child of another component, it'll first be removed from it current parent. + @param zOrder The index in the child-list at which this component should be inserted. + A value of -1 will insert it in front of the others, 0 is the back. + @see removeChildComponent, addAndMakeVisible, addChildAndSetID, getChild, ComponentListener::componentChildrenChanged + */ + void addChildComponent (Component& child, int zOrder = -1); + + /** Adds a child component to this one, and also makes the child visible if it isn't already. - Quite a useful function, this is just the same as calling setVisible (true) on the child - and then addChildComponent(). See addChildComponent() for more details. + This is the same as calling setVisible (true) on the child and then addChildComponent(). + See addChildComponent() for more details. */ void addAndMakeVisible (Component* child, int zOrder = -1); + /** Adds a child component to this one, and also makes the child visible if it isn't already. + + This is the same as calling setVisible (true) on the child and then addChildComponent(). + See addChildComponent() for more details. + */ + void addAndMakeVisible (Component& child, int zOrder = -1); + /** Adds a child component to this one, makes it visible, and sets its component ID. @see addAndMakeVisible, addChildComponent */ diff --git a/JuceLibraryCode/modules/juce_gui_basics/drawables/juce_Drawable.h b/JuceLibraryCode/modules/juce_gui_basics/drawables/juce_Drawable.h index da67b22..6a59c59 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/drawables/juce_Drawable.h +++ b/JuceLibraryCode/modules/juce_gui_basics/drawables/juce_Drawable.h @@ -142,7 +142,7 @@ public: into a Drawable tree. The object returned must be deleted by the caller. If something goes wrong - while parsing, it may return 0. + while parsing, it may return nullptr. SVG is a pretty large and complex spec, and this doesn't aim to be a full implementation, but it can return the basic vector objects. diff --git a/JuceLibraryCode/modules/juce_gui_basics/drawables/juce_SVGParser.cpp b/JuceLibraryCode/modules/juce_gui_basics/drawables/juce_SVGParser.cpp index fa19b2e..198bfdb 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/drawables/juce_SVGParser.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/drawables/juce_SVGParser.cpp @@ -944,7 +944,7 @@ private: } String getStyleAttribute (const XmlPath& xml, const String& attributeName, - const String& defaultValue = String::empty) const + const String& defaultValue = String()) const { if (xml->hasAttribute (attributeName)) return xml->getStringAttribute (attributeName, defaultValue); @@ -953,7 +953,7 @@ private: if (styleAtt.isNotEmpty()) { - const String value (getAttributeFromStyleList (styleAtt, attributeName, String::empty)); + const String value (getAttributeFromStyleList (styleAtt, attributeName, String())); if (value.isNotEmpty()) return value; @@ -991,7 +991,7 @@ private: if (xml.parent != nullptr) return getInheritedAttribute (*xml.parent, attributeName); - return String::empty; + return String(); } //============================================================================== @@ -1144,7 +1144,7 @@ private: StringArray tokens; tokens.addTokens (t.fromFirstOccurrenceOf ("(", false, false) .upToFirstOccurrenceOf (")", false, false), - ", ", String::empty); + ", ", ""); tokens.removeEmptyStrings (true); diff --git a/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_DirectoryContentsList.cpp b/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_DirectoryContentsList.cpp index f3e62c7..97e9482 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_DirectoryContentsList.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_DirectoryContentsList.cpp @@ -136,7 +136,7 @@ File DirectoryContentsList::getFile (const int index) const if (const FileInfo* const info = files [index]) return root.getChildFile (info->filename); - return File::nonexistent; + return File(); } bool DirectoryContentsList::contains (const File& targetFile) const diff --git a/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileBrowserComponent.cpp b/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileBrowserComponent.cpp index 33c485d..4a2f023 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileBrowserComponent.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileBrowserComponent.cpp @@ -84,19 +84,19 @@ FileBrowserComponent::FileBrowserComponent (int flags_, fileListComponent->addListener (this); - addAndMakeVisible (¤tPathBox); + addAndMakeVisible (currentPathBox); currentPathBox.setEditableText (true); resetRecentPaths(); currentPathBox.addListener (this); - addAndMakeVisible (&filenameBox); + addAndMakeVisible (filenameBox); filenameBox.setMultiLine (false); filenameBox.setSelectAllWhenFocused (true); filenameBox.setText (filename, false); filenameBox.addListener (this); filenameBox.setReadOnly ((flags & (filenameBoxIsReadOnly | canSelectMultipleItems)) != 0); - addAndMakeVisible (&fileLabel); + addAndMakeVisible (fileLabel); fileLabel.attachToComponent (&filenameBox, true); addAndMakeVisible (goUpButton = getLookAndFeel().createFileBrowserGoUpButton()); diff --git a/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileChooserDialogBox.cpp b/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileChooserDialogBox.cpp index 745f8ee..adf1a74 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileChooserDialogBox.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileChooserDialogBox.cpp @@ -33,15 +33,15 @@ public: newFolderButton (TRANS ("New Folder")), instructions (desc) { - addAndMakeVisible (&chooserComponent); + addAndMakeVisible (chooserComponent); - addAndMakeVisible (&okButton); + addAndMakeVisible (okButton); okButton.addShortcut (KeyPress (KeyPress::returnKey)); - addAndMakeVisible (&cancelButton); + addAndMakeVisible (cancelButton); cancelButton.addShortcut (KeyPress (KeyPress::escapeKey)); - addChildComponent (&newFolderButton); + addChildComponent (newFolderButton); setInterceptsMouseClicks (false, true); } diff --git a/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileSearchPathListComponent.cpp b/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileSearchPathListComponent.cpp index 2840480..a2cad46 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileSearchPathListComponent.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FileSearchPathListComponent.cpp @@ -30,23 +30,23 @@ FileSearchPathListComponent::FileSearchPathListComponent() downButton (String::empty, DrawableButton::ImageOnButtonBackground) { listBox.setModel (this); - addAndMakeVisible (&listBox); + addAndMakeVisible (listBox); listBox.setColour (ListBox::backgroundColourId, Colours::black.withAlpha (0.02f)); listBox.setColour (ListBox::outlineColourId, Colours::black.withAlpha (0.1f)); listBox.setOutlineThickness (1); - addAndMakeVisible (&addButton); + addAndMakeVisible (addButton); addButton.addListener (this); addButton.setConnectedEdges (Button::ConnectedOnLeft | Button::ConnectedOnRight | Button::ConnectedOnBottom | Button::ConnectedOnTop); - addAndMakeVisible (&removeButton); + addAndMakeVisible (removeButton); removeButton.addListener (this); removeButton.setConnectedEdges (Button::ConnectedOnLeft | Button::ConnectedOnRight | Button::ConnectedOnBottom | Button::ConnectedOnTop); - addAndMakeVisible (&changeButton); + addAndMakeVisible (changeButton); changeButton.addListener (this); - addAndMakeVisible (&upButton); + addAndMakeVisible (upButton); upButton.addListener (this); { @@ -59,7 +59,7 @@ FileSearchPathListComponent::FileSearchPathListComponent() upButton.setImages (&arrowImage); } - addAndMakeVisible (&downButton); + addAndMakeVisible (downButton); downButton.addListener (this); { diff --git a/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FilenameComponent.cpp b/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FilenameComponent.cpp index 84c5c64..e4f18bc 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FilenameComponent.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_FilenameComponent.cpp @@ -38,7 +38,7 @@ FilenameComponent::FilenameComponent (const String& name, wildcard (fileBrowserWildcard), enforcedSuffix (suffix) { - addAndMakeVisible (&filenameBox); + addAndMakeVisible (filenameBox); filenameBox.setEditableText (canEditFilename); filenameBox.addListener (this); filenameBox.setTextWhenNothingSelected (textWhenNothingSelected); diff --git a/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_ImagePreviewComponent.cpp b/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_ImagePreviewComponent.cpp index 8089683..537aae3 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_ImagePreviewComponent.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/filebrowser/juce_ImagePreviewComponent.cpp @@ -58,7 +58,7 @@ void ImagePreviewComponent::timerCallback() stopTimer(); currentThumbnail = Image::null; - currentDetails = String::empty; + currentDetails.clear(); repaint(); ScopedPointer in (fileToLoad.createInputStream()); diff --git a/JuceLibraryCode/modules/juce_gui_basics/juce_module_info b/JuceLibraryCode/modules/juce_gui_basics/juce_module_info index e3ef339..a66035f 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/juce_module_info +++ b/JuceLibraryCode/modules/juce_gui_basics/juce_module_info @@ -1,7 +1,7 @@ { "id": "juce_gui_basics", "name": "JUCE GUI core classes", - "version": "3.0.0", + "version": "3.0.1", "description": "Basic user-interface components and related classes.", "website": "http://www.juce.com/juce", "license": "GPL/Commercial", diff --git a/JuceLibraryCode/modules/juce_gui_basics/keyboard/juce_KeyboardFocusTraverser.h b/JuceLibraryCode/modules/juce_gui_basics/keyboard/juce_KeyboardFocusTraverser.h index a22311d..94ef5f7 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/keyboard/juce_KeyboardFocusTraverser.h +++ b/JuceLibraryCode/modules/juce_gui_basics/keyboard/juce_KeyboardFocusTraverser.h @@ -60,7 +60,7 @@ public: The default implementation will return the next component which is to the right of or below this one. - This may return 0 if there's no suitable candidate. + This may return nullptr if there's no suitable candidate. */ virtual Component* getNextComponent (Component* current); @@ -70,7 +70,7 @@ public: The default implementation will return the next component which is to the left of or above this one. - This may return 0 if there's no suitable candidate. + This may return nullptr if there's no suitable candidate. */ virtual Component* getPreviousComponent (Component* current); @@ -80,7 +80,7 @@ public: The default implementation will just return the foremost child component that wants focus. - This may return 0 if there's no suitable candidate. + This may return nullptr if there's no suitable candidate. */ virtual Component* getDefaultComponent (Component* parentComponent); }; diff --git a/JuceLibraryCode/modules/juce_gui_basics/layout/juce_ComponentAnimator.cpp b/JuceLibraryCode/modules/juce_gui_basics/layout/juce_ComponentAnimator.cpp index efc952d..b7d052c 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/layout/juce_ComponentAnimator.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/layout/juce_ComponentAnimator.cpp @@ -137,6 +137,7 @@ public: public: ProxyComponent (Component& c) { + setWantsKeyboardFocus (false); setBounds (c.getBounds()); setTransform (c.getTransform()); setAlpha (c.getAlpha()); diff --git a/JuceLibraryCode/modules/juce_gui_basics/layout/juce_Viewport.cpp b/JuceLibraryCode/modules/juce_gui_basics/layout/juce_Viewport.cpp index d026740..d4d0e72 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/layout/juce_Viewport.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/layout/juce_Viewport.cpp @@ -34,11 +34,11 @@ Viewport::Viewport (const String& name) horizontalScrollBar (false) { // content holder is used to clip the contents so they don't overlap the scrollbars - addAndMakeVisible (&contentHolder); + addAndMakeVisible (contentHolder); contentHolder.setInterceptsMouseClicks (false, true); - addChildComponent (&verticalScrollBar); - addChildComponent (&horizontalScrollBar); + addChildComponent (verticalScrollBar); + addChildComponent (horizontalScrollBar); verticalScrollBar.addListener (this); horizontalScrollBar.addListener (this); diff --git a/JuceLibraryCode/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V1.h b/JuceLibraryCode/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V1.h index 8b977bc..bc2b6db 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V1.h +++ b/JuceLibraryCode/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V1.h @@ -98,4 +98,4 @@ private: }; -#endif // JUCE_LOOKANDFEEL_H_INCLUDED +#endif // JUCE_LOOKANDFEEL_V1_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V2.cpp b/JuceLibraryCode/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V2.cpp index 9b3555f..bf6ebe8 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V2.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V2.cpp @@ -819,17 +819,17 @@ int LookAndFeel_V2::getTreeViewIndentSize (TreeView&) //============================================================================== void LookAndFeel_V2::drawBubble (Graphics& g, BubbleComponent& comp, - const Point& tip, const Rectangle& body) + const Point& tip, const Rectangle& body) { Path p; - p.addBubble (body, body.getUnion (Rectangle (tip.x, tip.y, 1.0f, 1.0f)), + p.addBubble (body.reduced (0.5f), body.getUnion (Rectangle (tip.x, tip.y, 1.0f, 1.0f)), tip, 5.0f, jmin (15.0f, body.getWidth() * 0.2f, body.getHeight() * 0.2f)); g.setColour (comp.findColour (BubbleComponent::backgroundColourId)); g.fillPath (p); g.setColour (comp.findColour (BubbleComponent::outlineColourId)); - g.strokePath (p, PathStrokeType (1.33f)); + g.strokePath (p, PathStrokeType (1.0f)); } @@ -900,24 +900,23 @@ void LookAndFeel_V2::drawPopupMenuUpDownArrow (Graphics& g, int width, int heigh g.fillPath (p); } -void LookAndFeel_V2::drawPopupMenuItem (Graphics& g, int width, int height, +void LookAndFeel_V2::drawPopupMenuItem (Graphics& g, const Rectangle& area, const bool isSeparator, const bool isActive, const bool isHighlighted, const bool isTicked, const bool hasSubMenu, const String& text, const String& shortcutKeyText, - Image* image, const Colour* const textColourToUse) + const Drawable* icon, const Colour* const textColourToUse) { - const float halfH = height * 0.5f; - if (isSeparator) { - const float separatorIndent = 5.5f; + Rectangle r (area.reduced (5, 0)); + r.removeFromTop (r.getHeight() / 2 - 1); g.setColour (Colour (0x33000000)); - g.drawLine (separatorIndent, halfH, width - separatorIndent, halfH); + g.fillRect (r.removeFromTop (1)); g.setColour (Colour (0x66ffffff)); - g.drawLine (separatorIndent, halfH + 1.0f, width - separatorIndent, halfH + 1.0f); + g.fillRect (r.removeFromTop (1)); } else { @@ -926,10 +925,12 @@ void LookAndFeel_V2::drawPopupMenuItem (Graphics& g, int width, int height, if (textColourToUse != nullptr) textColour = *textColourToUse; + Rectangle r (area.reduced (1)); + if (isHighlighted) { g.setColour (findColour (PopupMenu::highlightedBackgroundColourId)); - g.fillRect (1, 1, width - 2, height - 2); + g.fillRect (r); g.setColour (findColour (PopupMenu::highlightedTextColourId)); } @@ -943,51 +944,31 @@ void LookAndFeel_V2::drawPopupMenuItem (Graphics& g, int width, int height, Font font (getPopupMenuFont()); - if (font.getHeight() > height / 1.3f) - font.setHeight (height / 1.3f); + const float maxFontHeight = area.getHeight() / 1.3f; + + if (font.getHeight() > maxFontHeight) + font.setHeight (maxFontHeight); g.setFont (font); - const int leftBorder = (height * 5) / 4; - const int rightBorder = 4; + Rectangle iconArea (r.removeFromLeft ((r.getHeight() * 5) / 4).reduced (3).toFloat()); - if (image != nullptr) + if (icon != nullptr) { - g.drawImageWithin (*image, - 2, 1, leftBorder - 4, height - 2, - RectanglePlacement::centred | RectanglePlacement::onlyReduceInSize, false); + icon->drawWithin (g, iconArea, RectanglePlacement::centred | RectanglePlacement::onlyReduceInSize, 1.0f); } else if (isTicked) { const Path tick (getTickShape (1.0f)); - const float th = font.getAscent(); - const float ty = halfH - th * 0.5f; - - g.fillPath (tick, tick.getTransformToScaleToFit (2.0f, ty, (float) (leftBorder - 4), - th, true)); - } - - g.drawFittedText (text, - leftBorder, 0, width - (leftBorder + rightBorder), height, - Justification::centredLeft, 1); - - if (shortcutKeyText.isNotEmpty()) - { - Font f2 (font); - f2.setHeight (f2.getHeight() * 0.75f); - f2.setHorizontalScale (0.95f); - g.setFont (f2); - - g.drawText (shortcutKeyText, - leftBorder, 0, width - (leftBorder + rightBorder + 4), height, - Justification::centredRight, - true); + g.fillPath (tick, tick.getTransformToScaleToFit (iconArea, true)); } if (hasSubMenu) { const float arrowH = 0.6f * getPopupMenuFont().getAscent(); - const float x = width - height * 0.6f; + + const float x = (float) r.removeFromRight ((int) arrowH).getX(); + const float halfH = (float) r.getCentreY(); Path p; p.addTriangle (x, halfH - arrowH * 0.5f, @@ -996,6 +977,19 @@ void LookAndFeel_V2::drawPopupMenuItem (Graphics& g, int width, int height, g.fillPath (p); } + + r.removeFromRight (3); + g.drawFittedText (text, r, Justification::centredLeft, 1); + + if (shortcutKeyText.isNotEmpty()) + { + Font f2 (font); + f2.setHeight (f2.getHeight() * 0.75f); + f2.setHorizontalScale (0.95f); + g.setFont (f2); + + g.drawText (shortcutKeyText, r, Justification::centredRight, true); + } } } diff --git a/JuceLibraryCode/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V2.h b/JuceLibraryCode/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V2.h index 430c90d..8079e5a 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V2.h +++ b/JuceLibraryCode/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V2.h @@ -133,10 +133,10 @@ public: //============================================================================== void drawPopupMenuBackground (Graphics&, int width, int height) override; - void drawPopupMenuItem (Graphics&, int width, int height, + void drawPopupMenuItem (Graphics&, const Rectangle& area, bool isSeparator, bool isActive, bool isHighlighted, bool isTicked, bool hasSubMenu, const String& text, const String& shortcutKeyText, - Image* image, const Colour* textColour) override; + const Drawable* icon, const Colour* textColour) override; Font getPopupMenuFont() override; @@ -344,4 +344,4 @@ private: }; -#endif // JUCE_LOOKANDFEEL_H_INCLUDED +#endif // JUCE_LOOKANDFEEL_V2_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V3.cpp b/JuceLibraryCode/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V3.cpp index cffef3d..2007252 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V3.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V3.cpp @@ -34,7 +34,7 @@ LookAndFeel_V3::LookAndFeel_V3() setColour (TabbedComponent::outlineColourId, Colour (0xff999999)); setColour (Slider::trackColourId, Colour (0xbbffffff)); setColour (Slider::thumbColourId, Colour (0xffddddff)); - + setColour (BubbleComponent::backgroundColourId, Colour (0xeeeeeedd)); setColour (ScrollBar::thumbColourId, Colour::greyLevel (0.8f).contrasting().withAlpha (0.13f)); } @@ -190,7 +190,6 @@ void LookAndFeel_V3::drawTabButton (TabBarButton& button, Graphics& g, bool isMo if (button.getToggleState()) { g.setColour (bkg); - g.fillRect (activeArea); } else { @@ -207,9 +206,10 @@ void LookAndFeel_V3::drawTabButton (TabBarButton& button, Graphics& g, bool isMo g.setGradientFill (ColourGradient (bkg.brighter (0.2f), (float) p1.x, (float) p1.y, bkg.darker (0.1f), (float) p2.x, (float) p2.y, false)); - g.fillRect (activeArea); } + g.fillRect (activeArea); + g.setColour (button.findColour (TabbedButtonBar::tabOutlineColourId)); Rectangle r (activeArea); @@ -604,3 +604,27 @@ Button* LookAndFeel_V3::createDocumentWindowButton (int buttonType) jassertfalse; return nullptr; } + +Path LookAndFeel_V3::getTickShape (const float height) +{ + static const unsigned char pathData[] + = { 110,109,32,210,202,64,126,183,148,64,108,39,244,247,64,245,76,124,64,108,178,131,27,65,246,76,252,64,108,175,242,4,65,246,76,252, + 64,108,236,5,68,65,0,0,160,180,108,240,150,90,65,21,136,52,63,108,48,59,16,65,0,0,32,65,108,32,210,202,64,126,183,148,64, 99,101,0,0 }; + + Path p; + p.loadPathFromData (pathData, sizeof (pathData)); + p.scaleToFit (0, 0, height * 2.0f, height, true); + return p; +} + +Path LookAndFeel_V3::getCrossShape (const float height) +{ + static const unsigned char pathData[] + = { 110,109,88,57,198,65,29,90,171,65,108,63,53,154,65,8,172,126,65,108,76,55,198,65,215,163,38,65,108,141,151,175,65,82,184,242,64,108,117,147,131,65,90,100,81,65,108,184,30,47,65,82,184,242,64,108,59,223,1,65,215,163,38,65,108,84,227,89,65,8,172,126,65, + 108,35,219,1,65,29,90,171,65,108,209,34,47,65,231,251,193,65,108,117,147,131,65,207,247,149,65,108,129,149,175,65,231,251,193,65,99,101,0,0 }; + + Path p; + p.loadPathFromData (pathData, sizeof (pathData)); + p.scaleToFit (0, 0, height * 2.0f, height, true); + return p; +} diff --git a/JuceLibraryCode/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V3.h b/JuceLibraryCode/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V3.h index 412331e..ad97552 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V3.h +++ b/JuceLibraryCode/modules/juce_gui_basics/lookandfeel/juce_LookAndFeel_V3.h @@ -82,6 +82,9 @@ public: void drawConcertinaPanelHeader (Graphics&, const Rectangle& area, bool isMouseOver, bool isMouseDown, ConcertinaPanel&, Component&) override; + Path getTickShape (float height) override; + Path getCrossShape (float height) override; + static void createTabTextLayout (const TabBarButton& button, float length, float depth, Colour colour, TextLayout&); private: @@ -90,4 +93,4 @@ private: }; -#endif // JUCE_LOOKANDFEEL_H_INCLUDED +#endif // JUCE_LOOKANDFEEL_V3_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_gui_basics/menus/juce_PopupMenu.cpp b/JuceLibraryCode/modules/juce_gui_basics/menus/juce_PopupMenu.cpp index 605bcd2..abfc922 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/menus/juce_PopupMenu.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/menus/juce_PopupMenu.cpp @@ -45,7 +45,7 @@ public: const String& name, const bool active, const bool ticked, - const Image& im, + Drawable* drawable, const Colour colour, const bool useColour, CustomComponent* const custom, @@ -54,8 +54,8 @@ public: : itemID (itemId), text (name), textColour (colour), isActive (active), isSeparator (false), isTicked (ticked), - usesColour (useColour), image (im), customComp (custom), - subMenu (createCopyIfNotNull (sub)), commandManager (manager) + usesColour (useColour), iconDrawable (drawable), + customComp (custom), subMenu (createCopyIfNotNull (sub)), commandManager (manager) { if (commandManager != nullptr && itemID != 0) { @@ -92,7 +92,7 @@ public: isSeparator (other.isSeparator), isTicked (other.isTicked), usesColour (other.usesColour), - image (other.image), + iconDrawable (other.iconDrawable != nullptr ? other.iconDrawable->createCopy() : nullptr), customComp (other.customComp), subMenu (createCopyIfNotNull (other.subMenu.get())), commandManager (other.commandManager) @@ -106,7 +106,7 @@ public: String text; const Colour textColour; const bool isActive, isSeparator, isTicked, usesColour; - Image image; + ScopedPointer iconDrawable; ReferenceCountedObjectPtr customComp; ScopedPointer subMenu; ApplicationCommandManager* const commandManager; @@ -175,14 +175,14 @@ public: } getLookAndFeel() - .drawPopupMenuItem (g, getWidth(), getHeight(), + .drawPopupMenuItem (g, getLocalBounds(), itemInfo.isSeparator, itemInfo.isActive, isHighlighted, itemInfo.isTicked, itemInfo.subMenu != nullptr && (itemInfo.itemID == 0 || itemInfo.subMenu->getNumItems() > 0), mainText, endText, - itemInfo.image.isValid() ? &itemInfo.image : nullptr, + itemInfo.iconDrawable, itemInfo.usesColour ? &(itemInfo.textColour) : nullptr); } } @@ -1288,8 +1288,40 @@ void PopupMenu::clear() items.clear(); } -void PopupMenu::addItem (const int itemResultID, const String& itemText, - const bool isActive, const bool isTicked, const Image& iconToUse) +void PopupMenu::addItem (int itemResultID, const String& itemText, bool isActive, bool isTicked) +{ + jassert (itemResultID != 0); // 0 is used as a return value to indicate that the user + // didn't pick anything, so you shouldn't use it as the id + // for an item.. + + items.add (new Item (itemResultID, itemText, isActive, isTicked, nullptr, + Colours::black, false, nullptr, nullptr, nullptr)); +} + +static Drawable* createDrawableFromImage (const Image& im) +{ + if (im.isValid()) + { + DrawableImage* d = new DrawableImage(); + d->setImage (im); + return d; + } + + return nullptr; +} + +void PopupMenu::addItem (int itemResultID, const String& itemText, bool isActive, bool isTicked, const Image& iconToUse) +{ + jassert (itemResultID != 0); // 0 is used as a return value to indicate that the user + // didn't pick anything, so you shouldn't use it as the id + // for an item.. + + + items.add (new Item (itemResultID, itemText, isActive, isTicked, createDrawableFromImage (iconToUse), + Colours::black, false, nullptr, nullptr, nullptr)); +} + +void PopupMenu::addItem (int itemResultID, const String& itemText, bool isActive, bool isTicked, Drawable* iconToUse) { jassert (itemResultID != 0); // 0 is used as a return value to indicate that the user // didn't pick anything, so you shouldn't use it as the id @@ -1315,7 +1347,7 @@ void PopupMenu::addCommandItem (ApplicationCommandManager* commandManager, : info.shortName, target != nullptr && (info.flags & ApplicationCommandInfo::isDisabled) == 0, (info.flags & ApplicationCommandInfo::isTicked) != 0, - Image::null, + nullptr, Colours::black, false, nullptr, nullptr, @@ -1323,50 +1355,49 @@ void PopupMenu::addCommandItem (ApplicationCommandManager* commandManager, } } -void PopupMenu::addColouredItem (const int itemResultID, - const String& itemText, - Colour itemTextColour, - const bool isActive, - const bool isTicked, - const Image& iconToUse) +void PopupMenu::addColouredItem (int itemResultID, const String& itemText, Colour itemTextColour, + bool isActive, bool isTicked, const Image& iconToUse) { jassert (itemResultID != 0); // 0 is used as a return value to indicate that the user // didn't pick anything, so you shouldn't use it as the id // for an item.. - items.add (new Item (itemResultID, itemText, isActive, isTicked, iconToUse, + items.add (new Item (itemResultID, itemText, isActive, isTicked, createDrawableFromImage (iconToUse), itemTextColour, true, nullptr, nullptr, nullptr)); } -void PopupMenu::addCustomItem (const int itemID, CustomComponent* const cc, const PopupMenu* subMenu) +void PopupMenu::addCustomItem (int itemID, CustomComponent* cc, const PopupMenu* subMenu) { jassert (itemID != 0); // 0 is used as a return value to indicate that the user // didn't pick anything, so you shouldn't use it as the id // for an item.. - items.add (new Item (itemID, String::empty, true, false, Image::null, + items.add (new Item (itemID, String::empty, true, false, nullptr, Colours::black, false, cc, subMenu, nullptr)); } -void PopupMenu::addCustomItem (const int itemResultID, - Component* customComponent, - int idealWidth, int idealHeight, - const bool triggerMenuItemAutomaticallyWhenClicked, - const PopupMenu* subMenu) +void PopupMenu::addCustomItem (int itemResultID, Component* customComponent, int idealWidth, int idealHeight, + bool triggerMenuItemAutomaticallyWhenClicked, const PopupMenu* subMenu) { - items.add (new Item (itemResultID, String::empty, true, false, Image::null, - Colours::black, false, + items.add (new Item (itemResultID, String::empty, true, false, nullptr, Colours::black, false, new HelperClasses::NormalComponentWrapper (customComponent, idealWidth, idealHeight, triggerMenuItemAutomaticallyWhenClicked), subMenu, nullptr)); } -void PopupMenu::addSubMenu (const String& subMenuName, - const PopupMenu& subMenu, - const bool isActive, - const Image& iconToUse, - const bool isTicked, - const int itemResultID) +void PopupMenu::addSubMenu (const String& subMenuName, const PopupMenu& subMenu, bool isActive) +{ + addSubMenu (subMenuName, subMenu, isActive, nullptr, false, 0); +} + +void PopupMenu::addSubMenu (const String& subMenuName, const PopupMenu& subMenu, bool isActive, + const Image& iconToUse, bool isTicked, int itemResultID) +{ + addSubMenu (subMenuName, subMenu, isActive, createDrawableFromImage (iconToUse), isTicked, itemResultID); +} + +void PopupMenu::addSubMenu (const String& subMenuName, const PopupMenu& subMenu, bool isActive, + Drawable* iconToUse, bool isTicked, int itemResultID) { items.add (new Item (itemResultID, subMenuName, isActive && (itemResultID != 0 || subMenu.getNumItems() > 0), isTicked, iconToUse, Colours::black, false, nullptr, &subMenu, nullptr)); @@ -1673,7 +1704,7 @@ void PopupMenu::CustomComponent::triggerMenuItem() { if (HelperClasses::ItemComponent* const mic = dynamic_cast (getParentComponent())) { - if (HelperClasses::MenuWindow* const pmw = dynamic_cast (mic->getParentComponent())) + if (HelperClasses::MenuWindow* const pmw = dynamic_cast (mic->getParentComponent())) { pmw->dismissMenu (&mic->itemInfo); } @@ -1727,10 +1758,10 @@ bool PopupMenu::MenuItemIterator::next() isSeparator = item->isSeparator; isTicked = item->isTicked; isEnabled = item->isActive; - isSectionHeader = dynamic_cast (static_cast (item->customComp)) != nullptr; + isSectionHeader = dynamic_cast (static_cast (item->customComp)) != nullptr; isCustomComponent = (! isSectionHeader) && item->customComp != nullptr; customColour = item->usesColour ? &(item->textColour) : nullptr; - customImage = item->image; + icon = item->iconDrawable; commandManager = item->commandManager; return true; @@ -1738,8 +1769,7 @@ bool PopupMenu::MenuItemIterator::next() void PopupMenu::MenuItemIterator::addItemTo (PopupMenu& targetMenu) { - targetMenu.items.add (new Item (itemId, itemName, isEnabled, isTicked, customImage, - customColour != nullptr ? *customColour : Colours::black, customColour != nullptr, - nullptr, - subMenu, commandManager)); + targetMenu.items.add (new Item (itemId, itemName, isEnabled, isTicked, icon != nullptr ? icon->createCopy() : nullptr, + customColour != nullptr ? *customColour : Colours::black, + customColour != nullptr, nullptr, subMenu, commandManager)); } diff --git a/JuceLibraryCode/modules/juce_gui_basics/menus/juce_PopupMenu.h b/JuceLibraryCode/modules/juce_gui_basics/menus/juce_PopupMenu.h index e4622d6..82545a3 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/menus/juce_PopupMenu.h +++ b/JuceLibraryCode/modules/juce_gui_basics/menus/juce_PopupMenu.h @@ -111,18 +111,52 @@ public: @param itemText the text to show. @param isEnabled if false, the item will be shown 'greyed-out' and can't be picked @param isTicked if true, the item will be shown with a tick next to it - @param iconToUse if this is non-zero, it should be an image that will be - displayed to the left of the item. This method will take its - own copy of the image passed-in, so there's no need to keep - it hanging around. @see addSeparator, addColouredItem, addCustomItem, addSubMenu */ void addItem (int itemResultID, const String& itemText, bool isEnabled = true, - bool isTicked = false, - const Image& iconToUse = Image::null); + bool isTicked = false); + + /** Appends a new item with an icon. + + @param itemResultID the number that will be returned from the show() method + if the user picks this item. The value should never be + zero, because that's used to indicate that the user didn't + select anything. + @param itemText the text to show. + @param isEnabled if false, the item will be shown 'greyed-out' and can't be picked + @param isTicked if true, the item will be shown with a tick next to it + @param iconToUse if this is a valid image, it will be displayed to the left of the item. + + @see addSeparator, addColouredItem, addCustomItem, addSubMenu + */ + void addItem (int itemResultID, + const String& itemText, + bool isEnabled, + bool isTicked, + const Image& iconToUse); + + /** Appends a new item with an icon. + + @param itemResultID the number that will be returned from the show() method + if the user picks this item. The value should never be + zero, because that's used to indicate that the user didn't + select anything. + @param itemText the text to show. + @param isEnabled if false, the item will be shown 'greyed-out' and can't be picked + @param isTicked if true, the item will be shown with a tick next to it + @param iconToUse a Drawable object to use as the icon to the left of the item. + The menu will take ownership of this drawable object and will + delete it later when no longer needed + @see addSeparator, addColouredItem, addCustomItem, addSubMenu + */ + void addItem (int itemResultID, + const String& itemText, + bool isEnabled, + bool isTicked, + Drawable* iconToUse); /** Adds an item that represents one of the commands in a command manager object. @@ -177,8 +211,35 @@ public: */ void addSubMenu (const String& subMenuName, const PopupMenu& subMenu, - bool isEnabled = true, - const Image& iconToUse = Image::null, + bool isEnabled = true); + + /** Appends a sub-menu with an icon. + + If the menu that's passed in is empty, it will appear as an inactive item. + If the itemResultID argument is non-zero, then the sub-menu item itself can be + clicked to trigger it as a command. + */ + void addSubMenu (const String& subMenuName, + const PopupMenu& subMenu, + bool isEnabled, + const Image& iconToUse, + bool isTicked = false, + int itemResultID = 0); + + /** Appends a sub-menu with an icon. + + If the menu that's passed in is empty, it will appear as an inactive item. + If the itemResultID argument is non-zero, then the sub-menu item itself can be + clicked to trigger it as a command. + + The iconToUse parameter is a Drawable object to use as the icon to the left of + the item. The menu will take ownership of this drawable object and will delete it + later when no longer needed + */ + void addSubMenu (const String& subMenuName, + const PopupMenu& subMenu, + bool isEnabled, + Drawable* iconToUse, bool isTicked = false, int itemResultID = 0); @@ -408,7 +469,7 @@ public: bool isCustomComponent; bool isSectionHeader; const Colour* customColour; - Image customImage; + const Drawable* icon; ApplicationCommandManager* commandManager; private: @@ -493,12 +554,12 @@ public: virtual void drawPopupMenuBackground (Graphics&, int width, int height) = 0; /** Draws one of the items in a popup menu. */ - virtual void drawPopupMenuItem (Graphics&, int width, int height, + virtual void drawPopupMenuItem (Graphics&, const Rectangle& area, bool isSeparator, bool isActive, bool isHighlighted, bool isTicked, bool hasSubMenu, const String& text, const String& shortcutKeyText, - Image* icon, + const Drawable* icon, const Colour* textColour) = 0; /** Returns the size and style of font to use in popup menus. */ @@ -549,6 +610,11 @@ private: Component* createWindow (const Options&, ApplicationCommandManager**) const; int showWithOptionalCallback (const Options&, ModalComponentManager::Callback*, bool); + #if JUCE_CATCH_DEPRECATED_CODE_MISUSE + // These methods have new implementations now - see its new definition + int drawPopupMenuItem (Graphics&, int, int, bool, bool, bool, bool, bool, const String&, const String&, Image*, const Colour*) { return 0; } + #endif + JUCE_LEAK_DETECTOR (PopupMenu) }; diff --git a/JuceLibraryCode/modules/juce_gui_basics/mouse/juce_DragAndDropContainer.cpp b/JuceLibraryCode/modules/juce_gui_basics/mouse/juce_DragAndDropContainer.cpp index 44d52f1..0806461 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/mouse/juce_DragAndDropContainer.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/mouse/juce_DragAndDropContainer.cpp @@ -154,10 +154,14 @@ public: else if (now > lastTimeOverTarget + RelativeTime::milliseconds (700)) checkForExternalDrag (details, screenPos); } + + forceMouseCursorUpdate(); } void timerCallback() override { + forceMouseCursorUpdate(); + if (sourceDetails.sourceComponent == nullptr) { delete this; @@ -201,6 +205,11 @@ private: bool hasCheckedForExternalDrag; Time lastTimeOverTarget; + void forceMouseCursorUpdate() + { + Desktop::getInstance().getMainMouseSource().forceMouseCursorUpdate(); + } + DragAndDropTarget* getCurrentlyOver() const noexcept { return dynamic_cast (currentlyOverComp.get()); diff --git a/JuceLibraryCode/modules/juce_gui_basics/mouse/juce_DragAndDropContainer.h b/JuceLibraryCode/modules/juce_gui_basics/mouse/juce_DragAndDropContainer.h index 7d0fed0..9843753 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/mouse/juce_DragAndDropContainer.h +++ b/JuceLibraryCode/modules/juce_gui_basics/mouse/juce_DragAndDropContainer.h @@ -110,7 +110,7 @@ public: It's useful when a component wants to call startDragging but doesn't know the DragAndDropContainer it should to use. - Obviously this may return 0 if it doesn't find a suitable component. + Obviously this may return nullptr if it doesn't find a suitable component. */ static DragAndDropContainer* findParentDragContainerFor (Component* childComponent); diff --git a/JuceLibraryCode/modules/juce_gui_basics/native/juce_ios_Windowing.mm b/JuceLibraryCode/modules/juce_gui_basics/native/juce_ios_Windowing.mm index 3852983..dff4c25 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/native/juce_ios_Windowing.mm +++ b/JuceLibraryCode/modules/juce_gui_basics/native/juce_ios_Windowing.mm @@ -110,7 +110,7 @@ public: iOSMessageBox (const String& title, const String& message, NSString* button1, NSString* button2, NSString* button3, ModalComponentManager::Callback* cb, const bool async) - : result (0), delegate (nil), alert (nil), + : result (0), resultReceived (false), delegate (nil), alert (nil), callback (cb), isYesNo (button3 != nil), isAsync (async) { delegate = [[JuceAlertBoxDelegate alloc] init]; @@ -137,7 +137,7 @@ public: JUCE_AUTORELEASEPOOL { - while (! alert.hidden && alert.superview != nil) + while (! (alert.hidden || resultReceived)) [[NSRunLoop mainRunLoop] runUntilDate: [NSDate dateWithTimeIntervalSinceNow: 0.01]]; } @@ -147,6 +147,7 @@ public: void buttonClicked (const int buttonIndex) noexcept { result = buttonIndex; + resultReceived = true; if (callback != nullptr) callback->modalStateFinished (result); @@ -157,6 +158,7 @@ public: private: int result; + bool resultReceived; JuceAlertBoxDelegate* delegate; UIAlertView* alert; ScopedPointer callback; diff --git a/JuceLibraryCode/modules/juce_gui_basics/native/juce_mac_MouseCursor.mm b/JuceLibraryCode/modules/juce_gui_basics/native/juce_mac_MouseCursor.mm index 4739f2e..4811607 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/native/juce_mac_MouseCursor.mm +++ b/JuceLibraryCode/modules/juce_gui_basics/native/juce_mac_MouseCursor.mm @@ -52,15 +52,20 @@ namespace MouseCursorHelpers static void* fromWebKitFile (const char* filename, float hx, float hy) { - FileInputStream fileStream (File ("/System/Library/Frameworks/WebKit.framework/Frameworks/WebCore.framework/Resources").getChildFile (filename)); - BufferedInputStream buf (fileStream, 4096); + FileInputStream fileStream (File ("/System/Library/Frameworks/WebKit.framework/Frameworks/WebCore.framework/Resources") + .getChildFile (filename)); - PNGImageFormat pngFormat; - Image im (pngFormat.decodeImage (buf)); + if (fileStream.openedOk()) + { + BufferedInputStream buf (fileStream, 4096); + + PNGImageFormat pngFormat; + Image im (pngFormat.decodeImage (buf)); - if (im.isValid()) - return CustomMouseCursorInfo (im, (int) (hx * im.getWidth()), - (int) (hy * im.getHeight())).create(); + if (im.isValid()) + return CustomMouseCursorInfo (im, (int) (hx * im.getWidth()), + (int) (hy * im.getHeight())).create(); + } return nullptr; } diff --git a/JuceLibraryCode/modules/juce_gui_basics/native/juce_mac_NSViewComponentPeer.mm b/JuceLibraryCode/modules/juce_gui_basics/native/juce_mac_NSViewComponentPeer.mm index 7796380..54bce81 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/native/juce_mac_NSViewComponentPeer.mm +++ b/JuceLibraryCode/modules/juce_gui_basics/native/juce_mac_NSViewComponentPeer.mm @@ -1452,7 +1452,7 @@ private: if (target != nullptr) [(NSView*) self interpretKeyEvents: [NSArray arrayWithObject: ev]]; else - owner->stringBeingComposed = String::empty; + owner->stringBeingComposed.clear(); if ((! owner->textWasInserted) && (owner == nullptr || ! owner->redirectKeyDown (ev))) { @@ -1490,7 +1490,7 @@ private: } } - owner->stringBeingComposed = String::empty; + owner->stringBeingComposed.clear(); } } @@ -1525,7 +1525,7 @@ private: owner->textWasInserted = true; } - owner->stringBeingComposed = String::empty; + owner->stringBeingComposed.clear(); } } } diff --git a/JuceLibraryCode/modules/juce_gui_basics/native/juce_mac_Windowing.mm b/JuceLibraryCode/modules/juce_gui_basics/native/juce_mac_Windowing.mm index 709993e..7744d03 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/native/juce_mac_Windowing.mm +++ b/JuceLibraryCode/modules/juce_gui_basics/native/juce_mac_Windowing.mm @@ -439,7 +439,7 @@ String SystemClipboard::getTextFromClipboard() { NSString* text = [[NSPasteboard generalPasteboard] stringForType: NSStringPboardType]; - return text == nil ? String::empty + return text == nil ? String() : nsStringToJuce (text); } diff --git a/JuceLibraryCode/modules/juce_gui_basics/native/juce_win32_FileChooser.cpp b/JuceLibraryCode/modules/juce_gui_basics/native/juce_win32_FileChooser.cpp index da839eb..930bf55 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/native/juce_win32_FileChooser.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/native/juce_win32_FileChooser.cpp @@ -183,7 +183,7 @@ void FileChooser::showPlatformDialog (Array& results, const String& title_ if (! SHGetPathFromIDListW (list, files)) { files[0] = 0; - info.returnedString = String::empty; + info.returnedString.clear(); } LPMALLOC al; diff --git a/JuceLibraryCode/modules/juce_gui_basics/properties/juce_BooleanPropertyComponent.cpp b/JuceLibraryCode/modules/juce_gui_basics/properties/juce_BooleanPropertyComponent.cpp index adf1ecf..6f0d458 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/properties/juce_BooleanPropertyComponent.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/properties/juce_BooleanPropertyComponent.cpp @@ -29,7 +29,7 @@ BooleanPropertyComponent::BooleanPropertyComponent (const String& name, onText (buttonTextWhenTrue), offText (buttonTextWhenFalse) { - addAndMakeVisible (&button); + addAndMakeVisible (button); button.setClickingTogglesState (false); button.addListener (this); } @@ -41,7 +41,7 @@ BooleanPropertyComponent::BooleanPropertyComponent (const Value& valueToControl, onText (buttonText), offText (buttonText) { - addAndMakeVisible (&button); + addAndMakeVisible (button); button.setClickingTogglesState (false); button.setButtonText (buttonText); button.getToggleStateValue().referTo (valueToControl); diff --git a/JuceLibraryCode/modules/juce_gui_basics/properties/juce_ButtonPropertyComponent.cpp b/JuceLibraryCode/modules/juce_gui_basics/properties/juce_ButtonPropertyComponent.cpp index 1459dd3..cf03191 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/properties/juce_ButtonPropertyComponent.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/properties/juce_ButtonPropertyComponent.cpp @@ -26,7 +26,7 @@ ButtonPropertyComponent::ButtonPropertyComponent (const String& name, const bool triggerOnMouseDown) : PropertyComponent (name) { - addAndMakeVisible (&button); + addAndMakeVisible (button); button.setTriggeredOnMouseDown (triggerOnMouseDown); button.addListener (this); } diff --git a/JuceLibraryCode/modules/juce_gui_basics/properties/juce_ChoicePropertyComponent.cpp b/JuceLibraryCode/modules/juce_gui_basics/properties/juce_ChoicePropertyComponent.cpp index 9168e43..66b5481 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/properties/juce_ChoicePropertyComponent.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/properties/juce_ChoicePropertyComponent.cpp @@ -96,7 +96,7 @@ ChoicePropertyComponent::~ChoicePropertyComponent() //============================================================================== void ChoicePropertyComponent::createComboBox() { - addAndMakeVisible (&comboBox); + addAndMakeVisible (comboBox); for (int i = 0; i < choices.size(); ++i) { diff --git a/JuceLibraryCode/modules/juce_gui_basics/properties/juce_PropertyPanel.cpp b/JuceLibraryCode/modules/juce_gui_basics/properties/juce_PropertyPanel.cpp index fd1c21d..dc93d7c 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/properties/juce_PropertyPanel.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/properties/juce_PropertyPanel.cpp @@ -193,7 +193,7 @@ void PropertyPanel::init() { messageWhenEmpty = TRANS("(nothing selected)"); - addAndMakeVisible (&viewport); + addAndMakeVisible (viewport); viewport.setViewedComponent (propertyHolderComponent = new PropertyHolderComponent()); viewport.setFocusContainer (true); } diff --git a/JuceLibraryCode/modules/juce_gui_basics/properties/juce_PropertyPanel.h b/JuceLibraryCode/modules/juce_gui_basics/properties/juce_PropertyPanel.h index 2ec47f6..3bd6115 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/properties/juce_PropertyPanel.h +++ b/JuceLibraryCode/modules/juce_gui_basics/properties/juce_PropertyPanel.h @@ -63,7 +63,7 @@ public: These properties are added without them being inside a named section. If you want them to be kept together in a collapsible section, use addSection() instead. */ - void addProperties (const Array & newPropertyComponents); + void addProperties (const Array& newPropertyComponents); /** Adds a set of properties to the panel. @@ -76,7 +76,7 @@ public: To add properies without them being in a section, use addProperties(). */ void addSection (const String& sectionTitle, - const Array & newPropertyComponents, + const Array& newPropertyComponents, bool shouldSectionInitiallyBeOpen = true); /** Calls the refresh() method of all PropertyComponents in the panel */ diff --git a/JuceLibraryCode/modules/juce_gui_basics/properties/juce_SliderPropertyComponent.cpp b/JuceLibraryCode/modules/juce_gui_basics/properties/juce_SliderPropertyComponent.cpp index def6b12..d1190ae 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/properties/juce_SliderPropertyComponent.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/properties/juce_SliderPropertyComponent.cpp @@ -29,7 +29,7 @@ SliderPropertyComponent::SliderPropertyComponent (const String& name, const double skewFactor) : PropertyComponent (name) { - addAndMakeVisible (&slider); + addAndMakeVisible (slider); slider.setRange (rangeMin, rangeMax, interval); slider.setSkewFactor (skewFactor); @@ -46,7 +46,7 @@ SliderPropertyComponent::SliderPropertyComponent (const Value& valueToControl, const double skewFactor) : PropertyComponent (name) { - addAndMakeVisible (&slider); + addAndMakeVisible (slider); slider.setRange (rangeMin, rangeMax, interval); slider.setSkewFactor (skewFactor); diff --git a/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_ListBox.cpp b/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_ListBox.cpp index acc378b..5c2dddb 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_ListBox.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_ListBox.cpp @@ -952,6 +952,6 @@ void ListBoxModel::selectedRowsChanged (int) {} void ListBoxModel::deleteKeyPressed (int) {} void ListBoxModel::returnKeyPressed (int) {} void ListBoxModel::listWasScrolled() {} -var ListBoxModel::getDragSourceDescription (const SparseSet&) { return var::null; } +var ListBoxModel::getDragSourceDescription (const SparseSet&) { return var(); } String ListBoxModel::getTooltipForRow (int) { return String::empty; } MouseCursor ListBoxModel::getMouseCursorForRow (int) { return MouseCursor::NormalCursor; } diff --git a/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_Slider.cpp b/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_Slider.cpp index 29c15db..c59f9fc 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_Slider.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_Slider.cpp @@ -128,7 +128,7 @@ public: if (newInt != 0) { - int v = abs ((int) (newInt * 10000000)); + int v = std::abs (roundToInt (newInt * 10000000)); while ((v % 10) == 0) { @@ -408,7 +408,12 @@ public: void updateText() { if (valueBox != nullptr) - valueBox->setText (owner.getTextFromValue (currentValue.getValue()), dontSendNotification); + { + String newValue (owner.getTextFromValue (currentValue.getValue())); + + if (newValue != valueBox->getText()) + valueBox->setText (newValue, dontSendNotification); + } } double constrainedValue (double value) const diff --git a/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_TableHeaderComponent.cpp b/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_TableHeaderComponent.cpp index 4c43a35..9ee0f16 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_TableHeaderComponent.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_TableHeaderComponent.cpp @@ -440,7 +440,7 @@ String TableHeaderComponent::toString() const e->setAttribute ("width", ci->width); } - return doc.createDocument (String::empty, true, false); + return doc.createDocument ("", true, false); } void TableHeaderComponent::restoreFromString (const String& storedVersion) diff --git a/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_TableListBox.cpp b/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_TableListBox.cpp index 0b49d1c..3e2df4d 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_TableListBox.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_TableListBox.cpp @@ -466,7 +466,7 @@ void TableListBoxModel::returnKeyPressed (int) {} void TableListBoxModel::listWasScrolled() {} String TableListBoxModel::getCellTooltip (int /*rowNumber*/, int /*columnId*/) { return String::empty; } -var TableListBoxModel::getDragSourceDescription (const SparseSet&) { return var::null; } +var TableListBoxModel::getDragSourceDescription (const SparseSet&) { return var(); } Component* TableListBoxModel::refreshComponentForCell (int, int, bool, Component* existingComponentToUpdate) { diff --git a/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_TableListBox.h b/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_TableListBox.h index 883aca1..9f83e86 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_TableListBox.h +++ b/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_TableListBox.h @@ -278,7 +278,7 @@ public: /** Returns the component that currently represents a given cell. If the component for this cell is off-screen or if the position is out-of-range, - this may return 0. + this may return nullptr. @see getCellPosition */ Component* getCellComponent (int columnId, int rowNumber) const; @@ -291,31 +291,31 @@ public: //============================================================================== /** @internal */ - int getNumRows(); + int getNumRows() override; /** @internal */ - void paintListBoxItem (int, Graphics&, int, int, bool); + void paintListBoxItem (int, Graphics&, int, int, bool) override; /** @internal */ - Component* refreshComponentForRow (int rowNumber, bool isRowSelected, Component* existingComponentToUpdate); + Component* refreshComponentForRow (int rowNumber, bool isRowSelected, Component* existingComponentToUpdate) override; /** @internal */ - void selectedRowsChanged (int lastRowSelected); + void selectedRowsChanged (int lastRowSelected) override; /** @internal */ - void deleteKeyPressed (int currentSelectedRow); + void deleteKeyPressed (int currentSelectedRow) override; /** @internal */ - void returnKeyPressed (int currentSelectedRow); + void returnKeyPressed (int currentSelectedRow) override; /** @internal */ - void backgroundClicked(); + void backgroundClicked() override; /** @internal */ - void listWasScrolled(); + void listWasScrolled() override; /** @internal */ - void tableColumnsChanged (TableHeaderComponent*); + void tableColumnsChanged (TableHeaderComponent*) override; /** @internal */ - void tableColumnsResized (TableHeaderComponent*); + void tableColumnsResized (TableHeaderComponent*) override; /** @internal */ - void tableSortOrderChanged (TableHeaderComponent*); + void tableSortOrderChanged (TableHeaderComponent*) override; /** @internal */ - void tableColumnDraggingChanged (TableHeaderComponent*, int); + void tableColumnDraggingChanged (TableHeaderComponent*, int) override; /** @internal */ - void resized(); + void resized() override; private: diff --git a/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_Toolbar.cpp b/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_Toolbar.cpp index d16b712..fc7b4a2 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_Toolbar.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_Toolbar.cpp @@ -228,7 +228,7 @@ public: private: Component::SafePointer owner; const int height; - Array oldIndexes; + Array oldIndexes; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MissingItemsComponent) }; @@ -285,7 +285,7 @@ void Toolbar::addItemInternal (ToolbarItemFactory& factory, if (ToolbarItemComponent* const tc = createItem (factory, itemId)) { #if JUCE_DEBUG - Array allowedIds; + Array allowedIds; factory.getAllToolbarItemIds (allowedIds); // If your factory can create an item for a given ID, it must also return @@ -308,7 +308,7 @@ void Toolbar::addItem (ToolbarItemFactory& factory, void Toolbar::addDefaultItems (ToolbarItemFactory& factoryToUse) { - Array ids; + Array ids; factoryToUse.getDefaultItemSet (ids); clear(); @@ -675,32 +675,27 @@ public: void positionNearBar() { const Rectangle screenSize (toolbar.getParentMonitorArea()); - const int tbx = toolbar.getScreenX(); - const int tby = toolbar.getScreenY(); + Point pos (toolbar.getScreenPosition()); const int gap = 8; - int x, y; - if (toolbar.isVertical()) { - y = tby; - - if (tbx > screenSize.getCentreX()) - x = tbx - getWidth() - gap; + if (pos.x > screenSize.getCentreX()) + pos.x -= getWidth() - gap; else - x = tbx + toolbar.getWidth() + gap; + pos.x += toolbar.getWidth() + gap; } else { - x = tbx + (toolbar.getWidth() - getWidth()) / 2; + pos.x += (toolbar.getWidth() - getWidth()) / 2; - if (tby > screenSize.getCentreY()) - y = tby - getHeight() - gap; + if (pos.y > screenSize.getCentreY()) + pos.y -= getHeight() - gap; else - y = tby + toolbar.getHeight() + gap; + pos.y += toolbar.getHeight() + gap; } - setTopLeftPosition (x, y); + setTopLeftPosition (pos); } private: @@ -718,13 +713,13 @@ private: + TRANS ("Items on the toolbar can also be dragged around to change their order, or dragged off the edge to delete them.")), defaultButton (TRANS ("Restore to default set of items")) { - addAndMakeVisible (&palette); + addAndMakeVisible (palette); if ((optionFlags & (Toolbar::allowIconsOnlyChoice | Toolbar::allowIconsWithTextChoice | Toolbar::allowTextOnlyChoice)) != 0) { - addAndMakeVisible (&styleBox); + addAndMakeVisible (styleBox); styleBox.setEditableText (false); if ((optionFlags & Toolbar::allowIconsOnlyChoice) != 0) styleBox.addItem (TRANS("Show icons only"), 1); @@ -746,11 +741,11 @@ private: if ((optionFlags & Toolbar::showResetToDefaultsButton) != 0) { - addAndMakeVisible (&defaultButton); + addAndMakeVisible (defaultButton); defaultButton.addListener (this); } - addAndMakeVisible (&instructions); + addAndMakeVisible (instructions); instructions.setFont (Font (13.0f)); setSize (500, 300); diff --git a/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_ToolbarItemPalette.cpp b/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_ToolbarItemPalette.cpp index 8f3bb5d..a4b6d52 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_ToolbarItemPalette.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_ToolbarItemPalette.cpp @@ -28,13 +28,13 @@ ToolbarItemPalette::ToolbarItemPalette (ToolbarItemFactory& tbf, Toolbar& bar) Component* const itemHolder = new Component(); viewport.setViewedComponent (itemHolder); - Array allIds; + Array allIds; factory.getAllToolbarItemIds (allIds); for (int i = 0; i < allIds.size(); ++i) addComponent (allIds.getUnchecked (i), -1); - addAndMakeVisible (&viewport); + addAndMakeVisible (viewport); } ToolbarItemPalette::~ToolbarItemPalette() diff --git a/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_TreeView.cpp b/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_TreeView.cpp index 12c95af..097ea89 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_TreeView.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/widgets/juce_TreeView.cpp @@ -1371,7 +1371,7 @@ String TreeViewItem::getTooltip() var TreeViewItem::getDragSourceDescription() { - return var::null; + return var(); } bool TreeViewItem::isInterestedInFileDrag (const StringArray&) diff --git a/JuceLibraryCode/modules/juce_gui_basics/windows/juce_CallOutBox.cpp b/JuceLibraryCode/modules/juce_gui_basics/windows/juce_CallOutBox.cpp index 742bda9..1276f43 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/windows/juce_CallOutBox.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/windows/juce_CallOutBox.cpp @@ -25,7 +25,7 @@ CallOutBox::CallOutBox (Component& c, const Rectangle& area, Component* const parent) : borderSpace (20), arrowSize (16.0f), content (c) { - addAndMakeVisible (&content); + addAndMakeVisible (content); if (parent != nullptr) { diff --git a/JuceLibraryCode/modules/juce_gui_basics/windows/juce_ComponentPeer.h b/JuceLibraryCode/modules/juce_gui_basics/windows/juce_ComponentPeer.h index 4a4b515..2dc2d3f 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/windows/juce_ComponentPeer.h +++ b/JuceLibraryCode/modules/juce_gui_basics/windows/juce_ComponentPeer.h @@ -310,7 +310,7 @@ public: Point position; bool isEmpty() const noexcept { return files.size() == 0 && text.isEmpty(); } - void clear() noexcept { files.clear(); text = String::empty; } + void clear() noexcept { files.clear(); text.clear(); } }; bool handleDragMove (const DragInfo&); diff --git a/JuceLibraryCode/modules/juce_gui_basics/windows/juce_ThreadWithProgressWindow.cpp b/JuceLibraryCode/modules/juce_gui_basics/windows/juce_ThreadWithProgressWindow.cpp index 9a3a027..82d12aa 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/windows/juce_ThreadWithProgressWindow.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/windows/juce_ThreadWithProgressWindow.cpp @@ -33,8 +33,10 @@ ThreadWithProgressWindow::ThreadWithProgressWindow (const String& title, timeOutMsWhenCancelling (timeOutMsWhenCancelling_) { alertWindow = LookAndFeel::getDefaultLookAndFeel() - .createAlertWindow (title, String::empty, cancelButtonText, - String::empty, String::empty, + .createAlertWindow (title, String(), + cancelButtonText.isEmpty() ? TRANS("Cancel") + : cancelButtonText, + String(), String(), AlertWindow::NoIcon, hasCancelButton ? 1 : 0, componentToCentreAround); diff --git a/JuceLibraryCode/modules/juce_gui_basics/windows/juce_ThreadWithProgressWindow.h b/JuceLibraryCode/modules/juce_gui_basics/windows/juce_ThreadWithProgressWindow.h index 862d6d0..7bf8522 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/windows/juce_ThreadWithProgressWindow.h +++ b/JuceLibraryCode/modules/juce_gui_basics/windows/juce_ThreadWithProgressWindow.h @@ -96,7 +96,7 @@ public: the thread to stop before killing it forcibly (see Thread::stopThread() ) @param cancelButtonText the text that should be shown in the cancel button - (if it has one) + (if it has one). Leave this empty for the default "Cancel" @param componentToCentreAround if this is non-null, the window will be positioned so that it's centred around this component. */ @@ -104,7 +104,7 @@ public: bool hasProgressBar, bool hasCancelButton, int timeOutMsWhenCancelling = 10000, - const String& cancelButtonText = "Cancel", + const String& cancelButtonText = String(), Component* componentToCentreAround = nullptr); /** Destructor. */ diff --git a/JuceLibraryCode/modules/juce_gui_basics/windows/juce_TooltipWindow.cpp b/JuceLibraryCode/modules/juce_gui_basics/windows/juce_TooltipWindow.cpp index 6634efc..cc1ff7a 100644 --- a/JuceLibraryCode/modules/juce_gui_basics/windows/juce_TooltipWindow.cpp +++ b/JuceLibraryCode/modules/juce_gui_basics/windows/juce_TooltipWindow.cpp @@ -115,7 +115,7 @@ String TooltipWindow::getTipFor (Component* const c) void TooltipWindow::hideTip() { - tipShowing = String::empty; + tipShowing.clear(); removeFromDesktop(); setVisible (false); } diff --git a/JuceLibraryCode/modules/juce_gui_extra/code_editor/juce_CPlusPlusCodeTokeniserFunctions.h b/JuceLibraryCode/modules/juce_gui_extra/code_editor/juce_CPlusPlusCodeTokeniserFunctions.h index ec18615..a3a09f2 100644 --- a/JuceLibraryCode/modules/juce_gui_extra/code_editor/juce_CPlusPlusCodeTokeniserFunctions.h +++ b/JuceLibraryCode/modules/juce_gui_extra/code_editor/juce_CPlusPlusCodeTokeniserFunctions.h @@ -46,33 +46,34 @@ struct CppTokeniserFunctions static bool isReservedKeyword (String::CharPointerType token, const int tokenLength) noexcept { static const char* const keywords2Char[] = - { "if", "do", "or", "id", 0 }; + { "if", "do", "or", nullptr }; static const char* const keywords3Char[] = - { "for", "int", "new", "try", "xor", "and", "asm", "not", 0 }; + { "for", "int", "new", "try", "xor", "and", "asm", "not", nullptr }; static const char* const keywords4Char[] = { "bool", "void", "this", "true", "long", "else", "char", - "enum", "case", "goto", "auto", 0 }; + "enum", "case", "goto", "auto", nullptr }; static const char* const keywords5Char[] = - { "while", "bitor", "break", "catch", "class", "compl", "const", "false", - "float", "short", "throw", "union", "using", "or_eq", 0 }; + { "float", "const", "while", "break", "false", "catch", "class", "bitor", + "compl", "or_eq", "short", "throw", "union", "using", "final", nullptr }; static const char* const keywords6Char[] = - { "return", "struct", "and_eq", "bitand", "delete", "double", "extern", - "friend", "inline", "not_eq", "public", "sizeof", "static", "signed", - "switch", "typeid", "wchar_t", "xor_eq", 0}; + { "return", "and_eq", "bitand", "delete", "double", "export", "extern", + "friend", "inline", "not_eq", "public", "signed", "sizeof", "static", + "struct", "switch", "typeid", "xor_eq", nullptr }; static const char* const keywords7Char[] = - { "default", "mutable", "private", "typedef", "nullptr", "virtual", 0 }; + { "nullptr", "alignas", "alignof", "default", "mutable", "private", + "typedef", "virtual", "wchar_t", nullptr }; static const char* const keywordsOther[] = - { "noexcept", "const_cast", "continue", "explicit", "namespace", - "operator", "protected", "register", "reinterpret_cast", "static_cast", - "template", "typename", "unsigned", "volatile", "constexpr", - "@implementation", "@interface", "@end", "@synthesize", "@dynamic", "@public", - "@private", "@property", "@protected", "@class", 0 }; + { "char16_t", "char32_t", "const_cast", "constexpr", "continue", "decltype", "dynamic_cast", + "explicit", "namespace", "noexcept", "operator", "protected", "register", "reinterpret_cast", + "static_assert", "static_cast", "template", "thread_local", "typename", "unsigned", "volatile", + "@class", "@dynamic", "@end", "@implementation", "@interface", "@public", "@private", + "@protected", "@property", "@synthesize", nullptr }; const char* const* k; @@ -100,7 +101,7 @@ struct CppTokeniserFunctions return false; } - template + template static int parseIdentifier (Iterator& source) noexcept { int tokenLength = 0; @@ -128,7 +129,7 @@ struct CppTokeniserFunctions return CPlusPlusCodeTokeniser::tokenType_identifier; } - template + template static bool skipNumberSuffix (Iterator& source) { const juce_wchar c = source.peekNextChar(); @@ -148,7 +149,7 @@ struct CppTokeniserFunctions || (c >= 'A' && c <= 'F'); } - template + template static bool parseHexLiteral (Iterator& source) noexcept { if (source.peekNextChar() == '-') @@ -179,7 +180,7 @@ struct CppTokeniserFunctions return c >= '0' && c <= '7'; } - template + template static bool parseOctalLiteral (Iterator& source) noexcept { if (source.peekNextChar() == '-') @@ -202,7 +203,7 @@ struct CppTokeniserFunctions return c >= '0' && c <= '9'; } - template + template static bool parseDecimalLiteral (Iterator& source) noexcept { if (source.peekNextChar() == '-') @@ -221,7 +222,7 @@ struct CppTokeniserFunctions return skipNumberSuffix (source); } - template + template static bool parseFloatLiteral (Iterator& source) noexcept { if (source.peekNextChar() == '-') @@ -282,7 +283,7 @@ struct CppTokeniserFunctions return true; } - template + template static int parseNumber (Iterator& source) { const Iterator original (source); @@ -302,7 +303,7 @@ struct CppTokeniserFunctions return CPlusPlusCodeTokeniser::tokenType_error; } - template + template static void skipQuotedString (Iterator& source) noexcept { const juce_wchar quote = source.nextChar(); @@ -319,7 +320,7 @@ struct CppTokeniserFunctions } } - template + template static void skipComment (Iterator& source) noexcept { bool lastWasStar = false; @@ -335,7 +336,7 @@ struct CppTokeniserFunctions } } - template + template static void skipPreprocessorLine (Iterator& source) noexcept { bool lastWasBackslash = false; @@ -378,14 +379,14 @@ struct CppTokeniserFunctions } } - template + template static void skipIfNextCharMatches (Iterator& source, const juce_wchar c) noexcept { if (source.peekNextChar() == c) source.skip(); } - template + template static void skipIfNextCharMatches (Iterator& source, const juce_wchar c1, const juce_wchar c2) noexcept { const juce_wchar c = source.peekNextChar(); @@ -394,10 +395,9 @@ struct CppTokeniserFunctions source.skip(); } - template + template static int readNextToken (Iterator& source) { - int result = CPlusPlusCodeTokeniser::tokenType_error; source.skipWhitespace(); const juce_wchar firstChar = source.peekNextChar(); @@ -405,135 +405,116 @@ struct CppTokeniserFunctions switch (firstChar) { case 0: - source.skip(); break; - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': case '.': - result = parseNumber (source); + { + int result = parseNumber (source); if (result == CPlusPlusCodeTokeniser::tokenType_error) { source.skip(); if (firstChar == '.') - result = CPlusPlusCodeTokeniser::tokenType_punctuation; + return CPlusPlusCodeTokeniser::tokenType_punctuation; } - break; + return result; + } case ',': case ';': case ':': source.skip(); - result = CPlusPlusCodeTokeniser::tokenType_punctuation; - break; + return CPlusPlusCodeTokeniser::tokenType_punctuation; - case '(': - case ')': - case '{': - case '}': - case '[': - case ']': + case '(': case ')': + case '{': case '}': + case '[': case ']': source.skip(); - result = CPlusPlusCodeTokeniser::tokenType_bracket; - break; + return CPlusPlusCodeTokeniser::tokenType_bracket; case '"': case '\'': skipQuotedString (source); - result = CPlusPlusCodeTokeniser::tokenType_string; - break; + return CPlusPlusCodeTokeniser::tokenType_string; case '+': - result = CPlusPlusCodeTokeniser::tokenType_operator; source.skip(); skipIfNextCharMatches (source, '+', '='); - break; + return CPlusPlusCodeTokeniser::tokenType_operator; case '-': + { source.skip(); - result = parseNumber (source); + int result = parseNumber (source); if (result == CPlusPlusCodeTokeniser::tokenType_error) { - result = CPlusPlusCodeTokeniser::tokenType_operator; skipIfNextCharMatches (source, '-', '='); + return CPlusPlusCodeTokeniser::tokenType_operator; } - break; - case '*': - case '%': - case '=': - case '!': - result = CPlusPlusCodeTokeniser::tokenType_operator; + return result; + } + + case '*': case '%': + case '=': case '!': source.skip(); skipIfNextCharMatches (source, '='); - break; + return CPlusPlusCodeTokeniser::tokenType_operator; case '/': - result = CPlusPlusCodeTokeniser::tokenType_operator; + { source.skip(); + juce_wchar nextChar = source.peekNextChar(); - if (source.peekNextChar() == '=') + if (nextChar == '/') { - source.skip(); - } - else if (source.peekNextChar() == '/') - { - result = CPlusPlusCodeTokeniser::tokenType_comment; source.skipToEndOfLine(); + return CPlusPlusCodeTokeniser::tokenType_comment; } - else if (source.peekNextChar() == '*') + + if (nextChar == '*') { source.skip(); - result = CPlusPlusCodeTokeniser::tokenType_comment; skipComment (source); + return CPlusPlusCodeTokeniser::tokenType_comment; } - break; + if (nextChar == '=') + source.skip(); + + return CPlusPlusCodeTokeniser::tokenType_operator; + } case '?': case '~': source.skip(); - result = CPlusPlusCodeTokeniser::tokenType_operator; - break; + return CPlusPlusCodeTokeniser::tokenType_operator; - case '<': - case '>': - case '|': - case '&': - case '^': + case '<': case '>': + case '|': case '&': case '^': source.skip(); - result = CPlusPlusCodeTokeniser::tokenType_operator; skipIfNextCharMatches (source, firstChar); skipIfNextCharMatches (source, '='); - break; + return CPlusPlusCodeTokeniser::tokenType_operator; case '#': - result = CPlusPlusCodeTokeniser::tokenType_preprocessor; skipPreprocessorLine (source); - break; + return CPlusPlusCodeTokeniser::tokenType_preprocessor; default: if (isIdentifierStart (firstChar)) - result = parseIdentifier (source); - else - source.skip(); + return parseIdentifier (source); + source.skip(); break; } - return result; + return CPlusPlusCodeTokeniser::tokenType_error; } /** A class that can be passed to the CppTokeniserFunctions functions in order to diff --git a/JuceLibraryCode/modules/juce_gui_extra/code_editor/juce_CodeDocument.cpp b/JuceLibraryCode/modules/juce_gui_extra/code_editor/juce_CodeDocument.cpp index beb5d87..b4b9a9f 100644 --- a/JuceLibraryCode/modules/juce_gui_extra/code_editor/juce_CodeDocument.cpp +++ b/JuceLibraryCode/modules/juce_gui_extra/code_editor/juce_CodeDocument.cpp @@ -577,9 +577,8 @@ void CodeDocument::insertText (const int insertIndex, const String& text) void CodeDocument::replaceSection (const int start, const int end, const String& newText) { - insertText (start, newText); - const int newTextLen = newText.length(); - deleteSection (start + newTextLen, end + newTextLen); + insertText (end, newText); + deleteSection (start, end); } void CodeDocument::applyChanges (const String& newContent) diff --git a/JuceLibraryCode/modules/juce_gui_extra/code_editor/juce_CodeEditorComponent.cpp b/JuceLibraryCode/modules/juce_gui_extra/code_editor/juce_CodeEditorComponent.cpp index 70b0a8f..cd10127 100644 --- a/JuceLibraryCode/modules/juce_gui_extra/code_editor/juce_CodeEditorComponent.cpp +++ b/JuceLibraryCode/modules/juce_gui_extra/code_editor/juce_CodeEditorComponent.cpp @@ -340,8 +340,10 @@ CodeEditorComponent::CodeEditorComponent (CodeDocument& doc, CodeTokeniser* cons columnsOnScreen (0), scrollbarThickness (16), columnToTryToMaintain (-1), + readOnly (false), useSpacesForTabs (false), showLineNumbers (false), + shouldFollowDocumentChanges (false), xOffset (0), caretPos (doc, 0, 0), selectionStart (doc, 0, 0), @@ -363,10 +365,10 @@ CodeEditorComponent::CodeEditorComponent (CodeDocument& doc, CodeTokeniser* cons addAndMakeVisible (caret = getLookAndFeel().createCaretComponent (this)); - addAndMakeVisible (&verticalScrollBar); + addAndMakeVisible (verticalScrollBar); verticalScrollBar.setSingleStepSize (1.0); - addAndMakeVisible (&horizontalScrollBar); + addAndMakeVisible (horizontalScrollBar); horizontalScrollBar.setSingleStepSize (1.0); Font f (12.0f); @@ -434,6 +436,11 @@ void CodeEditorComponent::setLineNumbersShown (const bool shouldBeShown) } } +void CodeEditorComponent::setReadOnly (bool b) noexcept +{ + readOnly = b; +} + //============================================================================== void CodeEditorComponent::resized() { @@ -559,9 +566,10 @@ void CodeEditorComponent::codeDocumentChanged (const int startIndex, const int e && affectedTextStart.getPosition() <= selectionEnd.getPosition()) deselectAll(); - if (caretPos.getPosition() > affectedTextEnd.getPosition() - || caretPos.getPosition() < affectedTextStart.getPosition()) - moveCaretTo (affectedTextStart, false); + if (shouldFollowDocumentChanges) + if (caretPos.getPosition() > affectedTextEnd.getPosition() + || caretPos.getPosition() < affectedTextStart.getPosition()) + moveCaretTo (affectedTextStart, false); updateScrollBars(); } @@ -743,37 +751,43 @@ void CodeEditorComponent::insertTextAtCaret (const String& newText) void CodeEditorComponent::insertText (const String& newText) { - document.deleteSection (selectionStart, selectionEnd); + if (! readOnly) + { + document.deleteSection (selectionStart, selectionEnd); - if (newText.isNotEmpty()) - document.insertText (caretPos, newText); + if (newText.isNotEmpty()) + document.insertText (caretPos, newText); - scrollToKeepCaretOnScreen(); + scrollToKeepCaretOnScreen(); + } } void CodeEditorComponent::insertTabAtCaret() { - if (CharacterFunctions::isWhitespace (caretPos.getCharacter()) - && caretPos.getLineNumber() == caretPos.movedBy (1).getLineNumber()) + if (! readOnly) { - moveCaretTo (document.findWordBreakAfter (caretPos), false); - } + if (CharacterFunctions::isWhitespace (caretPos.getCharacter()) + && caretPos.getLineNumber() == caretPos.movedBy (1).getLineNumber()) + { + moveCaretTo (document.findWordBreakAfter (caretPos), false); + } - if (useSpacesForTabs) - { - const int caretCol = indexToColumn (caretPos.getLineNumber(), caretPos.getIndexInLine()); - const int spacesNeeded = spacesPerTab - (caretCol % spacesPerTab); - insertTextAtCaret (String::repeatedString (" ", spacesNeeded)); - } - else - { - insertTextAtCaret ("\t"); + if (useSpacesForTabs) + { + const int caretCol = indexToColumn (caretPos.getLineNumber(), caretPos.getIndexInLine()); + const int spacesNeeded = spacesPerTab - (caretCol % spacesPerTab); + insertTextAtCaret (String::repeatedString (" ", spacesNeeded)); + } + else + { + insertTextAtCaret ("\t"); + } } } bool CodeEditorComponent::deleteWhitespaceBackwardsToTabStop() { - if (getHighlightedRegion().isEmpty()) + if (getHighlightedRegion().isEmpty() && ! readOnly) { for (;;) { @@ -802,43 +816,46 @@ void CodeEditorComponent::unindentSelection() { indentSelectedLines (-spacesPe void CodeEditorComponent::indentSelectedLines (const int spacesToAdd) { - newTransaction(); - - CodeDocument::Position oldSelectionStart (selectionStart), oldSelectionEnd (selectionEnd), oldCaret (caretPos); - oldSelectionStart.setPositionMaintained (true); - oldSelectionEnd.setPositionMaintained (true); - oldCaret.setPositionMaintained (true); + if (! readOnly) + { + newTransaction(); - const int lineStart = selectionStart.getLineNumber(); - int lineEnd = selectionEnd.getLineNumber(); + CodeDocument::Position oldSelectionStart (selectionStart), oldSelectionEnd (selectionEnd), oldCaret (caretPos); + oldSelectionStart.setPositionMaintained (true); + oldSelectionEnd.setPositionMaintained (true); + oldCaret.setPositionMaintained (true); - if (lineEnd > lineStart && selectionEnd.getIndexInLine() == 0) - --lineEnd; + const int lineStart = selectionStart.getLineNumber(); + int lineEnd = selectionEnd.getLineNumber(); - for (int line = lineStart; line <= lineEnd; ++line) - { - const String lineText (document.getLine (line)); - const int nonWhitespaceStart = CodeEditorHelpers::findFirstNonWhitespaceChar (lineText); + if (lineEnd > lineStart && selectionEnd.getIndexInLine() == 0) + --lineEnd; - if (nonWhitespaceStart > 0 || lineText.trimStart().isNotEmpty()) + for (int line = lineStart; line <= lineEnd; ++line) { - const CodeDocument::Position wsStart (document, line, 0); - const CodeDocument::Position wsEnd (document, line, nonWhitespaceStart); - - const int numLeadingSpaces = indexToColumn (line, wsEnd.getIndexInLine()); - const int newNumLeadingSpaces = jmax (0, numLeadingSpaces + spacesToAdd); + const String lineText (document.getLine (line)); + const int nonWhitespaceStart = CodeEditorHelpers::findFirstNonWhitespaceChar (lineText); - if (newNumLeadingSpaces != numLeadingSpaces) + if (nonWhitespaceStart > 0 || lineText.trimStart().isNotEmpty()) { - document.deleteSection (wsStart, wsEnd); - document.insertText (wsStart, getTabString (newNumLeadingSpaces)); + const CodeDocument::Position wsStart (document, line, 0); + const CodeDocument::Position wsEnd (document, line, nonWhitespaceStart); + + const int numLeadingSpaces = indexToColumn (line, wsEnd.getIndexInLine()); + const int newNumLeadingSpaces = jmax (0, numLeadingSpaces + spacesToAdd); + + if (newNumLeadingSpaces != numLeadingSpaces) + { + document.deleteSection (wsStart, wsEnd); + document.insertText (wsStart, getTabString (newNumLeadingSpaces)); + } } } - } - selectionStart = oldSelectionStart; - selectionEnd = oldSelectionEnd; - caretPos = oldCaret; + selectionStart = oldSelectionStart; + selectionEnd = oldSelectionEnd; + caretPos = oldCaret; + } } void CodeEditorComponent::cut() @@ -1114,6 +1131,10 @@ void CodeEditorComponent::selectRegion (const CodeDocument::Position& start, //============================================================================== bool CodeEditorComponent::undo() { + if (readOnly) + return false; + + ScopedValueSetter svs (shouldFollowDocumentChanges, true, false); document.undo(); scrollToKeepCaretOnScreen(); return true; @@ -1121,6 +1142,10 @@ bool CodeEditorComponent::undo() bool CodeEditorComponent::redo() { + if (readOnly) + return false; + + ScopedValueSetter svs (shouldFollowDocumentChanges, true, false); document.redo(); scrollToKeepCaretOnScreen(); return true; @@ -1165,6 +1190,9 @@ bool CodeEditorComponent::keyPressed (const KeyPress& key) { if (! TextEditorKeyMapper::invokeKeyFunction (*this, key)) { + if (readOnly) + return false; + if (key == KeyPress::tabKey || key.getTextCharacter() == '\t') handleTabKey(); else if (key == KeyPress::returnKey) handleReturnKey(); else if (key == KeyPress::escapeKey) handleEscapeKey(); @@ -1220,7 +1248,7 @@ void CodeEditorComponent::getCommandInfo (const CommandID commandID, Application { case StandardApplicationCommandIDs::cut: result.setInfo (TRANS ("Cut"), TRANS ("Copies the currently selected text to the clipboard and deletes it."), "Editing", 0); - result.setActive (anythingSelected); + result.setActive (anythingSelected && ! readOnly); result.defaultKeypresses.add (KeyPress ('x', ModifierKeys::commandModifier, 0)); break; @@ -1232,12 +1260,13 @@ void CodeEditorComponent::getCommandInfo (const CommandID commandID, Application case StandardApplicationCommandIDs::paste: result.setInfo (TRANS ("Paste"), TRANS ("Inserts text from the clipboard."), "Editing", 0); + result.setActive (! readOnly); result.defaultKeypresses.add (KeyPress ('v', ModifierKeys::commandModifier, 0)); break; case StandardApplicationCommandIDs::del: result.setInfo (TRANS ("Delete"), TRANS ("Deletes any selected text."), "Editing", 0); - result.setActive (anythingSelected); + result.setActive (anythingSelected && ! readOnly); break; case StandardApplicationCommandIDs::selectAll: @@ -1248,13 +1277,13 @@ void CodeEditorComponent::getCommandInfo (const CommandID commandID, Application case StandardApplicationCommandIDs::undo: result.setInfo (TRANS ("Undo"), TRANS ("Undo"), "Editing", 0); result.defaultKeypresses.add (KeyPress ('z', ModifierKeys::commandModifier, 0)); - result.setActive (document.getUndoManager().canUndo()); + result.setActive (document.getUndoManager().canUndo() && ! readOnly); break; case StandardApplicationCommandIDs::redo: result.setInfo (TRANS ("Redo"), TRANS ("Redo"), "Editing", 0); result.defaultKeypresses.add (KeyPress ('z', ModifierKeys::commandModifier | ModifierKeys::shiftModifier, 0)); - result.setActive (document.getUndoManager().canRedo()); + result.setActive (document.getUndoManager().canRedo() && ! readOnly); break; default: diff --git a/JuceLibraryCode/modules/juce_gui_extra/code_editor/juce_CodeEditorComponent.h b/JuceLibraryCode/modules/juce_gui_extra/code_editor/juce_CodeEditorComponent.h index d772a46..bb3fb16 100644 --- a/JuceLibraryCode/modules/juce_gui_extra/code_editor/juce_CodeEditorComponent.h +++ b/JuceLibraryCode/modules/juce_gui_extra/code_editor/juce_CodeEditorComponent.h @@ -204,6 +204,12 @@ public: /** Returns the font that the editor is using. */ const Font& getFont() const noexcept { return font; } + /** Makes the editor read-only. */ + void setReadOnly (bool shouldBeReadOnly) noexcept; + + /** Returns true if the editor is set to be read-only. */ + bool isReadOnly() const noexcept { return readOnly; } + //============================================================================== struct JUCE_API ColourScheme { @@ -352,7 +358,7 @@ private: float charWidth; int lineHeight, linesOnScreen, columnsOnScreen; int scrollbarThickness, columnToTryToMaintain; - bool useSpacesForTabs, showLineNumbers; + bool readOnly, useSpacesForTabs, showLineNumbers, shouldFollowDocumentChanges; double xOffset; CodeDocument::Position caretPos, selectionStart, selectionEnd; diff --git a/JuceLibraryCode/modules/juce_gui_extra/code_editor/juce_LuaCodeTokeniser.cpp b/JuceLibraryCode/modules/juce_gui_extra/code_editor/juce_LuaCodeTokeniser.cpp new file mode 100644 index 0000000..a58b2bd --- /dev/null +++ b/JuceLibraryCode/modules/juce_gui_extra/code_editor/juce_LuaCodeTokeniser.cpp @@ -0,0 +1,233 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. + + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 + + Details of these licenses can be found at: www.gnu.org/licenses + + 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. + + ------------------------------------------------------------------------------ + + To release a closed-source product which uses JUCE, commercial licenses are + available: visit www.juce.com for more information. + + ============================================================================== +*/ + +struct LuaTokeniserFunctions +{ + static bool isReservedKeyword (String::CharPointerType token, const int tokenLength) noexcept + { + static const char* const keywords2Char[] = + { "if", "or", "in", "do", nullptr }; + + static const char* const keywords3Char[] = + { "and", "end", "for", "nil", "not", nullptr }; + + static const char* const keywords4Char[] = + { "then", "true", "else", nullptr }; + + static const char* const keywords5Char[] = + { "false", "local", "until", "while", "break", nullptr }; + + static const char* const keywords6Char[] = + { "repeat", "return", "elseif", nullptr}; + + static const char* const keywordsOther[] = + { "function", "@interface", "@end", "@synthesize", "@dynamic", "@public", + "@private", "@property", "@protected", "@class", nullptr }; + + const char* const* k; + + switch (tokenLength) + { + case 2: k = keywords2Char; break; + case 3: k = keywords3Char; break; + case 4: k = keywords4Char; break; + case 5: k = keywords5Char; break; + case 6: k = keywords6Char; break; + + default: + if (tokenLength < 2 || tokenLength > 16) + return false; + + k = keywordsOther; + break; + } + + for (int i = 0; k[i] != 0; ++i) + if (token.compare (CharPointer_ASCII (k[i])) == 0) + return true; + + return false; + } + + template + static int parseIdentifier (Iterator& source) noexcept + { + int tokenLength = 0; + String::CharPointerType::CharType possibleIdentifier [100]; + String::CharPointerType possible (possibleIdentifier); + + while (CppTokeniserFunctions::isIdentifierBody (source.peekNextChar())) + { + const juce_wchar c = source.nextChar(); + + if (tokenLength < 20) + possible.write (c); + + ++tokenLength; + } + + if (tokenLength > 1 && tokenLength <= 16) + { + possible.writeNull(); + + if (isReservedKeyword (String::CharPointerType (possibleIdentifier), tokenLength)) + return LuaTokeniser::tokenType_keyword; + } + + return LuaTokeniser::tokenType_identifier; + } + + template + static int readNextToken (Iterator& source) + { + source.skipWhitespace(); + + const juce_wchar firstChar = source.peekNextChar(); + + switch (firstChar) + { + case 0: + break; + + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + case '.': + { + int result = CppTokeniserFunctions::parseNumber (source); + + if (result == LuaTokeniser::tokenType_error) + { + source.skip(); + + if (firstChar == '.') + return LuaTokeniser::tokenType_punctuation; + } + + return result; + } + + case ',': + case ';': + case ':': + source.skip(); + return LuaTokeniser::tokenType_punctuation; + + case '(': case ')': + case '{': case '}': + case '[': case ']': + source.skip(); + return LuaTokeniser::tokenType_bracket; + + case '"': + case '\'': + CppTokeniserFunctions::skipQuotedString (source); + return LuaTokeniser::tokenType_string; + + case '+': + source.skip(); + CppTokeniserFunctions::skipIfNextCharMatches (source, '+', '='); + return LuaTokeniser::tokenType_operator; + + case '-': + { + source.skip(); + int result = CppTokeniserFunctions::parseNumber (source); + + if (source.peekNextChar() == '-') + { + source.skipToEndOfLine(); + return LuaTokeniser::tokenType_comment; + } + + if (result == LuaTokeniser::tokenType_error) + { + CppTokeniserFunctions::skipIfNextCharMatches (source, '-', '='); + return LuaTokeniser::tokenType_operator; + } + + return result; + } + + case '*': case '%': + case '=': case '!': + source.skip(); + CppTokeniserFunctions::skipIfNextCharMatches (source, '='); + return LuaTokeniser::tokenType_operator; + + case '?': + case '~': + source.skip(); + return LuaTokeniser::tokenType_operator; + + case '<': case '>': + case '|': case '&': case '^': + source.skip(); + CppTokeniserFunctions::skipIfNextCharMatches (source, firstChar); + CppTokeniserFunctions::skipIfNextCharMatches (source, '='); + return LuaTokeniser::tokenType_operator; + + default: + if (CppTokeniserFunctions::isIdentifierStart (firstChar)) + return parseIdentifier (source); + + source.skip(); + break; + } + + return LuaTokeniser::tokenType_error; + } +}; + +//============================================================================== +LuaTokeniser::LuaTokeniser() {} +LuaTokeniser::~LuaTokeniser() {} + +int LuaTokeniser::readNextToken (CodeDocument::Iterator& source) +{ + return LuaTokeniserFunctions::readNextToken (source); +} + +CodeEditorComponent::ColourScheme LuaTokeniser::getDefaultColourScheme() +{ + static const CodeEditorComponent::ColourScheme::TokenType types[] = + { + { "Error", Colour (0xffcc0000) }, + { "Comment", Colour (0xff3c3c3c) }, + { "Keyword", Colour (0xff0000cc) }, + { "Operator", Colour (0xff225500) }, + { "Identifier", Colour (0xff000000) }, + { "Integer", Colour (0xff880000) }, + { "Float", Colour (0xff885500) }, + { "String", Colour (0xff990099) }, + { "Bracket", Colour (0xff000055) }, + { "Punctuation", Colour (0xff004400) } + }; + + CodeEditorComponent::ColourScheme cs; + + for (unsigned int i = 0; i < sizeof (types) / sizeof (types[0]); ++i) // (NB: numElementsInArray doesn't work here in GCC4.2) + cs.set (types[i].name, types[i].colour); + + return cs; +} diff --git a/JuceLibraryCode/modules/juce_gui_extra/code_editor/juce_LuaCodeTokeniser.h b/JuceLibraryCode/modules/juce_gui_extra/code_editor/juce_LuaCodeTokeniser.h new file mode 100644 index 0000000..0cee34b --- /dev/null +++ b/JuceLibraryCode/modules/juce_gui_extra/code_editor/juce_LuaCodeTokeniser.h @@ -0,0 +1,63 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. + + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 + + Details of these licenses can be found at: www.gnu.org/licenses + + 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. + + ------------------------------------------------------------------------------ + + To release a closed-source product which uses JUCE, commercial licenses are + available: visit www.juce.com for more information. + + ============================================================================== +*/ + +#ifndef JUCE_LUACODETOKENISER_H_INCLUDED +#define JUCE_LUACODETOKENISER_H_INCLUDED + + +//============================================================================== +/** +*/ +class JUCE_API LuaTokeniser : public CodeTokeniser +{ +public: + //============================================================================== + LuaTokeniser(); + ~LuaTokeniser(); + + //============================================================================== + int readNextToken (CodeDocument::Iterator&) override; + CodeEditorComponent::ColourScheme getDefaultColourScheme() override; + + /** The token values returned by this tokeniser. */ + enum TokenType + { + tokenType_error = 0, + tokenType_comment, + tokenType_keyword, + tokenType_operator, + tokenType_identifier, + tokenType_integer, + tokenType_float, + tokenType_string, + tokenType_bracket, + tokenType_punctuation + }; + +private: + //============================================================================== + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LuaTokeniser) +}; + +#endif // JUCE_LUACODETOKENISER_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_gui_extra/code_editor/juce_XMLCodeTokeniser.cpp b/JuceLibraryCode/modules/juce_gui_extra/code_editor/juce_XMLCodeTokeniser.cpp new file mode 100644 index 0000000..ecaba96 --- /dev/null +++ b/JuceLibraryCode/modules/juce_gui_extra/code_editor/juce_XMLCodeTokeniser.cpp @@ -0,0 +1,166 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. + + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 + + Details of these licenses can be found at: www.gnu.org/licenses + + 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. + + ------------------------------------------------------------------------------ + + To release a closed-source product which uses JUCE, commercial licenses are + available: visit www.juce.com for more information. + + ============================================================================== +*/ + +XmlTokeniser::XmlTokeniser() {} +XmlTokeniser::~XmlTokeniser() {} + +CodeEditorComponent::ColourScheme XmlTokeniser::getDefaultColourScheme() +{ + struct Type + { + const char* name; + uint32 colour; + }; + + const Type types[] = + { + { "Error", 0xffcc0000 }, + { "Comment", 0xff00aa00 }, + { "Keyword", 0xff0000cc }, + { "Operator", 0xff225500 }, + { "Identifier", 0xff000000 }, + { "String", 0xff990099 }, + { "Bracket", 0xff000055 }, + { "Punctuation", 0xff004400 }, + { "Preprocessor Text", 0xff660000 } + }; + + CodeEditorComponent::ColourScheme cs; + + for (unsigned int i = 0; i < sizeof (types) / sizeof (types[0]); ++i) // (NB: numElementsInArray doesn't work here in GCC4.2) + cs.set (types[i].name, Colour (types[i].colour)); + + return cs; +}; + +template +static void skipToEndOfXmlDTD (Iterator& source) noexcept +{ + bool lastWasQuestionMark = false; + + for (;;) + { + const juce_wchar c = source.nextChar(); + + if (c == 0 || (c == '>' && lastWasQuestionMark)) + break; + + lastWasQuestionMark = (c == '?'); + } +} + +template +static void skipToEndOfXmlComment (Iterator& source) noexcept +{ + juce_wchar last[2] = { 0 }; + + for (;;) + { + const juce_wchar c = source.nextChar(); + + if (c == 0 || (c == '>' && last[0] == '-' && last[1] == '-')) + break; + + last[1] = last[0]; + last[0] = c; + } +} + +int XmlTokeniser::readNextToken (CodeDocument::Iterator& source) +{ + source.skipWhitespace(); + const juce_wchar firstChar = source.peekNextChar(); + + switch (firstChar) + { + case 0: break; + + case '"': + case '\'': + CppTokeniserFunctions::skipQuotedString (source); + return tokenType_string; + + case '<': + { + source.skip(); + source.skipWhitespace(); + const juce_wchar nextChar = source.peekNextChar(); + + if (nextChar == '?') + { + source.skip(); + skipToEndOfXmlDTD (source); + return tokenType_preprocessor; + } + + if (nextChar == '!') + { + source.skip(); + + if (source.peekNextChar() == '-') + { + source.skip(); + + if (source.peekNextChar() == '-') + { + skipToEndOfXmlComment (source); + return tokenType_comment; + } + } + } + + CppTokeniserFunctions::skipIfNextCharMatches (source, '/'); + CppTokeniserFunctions::parseIdentifier (source); + source.skipWhitespace(); + CppTokeniserFunctions::skipIfNextCharMatches (source, '/'); + source.skipWhitespace(); + CppTokeniserFunctions::skipIfNextCharMatches (source, '>'); + return tokenType_keyword; + } + + case '>': + source.skip(); + return tokenType_keyword; + + case '/': + source.skip(); + source.skipWhitespace(); + CppTokeniserFunctions::skipIfNextCharMatches (source, '>'); + return tokenType_keyword; + + case '=': + case ':': + source.skip(); + return tokenType_operator; + + default: + if (CppTokeniserFunctions::isIdentifierStart (firstChar)) + CppTokeniserFunctions::parseIdentifier (source); + + source.skip(); + break; + }; + + return tokenType_identifier; +} diff --git a/JuceLibraryCode/modules/juce_gui_extra/code_editor/juce_XMLCodeTokeniser.h b/JuceLibraryCode/modules/juce_gui_extra/code_editor/juce_XMLCodeTokeniser.h new file mode 100644 index 0000000..3bf5860 --- /dev/null +++ b/JuceLibraryCode/modules/juce_gui_extra/code_editor/juce_XMLCodeTokeniser.h @@ -0,0 +1,62 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. + + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 + + Details of these licenses can be found at: www.gnu.org/licenses + + 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. + + ------------------------------------------------------------------------------ + + To release a closed-source product which uses JUCE, commercial licenses are + available: visit www.juce.com for more information. + + ============================================================================== +*/ + +#ifndef JUCE_XMLCODETOKENISER_H_INCLUDED +#define JUCE_XMLCODETOKENISER_H_INCLUDED + + +//============================================================================== +/** +*/ +class JUCE_API XmlTokeniser : public CodeTokeniser +{ +public: + //============================================================================== + XmlTokeniser(); + ~XmlTokeniser(); + + //============================================================================== + int readNextToken (CodeDocument::Iterator&) override; + CodeEditorComponent::ColourScheme getDefaultColourScheme() override; + + /** The token values returned by this tokeniser. */ + enum TokenType + { + tokenType_error = 0, + tokenType_comment, + tokenType_keyword, + tokenType_operator, + tokenType_identifier, + tokenType_string, + tokenType_bracket, + tokenType_punctuation, + tokenType_preprocessor + }; + +private: + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (XmlTokeniser) +}; + + +#endif // JUCE_XMLCODETOKENISER_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_gui_extra/juce_gui_extra.cpp b/JuceLibraryCode/modules/juce_gui_extra/juce_gui_extra.cpp index 3b2eb94..a9fab23 100644 --- a/JuceLibraryCode/modules/juce_gui_extra/juce_gui_extra.cpp +++ b/JuceLibraryCode/modules/juce_gui_extra/juce_gui_extra.cpp @@ -86,6 +86,8 @@ namespace juce #include "code_editor/juce_CodeDocument.cpp" #include "code_editor/juce_CodeEditorComponent.cpp" #include "code_editor/juce_CPlusPlusCodeTokeniser.cpp" +#include "code_editor/juce_XMLCodeTokeniser.cpp" +#include "code_editor/juce_LuaCodeTokeniser.cpp" #include "misc/juce_BubbleMessageComponent.cpp" #include "misc/juce_ColourSelector.cpp" #include "misc/juce_KeyMappingEditorComponent.cpp" @@ -93,6 +95,7 @@ namespace juce #include "misc/juce_RecentlyOpenedFilesList.cpp" #include "misc/juce_SplashScreen.cpp" #include "misc/juce_SystemTrayIconComponent.cpp" +#include "misc/juce_LiveConstantEditor.cpp" } @@ -144,4 +147,9 @@ namespace juce #endif #endif +#if JUCE_WEB_BROWSER + bool WebBrowserComponent::pageAboutToLoad (const String&) { return true; } + void WebBrowserComponent::pageFinishedLoading (const String&) {} + void WebBrowserComponent::windowCloseRequest() {} +#endif } diff --git a/JuceLibraryCode/modules/juce_gui_extra/juce_gui_extra.h b/JuceLibraryCode/modules/juce_gui_extra/juce_gui_extra.h index aead733..36532f1 100644 --- a/JuceLibraryCode/modules/juce_gui_extra/juce_gui_extra.h +++ b/JuceLibraryCode/modules/juce_gui_extra/juce_gui_extra.h @@ -37,6 +37,16 @@ #define JUCE_WEB_BROWSER 1 #endif +/** Config: JUCE_ENABLE_LIVE_CONSTANT_EDITOR + This lets you turn on the JUCE_ENABLE_LIVE_CONSTANT_EDITOR support. See the documentation + for that macro for more details. +*/ +#ifndef JUCE_ENABLE_LIVE_CONSTANT_EDITOR + #if JUCE_DEBUG + #define JUCE_ENABLE_LIVE_CONSTANT_EDITOR 1 + #endif +#endif + //============================================================================= namespace juce { @@ -47,6 +57,8 @@ namespace juce #include "code_editor/juce_CodeTokeniser.h" #include "code_editor/juce_CPlusPlusCodeTokeniser.h" #include "code_editor/juce_CPlusPlusCodeTokeniserFunctions.h" +#include "code_editor/juce_XMLCodeTokeniser.h" +#include "code_editor/juce_LuaCodeTokeniser.h" #include "embedding/juce_ActiveXControlComponent.h" #include "embedding/juce_NSViewComponent.h" #include "embedding/juce_UIViewComponent.h" @@ -59,6 +71,7 @@ namespace juce #include "misc/juce_SplashScreen.h" #include "misc/juce_SystemTrayIconComponent.h" #include "misc/juce_WebBrowserComponent.h" +#include "misc/juce_LiveConstantEditor.h" } diff --git a/JuceLibraryCode/modules/juce_gui_extra/juce_module_info b/JuceLibraryCode/modules/juce_gui_extra/juce_module_info index f84e0a3..5bbabdc 100644 --- a/JuceLibraryCode/modules/juce_gui_extra/juce_module_info +++ b/JuceLibraryCode/modules/juce_gui_extra/juce_module_info @@ -1,7 +1,7 @@ { "id": "juce_gui_extra", "name": "JUCE extended GUI classes", - "version": "3.0.0", + "version": "3.0.1", "description": "Miscellaneous GUI classes for specialised tasks.", "website": "http://www.juce.com/juce", "license": "GPL/Commercial", diff --git a/JuceLibraryCode/modules/juce_gui_extra/misc/juce_ColourSelector.cpp b/JuceLibraryCode/modules/juce_gui_extra/misc/juce_ColourSelector.cpp index 1343b21..b41379d 100644 --- a/JuceLibraryCode/modules/juce_gui_extra/misc/juce_ColourSelector.cpp +++ b/JuceLibraryCode/modules/juce_gui_extra/misc/juce_ColourSelector.cpp @@ -73,7 +73,7 @@ public: ColourSpaceView (ColourSelector& cs, float& hue, float& sat, float& val, const int edgeSize) : owner (cs), h (hue), s (sat), v (val), lastHue (0.0f), edge (edgeSize) { - addAndMakeVisible (&marker); + addAndMakeVisible (marker); setMouseCursor (MouseCursor::CrosshairCursor); } @@ -199,7 +199,7 @@ public: HueSelectorComp (ColourSelector& cs, float& hue, const int edgeSize) : owner (cs), h (hue), edge (edgeSize) { - addAndMakeVisible (&marker); + addAndMakeVisible (marker); } void paint (Graphics& g) override diff --git a/JuceLibraryCode/modules/juce_gui_extra/misc/juce_KeyMappingEditorComponent.cpp b/JuceLibraryCode/modules/juce_gui_extra/misc/juce_KeyMappingEditorComponent.cpp index 6299abf..bfded7a 100644 --- a/JuceLibraryCode/modules/juce_gui_extra/misc/juce_KeyMappingEditorComponent.cpp +++ b/JuceLibraryCode/modules/juce_gui_extra/misc/juce_KeyMappingEditorComponent.cpp @@ -403,11 +403,11 @@ KeyMappingEditorComponent::KeyMappingEditorComponent (KeyPressMappingSet& mappin if (showResetToDefaultButton) { - addAndMakeVisible (&resetButton); + addAndMakeVisible (resetButton); resetButton.addListener (treeItem); } - addAndMakeVisible (&tree); + addAndMakeVisible (tree); tree.setColour (TreeView::backgroundColourId, findColour (backgroundColourId)); tree.setRootItemVisible (false); tree.setDefaultOpenness (true); diff --git a/JuceLibraryCode/modules/juce_gui_extra/misc/juce_LiveConstantEditor.cpp b/JuceLibraryCode/modules/juce_gui_extra/misc/juce_LiveConstantEditor.cpp new file mode 100644 index 0000000..38c9ab9 --- /dev/null +++ b/JuceLibraryCode/modules/juce_gui_extra/misc/juce_LiveConstantEditor.cpp @@ -0,0 +1,466 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. + + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 + + Details of these licenses can be found at: www.gnu.org/licenses + + 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. + + ------------------------------------------------------------------------------ + + To release a closed-source product which uses JUCE, commercial licenses are + available: visit www.juce.com for more information. + + ============================================================================== +*/ + +#if JUCE_ENABLE_LIVE_CONSTANT_EDITOR + +namespace LiveConstantEditor +{ + +//============================================================================== +class AllComponentRepainter : private Timer, + private DeletedAtShutdown +{ +public: + AllComponentRepainter() {} + + static AllComponentRepainter& getInstance() + { + static AllComponentRepainter* instance = new AllComponentRepainter(); + return *instance; + } + + void trigger() + { + if (! isTimerRunning()) + startTimer (100); + } + +private: + void timerCallback() override + { + stopTimer(); + + for (int i = TopLevelWindow::getNumTopLevelWindows(); --i >= 0;) + if (Component* c = TopLevelWindow::getTopLevelWindow(i)) + repaintAndResizeAllComps (c); + } + + static void repaintAndResizeAllComps (Component::SafePointer c) + { + if (c->isVisible()) + { + c->repaint(); + c->resized(); + + for (int i = c->getNumChildComponents(); --i >= 0;) + if (c != nullptr) + if (Component* child = c->getChildComponent(i)) + repaintAndResizeAllComps (child); + } + } +}; + +//============================================================================== +int64 parseInt (String s) +{ + s = s.retainCharacters ("0123456789abcdefABCDEFx"); + + if (s.startsWith ("0x")) + return s.substring(2).getHexValue64(); + + return s.getLargeIntValue(); +} + +double parseDouble (const String& s) +{ + return s.retainCharacters ("0123456789.eE-").getDoubleValue(); +} + +String intToString (int v, bool preferHex) { return preferHex ? "0x" + String::toHexString (v) : String (v); } +String intToString (int64 v, bool preferHex) { return preferHex ? "0x" + String::toHexString (v) : String (v); } + +//============================================================================== +LiveValueBase::LiveValueBase (const char* file, int line) + : sourceFile (file), sourceLine (line) +{ + name = File (sourceFile).getFileName() + " : " + String (sourceLine); +} + +LiveValueBase::~LiveValueBase() +{ +} + +//============================================================================== +LivePropertyEditorBase::LivePropertyEditorBase (LiveValueBase& v, CodeDocument& d) + : value (v), resetButton ("reset"), document (d), sourceEditor (document, &tokeniser), wasHex (false) +{ + setSize (600, 100); + + addAndMakeVisible (name); + addAndMakeVisible (resetButton); + addAndMakeVisible (valueEditor); + addAndMakeVisible (sourceEditor); + + findOriginalValueInCode(); + selectOriginalValue(); + + name.setFont (13.0f); + name.setText (v.name, dontSendNotification); + valueEditor.setText (v.getStringValue (wasHex), dontSendNotification); + valueEditor.addListener (this); + sourceEditor.setReadOnly (true); + resetButton.addListener (this); +} + +void LivePropertyEditorBase::paint (Graphics& g) +{ + g.setColour (Colours::white); + g.fillRect (getLocalBounds().removeFromBottom (1)); +} + +void LivePropertyEditorBase::resized() +{ + Rectangle r (getLocalBounds().reduced (0, 3).withTrimmedBottom (1)); + + Rectangle left (r.removeFromLeft (jmax (200, r.getWidth() / 3))); + + Rectangle top (left.removeFromTop (25)); + resetButton.setBounds (top.removeFromRight (35).reduced (0, 3)); + name.setBounds (top); + valueEditor.setBounds (left.removeFromTop (25)); + left.removeFromTop (2); + + if (customComp != nullptr) + customComp->setBounds (left); + + r.removeFromLeft (4); + sourceEditor.setBounds (r); +} + +void LivePropertyEditorBase::textEditorTextChanged (TextEditor&) +{ + applyNewValue (valueEditor.getText()); +} + +void LivePropertyEditorBase::buttonClicked (Button*) +{ + applyNewValue (value.getOriginalStringValue (wasHex)); +} + +void LivePropertyEditorBase::applyNewValue (const String& s) +{ + value.setStringValue (s); + + document.replaceSection (valueStart.getPosition(), valueEnd.getPosition(), value.getCodeValue (wasHex)); + document.clearUndoHistory(); + selectOriginalValue(); + + valueEditor.setText (s, dontSendNotification); + AllComponentRepainter::getInstance().trigger(); +} + +void LivePropertyEditorBase::selectOriginalValue() +{ + sourceEditor.selectRegion (valueStart, valueEnd); +} + +void LivePropertyEditorBase::findOriginalValueInCode() +{ + CodeDocument::Position pos (document, value.sourceLine, 0); + String line (pos.getLineText()); + String::CharPointerType p (line.getCharPointer()); + + p = CharacterFunctions::find (p, CharPointer_ASCII ("JUCE_LIVE_CONSTANT")); + + if (p.isEmpty()) + { + // Not sure how this would happen - some kind of mix-up between source code and line numbers.. + jassertfalse; + return; + } + + p += (int) (sizeof ("JUCE_LIVE_CONSTANT") - 1); + p = p.findEndOfWhitespace(); + + if (! CharacterFunctions::find (p, CharPointer_ASCII ("JUCE_LIVE_CONSTANT")).isEmpty()) + { + // Aargh! You've added two JUCE_LIVE_CONSTANT macros on the same line! + // They're identified by their line number, so you must make sure each + // one goes on a separate line! + jassertfalse; + } + + if (p.getAndAdvance() == '(') + { + String::CharPointerType start (p), end (p); + + int depth = 1; + + while (! end.isEmpty()) + { + const juce_wchar c = end.getAndAdvance(); + + if (c == '(') ++depth; + if (c == ')') --depth; + + if (depth == 0) + { + --end; + break; + } + } + + if (end > start) + { + valueStart = CodeDocument::Position (document, value.sourceLine, (int) (start - line.getCharPointer())); + valueEnd = CodeDocument::Position (document, value.sourceLine, (int) (end - line.getCharPointer())); + + valueStart.setPositionMaintained (true); + valueEnd.setPositionMaintained (true); + + wasHex = String (start, end).containsIgnoreCase ("0x"); + } + } +} + +//============================================================================== +class ValueListHolderComponent : public Component +{ +public: + ValueListHolderComponent (ValueList& l) : valueList (l) + { + setVisible (true); + } + + void addItem (int width, LiveValueBase& v, CodeDocument& doc) + { + addAndMakeVisible (editors.add (v.createPropertyComponent (doc))); + layout (width); + } + + void layout (int width) + { + setSize (width, editors.size() * itemHeight); + resized(); + } + + void resized() override + { + Rectangle r (getLocalBounds().reduced (2, 0)); + + for (int i = 0; i < editors.size(); ++i) + editors.getUnchecked(i)->setBounds (r.removeFromTop (itemHeight)); + } + + enum { itemHeight = 120 }; + + ValueList& valueList; + OwnedArray editors; +}; + +//============================================================================== +class ValueList::EditorWindow : public DocumentWindow, + private DeletedAtShutdown +{ +public: + EditorWindow (ValueList& list) + : DocumentWindow ("Live Values", Colours::lightgrey, DocumentWindow::closeButton) + { + setLookAndFeel (&lookAndFeel); + setUsingNativeTitleBar (true); + + viewport.setViewedComponent (new ValueListHolderComponent (list), true); + viewport.setSize (700, 600); + viewport.setScrollBarsShown (true, false); + + setContentNonOwned (&viewport, true); + setResizable (true, false); + setResizeLimits (500, 400, 10000, 10000); + centreWithSize (getWidth(), getHeight()); + setVisible (true); + } + + void closeButtonPressed() override + { + setVisible (false); + } + + void updateItems (ValueList& list) + { + if (ValueListHolderComponent* l = dynamic_cast (viewport.getViewedComponent())) + { + while (l->getNumChildComponents() < list.values.size()) + { + if (LiveValueBase* v = list.values [l->getNumChildComponents()]) + l->addItem (viewport.getMaximumVisibleWidth(), *v, list.getDocument (v->sourceFile)); + else + break; + } + + setVisible (true); + } + } + + void resized() override + { + DocumentWindow::resized(); + + if (ValueListHolderComponent* l = dynamic_cast (viewport.getViewedComponent())) + l->layout (viewport.getMaximumVisibleWidth()); + } + + Viewport viewport; + LookAndFeel_V3 lookAndFeel; +}; + +//============================================================================== +ValueList::ValueList() {} +ValueList::~ValueList() {} + +ValueList& ValueList::getInstance() +{ + static ValueList* i = new ValueList(); + return *i; +} + +void ValueList::addValue (LiveValueBase* v) +{ + values.add (v); + triggerAsyncUpdate(); +} + +void ValueList::handleAsyncUpdate() +{ + if (editorWindow == nullptr) + editorWindow = new EditorWindow (*this); + + editorWindow->updateItems (*this); +} + +CodeDocument& ValueList::getDocument (const File& file) +{ + const int index = documentFiles.indexOf (file.getFullPathName()); + + if (index >= 0) + return *documents.getUnchecked (index); + + CodeDocument* doc = documents.add (new CodeDocument()); + documentFiles.add (file); + doc->replaceAllContent (file.loadFileAsString()); + doc->clearUndoHistory(); + return *doc; +} + +//============================================================================== +struct ColourEditorComp : public Component, + private ChangeListener +{ + ColourEditorComp (LivePropertyEditorBase& e) : editor (e) + { + setMouseCursor (MouseCursor::PointingHandCursor); + } + + Colour getColour() const + { + return Colour ((int) parseInt (editor.value.getStringValue (false))); + } + + void paint (Graphics& g) override + { + g.fillCheckerBoard (getLocalBounds(), 6, 6, + Colour (0xffdddddd).overlaidWith (getColour()), + Colour (0xffffffff).overlaidWith (getColour())); + } + + void mouseDown (const MouseEvent&) override + { + ColourSelector* colourSelector = new ColourSelector(); + colourSelector->setName ("Colour"); + colourSelector->setCurrentColour (getColour()); + colourSelector->addChangeListener (this); + colourSelector->setColour (ColourSelector::backgroundColourId, Colours::transparentBlack); + colourSelector->setSize (300, 400); + + CallOutBox::launchAsynchronously (colourSelector, getScreenBounds(), nullptr); + } + + void changeListenerCallback (ChangeBroadcaster* source) override + { + if (ColourSelector* cs = dynamic_cast (source)) + editor.applyNewValue (getAsString (cs->getCurrentColour(), true)); + + repaint(); + } + + LivePropertyEditorBase& editor; +}; + +Component* createColourEditor (LivePropertyEditorBase& editor) +{ + return new ColourEditorComp (editor); +} + +//============================================================================== +class SliderComp : public Component, + private Slider::Listener +{ +public: + SliderComp (LivePropertyEditorBase& e, bool useFloat) + : editor (e), isFloat (useFloat) + { + slider.setTextBoxStyle (Slider::NoTextBox, true, 0, 0); + addAndMakeVisible (slider); + updateRange(); + slider.addListener (this); + } + + void updateRange() + { + double v = isFloat ? parseDouble (editor.value.getStringValue (false)) + : (double) parseInt (editor.value.getStringValue (false)); + + double range = isFloat ? 10 : 100; + + slider.setRange (v - range, v + range); + slider.setValue (v, dontSendNotification); + } + +private: + LivePropertyEditorBase& editor; + Slider slider; + bool isFloat; + + void sliderValueChanged (Slider*) + { + editor.applyNewValue (isFloat ? getAsString ((double) slider.getValue(), editor.wasHex) + : getAsString ((int64) slider.getValue(), editor.wasHex)); + + } + + void sliderDragStarted (Slider*) {} + void sliderDragEnded (Slider*) { updateRange(); } + + void resized() + { + slider.setBounds (getLocalBounds().removeFromTop (25)); + } +}; + + +Component* createIntegerSlider (LivePropertyEditorBase& editor) { return new SliderComp (editor, false); } +Component* createFloatSlider (LivePropertyEditorBase& editor) { return new SliderComp (editor, true); } + +} + +#endif diff --git a/JuceLibraryCode/modules/juce_gui_extra/misc/juce_LiveConstantEditor.h b/JuceLibraryCode/modules/juce_gui_extra/misc/juce_LiveConstantEditor.h new file mode 100644 index 0000000..db0e728 --- /dev/null +++ b/JuceLibraryCode/modules/juce_gui_extra/misc/juce_LiveConstantEditor.h @@ -0,0 +1,298 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. + + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 + + Details of these licenses can be found at: www.gnu.org/licenses + + 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. + + ------------------------------------------------------------------------------ + + To release a closed-source product which uses JUCE, commercial licenses are + available: visit www.juce.com for more information. + + ============================================================================== +*/ + +#ifndef JUCE_LIVECONSTANTEDITOR_H_INCLUDED +#define JUCE_LIVECONSTANTEDITOR_H_INCLUDED + +#if JUCE_ENABLE_LIVE_CONSTANT_EDITOR && ! DOXYGEN + +//============================================================================== +/** You can safely ignore all the stuff in this namespace - it's a bunch of boilerplate + code used to implement the JUCE_LIVE_CONSTANT functionality. +*/ +namespace LiveConstantEditor +{ + int64 parseInt (String); + double parseDouble (const String&); + String intToString (int, bool preferHex); + String intToString (int64, bool preferHex); + + template + static void setFromString (Type& v, const String& s) { v = static_cast (s); } + inline void setFromString (char& v, const String& s) { v = (char) parseInt (s); } + inline void setFromString (unsigned char& v, const String& s) { v = (unsigned char) parseInt (s); } + inline void setFromString (short& v, const String& s) { v = (short) parseInt (s); } + inline void setFromString (unsigned short& v, const String& s) { v = (unsigned short) parseInt (s); } + inline void setFromString (int& v, const String& s) { v = (int) parseInt (s); } + inline void setFromString (unsigned int& v, const String& s) { v = (unsigned int) parseInt (s); } + inline void setFromString (long& v, const String& s) { v = (long) parseInt (s); } + inline void setFromString (unsigned long& v, const String& s) { v = (unsigned long) parseInt (s); } + inline void setFromString (int64& v, const String& s) { v = (int64) parseInt (s); } + inline void setFromString (uint64& v, const String& s) { v = (uint64) parseInt (s); } + inline void setFromString (double& v, const String& s) { v = parseDouble (s); } + inline void setFromString (float& v, const String& s) { v = (float) parseDouble (s); } + inline void setFromString (String& v, const String& s) { v = s; } + inline void setFromString (Colour& v, const String& s) { v = Colour ((int) parseInt (s)); } + + template + inline String getAsString (const Type& v, bool) { return String (v); } + inline String getAsString (char v, bool preferHex) { return intToString ((int) v, preferHex); } + inline String getAsString (unsigned char v, bool preferHex) { return intToString ((int) v, preferHex); } + inline String getAsString (short v, bool preferHex) { return intToString ((int) v, preferHex); } + inline String getAsString (unsigned short v, bool preferHex) { return intToString ((int) v, preferHex); } + inline String getAsString (int v, bool preferHex) { return intToString ((int) v, preferHex); } + inline String getAsString (unsigned int v, bool preferHex) { return intToString ((int) v, preferHex); } + inline String getAsString (int64 v, bool preferHex) { return intToString ((int64) v, preferHex); } + inline String getAsString (uint64 v, bool preferHex) { return intToString ((int64) v, preferHex); } + inline String getAsString (Colour v, bool) { return intToString ((int) v.getARGB(), true); } + + template + inline String getAsCode (Type& v, bool preferHex) { return getAsString (v, preferHex); } + inline String getAsCode (Colour v, bool) { return "Colour (0x" + String::toHexString ((int) v.getARGB()).paddedLeft ('0', 8) + ")"; } + inline String getAsCode (const String& v, bool) { return "\"" + v + "\""; } + inline String getAsCode (const char* v, bool) { return getAsCode (String (v), false); } + + template + inline const char* castToCharPointer (const Type&) { return ""; } + inline const char* castToCharPointer (const String& s) { return s.toRawUTF8(); } + + struct LivePropertyEditorBase; + + //============================================================================== + struct JUCE_API LiveValueBase + { + LiveValueBase (const char* file, int line); + virtual ~LiveValueBase(); + + virtual LivePropertyEditorBase* createPropertyComponent (CodeDocument&) = 0; + virtual String getStringValue (bool preferHex) const = 0; + virtual String getCodeValue (bool preferHex) const = 0; + virtual void setStringValue (const String&) = 0; + virtual String getOriginalStringValue (bool preferHex) const = 0; + + String name, sourceFile; + int sourceLine; + + JUCE_DECLARE_NON_COPYABLE (LiveValueBase) + }; + + //============================================================================== + struct JUCE_API LivePropertyEditorBase : public Component, + private TextEditor::Listener, + private ButtonListener + { + LivePropertyEditorBase (LiveValueBase&, CodeDocument&); + + void paint (Graphics&) override; + void resized() override; + void textEditorTextChanged (TextEditor&) override; + void buttonClicked (Button*) override; + + void applyNewValue (const String&); + void selectOriginalValue(); + void findOriginalValueInCode(); + + LiveValueBase& value; + Label name; + TextEditor valueEditor; + TextButton resetButton; + CodeDocument& document; + CPlusPlusCodeTokeniser tokeniser; + CodeEditorComponent sourceEditor; + CodeDocument::Position valueStart, valueEnd; + ScopedPointer customComp; + bool wasHex; + + JUCE_DECLARE_NON_COPYABLE (LivePropertyEditorBase) + }; + + //============================================================================== + Component* createColourEditor (LivePropertyEditorBase&); + Component* createIntegerSlider (LivePropertyEditorBase&); + Component* createFloatSlider (LivePropertyEditorBase&); + + template struct CustomEditor { static Component* create (LivePropertyEditorBase&) { return nullptr; } }; + template<> struct CustomEditor { static Component* create (LivePropertyEditorBase& e) { return createIntegerSlider (e); } }; + template<> struct CustomEditor { static Component* create (LivePropertyEditorBase& e) { return createIntegerSlider (e); } }; + template<> struct CustomEditor { static Component* create (LivePropertyEditorBase& e) { return createIntegerSlider (e); } }; + template<> struct CustomEditor { static Component* create (LivePropertyEditorBase& e) { return createIntegerSlider (e); } }; + template<> struct CustomEditor { static Component* create (LivePropertyEditorBase& e) { return createIntegerSlider (e); } }; + template<> struct CustomEditor { static Component* create (LivePropertyEditorBase& e) { return createIntegerSlider (e); } }; + template<> struct CustomEditor { static Component* create (LivePropertyEditorBase& e) { return createIntegerSlider (e); } }; + template<> struct CustomEditor { static Component* create (LivePropertyEditorBase& e) { return createIntegerSlider (e); } }; + template<> struct CustomEditor { static Component* create (LivePropertyEditorBase& e) { return createFloatSlider (e); } }; + template<> struct CustomEditor { static Component* create (LivePropertyEditorBase& e) { return createFloatSlider (e); } }; + template<> struct CustomEditor { static Component* create (LivePropertyEditorBase& e) { return createColourEditor (e); } }; + + template + struct LivePropertyEditor : public LivePropertyEditorBase + { + template + LivePropertyEditor (ValueType& v, CodeDocument& d) : LivePropertyEditorBase (v, d) + { + addAndMakeVisible (customComp = CustomEditor::create (*this)); + } + }; + + //============================================================================== + template + struct LiveValue : public LiveValueBase + { + LiveValue (const char* file, int line, const Type& initialValue) + : LiveValueBase (file, line), value (initialValue), originalValue (initialValue) + {} + + operator Type() const noexcept { return value; } + operator const char*() const { return castToCharPointer (value); } + + LivePropertyEditorBase* createPropertyComponent (CodeDocument& doc) override + { + return new LivePropertyEditor (*this, doc); + } + + String getStringValue (bool preferHex) const override { return getAsString (value, preferHex); } + String getCodeValue (bool preferHex) const override { return getAsCode (value, preferHex); } + String getOriginalStringValue (bool preferHex) const override { return getAsString (originalValue, preferHex); } + void setStringValue (const String& s) override { setFromString (value, s); } + + Type value, originalValue; + + JUCE_DECLARE_NON_COPYABLE (LiveValue) + }; + + //============================================================================== + class JUCE_API ValueList : private AsyncUpdater, + private DeletedAtShutdown + { + public: + ValueList(); + ~ValueList(); + + static ValueList& getInstance(); + + template + LiveValue& getValue (const char* file, int line, const Type& initialValue) + { + const ScopedLock sl (lock); + typedef LiveValue ValueType; + + for (int i = 0; i < values.size(); ++i) + { + LiveValueBase* v = values.getUnchecked(i); + + if (v->sourceLine == line && v->sourceFile == file) + return *static_cast (v); + } + + ValueType* v = new ValueType (file, line, initialValue); + addValue (v); + return *v; + } + + private: + OwnedArray values; + OwnedArray documents; + Array documentFiles; + class EditorWindow; + friend class EditorWindow; + friend struct ContainerDeletePolicy; + Component::SafePointer editorWindow; + CriticalSection lock; + + CodeDocument& getDocument (const File&); + void addValue (LiveValueBase*); + void handleAsyncUpdate() override; + }; + + template + inline LiveValue& getValue (const char* file, int line, const Type& initialValue) + { + return ValueList::getInstance().getValue (file, line, initialValue); + } + + inline LiveValue& getValue (const char* file, int line, const char* initialValue) + { + return getValue (file, line, String (initialValue)); + } +} + +#endif + +//============================================================================== +#if JUCE_ENABLE_LIVE_CONSTANT_EDITOR || DOXYGEN + /** + This macro wraps a primitive constant value in some cunning boilerplate code that allows + its value to be interactively tweaked in a popup window while your application is running. + + In a release build, this macro disappears and is replaced by only the constant that it + wraps, but if JUCE_ENABLE_LIVE_CONSTANT_EDITOR is enabled, it injects a class wrapper + that automatically pops-up a window containing an editor that allows the value to be + tweaked at run-time. The editor window will also force all visible components to be + resized and repainted whenever a value is changed, so that if you use this to wrap + a colour or layout parameter, you'll be able to immediately see the effects of changing it. + + The editor will also load the original source-file that contains each JUCE_LIVE_CONSTANT + macro, and will display a preview of the modified source code as you adjust the values. + + Things to note: + + - Only one of these per line! The __FILE__ and __LINE__ macros are used to identify + the value, so things will get confused if you have more than one per line + - Obviously because it needs to load the source code based on the __FILE__ macro, + it'll only work if the source files are stored locally in the same location as they + were when you compiled the program. + - It's only designed to cope with simple types: primitives, string literals, and + the Colour class, so if you try using it for other classes or complex expressions, + good luck! + - The editor window will get popped up whenever a new value is used for the first + time. You can close the window, but there's no way to get it back without restarting + the app! + + e.g. in this example the colours, font size, and text used in the paint method can + all be adjusted live: + @code + void MyComp::paint (Graphics& g) override + { + g.fillAll (JUCE_LIVE_CONSTANT (Colour (0xffddddff))); + + Colour fontColour = JUCE_LIVE_CONSTANT (Colour (0xff005500)); + float fontSize = JUCE_LIVE_CONSTANT (16.0f); + + g.setColour (fontColour); + g.setFont (fontSize); + + g.drawFittedText (JUCE_LIVE_CONSTANT ("Hello world!"), + getLocalBounds(), Justification::centred, 2); + } + @endcode + */ + #define JUCE_LIVE_CONSTANT(initialValue) \ + (LiveConstantEditor::getValue (__FILE__, __LINE__ - 1, initialValue)) +#else + #define JUCE_LIVE_CONSTANT(initialValue) \ + (initialValue) +#endif + + +#endif // JUCE_LIVECONSTANTEDITOR_H_INCLUDED diff --git a/JuceLibraryCode/modules/juce_gui_extra/misc/juce_WebBrowserComponent.h b/JuceLibraryCode/modules/juce_gui_extra/misc/juce_WebBrowserComponent.h index c6f5925..6afad60 100644 --- a/JuceLibraryCode/modules/juce_gui_extra/misc/juce_WebBrowserComponent.h +++ b/JuceLibraryCode/modules/juce_gui_extra/misc/juce_WebBrowserComponent.h @@ -93,6 +93,11 @@ public: /** This callback happens when the browser has finished loading a page. */ virtual void pageFinishedLoading (const String& url); + /** This callback occurs when a script or other activity in the browser asks for + the window to be closed. + */ + virtual void windowCloseRequest(); + //============================================================================== /** @internal */ void paint (Graphics&) override; diff --git a/JuceLibraryCode/modules/juce_gui_extra/native/juce_android_WebBrowserComponent.cpp b/JuceLibraryCode/modules/juce_gui_extra/native/juce_android_WebBrowserComponent.cpp index 971715d..dcf786d 100644 --- a/JuceLibraryCode/modules/juce_gui_extra/native/juce_android_WebBrowserComponent.cpp +++ b/JuceLibraryCode/modules/juce_gui_extra/native/juce_android_WebBrowserComponent.cpp @@ -60,14 +60,14 @@ void WebBrowserComponent::stop() void WebBrowserComponent::goBack() { - lastURL = String::empty; + lastURL.clear(); blankPageShown = false; } void WebBrowserComponent::goForward() { - lastURL = String::empty; + lastURL.clear(); } @@ -90,7 +90,7 @@ void WebBrowserComponent::reloadLastURL() if (lastURL.isNotEmpty()) { goToURL (lastURL, &lastHeaders, &lastPostData); - lastURL = String::empty; + lastURL.clear(); } } @@ -107,6 +107,3 @@ void WebBrowserComponent::visibilityChanged() { checkWindowAssociation(); } - -bool WebBrowserComponent::pageAboutToLoad (const String&) { return true; } -void WebBrowserComponent::pageFinishedLoading (const String&) {} diff --git a/JuceLibraryCode/modules/juce_gui_extra/native/juce_linux_WebBrowserComponent.cpp b/JuceLibraryCode/modules/juce_gui_extra/native/juce_linux_WebBrowserComponent.cpp index d9212e6..1a36b74 100644 --- a/JuceLibraryCode/modules/juce_gui_extra/native/juce_linux_WebBrowserComponent.cpp +++ b/JuceLibraryCode/modules/juce_gui_extra/native/juce_linux_WebBrowserComponent.cpp @@ -65,14 +65,14 @@ void WebBrowserComponent::stop() void WebBrowserComponent::goBack() { - lastURL = String::empty; + lastURL.clear(); blankPageShown = false; } void WebBrowserComponent::goForward() { - lastURL = String::empty; + lastURL.clear(); } @@ -95,7 +95,7 @@ void WebBrowserComponent::reloadLastURL() if (lastURL.isNotEmpty()) { goToURL (lastURL, &lastHeaders, &lastPostData); - lastURL = String::empty; + lastURL.clear(); } } @@ -112,6 +112,3 @@ void WebBrowserComponent::visibilityChanged() { checkWindowAssociation(); } - -bool WebBrowserComponent::pageAboutToLoad (const String&) { return true; } -void WebBrowserComponent::pageFinishedLoading (const String&) {} diff --git a/JuceLibraryCode/modules/juce_gui_extra/native/juce_mac_WebBrowserComponent.mm b/JuceLibraryCode/modules/juce_gui_extra/native/juce_mac_WebBrowserComponent.mm index a7997bb..286ff80 100644 --- a/JuceLibraryCode/modules/juce_gui_extra/native/juce_mac_WebBrowserComponent.mm +++ b/JuceLibraryCode/modules/juce_gui_extra/native/juce_mac_WebBrowserComponent.mm @@ -33,6 +33,7 @@ struct DownloadClickDetectorClass : public ObjCClass addMethod (@selector (webView:decidePolicyForNavigationAction:request:frame:decisionListener:), decidePolicyForNavigationAction, "v@:@@@@@"); addMethod (@selector (webView:didFinishLoadForFrame:), didFinishLoadForFrame, "v@:@@"); + addMethod (@selector (webView:willCloseFrame:), willCloseFrame, "v@:@@"); registerClass(); } @@ -60,6 +61,11 @@ private: getOwner (self)->pageFinishedLoading (nsStringToJuce ([url absoluteString])); } } + + static void willCloseFrame (id self, SEL, WebView*, WebFrame*) + { + getOwner (self)->windowCloseRequest(); + } }; #else @@ -279,14 +285,14 @@ void WebBrowserComponent::stop() void WebBrowserComponent::goBack() { - lastURL = String::empty; + lastURL.clear(); blankPageShown = false; browser->goBack(); } void WebBrowserComponent::goForward() { - lastURL = String::empty; + lastURL.clear(); browser->goForward(); } @@ -328,7 +334,7 @@ void WebBrowserComponent::reloadLastURL() if (lastURL.isNotEmpty()) { goToURL (lastURL, &lastHeaders, &lastPostData); - lastURL = String::empty; + lastURL.clear(); } } @@ -346,6 +352,3 @@ void WebBrowserComponent::visibilityChanged() { checkWindowAssociation(); } - -bool WebBrowserComponent::pageAboutToLoad (const String&) { return true; } -void WebBrowserComponent::pageFinishedLoading (const String&) {} diff --git a/JuceLibraryCode/modules/juce_gui_extra/native/juce_win32_WebBrowserComponent.cpp b/JuceLibraryCode/modules/juce_gui_extra/native/juce_win32_WebBrowserComponent.cpp index 3b40c50..76f4c1d 100644 --- a/JuceLibraryCode/modules/juce_gui_extra/native/juce_win32_WebBrowserComponent.cpp +++ b/JuceLibraryCode/modules/juce_gui_extra/native/juce_win32_WebBrowserComponent.cpp @@ -158,6 +158,16 @@ private: owner.pageFinishedLoading (getStringFromVariant (pDispParams->rgvarg[0].pvarVal)); return S_OK; } + else if (dispIdMember == DISPID_WINDOWCLOSING) + { + owner.windowCloseRequest(); + + // setting this bool tells the browser to ignore the event - we'll handle it. + if (pDispParams->cArgs > 0 && pDispParams->rgvarg[0].vt == (VT_BYREF | VT_BOOL)) + *pDispParams->rgvarg[0].pboolVal = VARIANT_TRUE; + + return S_OK; + } return E_NOTIMPL; } @@ -225,7 +235,7 @@ void WebBrowserComponent::stop() void WebBrowserComponent::goBack() { - lastURL = String::empty; + lastURL.clear(); blankPageShown = false; if (browser->browser != nullptr) @@ -234,7 +244,7 @@ void WebBrowserComponent::goBack() void WebBrowserComponent::goForward() { - lastURL = String::empty; + lastURL.clear(); if (browser->browser != nullptr) browser->browser->GoForward(); @@ -287,7 +297,7 @@ void WebBrowserComponent::reloadLastURL() if (lastURL.isNotEmpty()) { goToURL (lastURL, &lastHeaders, &lastPostData); - lastURL = String::empty; + lastURL.clear(); } } @@ -305,6 +315,3 @@ void WebBrowserComponent::visibilityChanged() { checkWindowAssociation(); } - -bool WebBrowserComponent::pageAboutToLoad (const String&) { return true; } -void WebBrowserComponent::pageFinishedLoading (const String&) {}