Conflicts: examples/SimpleFFTExample/SimpleFFTExample.jucer modules/juce_audio_processors/format_types/juce_LADSPAPluginFormat.cpp modules/juce_gui_basics/misc/juce_JUCESplashScreen.cpp modules/juce_gui_basics/misc/juce_JUCESplashScreen.hpull/8/head
| @@ -1,303 +1,358 @@ | |||
| JUCE breaking changes | |||
| ===================== | |||
| Develop Branch | |||
| ============= | |||
| Change | |||
| ------ | |||
| The String (bool) constructor and operator<< (String&, bool) have been | |||
| explicitly deleted. | |||
| Possible Issues | |||
| --------------- | |||
| Previous code which relied on an implicit bool to int type conversion to | |||
| produce a String will not compile. | |||
| Workaround | |||
| ---------- | |||
| Cast your bool to an integer to generate a string representation of it. | |||
| Rationale | |||
| --------- | |||
| Letting things implicitly convert to bool to produce a String opens the door to | |||
| all kinds of nasty type conversion edge cases. Furthermore, before this change, | |||
| MacOS would automatically convert bools to ints but this wouldn't occur on | |||
| different platform. Now the behaviour is consistent across all operating | |||
| systems supported by JUCE. | |||
| Change | |||
| ------ | |||
| The writeAsJSON virtual method of the DynamicObject class requires an | |||
| additional parameter, maximumDecimalPlaces, to specify the maximum precision of | |||
| floating point numbers. | |||
| Possible Issues | |||
| --------------- | |||
| Classes which inherit from DynamicObject and override this method will need to | |||
| update their method signature. | |||
| Workaround | |||
| ---------- | |||
| Your custom DynamicObject class can choose to ignore the additional parameter | |||
| if you don't wish to support this behaviour. | |||
| Rationale | |||
| --------- | |||
| When serialising the results of calculations to JSON the rounding of floating | |||
| point numbers can result in numbers with 17 significant figures where only a | |||
| few are required. This change to DynamicObject is required to support | |||
| truncating those numbers. | |||
| Version 5.1.0 | |||
| ============= | |||
| Change | |||
| ------ | |||
| The option to set the C++ language standard is now located in the project | |||
| settings instead of the build configuration settings. | |||
| Possible Issues | |||
| --------------- | |||
| Projects that had a specific verison of the C++ language standard set for | |||
| exporter build configurations will instead use the default (C++11) when | |||
| re-saving with the new Projucer. | |||
| Workaround | |||
| ---------- | |||
| Change the "C++ Language Standard" setting in the main project settings to the | |||
| required version - the Projucer will add this value to the exported project as | |||
| a compiler flag when saving exporters. | |||
| Rationale | |||
| --------- | |||
| Having a different C++ language standard option for each build configuration | |||
| was unnecessary and was not fully implemented for all exporters. Changing it to | |||
| a per-project settings means that the preference will propagate to all | |||
| exporters and only needs to be set in one place. | |||
| Change | |||
| ------ | |||
| PopupMenus now scale according to the AffineTransform and scaling factor of | |||
| their target components. | |||
| Possible Issues | |||
| --------------- | |||
| Developers who have manually scaled their PopupMenus to fit the scaling factor | |||
| of the parent UI will now have the scaling applied two times in a row. | |||
| Workaround | |||
| ---------- | |||
| 1. Do not apply your own manual scaling to make your popups match the UI | |||
| scaling | |||
| or | |||
| 2. Override the Look&Feel method | |||
| PopupMenu::LookAndFeelMethods::shouldPopupMenuScaleWithTargetComponent and | |||
| return false. See | |||
| https://github.com/WeAreROLI/JUCE/blob/c288c94c2914af20f36c03ca9c5401fcb555e4e9/modules/juce_gui_basics/menus/juce_PopupMenu.h#725 | |||
| Rationale | |||
| --------- | |||
| Previously, PopupMenus would not scale if the GUI of the target component (or | |||
| any of it’s parents) were scaled. The only way to scale PopupMenus was via the | |||
| global scaling factor. This had several drawbacks as the global scaling factor | |||
| would scale everything. This was especially problematic in plug-in editors. | |||
| Change | |||
| ------ | |||
| Removed the setSecurityFlags() method from the Windows implementation of | |||
| WebInputStream as it disabled HTTPS security features. | |||
| Possible Issues | |||
| --------------- | |||
| Any code previously relying on connections to insecure webpages succeeding will | |||
| no longer work. | |||
| Workaround | |||
| ---------- | |||
| Check network connectivity on Windows and re-write any code that relied on | |||
| insecure connections. | |||
| Rationale | |||
| --------- | |||
| The previous behaviour resulted in network connections on Windows having all | |||
| the HTTPS security features disabled, exposing users to network attacks. HTTPS | |||
| connections on Windows are now secure and will fail when connecting to an | |||
| insecure web address. | |||
| Change | |||
| ------ | |||
| Pointer arithmetic on a pointer will have the same result regardless if it is | |||
| wrapped in JUCE's Atomic class or not. | |||
| Possible Issues | |||
| --------------- | |||
| Any code using pointer arithmetic on Atomic<T*> will now have a different | |||
| result leading to undefined behaviour or crashes. | |||
| Workaround | |||
| ---------- | |||
| Re-write your code in a way that it does not depend on your pointer being | |||
| wrapped in JUCE's Atomic or not. See rationale. | |||
| Rationale | |||
| --------- | |||
| Before this change, pointer arithmetic with JUCE's Atomic type would yield | |||
| confusing results. For example, the following code would assert before this | |||
| change: | |||
| int* a; Atomic<int*> b; | |||
| jassert (++a == ++b); | |||
| Pointer a in the above code would be advanced by sizeof(int) whereas the JUCE's | |||
| Atomic always advances it's underlying pointer by a single byte. The same is | |||
| true for operator+=/operator-= and operator--. The difference in behaviour is | |||
| confusing and unintuitive. Furthermore, this aligns JUCE's Atomic type with | |||
| std::atomic. | |||
| Version 4.3.1 | |||
| ============= | |||
| Change | |||
| ------ | |||
| JUCE has changed the way native VST3/AudioUnit parameter ids are calculated. | |||
| Possible Issues | |||
| --------------- | |||
| DAW projects with automation data written by an AudioUnit or VST3 plug-in built | |||
| with pre JUCE 4.3.1 versions will load incorrectly when opened by an AudioUnit | |||
| or VST3 built with JUCE versions 4.3.1 and later. Plug-ins using | |||
| JUCE_FORCE_USE_LEGACY_IDS are not affected. | |||
| Workaround | |||
| ---------- | |||
| Disable JUCE_USE_STUDIO_ONE_COMPATIBLE_PARAMETERS in the | |||
| juce_audio_plugin_client module config page in the Projucer. For new plug-ins, | |||
| be sure to use the default value for this property. | |||
| Rationale | |||
| -------- | |||
| JUCE needs to convert between its own JUCE parameter id format (strings) to the | |||
| native parameter id formats of the various plug-in backends. For VST3 and | |||
| AudioUnits, JUCE uses a hash function to generate a numeric id. However, some | |||
| VST3/AudioUnit hosts (specifically Studio One) have a bug that ignore any | |||
| parameters that have a negative parameter id. Therefore, the hash function for | |||
| VST3/AudioUnits needed to be changed to only return positive-valued hashes. | |||
| Version 4.3.0 | |||
| ============= | |||
| Change | |||
| ------ | |||
| A revised multi-bus API was released which supersedes the previously flawed | |||
| multi-bus API - JUCE versions 4.0.0 - 4.2.4 (inclusive). | |||
| Possible Issues | |||
| --------------- | |||
| If you have developed a plug-in with JUCE versions 4.0.0 - 4.2.4 (inclusive), | |||
| then you will need to update your plug-in to the new multi-bus API. Pre JUCE | |||
| 4.0.0 plug-ins are not affected apart from other breaking changes listed in | |||
| this document. | |||
| Woraround | |||
| --------- | |||
| None. | |||
| Rationale | |||
| -------- | |||
| A flawed multi-bus API was introduced with JUCE versions 4.0.0 up until version | |||
| 4.2.4 (inclusive) which was not API compatible with pre JUCE 4 plug-ins. JUCE | |||
| 4.3.0 releases a revised multi-bus API which restores pre JUCE 4 API | |||
| compatibility. However, the new multi-bus API is not compatible with the flawed | |||
| multi-bus API (JUCE version 4.0.0 - 4.2.4). | |||
| Change | |||
| ------ | |||
| JUCE now generates the AAX plug-in bus layout configuration id independent from | |||
| the position as it appears in the Projucer’s legacy "Channel layout | |||
| configuration" field. | |||
| Possible Issues | |||
| --------------- | |||
| ProTools projects generated with a < 4.3.0 JUCE versions of your plug-in, may | |||
| load the incorrect bus configuration when upgrading your plug-in to >= 4.3.0 | |||
| versions of JUCE. | |||
| Workaround | |||
| ---------- | |||
| Implement AudioProcessor’s getAAXPluginIDForMainBusConfig callback to manually | |||
| override which AAX plug-in id is associated to a specific bus layout of your | |||
| plug-in. This workaround is only necessary if you have released your plug-in | |||
| built with a version previous to JUCE 4.3.0. | |||
| Rationale | |||
| -------- | |||
| The new multi-bus API offers more features, flexibility and accuracy in | |||
| specifying bus layouts which cannot be expressed by the Projucer’s legacy | |||
| "Channel layout configuration" field. The native plug-in format backends use | |||
| the new multi-bus callback APIs to negotiate channel layouts with the host - | |||
| including the AAX plug-in ids assigned to specific bus layouts. With the | |||
| callback API, there is no notion of an order in which the channel | |||
| configurations appear - as was the case with the legacy "Channel layout | |||
| configuration" field - and therefore cannot be used to generate the AAX plug-in | |||
| id. To remain backward compatible to pre JUCE 4.0.0 plug-ins, JUCE does | |||
| transparently convert the legacy "Channel layout configuration" field to the | |||
| new callback based multi-bus API, but this does not take the order into account | |||
| in which the channel configurations appear in the legacy "Channel layout | |||
| configuration" field. | |||
| Version 4.2.1 | |||
| ============= | |||
| Change | |||
| ------ | |||
| JUCE now uses the paramID property used in AudioProcessorParameterWithID to | |||
| uniquely identify parameters to the host. | |||
| Possible Issues | |||
| --------------- | |||
| DAW projects with automation data written by an audio plug-in built with pre | |||
| JUCE 4.2.1 will load incorrectly when opened by an audio plug-in built with | |||
| JUCE 4.2.1 and later. | |||
| Workaround | |||
| ---------- | |||
| Enable JUCE_FORCE_USE_LEGACY_IDS in the juce_audio_plugin_client module config | |||
| page in the Projucer. For new plug-ins, be sure to disable this property. | |||
| Rationale | |||
| -------- | |||
| Each parameter of the AudioProcessor has an id associated so that the plug-in’s | |||
| host can uniquely identify parameters. The id has a different data-type for | |||
| different plug-in types (for example VST uses integers, AAX uses string | |||
| identifiers). Before 4.2.1, JUCE generated the parameter id by using the index | |||
| of the parameter, i.e. the first parameter had id zero, the second parameter | |||
| had id one, etc. This caused problems for certain plug-in types where JUCE | |||
| needs to add internal parameters to the plug-in (for example VST3 requires the | |||
| bypass control to be a parameter - so JUCE automatically creates this parameter | |||
| for you in the VST3 backend). This causes subtle problems if a parameter is | |||
| added to an update of an already published plug-in. The new parameter’s id | |||
| would be identical to the id of the bypass parameter in old versions of your | |||
| plug-in, causing seemingly random plug-in bypass behaviour when user’s upgrade | |||
| their plug-in. | |||
| Most plug-in backends differentiate between a parameter’s id an index, so this | |||
| distinction was adopted starting with JUCE 4.2.1 by deriving the parameter’s | |||
| unique id from the paramID property of AudioProcessorParameterWithID class. | |||
| JUCE breaking changes | |||
| ===================== | |||
| Version 5.1.2 | |||
| ============= | |||
| Change | |||
| ------ | |||
| The method used to classify AudioUnit, VST3 and AAX plug-in parameters as | |||
| either continuous or discrete has changed, and AudioUnit and AudioUnit v3 | |||
| parameters are marked as high precision by default. | |||
| Possible Issues | |||
| --------------- | |||
| Plug-ins: DAW projects with automation data written by an AudioUnit, AudioUnit | |||
| v3 VST3 or AAX plug-in built with JUCE version 5.1.1 or earlier may load | |||
| incorrectly when opened by an AudioUnit, AudioUnit v3, VST3 or AAX plug-in | |||
| built with JUCE version 5.1.2 and later. | |||
| Hosts: The AudioPluginInstance::getParameterNumSteps method now returns correct | |||
| values for AU and VST3 plug-ins. | |||
| Workaround | |||
| ---------- | |||
| Plug-ins: Enable JUCE_FORCE_LEGACY_PARAMETER_AUTOMATION_TYPE in the | |||
| juce_audio_plugin_client module config page in the Projucer. | |||
| Hosts: Use AudioPluginInstance::getDefaultNumParameterSteps as the number of | |||
| steps for all parameters. | |||
| Rationale | |||
| --------- | |||
| The old system for presenting plug-in parameters to a host as either continuous | |||
| or discrete is inconsistent between plug-in types and lacks sufficient | |||
| flexibility. This change harmonises the behaviour and allows individual | |||
| parameters to be marked as continuous or discrete. If AudioUnit and AudioUnit | |||
| v3 parameters are not marked as high precision then hosts like Logic Pro only | |||
| offer a limited number of parameter values, which again produces different | |||
| behaviour for different plug-in types. | |||
| Change | |||
| ------ | |||
| A new FrameRateType fps23976 has been added to AudioPlayHead, | |||
| Possible Issues | |||
| --------------- | |||
| Previously JUCE would report the FrameRateType fps24 for both 24 and 23.976 | |||
| fps. If your code uses switch statements (or similar) to handle all possible | |||
| frame rate types, then this change may cause it to fall through. | |||
| Workaround | |||
| ---------- | |||
| Add fps23976 to your switch statement and handle it appropriately. | |||
| Rationale | |||
| --------- | |||
| JUCE should be able to handle all popular frame rate codes but was missing | |||
| support for 23.976. | |||
| Change | |||
| ------ | |||
| The String (bool) constructor and operator<< (String&, bool) have been | |||
| explicitly deleted. | |||
| Possible Issues | |||
| --------------- | |||
| Previous code which relied on an implicit bool to int type conversion to | |||
| produce a String will not compile. | |||
| Workaround | |||
| ---------- | |||
| Cast your bool to an integer to generate a string representation of it. | |||
| Rationale | |||
| --------- | |||
| Letting things implicitly convert to bool to produce a String opens the door to | |||
| all kinds of nasty type conversion edge cases. Furthermore, before this change, | |||
| MacOS would automatically convert bools to ints but this wouldn't occur on | |||
| different platform. Now the behaviour is consistent across all operating | |||
| systems supported by JUCE. | |||
| Change | |||
| ------ | |||
| The writeAsJSON virtual method of the DynamicObject class requires an | |||
| additional parameter, maximumDecimalPlaces, to specify the maximum precision of | |||
| floating point numbers. | |||
| Possible Issues | |||
| --------------- | |||
| Classes which inherit from DynamicObject and override this method will need to | |||
| update their method signature. | |||
| Workaround | |||
| ---------- | |||
| Your custom DynamicObject class can choose to ignore the additional parameter | |||
| if you don't wish to support this behaviour. | |||
| Rationale | |||
| --------- | |||
| When serialising the results of calculations to JSON the rounding of floating | |||
| point numbers can result in numbers with 17 significant figures where only a | |||
| few are required. This change to DynamicObject is required to support | |||
| truncating those numbers. | |||
| Version 5.1.0 | |||
| ============= | |||
| Change | |||
| ------ | |||
| The option to set the C++ language standard is now located in the project | |||
| settings instead of the build configuration settings. | |||
| Possible Issues | |||
| --------------- | |||
| Projects that had a specific verison of the C++ language standard set for | |||
| exporter build configurations will instead use the default (C++11) when | |||
| re-saving with the new Projucer. | |||
| Workaround | |||
| ---------- | |||
| Change the "C++ Language Standard" setting in the main project settings to the | |||
| required version - the Projucer will add this value to the exported project as | |||
| a compiler flag when saving exporters. | |||
| Rationale | |||
| --------- | |||
| Having a different C++ language standard option for each build configuration | |||
| was unnecessary and was not fully implemented for all exporters. Changing it to | |||
| a per-project settings means that the preference will propagate to all | |||
| exporters and only needs to be set in one place. | |||
| Change | |||
| ------ | |||
| PopupMenus now scale according to the AffineTransform and scaling factor of | |||
| their target components. | |||
| Possible Issues | |||
| --------------- | |||
| Developers who have manually scaled their PopupMenus to fit the scaling factor | |||
| of the parent UI will now have the scaling applied two times in a row. | |||
| Workaround | |||
| ---------- | |||
| 1. Do not apply your own manual scaling to make your popups match the UI | |||
| scaling | |||
| or | |||
| 2. Override the Look&Feel method | |||
| PopupMenu::LookAndFeelMethods::shouldPopupMenuScaleWithTargetComponent and | |||
| return false. See | |||
| https://github.com/WeAreROLI/JUCE/blob/c288c94c2914af20f36c03ca9c5401fcb555e4e9/modules/juce_gui_basics/menus/juce_PopupMenu.h#725 | |||
| Rationale | |||
| --------- | |||
| Previously, PopupMenus would not scale if the GUI of the target component (or | |||
| any of it’s parents) were scaled. The only way to scale PopupMenus was via the | |||
| global scaling factor. This had several drawbacks as the global scaling factor | |||
| would scale everything. This was especially problematic in plug-in editors. | |||
| Change | |||
| ------ | |||
| Removed the setSecurityFlags() method from the Windows implementation of | |||
| WebInputStream as it disabled HTTPS security features. | |||
| Possible Issues | |||
| --------------- | |||
| Any code previously relying on connections to insecure webpages succeeding will | |||
| no longer work. | |||
| Workaround | |||
| ---------- | |||
| Check network connectivity on Windows and re-write any code that relied on | |||
| insecure connections. | |||
| Rationale | |||
| --------- | |||
| The previous behaviour resulted in network connections on Windows having all | |||
| the HTTPS security features disabled, exposing users to network attacks. HTTPS | |||
| connections on Windows are now secure and will fail when connecting to an | |||
| insecure web address. | |||
| Change | |||
| ------ | |||
| Pointer arithmetic on a pointer will have the same result regardless if it is | |||
| wrapped in JUCE's Atomic class or not. | |||
| Possible Issues | |||
| --------------- | |||
| Any code using pointer arithmetic on Atomic<T*> will now have a different | |||
| result leading to undefined behaviour or crashes. | |||
| Workaround | |||
| ---------- | |||
| Re-write your code in a way that it does not depend on your pointer being | |||
| wrapped in JUCE's Atomic or not. See rationale. | |||
| Rationale | |||
| --------- | |||
| Before this change, pointer arithmetic with JUCE's Atomic type would yield | |||
| confusing results. For example, the following code would assert before this | |||
| change: | |||
| int* a; Atomic<int*> b; | |||
| jassert (++a == ++b); | |||
| Pointer a in the above code would be advanced by sizeof(int) whereas the JUCE's | |||
| Atomic always advances it's underlying pointer by a single byte. The same is | |||
| true for operator+=/operator-= and operator--. The difference in behaviour is | |||
| confusing and unintuitive. Furthermore, this aligns JUCE's Atomic type with | |||
| std::atomic. | |||
| Version 4.3.1 | |||
| ============= | |||
| Change | |||
| ------ | |||
| JUCE has changed the way native VST3/AudioUnit parameter ids are calculated. | |||
| Possible Issues | |||
| --------------- | |||
| DAW projects with automation data written by an AudioUnit or VST3 plug-in built | |||
| with pre JUCE 4.3.1 versions will load incorrectly when opened by an AudioUnit | |||
| or VST3 built with JUCE versions 4.3.1 and later. Plug-ins using | |||
| JUCE_FORCE_USE_LEGACY_PARAM_IDS are not affected. | |||
| Workaround | |||
| ---------- | |||
| Disable JUCE_USE_STUDIO_ONE_COMPATIBLE_PARAMETERS in the | |||
| juce_audio_plugin_client module config page in the Projucer. For new plug-ins, | |||
| be sure to use the default value for this property. | |||
| Rationale | |||
| -------- | |||
| JUCE needs to convert between its own JUCE parameter id format (strings) to the | |||
| native parameter id formats of the various plug-in backends. For VST3 and | |||
| AudioUnits, JUCE uses a hash function to generate a numeric id. However, some | |||
| VST3/AudioUnit hosts (specifically Studio One) have a bug that ignore any | |||
| parameters that have a negative parameter id. Therefore, the hash function for | |||
| VST3/AudioUnits needed to be changed to only return positive-valued hashes. | |||
| Version 4.3.0 | |||
| ============= | |||
| Change | |||
| ------ | |||
| A revised multi-bus API was released which supersedes the previously flawed | |||
| multi-bus API - JUCE versions 4.0.0 - 4.2.4 (inclusive). | |||
| Possible Issues | |||
| --------------- | |||
| If you have developed a plug-in with JUCE versions 4.0.0 - 4.2.4 (inclusive), | |||
| then you will need to update your plug-in to the new multi-bus API. Pre JUCE | |||
| 4.0.0 plug-ins are not affected apart from other breaking changes listed in | |||
| this document. | |||
| Woraround | |||
| --------- | |||
| None. | |||
| Rationale | |||
| -------- | |||
| A flawed multi-bus API was introduced with JUCE versions 4.0.0 up until version | |||
| 4.2.4 (inclusive) which was not API compatible with pre JUCE 4 plug-ins. JUCE | |||
| 4.3.0 releases a revised multi-bus API which restores pre JUCE 4 API | |||
| compatibility. However, the new multi-bus API is not compatible with the flawed | |||
| multi-bus API (JUCE version 4.0.0 - 4.2.4). | |||
| Change | |||
| ------ | |||
| JUCE now generates the AAX plug-in bus layout configuration id independent from | |||
| the position as it appears in the Projucer’s legacy "Channel layout | |||
| configuration" field. | |||
| Possible Issues | |||
| --------------- | |||
| ProTools projects generated with a < 4.3.0 JUCE versions of your plug-in, may | |||
| load the incorrect bus configuration when upgrading your plug-in to >= 4.3.0 | |||
| versions of JUCE. | |||
| Workaround | |||
| ---------- | |||
| Implement AudioProcessor’s getAAXPluginIDForMainBusConfig callback to manually | |||
| override which AAX plug-in id is associated to a specific bus layout of your | |||
| plug-in. This workaround is only necessary if you have released your plug-in | |||
| built with a version previous to JUCE 4.3.0. | |||
| Rationale | |||
| -------- | |||
| The new multi-bus API offers more features, flexibility and accuracy in | |||
| specifying bus layouts which cannot be expressed by the Projucer’s legacy | |||
| "Channel layout configuration" field. The native plug-in format backends use | |||
| the new multi-bus callback APIs to negotiate channel layouts with the host - | |||
| including the AAX plug-in ids assigned to specific bus layouts. With the | |||
| callback API, there is no notion of an order in which the channel | |||
| configurations appear - as was the case with the legacy "Channel layout | |||
| configuration" field - and therefore cannot be used to generate the AAX plug-in | |||
| id. To remain backward compatible to pre JUCE 4.0.0 plug-ins, JUCE does | |||
| transparently convert the legacy "Channel layout configuration" field to the | |||
| new callback based multi-bus API, but this does not take the order into account | |||
| in which the channel configurations appear in the legacy "Channel layout | |||
| configuration" field. | |||
| Version 4.2.1 | |||
| ============= | |||
| Change | |||
| ------ | |||
| JUCE now uses the paramID property used in AudioProcessorParameterWithID to | |||
| uniquely identify parameters to the host. | |||
| Possible Issues | |||
| --------------- | |||
| DAW projects with automation data written by an audio plug-in built with pre | |||
| JUCE 4.2.1 will load incorrectly when opened by an audio plug-in built with | |||
| JUCE 4.2.1 and later. | |||
| Workaround | |||
| ---------- | |||
| Enable JUCE_FORCE_USE_LEGACY_PARAM_IDS in the juce_audio_plugin_client module config | |||
| page in the Projucer. For new plug-ins, be sure to disable this property. | |||
| Rationale | |||
| -------- | |||
| Each parameter of the AudioProcessor has an id associated so that the plug-in’s | |||
| host can uniquely identify parameters. The id has a different data-type for | |||
| different plug-in types (for example VST uses integers, AAX uses string | |||
| identifiers). Before 4.2.1, JUCE generated the parameter id by using the index | |||
| of the parameter, i.e. the first parameter had id zero, the second parameter | |||
| had id one, etc. This caused problems for certain plug-in types where JUCE | |||
| needs to add internal parameters to the plug-in (for example VST3 requires the | |||
| bypass control to be a parameter - so JUCE automatically creates this parameter | |||
| for you in the VST3 backend). This causes subtle problems if a parameter is | |||
| added to an update of an already published plug-in. The new parameter’s id | |||
| would be identical to the id of the bypass parameter in old versions of your | |||
| plug-in, causing seemingly random plug-in bypass behaviour when user’s upgrade | |||
| their plug-in. | |||
| Most plug-in backends differentiate between a parameter’s id an index, so this | |||
| distinction was adopted starting with JUCE 4.2.1 by deriving the parameter’s | |||
| unique id from the paramID property of AudioProcessorParameterWithID class. | |||
| @@ -1,56 +1,70 @@ | |||
| == Major JUCE features and updates == | |||
| == Major JUCE features and updates == | |||
| This file just lists the more notable headline features. For more detailed info | |||
| about minor changes and bugfixes, please see the git log! | |||
| Version 5.1.1 | |||
| - Fixed Windows live build engine on Visual Studio 2017 | |||
| - Fixed a compiler error in juce_MathFunctions.h in Visual Studio 2013 | |||
| - Fixed a potential crash when using the ProcessorDuplicator | |||
| - Fixed a compiler-error in Filter::IIR | |||
| - Fixed an issue where the WavFileFormatWriter could not create files with discrete channels | |||
| - Fixed an issue where a window which is beneath a hidden window would not receive any clicks on Linux | |||
| - Altered the format of BREAKING-CHANGES.txt to display better on GitHub | |||
| - Projucer: Fixed an issue in exporter tilde expansion | |||
| - Fixed compiler errors when building the DSP module with a static version of FFTW | |||
| - Fixed an audio glitch when bypassing the convolution engine | |||
| - Fixed an issue where a JUCE VST2 would not correctly report that it supports resizing of it’s plugin editor | |||
| - Various documentation tweaks and fixes | |||
| Version 5.1.0 | |||
| - Release of the JUCE DSP module | |||
| - Multichannel audio readers and writers | |||
| - Plugin editor Hi-DPI scaling support | |||
| - Major improvements to Projucer module search paths | |||
| - Added Projucer support for iOS app groups | |||
| - Added support for AVFoundation and deprecated the use of Quicktime | |||
| - Added a new real-time audio thread priority for Android | |||
| - Various Projucer UI fixes | |||
| - Various documentation fixes | |||
| - Various minor improvements and bug fixes | |||
| Version 5.0.2 | |||
| - Improved project save speed in the Projucer | |||
| - Added option to save individual exporters in the Projucer | |||
| - Added the ability to create custom colour schemes for the Projucer’s code editor | |||
| - Minor fixes to JUCE’s SVG parser | |||
| - Various bug fixes in the way JUCE handles Hi-DPI monitors | |||
| - Improved code browsing in Visual Studio Exports | |||
| - Improved the handling of audio device buffer size changes on iOS | |||
| - Fixed bug in the Win32 FileChooser dialog when selecting a nonexistent root drive | |||
| - Fixed a Projucer crash when saving projects with no targets | |||
| - Fixed a bug where Projucer generated Makefiles would not trigger a recompilation when header files had changed | |||
| - The standalone plugin target is now compatible with effect plug-ins | |||
| - Fixed an issue where it was not possible to use the live build engine on plugin projects | |||
| - Improved the way the Projucer’s live-build engine searches for platform headers on Windows | |||
| - Fixed an issue where the Projucer would complain about not having internet even if the user had a license | |||
| - Fixed a use-after-free in the AUv3 wrapper | |||
| - Fixed an issue where the channel layout would not be reported correctly in the AUv3 wrapper | |||
| - Fixed a potential memory overrun issue when hosting VST2 plugins with more than eight channels | |||
| - Fixed a problem with the Mac main menu bar showing menus in the wrong position | |||
| - Various Projucer UI fixes | |||
| - Various documentation fixes | |||
| about minor changes and bugfixes, please see the git log! | |||
| Version 5.1.2 | |||
| - Fixed multiple plugin-resizing bugs | |||
| - Added support for AUv3 MIDI and screen size negotiation | |||
| - Added support for Xcode 9 and iOS 11 | |||
| - Added an In-App Purchases module | |||
| - Added backwards compatible constexpr support | |||
| - Standalone plug-in improvements | |||
| - Better .jucer file change monitoring in the Projucer | |||
| - Increased the speed of AU parameter lookup | |||
| - Improved the Android thread management when dealing with web requests | |||
| - Better denormal support | |||
| - Plug-in parameters can be explicitly marked as continuous or discrete | |||
| - Multiple documentation updates | |||
| Version 5.1.1 | |||
| - Fixed Windows live build engine on Visual Studio 2017 | |||
| - Fixed a compiler error in juce_MathFunctions.h in Visual Studio 2013 | |||
| - Fixed a potential crash when using the ProcessorDuplicator | |||
| - Fixed a compiler-error in Filter::IIR | |||
| - Fixed an issue where the WavFileFormatWriter could not create files with discrete channels | |||
| - Fixed an issue where a window which is beneath a hidden window would not receive any clicks on Linux | |||
| - Altered the format of BREAKING-CHANGES.txt to display better on GitHub | |||
| - Projucer: Fixed an issue in exporter tilde expansion | |||
| - Fixed compiler errors when building the DSP module with a static version of FFTW | |||
| - Fixed an audio glitch when bypassing the convolution engine | |||
| - Fixed an issue where a JUCE VST2 would not correctly report that it supports resizing of it’s plugin editor | |||
| - Various documentation tweaks and fixes | |||
| Version 5.1.0 | |||
| - Release of the JUCE DSP module | |||
| - Multichannel audio readers and writers | |||
| - Plugin editor Hi-DPI scaling support | |||
| - Major improvements to Projucer module search paths | |||
| - Added Projucer support for iOS app groups | |||
| - Added support for AVFoundation and deprecated the use of Quicktime | |||
| - Added a new real-time audio thread priority for Android | |||
| - Various Projucer UI fixes | |||
| - Various documentation fixes | |||
| - Various minor improvements and bug fixes | |||
| Version 5.0.2 | |||
| - Improved project save speed in the Projucer | |||
| - Added option to save individual exporters in the Projucer | |||
| - Added the ability to create custom colour schemes for the Projucer’s code editor | |||
| - Minor fixes to JUCE’s SVG parser | |||
| - Various bug fixes in the way JUCE handles Hi-DPI monitors | |||
| - Improved code browsing in Visual Studio Exports | |||
| - Improved the handling of audio device buffer size changes on iOS | |||
| - Fixed bug in the Win32 FileChooser dialog when selecting a nonexistent root drive | |||
| - Fixed a Projucer crash when saving projects with no targets | |||
| - Fixed a bug where Projucer generated Makefiles would not trigger a recompilation when header files had changed | |||
| - The standalone plugin target is now compatible with effect plug-ins | |||
| - Fixed an issue where it was not possible to use the live build engine on plugin projects | |||
| - Improved the way the Projucer’s live-build engine searches for platform headers on Windows | |||
| - Fixed an issue where the Projucer would complain about not having internet even if the user had a license | |||
| - Fixed a use-after-free in the AUv3 wrapper | |||
| - Fixed an issue where the channel layout would not be reported correctly in the AUv3 wrapper | |||
| - Fixed a potential memory overrun issue when hosting VST2 plugins with more than eight channels | |||
| - Fixed a problem with the Mac main menu bar showing menus in the wrong position | |||
| - Various Projucer UI fixes | |||
| - Various documentation fixes | |||
| - Various minor improvements and bug fixes | |||
| Version 5.0.1 | |||
| @@ -8,7 +8,7 @@ | |||
| pluginCode="AUv3" pluginChannelConfigs="" pluginIsSynth="1" pluginWantsMidiIn="1" | |||
| pluginProducesMidiOut="0" pluginIsMidiEffectPlugin="0" pluginEditorRequiresKeys="0" | |||
| pluginAUExportPrefix="AUv3SynthAU" pluginRTASCategory="" aaxIdentifier="com.roli.development.AUv3Synth" | |||
| pluginAAXCategory="AAX_ePlugInCategory_Dynamics" jucerVersion="5.1.1" | |||
| pluginAAXCategory="AAX_ePlugInCategory_Dynamics" jucerVersion="5.1.2" | |||
| buildStandalone="1" enableIAA="0" displaySplashScreen="0" reportAppUsage="0" | |||
| splashScreenColour="Dark" companyName="ROLI Ltd." cppLanguageStandard="11"> | |||
| <MAINGROUP id="h0gx6L" name="AUv3Synth"> | |||
| @@ -101,6 +101,10 @@ | |||
| //#define JUCE_FORCE_USE_LEGACY_PARAM_IDS 1 | |||
| #endif | |||
| #ifndef JUCE_FORCE_LEGACY_PARAMETER_AUTOMATION_TYPE | |||
| //#define JUCE_FORCE_LEGACY_PARAMETER_AUTOMATION_TYPE 1 | |||
| #endif | |||
| #ifndef JUCE_USE_STUDIO_ONE_COMPATIBLE_PARAMETERS | |||
| //#define JUCE_USE_STUDIO_ONE_COMPATIBLE_PARAMETERS 1 | |||
| #endif | |||
| @@ -96,19 +96,16 @@ public: | |||
| const String getName() const override { return "AUv3 Synth"; } | |||
| int getNumPrograms() override { return 4; } | |||
| int getCurrentProgram() override { return currentProgram; } | |||
| void setCurrentProgram (int index) override { currentProgram = index; } | |||
| void setCurrentProgram (int index) override { currentProgram = index; } | |||
| const String getProgramName (int index) override | |||
| { | |||
| switch (index) | |||
| { | |||
| case 0: | |||
| return "Piano"; | |||
| case 1: | |||
| return "Singing"; | |||
| case 2: | |||
| return "Pinched Balloon"; | |||
| case 3: | |||
| return "Gazeebo"; | |||
| case 0: return "Piano"; | |||
| case 1: return "Singing"; | |||
| case 2: return "Pinched Balloon"; | |||
| case 3: return "Gazeebo"; | |||
| } | |||
| return "<Unknown>"; | |||
| @@ -2,7 +2,7 @@ | |||
| <JUCERPROJECT id="LrATE6" name="AnimationAppExample" projectType="guiapp" version="1.0.0" | |||
| bundleIdentifier="com.roli.AnimationAppExample" includeBinaryInAppConfig="1" | |||
| jucerVersion="5.1.1" displaySplashScreen="0" reportAppUsage="0" | |||
| jucerVersion="5.1.2" displaySplashScreen="0" reportAppUsage="0" | |||
| splashScreenColour="Dark" companyName="ROLI Ltd." cppLanguageStandard="11"> | |||
| <MAINGROUP id="F3keCY" name="AnimationAppExample"> | |||
| <GROUP id="{5E4132EA-C4A0-CBDE-BEDA-FD6772DA79D5}" name="Source"> | |||
| @@ -2,7 +2,7 @@ | |||
| <JUCERPROJECT id="PAnJXP" name="AudioAppExample" projectType="guiapp" version="1.0.0" | |||
| bundleIdentifier="com.roli.AudioAppExample" includeBinaryInAppConfig="1" | |||
| jucerVersion="5.1.1" displaySplashScreen="0" reportAppUsage="0" | |||
| jucerVersion="5.1.2" displaySplashScreen="0" reportAppUsage="0" | |||
| splashScreenColour="Dark" companyName="ROLI Ltd." cppLanguageStandard="11"> | |||
| <MAINGROUP id="GaJIge" name="AudioAppExample"> | |||
| <GROUP id="{168FC5D4-FA65-8320-F83E-C14C416638E1}" name="Source"> | |||
| @@ -2,7 +2,7 @@ | |||
| <JUCERPROJECT id="aa4reI" name="BlocksDrawing" projectType="guiapp" version="1.0.0" | |||
| bundleIdentifier="com.yourcompany.BlocksDrawing" includeBinaryInAppConfig="1" | |||
| jucerVersion="5.1.1" displaySplashScreen="0" reportAppUsage="0" | |||
| jucerVersion="5.1.2" displaySplashScreen="0" reportAppUsage="0" | |||
| splashScreenColour="Dark" cppLanguageStandard="11"> | |||
| <MAINGROUP id="yXiPIx" name="BlocksDrawing"> | |||
| <GROUP id="{092A4D5B-31E2-5D03-9B41-81C10EC447E8}" name="Source"> | |||
| @@ -2,7 +2,7 @@ | |||
| <JUCERPROJECT id="X7eXs7" name="BlocksMonitor" projectType="guiapp" version="1.0.0" | |||
| bundleIdentifier="com.yourcompany.BlocksInfo" includeBinaryInAppConfig="1" | |||
| jucerVersion="5.1.1" displaySplashScreen="0" reportAppUsage="0" | |||
| jucerVersion="5.1.2" displaySplashScreen="0" reportAppUsage="0" | |||
| splashScreenColour="Dark" cppLanguageStandard="11"> | |||
| <MAINGROUP id="msZ9DB" name="BlocksMonitor"> | |||
| <GROUP id="{2C318C74-6596-8102-3CA6-602595447F25}" name="Source"> | |||
| @@ -2,7 +2,7 @@ | |||
| <JUCERPROJECT id="XsTycT" name="BlocksSynth" projectType="guiapp" version="1.0.0" | |||
| bundleIdentifier="com.yourcompany.BlocksSynth" includeBinaryInAppConfig="1" | |||
| jucerVersion="5.1.1" displaySplashScreen="0" reportAppUsage="0" | |||
| jucerVersion="5.1.2" displaySplashScreen="0" reportAppUsage="0" | |||
| splashScreenColour="Dark" cppLanguageStandard="11"> | |||
| <MAINGROUP id="onTNql" name="BlocksSynth"> | |||
| <GROUP id="{0CEBC63B-4C52-E77F-CD6E-A2E65F0C9B2B}" name="Source"> | |||
| @@ -2,7 +2,7 @@ | |||
| <JUCERPROJECT id="fyUrFS" name="BouncingBallWavetableDemo" projectType="guiapp" | |||
| version="1.0.0" bundleIdentifier="com.juce.BouncingBallWavetableDemo" | |||
| includeBinaryInAppConfig="1" jucerVersion="5.1.1" displaySplashScreen="0" | |||
| includeBinaryInAppConfig="1" jucerVersion="5.1.2" displaySplashScreen="0" | |||
| reportAppUsage="0" splashScreenColour="Dark" companyName="ROLI Ltd." | |||
| cppLanguageStandard="11"> | |||
| <MAINGROUP id="MgjqDB" name="BouncingBallWavetableDemo"> | |||
| @@ -2,7 +2,7 @@ | |||
| <JUCERPROJECT id="oNvA5C" name="ComponentTutorialExample" projectType="guiapp" | |||
| version="1.0.0" bundleIdentifier="com.roli.ComponentTutorialExample" | |||
| includeBinaryInAppConfig="1" jucerVersion="5.1.1" displaySplashScreen="0" | |||
| includeBinaryInAppConfig="1" jucerVersion="5.1.2" displaySplashScreen="0" | |||
| reportAppUsage="0" splashScreenColour="Dark" companyName="ROLI Ltd." | |||
| cppLanguageStandard="11"> | |||
| <MAINGROUP id="WydTVz" name="ComponentTutorialExample"> | |||
| @@ -11,7 +11,7 @@ | |||
| pluginProducesMidiOut="0" pluginIsMidiEffectPlugin="0" pluginEditorRequiresKeys="0" | |||
| pluginAUExportPrefix="DSPmoduleplugindemoAU" pluginRTASCategory="" | |||
| aaxIdentifier="com.yourcompany.DSPmoduleplugindemo" pluginAAXCategory="AAX_ePlugInCategory_Dynamics" | |||
| jucerVersion="5.1.1" companyName="ROLI Ltd." companyWebsite="www.juce.com" | |||
| jucerVersion="5.1.2" companyName="ROLI Ltd." companyWebsite="www.juce.com" | |||
| companyEmail="info@juce.com" cppLanguageStandard="14"> | |||
| <MAINGROUP id="EukfoT" name="DSPModulePluginDemo"> | |||
| <GROUP id="{03DB4847-1567-1194-A0D2-ECC6C6C5042C}" name="Resources"> | |||
| @@ -125,5 +125,5 @@ | |||
| <MODULE id="juce_gui_extra" showAllCode="1" useLocalCopy="0" useGlobalPath="0"/> | |||
| <MODULE id="juce_opengl" showAllCode="1" useLocalCopy="0" useGlobalPath="0"/> | |||
| </MODULES> | |||
| <JUCEOPTIONS JUCE_QUICKTIME="disabled"/> | |||
| <JUCEOPTIONS JUCE_QUICKTIME="disabled" JUCE_DSP_ENABLE_SNAP_TO_ZERO="disabled"/> | |||
| </JUCERPROJECT> | |||
| @@ -103,6 +103,10 @@ | |||
| //#define JUCE_FORCE_USE_LEGACY_PARAM_IDS 1 | |||
| #endif | |||
| #ifndef JUCE_FORCE_LEGACY_PARAMETER_AUTOMATION_TYPE | |||
| //#define JUCE_FORCE_LEGACY_PARAMETER_AUTOMATION_TYPE 1 | |||
| #endif | |||
| #ifndef JUCE_USE_STUDIO_ONE_COMPATIBLE_PARAMETERS | |||
| //#define JUCE_USE_STUDIO_ONE_COMPATIBLE_PARAMETERS 1 | |||
| #endif | |||
| @@ -187,6 +191,10 @@ | |||
| //#define JUCE_DSP_USE_STATIC_FFTW 1 | |||
| #endif | |||
| #ifndef JUCE_DSP_ENABLE_SNAP_TO_ZERO | |||
| #define JUCE_DSP_ENABLE_SNAP_TO_ZERO 0 | |||
| #endif | |||
| //============================================================================== | |||
| // juce_events flags: | |||
| @@ -123,6 +123,10 @@ DspModulePluginDemoAudioProcessorEditor::DspModulePluginDemoAudioProcessorEditor | |||
| cabinetSimButton.addListener (this); | |||
| cabinetSimButton.setButtonText (processor.cabinetSimParam->name); | |||
| addAndMakeVisible (oversamplingButton); | |||
| oversamplingButton.addListener (this); | |||
| oversamplingButton.setButtonText (processor.oversamplingParam->name); | |||
| //============================================================================== | |||
| setSize (600, 400); | |||
| } | |||
| @@ -172,9 +176,13 @@ void DspModulePluginDemoAudioProcessorEditor::resized() | |||
| //============================================================================== | |||
| auto buttonSlice = bounds.removeFromTop (30); | |||
| cabinetSimButton.setSize (200, bounds.getHeight()); | |||
| cabinetSimButton.setSize (200, buttonSlice.getHeight()); | |||
| cabinetSimButton.setCentrePosition (buttonSlice.getCentre()); | |||
| bounds.removeFromTop (15); | |||
| bounds.removeFromTop(5); | |||
| buttonSlice = bounds.removeFromTop (30); | |||
| oversamplingButton.setSize(200, buttonSlice.getHeight()); | |||
| oversamplingButton.setCentrePosition(buttonSlice.getCentre()); | |||
| } | |||
| //============================================================================== | |||
| void DspModulePluginDemoAudioProcessorEditor::comboBoxChanged (ComboBox* box) | |||
| @@ -198,3 +206,15 @@ void DspModulePluginDemoAudioProcessorEditor::comboBoxChanged (ComboBox* box) | |||
| processor.cabinetTypeParam->operator= (index); | |||
| } | |||
| } | |||
| void DspModulePluginDemoAudioProcessorEditor::buttonClicked (Button* button) | |||
| { | |||
| if (button == &cabinetSimButton) | |||
| { | |||
| processor.cabinetSimParam->operator= (cabinetSimButton.getToggleState()); | |||
| } | |||
| else if (button == &oversamplingButton) | |||
| { | |||
| processor.oversamplingParam->operator= (oversamplingButton.getToggleState()); | |||
| } | |||
| } | |||
| @@ -86,7 +86,7 @@ public: | |||
| private: | |||
| //============================================================================== | |||
| void comboBoxChanged (ComboBox*) override; | |||
| void buttonClicked (Button*) override { processor.cabinetSimParam->operator= (cabinetSimButton.getToggleState()); } | |||
| void buttonClicked (Button*) override; | |||
| //============================================================================== | |||
| DspModulePluginDemoAudioProcessor& processor; | |||
| @@ -94,7 +94,7 @@ private: | |||
| ScopedPointer<ParameterSlider> inputVolumeSlider, outputVolumeSlider, | |||
| lowPassFilterFreqSlider, highPassFilterFreqSlider; | |||
| ComboBox stereoBox, slopeBox, waveshaperBox, cabinetTypeBox; | |||
| ToggleButton cabinetSimButton; | |||
| ToggleButton cabinetSimButton, oversamplingButton; | |||
| Label inputVolumeLabel, outputVolumeLabel, lowPassFilterFreqLabel, | |||
| highPassFilterFreqLabel, stereoLabel, slopeLabel, waveshaperLabel, | |||
| @@ -38,6 +38,9 @@ DspModulePluginDemoAudioProcessor::DspModulePluginDemoAudioProcessor() | |||
| waveShapers { {std::tanh}, {dsp::FastMathApproximations::tanh} }, | |||
| clipping { clip } | |||
| { | |||
| // Oversampling 2 times with IIR filtering | |||
| oversampling = new dsp::Oversampling<float> (2, 1, dsp::Oversampling<float>::filterHalfBandPolyphaseIIR, false); | |||
| addParameter (inputVolumeParam = new AudioParameterFloat ("INPUT", "Input Volume", { 0.f, 60.f, 0.f, 1.0f }, 0.f, "dB")); | |||
| addParameter (highPassFilterFreqParam = new AudioParameterFloat ("HPFREQ", "Pre Highpass Freq.", { 20.f, 20000.f, 0.f, 0.5f }, 20.f, "Hz")); | |||
| addParameter (lowPassFilterFreqParam = new AudioParameterFloat ("LPFREQ", "Post Lowpass Freq.", { 20.f, 20000.f, 0.f, 0.5f }, 20000.f, "Hz")); | |||
| @@ -50,12 +53,11 @@ DspModulePluginDemoAudioProcessor::DspModulePluginDemoAudioProcessor() | |||
| "Cassette recorder cabinet" }, 0)); | |||
| addParameter (cabinetSimParam = new AudioParameterBool ("CABSIM", "Cabinet Sim", false)); | |||
| addParameter (oversamplingParam = new AudioParameterBool ("OVERS", "Oversampling", false)); | |||
| addParameter (outputVolumeParam = new AudioParameterFloat ("OUTPUT", "Output Volume", { -40.f, 40.f, 0.f, 1.0f }, 0.f, "dB")); | |||
| cabinetType.set (0); | |||
| } | |||
| DspModulePluginDemoAudioProcessor::~DspModulePluginDemoAudioProcessor() | |||
| @@ -82,8 +84,6 @@ void DspModulePluginDemoAudioProcessor::prepareToPlay (double sampleRate, int sa | |||
| auto channels = static_cast<uint32> (jmin (getMainBusNumInputChannels(), getMainBusNumOutputChannels())); | |||
| dsp::ProcessSpec spec { sampleRate, static_cast<uint32> (samplesPerBlock), channels }; | |||
| updateParameters(); | |||
| lowPassFilter.prepare (spec); | |||
| highPassFilter.prepare (spec); | |||
| @@ -92,6 +92,11 @@ void DspModulePluginDemoAudioProcessor::prepareToPlay (double sampleRate, int sa | |||
| convolution.prepare (spec); | |||
| cabinetType.set (-1); | |||
| oversampling->initProcessing (static_cast<size_t> (samplesPerBlock)); | |||
| updateParameters(); | |||
| reset(); | |||
| } | |||
| void DspModulePluginDemoAudioProcessor::reset() | |||
| @@ -99,6 +104,7 @@ void DspModulePluginDemoAudioProcessor::reset() | |||
| lowPassFilter.reset(); | |||
| highPassFilter.reset(); | |||
| convolution.reset(); | |||
| oversampling->reset(); | |||
| } | |||
| void DspModulePluginDemoAudioProcessor::releaseResources() | |||
| @@ -108,6 +114,8 @@ void DspModulePluginDemoAudioProcessor::releaseResources() | |||
| void DspModulePluginDemoAudioProcessor::process (dsp::ProcessContextReplacing<float> context) noexcept | |||
| { | |||
| ScopedNoDenormals noDenormals; | |||
| // Input volume applied with a LinearSmoothedValue | |||
| inputVolume.process (context); | |||
| @@ -115,20 +123,34 @@ void DspModulePluginDemoAudioProcessor::process (dsp::ProcessContextReplacing<fl | |||
| // Note : try frequencies around 700 Hz | |||
| highPassFilter.process (context); | |||
| // Upsampling | |||
| dsp::AudioBlock<float> oversampledBlock; | |||
| setLatencySamples (audioCurrentlyOversampled ? roundFloatToInt (oversampling->getLatencyInSamples()) : 0); | |||
| if (audioCurrentlyOversampled) | |||
| oversampledBlock = oversampling->processSamplesUp (context.getInputBlock()); | |||
| dsp::ProcessContextReplacing<float> waveshaperContext = audioCurrentlyOversampled ? dsp::ProcessContextReplacing<float> (oversampledBlock) : context; | |||
| // Waveshaper processing, for distortion generation, thanks to the input gain | |||
| // The fast tanh can be used instead of std::tanh to reduce the CPU load | |||
| auto waveshaperIndex = waveshaperParam->getIndex(); | |||
| if (isPositiveAndBelow (waveshaperIndex, (int) numWaveShapers) ) | |||
| { | |||
| waveShapers[waveshaperIndex].process (context); | |||
| waveShapers[waveshaperIndex].process (waveshaperContext); | |||
| if (waveshaperIndex == 1) | |||
| clipping.process(context); | |||
| clipping.process (waveshaperContext); | |||
| context.getOutputBlock() *= 0.7f; | |||
| waveshaperContext.getOutputBlock() *= 0.7f; | |||
| } | |||
| // Downsampling | |||
| if (audioCurrentlyOversampled) | |||
| oversampling->processSamplesDown (context.getOutputBlock()); | |||
| // Post-lowpass filtering | |||
| lowPassFilter.process (context); | |||
| @@ -209,13 +231,20 @@ bool DspModulePluginDemoAudioProcessor::producesMidi() const | |||
| //============================================================================== | |||
| void DspModulePluginDemoAudioProcessor::updateParameters() | |||
| { | |||
| auto newOversampling = oversamplingParam->get(); | |||
| if (newOversampling != audioCurrentlyOversampled) | |||
| { | |||
| audioCurrentlyOversampled = newOversampling; | |||
| oversampling->reset(); | |||
| } | |||
| //============================================================================== | |||
| auto inputdB = Decibels::decibelsToGain (inputVolumeParam->get()); | |||
| auto outputdB = Decibels::decibelsToGain (outputVolumeParam->get()); | |||
| if (inputVolume.getGainLinear() != inputdB) inputVolume.setGainLinear (inputdB); | |||
| if (outputVolume.getGainLinear() != outputdB) outputVolume.setGainLinear (outputdB); | |||
| dsp::IIR::Coefficients<float>::Ptr newHighPassCoeffs, newLowPassCoeffs; | |||
| auto newSlopeType = slopeParam->getIndex(); | |||
| if (newSlopeType == 0) | |||
| @@ -240,12 +269,13 @@ void DspModulePluginDemoAudioProcessor::updateParameters() | |||
| auto maxSize = static_cast<size_t> (roundDoubleToInt (8192 * getSampleRate() / 44100)); | |||
| if (type == 0) | |||
| convolution.loadImpulseResponse (BinaryData::Impulse1_wav, BinaryData::Impulse1_wavSize, false, maxSize); | |||
| convolution.loadImpulseResponse (BinaryData::Impulse1_wav, BinaryData::Impulse1_wavSize, false, true, maxSize); | |||
| else | |||
| convolution.loadImpulseResponse (BinaryData::Impulse2_wav, BinaryData::Impulse2_wavSize, false, maxSize); | |||
| convolution.loadImpulseResponse (BinaryData::Impulse2_wav, BinaryData::Impulse2_wavSize, false, true, maxSize); | |||
| } | |||
| cabinetIsBypassed = ! cabinetSimParam->get(); | |||
| } | |||
| //============================================================================== | |||
| @@ -88,6 +88,7 @@ public: | |||
| AudioParameterChoice* cabinetTypeParam; | |||
| AudioParameterBool* cabinetSimParam; | |||
| AudioParameterBool* oversamplingParam; | |||
| private: | |||
| //============================================================================== | |||
| @@ -103,6 +104,9 @@ private: | |||
| dsp::Gain<float> inputVolume, outputVolume; | |||
| ScopedPointer<dsp::Oversampling<float>> oversampling; | |||
| bool audioCurrentlyOversampled = false; | |||
| Atomic<int> cabinetType; | |||
| bool cabinetIsBypassed = false; | |||
| @@ -3,7 +3,7 @@ | |||
| <JUCERPROJECT id="yVderJ" name="DSPDemo" displaySplashScreen="1" reportAppUsage="1" | |||
| splashScreenColour="Dark" projectType="guiapp" version="1.0.0" | |||
| bundleIdentifier="com.roli.DSPDemo" includeBinaryInAppConfig="1" | |||
| jucerVersion="5.1.1" cppLanguageStandard="14" companyName="ROLI Ltd." | |||
| jucerVersion="5.1.2" cppLanguageStandard="14" companyName="ROLI Ltd." | |||
| companyWebsite="www.juce.com" companyEmail="info@juce.com"> | |||
| <MAINGROUP id="vJBLPL" name="DSPDemo"> | |||
| <GROUP id="{EF78091C-1BFD-651B-9BAA-893A127B90F5}" name="Source"> | |||
| @@ -174,6 +174,10 @@ | |||
| //#define JUCE_DSP_USE_STATIC_FFTW 1 | |||
| #endif | |||
| #ifndef JUCE_DSP_ENABLE_SNAP_TO_ZERO | |||
| //#define JUCE_DSP_ENABLE_SNAP_TO_ZERO 1 | |||
| #endif | |||
| //============================================================================== | |||
| // juce_events flags: | |||
| @@ -1565,11 +1565,11 @@ static const unsigned char temp_binary_data_3[] = | |||
| " if (cabinetTypeParameter->getCurrentSelectedID() == 2)\r\n" | |||
| " convolution.loadImpulseResponse (BinaryData::guitar_amp_wav,\r\n" | |||
| " BinaryData::guitar_amp_wavSize,\r\n" | |||
| " false, maxSize);\r\n" | |||
| " false, true, maxSize);\r\n" | |||
| " else\r\n" | |||
| " convolution.loadImpulseResponse (BinaryData::cassette_recorder_wav,\r\n" | |||
| " BinaryData::cassette_recorder_wavSize,\r\n" | |||
| " false, maxSize);\r\n" | |||
| " false, true, maxSize);\r\n" | |||
| " }\r\n" | |||
| " }\r\n" | |||
| " }\r\n" | |||
| @@ -2325,7 +2325,7 @@ const char* getNamedResource (const char* resourceNameUTF8, int& numBytes) throw | |||
| case 0x409ff6ec: numBytes = 37902; return cassette_recorder_wav; | |||
| case 0x69523d16: numBytes = 628; return EditorColourScheme_xml; | |||
| case 0x700ccf3c: numBytes = 90246; return guitar_amp_wav; | |||
| case 0x5922ccdf: numBytes = 2999; return ConvolutionDemo_cpp; | |||
| case 0x5922ccdf: numBytes = 3011; return ConvolutionDemo_cpp; | |||
| case 0x14aa0aae: numBytes = 2674; return FIRFilterDemo_cpp; | |||
| case 0xab621a06: numBytes = 1809; return GainDemo_cpp; | |||
| case 0x06a7a4b1: numBytes = 2819; return IIRFilterDemo_cpp; | |||
| @@ -18,7 +18,7 @@ namespace BinaryData | |||
| const int guitar_amp_wavSize = 90246; | |||
| extern const char* ConvolutionDemo_cpp; | |||
| const int ConvolutionDemo_cppSize = 2999; | |||
| const int ConvolutionDemo_cppSize = 3011; | |||
| extern const char* FIRFilterDemo_cpp; | |||
| const int FIRFilterDemo_cppSize = 2674; | |||
| @@ -65,11 +65,11 @@ struct ConvolutionDemo | |||
| if (cabinetTypeParameter->getCurrentSelectedID() == 2) | |||
| convolution.loadImpulseResponse (BinaryData::guitar_amp_wav, | |||
| BinaryData::guitar_amp_wavSize, | |||
| false, maxSize); | |||
| false, true, maxSize); | |||
| else | |||
| convolution.loadImpulseResponse (BinaryData::cassette_recorder_wav, | |||
| BinaryData::cassette_recorder_wavSize, | |||
| false, maxSize); | |||
| false, true, maxSize); | |||
| } | |||
| } | |||
| } | |||
| @@ -519,11 +519,18 @@ public class JuceDemo extends Activity | |||
| builder.setTitle (title) | |||
| .setMessage (message) | |||
| .setCancelable (true) | |||
| .setOnCancelListener (new DialogInterface.OnCancelListener() | |||
| { | |||
| public void onCancel (DialogInterface dialog) | |||
| { | |||
| JuceDemo.this.alertDismissed (callback, 0); | |||
| } | |||
| }) | |||
| .setPositiveButton ("OK", new DialogInterface.OnClickListener() | |||
| { | |||
| public void onClick (DialogInterface dialog, int id) | |||
| { | |||
| dialog.cancel(); | |||
| dialog.dismiss(); | |||
| JuceDemo.this.alertDismissed (callback, 0); | |||
| } | |||
| }); | |||
| @@ -538,11 +545,18 @@ public class JuceDemo extends Activity | |||
| builder.setTitle (title) | |||
| .setMessage (message) | |||
| .setCancelable (true) | |||
| .setOnCancelListener (new DialogInterface.OnCancelListener() | |||
| { | |||
| public void onCancel (DialogInterface dialog) | |||
| { | |||
| JuceDemo.this.alertDismissed (callback, 0); | |||
| } | |||
| }) | |||
| .setPositiveButton (okButtonText.isEmpty() ? "OK" : okButtonText, new DialogInterface.OnClickListener() | |||
| { | |||
| public void onClick (DialogInterface dialog, int id) | |||
| { | |||
| dialog.cancel(); | |||
| dialog.dismiss(); | |||
| JuceDemo.this.alertDismissed (callback, 1); | |||
| } | |||
| }) | |||
| @@ -550,7 +564,7 @@ public class JuceDemo extends Activity | |||
| { | |||
| public void onClick (DialogInterface dialog, int id) | |||
| { | |||
| dialog.cancel(); | |||
| dialog.dismiss(); | |||
| JuceDemo.this.alertDismissed (callback, 0); | |||
| } | |||
| }); | |||
| @@ -564,11 +578,18 @@ public class JuceDemo extends Activity | |||
| builder.setTitle (title) | |||
| .setMessage (message) | |||
| .setCancelable (true) | |||
| .setOnCancelListener (new DialogInterface.OnCancelListener() | |||
| { | |||
| public void onCancel (DialogInterface dialog) | |||
| { | |||
| JuceDemo.this.alertDismissed (callback, 0); | |||
| } | |||
| }) | |||
| .setPositiveButton ("Yes", new DialogInterface.OnClickListener() | |||
| { | |||
| public void onClick (DialogInterface dialog, int id) | |||
| { | |||
| dialog.cancel(); | |||
| dialog.dismiss(); | |||
| JuceDemo.this.alertDismissed (callback, 1); | |||
| } | |||
| }) | |||
| @@ -576,7 +597,7 @@ public class JuceDemo extends Activity | |||
| { | |||
| public void onClick (DialogInterface dialog, int id) | |||
| { | |||
| dialog.cancel(); | |||
| dialog.dismiss(); | |||
| JuceDemo.this.alertDismissed (callback, 2); | |||
| } | |||
| }) | |||
| @@ -584,7 +605,7 @@ public class JuceDemo extends Activity | |||
| { | |||
| public void onClick (DialogInterface dialog, int id) | |||
| { | |||
| dialog.cancel(); | |||
| dialog.dismiss(); | |||
| JuceDemo.this.alertDismissed (callback, 0); | |||
| } | |||
| }); | |||
| @@ -982,13 +1003,75 @@ public class JuceDemo extends Activity | |||
| //============================================================================== | |||
| public static class HTTPStream | |||
| { | |||
| public HTTPStream (HttpURLConnection connection_, | |||
| int[] statusCode_, | |||
| StringBuffer responseHeaders_) | |||
| public HTTPStream (String address, boolean isPostToUse, byte[] postDataToUse, | |||
| String headersToUse, int timeOutMsToUse, | |||
| int[] statusCodeToUse, StringBuffer responseHeadersToUse, | |||
| int numRedirectsToFollowToUse, String httpRequestCmdToUse) throws IOException | |||
| { | |||
| connection = connection_; | |||
| statusCode = statusCode_; | |||
| responseHeaders = responseHeaders_; | |||
| isPost = isPostToUse; | |||
| postData = postDataToUse; | |||
| headers = headersToUse; | |||
| timeOutMs = timeOutMsToUse; | |||
| statusCode = statusCodeToUse; | |||
| responseHeaders = responseHeadersToUse; | |||
| totalLength = -1; | |||
| numRedirectsToFollow = numRedirectsToFollowToUse; | |||
| httpRequestCmd = httpRequestCmdToUse; | |||
| connection = createConnection (address, isPost, postData, headers, timeOutMs, httpRequestCmd); | |||
| } | |||
| private final HttpURLConnection createConnection (String address, boolean isPost, byte[] postData, | |||
| String headers, int timeOutMs, String httpRequestCmdToUse) throws IOException | |||
| { | |||
| HttpURLConnection newConnection = (HttpURLConnection) (new URL(address).openConnection()); | |||
| try | |||
| { | |||
| newConnection.setInstanceFollowRedirects (false); | |||
| newConnection.setConnectTimeout (timeOutMs); | |||
| newConnection.setReadTimeout (timeOutMs); | |||
| // headers - if not empty, this string is appended onto the headers that are used for the request. It must therefore be a valid set of HTML header directives, separated by newlines. | |||
| // So convert headers string to an array, with an element for each line | |||
| String headerLines[] = headers.split("\\n"); | |||
| // Set request headers | |||
| for (int i = 0; i < headerLines.length; ++i) | |||
| { | |||
| int pos = headerLines[i].indexOf (":"); | |||
| if (pos > 0 && pos < headerLines[i].length()) | |||
| { | |||
| String field = headerLines[i].substring (0, pos); | |||
| String value = headerLines[i].substring (pos + 1); | |||
| if (value.length() > 0) | |||
| newConnection.setRequestProperty (field, value); | |||
| } | |||
| } | |||
| newConnection.setRequestMethod (httpRequestCmd); | |||
| if (isPost) | |||
| { | |||
| newConnection.setDoOutput (true); | |||
| if (postData != null) | |||
| { | |||
| OutputStream out = newConnection.getOutputStream(); | |||
| out.write(postData); | |||
| out.flush(); | |||
| } | |||
| } | |||
| return newConnection; | |||
| } | |||
| catch (Throwable e) | |||
| { | |||
| newConnection.disconnect(); | |||
| throw new IOException ("Connection error"); | |||
| } | |||
| } | |||
| private final InputStream getCancellableStream (final boolean isInput) throws ExecutionException | |||
| @@ -1011,19 +1094,12 @@ public class JuceDemo extends Activity | |||
| try | |||
| { | |||
| if (connection.getConnectTimeout() > 0) | |||
| return streamFuture.get (connection.getConnectTimeout(), TimeUnit.MILLISECONDS); | |||
| else | |||
| return streamFuture.get(); | |||
| return streamFuture.get(); | |||
| } | |||
| catch (InterruptedException e) | |||
| { | |||
| return null; | |||
| } | |||
| catch (TimeoutException e) | |||
| { | |||
| return null; | |||
| } | |||
| catch (CancellationException e) | |||
| { | |||
| return null; | |||
| @@ -1031,6 +1107,89 @@ public class JuceDemo extends Activity | |||
| } | |||
| public final boolean connect() | |||
| { | |||
| boolean result = false; | |||
| int numFollowedRedirects = 0; | |||
| while (true) | |||
| { | |||
| result = doConnect(); | |||
| if (! result) | |||
| return false; | |||
| if (++numFollowedRedirects > numRedirectsToFollow) | |||
| break; | |||
| int status = statusCode[0]; | |||
| if (status == 301 || status == 302 || status == 303 || status == 307) | |||
| { | |||
| // Assumes only one occurrence of "Location" | |||
| int pos1 = responseHeaders.indexOf ("Location:") + 10; | |||
| int pos2 = responseHeaders.indexOf ("\n", pos1); | |||
| if (pos2 > pos1) | |||
| { | |||
| String currentLocation = connection.getURL().toString(); | |||
| String newLocation = responseHeaders.substring (pos1, pos2); | |||
| try | |||
| { | |||
| // Handle newLocation whether it's absolute or relative | |||
| URL baseUrl = new URL (currentLocation); | |||
| URL newUrl = new URL (baseUrl, newLocation); | |||
| String transformedNewLocation = newUrl.toString(); | |||
| if (transformedNewLocation != currentLocation) | |||
| { | |||
| // Clear responseHeaders before next iteration | |||
| responseHeaders.delete (0, responseHeaders.length()); | |||
| synchronized (createStreamLock) | |||
| { | |||
| if (hasBeenCancelled.get()) | |||
| return false; | |||
| connection.disconnect(); | |||
| try | |||
| { | |||
| connection = createConnection (transformedNewLocation, isPost, | |||
| postData, headers, timeOutMs, | |||
| httpRequestCmd); | |||
| } | |||
| catch (Throwable e) | |||
| { | |||
| return false; | |||
| } | |||
| } | |||
| } | |||
| else | |||
| { | |||
| break; | |||
| } | |||
| } | |||
| catch (Throwable e) | |||
| { | |||
| return false; | |||
| } | |||
| } | |||
| else | |||
| { | |||
| break; | |||
| } | |||
| } | |||
| else | |||
| { | |||
| break; | |||
| } | |||
| } | |||
| return result; | |||
| } | |||
| private final boolean doConnect() | |||
| { | |||
| synchronized (createStreamLock) | |||
| { | |||
| @@ -1068,9 +1227,16 @@ public class JuceDemo extends Activity | |||
| {} | |||
| for (java.util.Map.Entry<String, java.util.List<String>> entry : connection.getHeaderFields().entrySet()) | |||
| { | |||
| if (entry.getKey() != null && entry.getValue() != null) | |||
| responseHeaders.append (entry.getKey() + ": " | |||
| + android.text.TextUtils.join (",", entry.getValue()) + "\n"); | |||
| { | |||
| responseHeaders.append(entry.getKey() + ": " | |||
| + android.text.TextUtils.join(",", entry.getValue()) + "\n"); | |||
| if (entry.getKey().compareTo ("Content-Length") == 0) | |||
| totalLength = Integer.decode (entry.getValue().get (0)); | |||
| } | |||
| } | |||
| return true; | |||
| } | |||
| @@ -1173,13 +1339,20 @@ public class JuceDemo extends Activity | |||
| } | |||
| public final long getPosition() { return position; } | |||
| public final long getTotalLength() { return -1; } | |||
| public final long getTotalLength() { return totalLength; } | |||
| public final boolean isExhausted() { return false; } | |||
| public final boolean setPosition (long newPos) { return false; } | |||
| private boolean isPost; | |||
| private byte[] postData; | |||
| private String headers; | |||
| private int timeOutMs; | |||
| String httpRequestCmd; | |||
| private HttpURLConnection connection; | |||
| private int[] statusCode; | |||
| private StringBuffer responseHeaders; | |||
| private int totalLength; | |||
| private int numRedirectsToFollow; | |||
| private InputStream inputStream; | |||
| private long position; | |||
| private final ReentrantLock createStreamLock = new ReentrantLock(); | |||
| @@ -1201,89 +1374,15 @@ public class JuceDemo extends Activity | |||
| else if (timeOutMs == 0) | |||
| timeOutMs = 30000; | |||
| // headers - if not empty, this string is appended onto the headers that are used for the request. It must therefore be a valid set of HTML header directives, separated by newlines. | |||
| // So convert headers string to an array, with an element for each line | |||
| String headerLines[] = headers.split("\\n"); | |||
| for (;;) | |||
| { | |||
| try | |||
| { | |||
| HttpURLConnection connection = (HttpURLConnection) (new URL(address).openConnection()); | |||
| if (connection != null) | |||
| { | |||
| try | |||
| { | |||
| connection.setInstanceFollowRedirects (false); | |||
| connection.setConnectTimeout (timeOutMs); | |||
| connection.setReadTimeout (timeOutMs); | |||
| HTTPStream httpStream = new HTTPStream (address, isPost, postData, headers, | |||
| timeOutMs, statusCode, responseHeaders, | |||
| numRedirectsToFollow, httpRequestCmd); | |||
| // Set request headers | |||
| for (int i = 0; i < headerLines.length; ++i) | |||
| { | |||
| int pos = headerLines[i].indexOf (":"); | |||
| if (pos > 0 && pos < headerLines[i].length()) | |||
| { | |||
| String field = headerLines[i].substring (0, pos); | |||
| String value = headerLines[i].substring (pos + 1); | |||
| if (value.length() > 0) | |||
| connection.setRequestProperty (field, value); | |||
| } | |||
| } | |||
| connection.setRequestMethod (httpRequestCmd); | |||
| if (isPost) | |||
| { | |||
| connection.setDoOutput (true); | |||
| if (postData != null) | |||
| { | |||
| OutputStream out = connection.getOutputStream(); | |||
| out.write(postData); | |||
| out.flush(); | |||
| } | |||
| } | |||
| HTTPStream httpStream = new HTTPStream (connection, statusCode, responseHeaders); | |||
| // Process redirect & continue as necessary | |||
| int status = statusCode[0]; | |||
| if (--numRedirectsToFollow >= 0 | |||
| && (status == 301 || status == 302 || status == 303 || status == 307)) | |||
| { | |||
| // Assumes only one occurrence of "Location" | |||
| int pos1 = responseHeaders.indexOf ("Location:") + 10; | |||
| int pos2 = responseHeaders.indexOf ("\n", pos1); | |||
| if (pos2 > pos1) | |||
| { | |||
| String newLocation = responseHeaders.substring(pos1, pos2); | |||
| // Handle newLocation whether it's absolute or relative | |||
| URL baseUrl = new URL (address); | |||
| URL newUrl = new URL (baseUrl, newLocation); | |||
| String transformedNewLocation = newUrl.toString(); | |||
| if (transformedNewLocation != address) | |||
| { | |||
| address = transformedNewLocation; | |||
| // Clear responseHeaders before next iteration | |||
| responseHeaders.delete (0, responseHeaders.length()); | |||
| continue; | |||
| } | |||
| } | |||
| } | |||
| return httpStream; | |||
| } | |||
| catch (Throwable e) | |||
| { | |||
| connection.disconnect(); | |||
| } | |||
| } | |||
| return httpStream; | |||
| } | |||
| catch (Throwable e) {} | |||
| @@ -1309,7 +1408,14 @@ public class JuceDemo extends Activity | |||
| return Environment.getExternalStoragePublicDirectory (type).getAbsolutePath(); | |||
| } | |||
| public static final String getDocumentsFolder() { return Environment.getDataDirectory().getAbsolutePath(); } | |||
| public static final String getDocumentsFolder() | |||
| { | |||
| if (getAndroidSDKVersion() >= 19) | |||
| return getFileLocation ("Documents"); | |||
| return Environment.getDataDirectory().getAbsolutePath(); | |||
| } | |||
| public static final String getPicturesFolder() { return getFileLocation (Environment.DIRECTORY_PICTURES); } | |||
| public static final String getMusicFolder() { return getFileLocation (Environment.DIRECTORY_MUSIC); } | |||
| public static final String getMoviesFolder() { return getFileLocation (Environment.DIRECTORY_MOVIES); } | |||
| @@ -1404,7 +1510,7 @@ public class JuceDemo extends Activity | |||
| return null; | |||
| } | |||
| public final int getAndroidSDKVersion() | |||
| public static final int getAndroidSDKVersion() | |||
| { | |||
| return android.os.Build.VERSION.SDK_INT; | |||
| } | |||
| @@ -1,7 +1,7 @@ | |||
| <?xml version="1.0" encoding="UTF-8"?> | |||
| <JUCERPROJECT id="sBBIzr" name="JuceDemo" projectType="guiapp" version="3.0.0" | |||
| bundleIdentifier="com.roli.JuceDemo" jucerVersion="5.1.1" defines="JUCE_UNIT_TESTS=1" | |||
| bundleIdentifier="com.roli.JuceDemo" jucerVersion="5.1.2" defines="JUCE_UNIT_TESTS=1" | |||
| includeBinaryInAppConfig="1" displaySplashScreen="0" reportAppUsage="0" | |||
| splashScreenColour="Dark" companyName="ROLI Ltd." cppLanguageStandard="11"> | |||
| <EXPORTFORMATS> | |||
| @@ -26,6 +26,8 @@ | |||
| #include "../JuceDemoHeader.h" | |||
| // these classes are C++11-only | |||
| #if JUCE_HAS_CONSTEXPR | |||
| struct GridDemo : public Component | |||
| { | |||
| @@ -116,3 +118,5 @@ struct GridDemo : public Component | |||
| // This static object will register this demo type in a global list of demos.. | |||
| static JuceDemoType<GridDemo> demo ("10 Components: GridDemo"); | |||
| #endif // JUCE_HAS_CONSTEXPR | |||
| @@ -26,7 +26,7 @@ | |||
| #include "../JuceDemoHeader.h" | |||
| #if JUCE_MAC || JUCE_DIRECTSHOW | |||
| #if JUCE_MAC || (JUCE_WINDOWS && ! JUCE_MINGW) | |||
| //============================================================================== | |||
| // so that we can easily have two video windows each with a file browser, wrap this up as a class.. | |||
| @@ -105,10 +105,13 @@ struct SlidersPage : public Component | |||
| : hintLabel ("hint", "Try right-clicking on a slider for an options menu. \n\n" | |||
| "Also, holding down CTRL while dragging will turn on a slider's velocity-sensitive mode") | |||
| { | |||
| Rectangle<int> layoutArea { 20, 20, 580, 430 }; | |||
| auto sliderArea = layoutArea.removeFromTop (320); | |||
| Slider* s = createSlider (false); | |||
| s->setSliderStyle (Slider::LinearVertical); | |||
| s->setTextBoxStyle (Slider::TextBoxBelow, false, 100, 20); | |||
| s->setBounds (10, 25, 70, 200); | |||
| s->setBounds (sliderArea.removeFromLeft (70)); | |||
| s->setDoubleClickReturnValue (true, 50.0); // double-clicking this slider will set it to 50.0 | |||
| s->setTextValueSuffix (" units"); | |||
| @@ -117,68 +120,85 @@ struct SlidersPage : public Component | |||
| s->setVelocityBasedMode (true); | |||
| s->setSkewFactor (0.5); | |||
| s->setTextBoxStyle (Slider::TextBoxAbove, true, 100, 20); | |||
| s->setBounds (85, 25, 70, 200); | |||
| s->setBounds (sliderArea.removeFromLeft (70)); | |||
| s->setTextValueSuffix (" rels"); | |||
| sliderArea.removeFromLeft (20); | |||
| auto horizonalSliderArea = sliderArea.removeFromLeft (180); | |||
| s = createSlider (true); | |||
| s->setSliderStyle (Slider::LinearHorizontal); | |||
| s->setTextBoxStyle (Slider::TextBoxLeft, false, 80, 20); | |||
| s->setBounds (180, 35, 150, 20); | |||
| s->setBounds (horizonalSliderArea.removeFromTop (20)); | |||
| s = createSlider (false); | |||
| s->setSliderStyle (Slider::LinearHorizontal); | |||
| s->setTextBoxStyle (Slider::NoTextBox, false, 0, 0); | |||
| s->setBounds (180, 65, 150, 20); | |||
| horizonalSliderArea.removeFromTop (20); | |||
| s->setBounds (horizonalSliderArea.removeFromTop (20)); | |||
| s->setPopupDisplayEnabled (true, false, this); | |||
| s->setTextValueSuffix (" nuns required to change a lightbulb"); | |||
| s = createSlider (false); | |||
| s->setSliderStyle (Slider::LinearHorizontal); | |||
| s->setTextBoxStyle (Slider::TextEntryBoxPosition::TextBoxAbove, false, 70, 20); | |||
| horizonalSliderArea.removeFromTop (20); | |||
| s->setBounds (horizonalSliderArea.removeFromTop (50)); | |||
| s->setPopupDisplayEnabled (true, false, this); | |||
| s = createSlider (false); | |||
| s->setSliderStyle (Slider::IncDecButtons); | |||
| s->setTextBoxStyle (Slider::TextBoxLeft, false, 50, 20); | |||
| s->setBounds (180, 105, 100, 20); | |||
| horizonalSliderArea.removeFromTop (20); | |||
| s->setBounds (horizonalSliderArea.removeFromTop (20)); | |||
| s->setIncDecButtonsMode (Slider::incDecButtonsDraggable_Vertical); | |||
| s = createSlider (false); | |||
| s->setSliderStyle (Slider::Rotary); | |||
| s->setRotaryParameters (float_Pi * 1.2f, float_Pi * 2.8f, false); | |||
| s->setTextBoxStyle (Slider::TextBoxRight, false, 70, 20); | |||
| s->setBounds (190, 145, 120, 40); | |||
| horizonalSliderArea.removeFromTop (15); | |||
| s->setBounds (horizonalSliderArea.removeFromTop (70)); | |||
| s->setTextValueSuffix (" mm"); | |||
| s = createSlider (false); | |||
| s->setSliderStyle (Slider::LinearBar); | |||
| s->setBounds (180, 195, 100, 30); | |||
| horizonalSliderArea.removeFromTop (10); | |||
| s->setBounds (horizonalSliderArea.removeFromTop (30)); | |||
| s->setTextValueSuffix (" gallons"); | |||
| sliderArea.removeFromLeft (20); | |||
| auto twoValueSliderArea = sliderArea.removeFromLeft (180); | |||
| s = createSlider (false); | |||
| s->setSliderStyle (Slider::TwoValueHorizontal); | |||
| s->setBounds (360, 20, 160, 40); | |||
| s->setBounds (twoValueSliderArea.removeFromTop (40)); | |||
| s = createSlider (false); | |||
| s->setSliderStyle (Slider::TwoValueVertical); | |||
| s->setBounds (360, 110, 40, 160); | |||
| s->setSliderStyle (Slider::ThreeValueHorizontal); | |||
| s->setPopupDisplayEnabled (true, false, this); | |||
| twoValueSliderArea.removeFromTop (10); | |||
| s->setBounds (twoValueSliderArea.removeFromTop (40)); | |||
| s = createSlider (false); | |||
| s->setSliderStyle (Slider::ThreeValueHorizontal); | |||
| s->setBounds (360, 70, 160, 40); | |||
| s->setSliderStyle (Slider::TwoValueVertical); | |||
| twoValueSliderArea.removeFromLeft (30); | |||
| s->setBounds (twoValueSliderArea.removeFromLeft (40)); | |||
| s = createSlider (false); | |||
| s->setSliderStyle (Slider::ThreeValueVertical); | |||
| s->setBounds (440, 110, 40, 160); | |||
| s->setPopupDisplayEnabled (true, false, this); | |||
| twoValueSliderArea.removeFromLeft (30); | |||
| s->setBounds (twoValueSliderArea.removeFromLeft (40)); | |||
| s = createSlider (false); | |||
| s->setSliderStyle (Slider::LinearBarVertical); | |||
| s->setTextBoxStyle (Slider::NoTextBox, false, 0, 0); | |||
| s->setBounds (540, 35, 20, 230); | |||
| sliderArea.removeFromLeft (20); | |||
| s->setBounds (sliderArea.removeFromLeft (20)); | |||
| s->setPopupDisplayEnabled (true, true, this); | |||
| s->setTextValueSuffix (" mickles in a muckle"); | |||
| for (int i = 7; i <= 10; ++i) | |||
| { | |||
| sliders.getUnchecked(i)->setTextBoxStyle (Slider::NoTextBox, false, 0, 0); | |||
| sliders.getUnchecked(i)->setPopupDisplayEnabled (true, false, this); | |||
| } | |||
| /* Here, we'll create a Value object, and tell a bunch of our sliders to use it as their | |||
| value source. By telling them all to share the same Value, they'll stay in sync with | |||
| each other. | |||
| @@ -188,7 +208,7 @@ struct SlidersPage : public Component | |||
| */ | |||
| Value sharedValue; | |||
| sharedValue = Random::getSystemRandom().nextDouble() * 100; | |||
| for (int i = 0; i < 7; ++i) | |||
| for (int i = 0; i < 8; ++i) | |||
| sliders.getUnchecked(i)->getValueObject().referTo (sharedValue); | |||
| // ..and now we'll do the same for all our min/max slider values.. | |||
| @@ -196,13 +216,15 @@ struct SlidersPage : public Component | |||
| sharedValueMin = Random::getSystemRandom().nextDouble() * 40.0; | |||
| sharedValueMax = Random::getSystemRandom().nextDouble() * 40.0 + 60.0; | |||
| for (int i = 7; i <= 10; ++i) | |||
| for (int i = 8; i <= 11; ++i) | |||
| { | |||
| sliders.getUnchecked(i)->getMaxValueObject().referTo (sharedValueMax); | |||
| sliders.getUnchecked(i)->getMinValueObject().referTo (sharedValueMin); | |||
| auto* selectedSlider = sliders.getUnchecked(i); | |||
| selectedSlider->setTextBoxStyle (Slider::NoTextBox, false, 0, 0); | |||
| selectedSlider->getMaxValueObject().referTo (sharedValueMax); | |||
| selectedSlider->getMinValueObject().referTo (sharedValueMin); | |||
| } | |||
| hintLabel.setBounds (20, 245, 350, 150); | |||
| hintLabel.setBounds (layoutArea); | |||
| addAndMakeVisible (hintLabel); | |||
| } | |||
| @@ -1,7 +1,7 @@ | |||
| <?xml version="1.0" encoding="UTF-8"?> | |||
| <JUCERPROJECT id="tTAKTK1s" name="HelloWorld" projectType="guiapp" juceFolder="../../../juce" | |||
| jucerVersion="5.1.1" version="1.0.0" bundleIdentifier="com.roli.jucehelloworld" | |||
| jucerVersion="5.1.2" version="1.0.0" bundleIdentifier="com.roli.jucehelloworld" | |||
| companyName="ROLI Ltd." includeBinaryInAppConfig="1" displaySplashScreen="0" | |||
| reportAppUsage="0" splashScreenColour="Dark" cppLanguageStandard="11"> | |||
| <EXPORTFORMATS> | |||
| @@ -8,7 +8,7 @@ SET(BINARY_NAME "juce_jni") | |||
| add_library("cpufeatures" STATIC "${ANDROID_NDK}/sources/android/cpufeatures/cpu-features.c") | |||
| set_source_files_properties("${ANDROID_NDK}/sources/android/cpufeatures/cpu-features.c" PROPERTIES COMPILE_FLAGS "-Wno-sign-conversion -Wno-gnu-statement-expression") | |||
| add_definitions("-DJUCE_ANDROID=1" "-DJUCE_ANDROID_API_VERSION=10" "-DJUCE_ANDROID_ACTIVITY_CLASSNAME=com_roli_juceinapppurchasesample_InAppPurchase" "-DJUCE_ANDROID_ACTIVITY_CLASSPATH=\"com/roli/juceinapppurchasesample/InAppPurchase\"" "-DJUCER_ANDROIDSTUDIO_7F0E4A25=1" "-DJUCE_APP_VERSION=0.0.2" "-DJUCE_APP_VERSION_HEX=0x2") | |||
| add_definitions("-DJUCE_ANDROID=1" "-DJUCE_ANDROID_API_VERSION=10" "-DJUCE_ANDROID_ACTIVITY_CLASSNAME=com_roli_juceinapppurchasesample_InAppPurchase" "-DJUCE_ANDROID_ACTIVITY_CLASSPATH=\"com/roli/juceinapppurchasesample/InAppPurchase\"" "-DJUCE_IN_APP_PURCHASES=1" "-DJUCER_ANDROIDSTUDIO_7F0E4A25=1" "-DJUCE_APP_VERSION=0.0.2" "-DJUCE_APP_VERSION_HEX=0x2") | |||
| include_directories( AFTER | |||
| "../../../JuceLibraryCode" | |||
| @@ -519,11 +519,18 @@ public class InAppPurchase extends Activity | |||
| builder.setTitle (title) | |||
| .setMessage (message) | |||
| .setCancelable (true) | |||
| .setOnCancelListener (new DialogInterface.OnCancelListener() | |||
| { | |||
| public void onCancel (DialogInterface dialog) | |||
| { | |||
| InAppPurchase.this.alertDismissed (callback, 0); | |||
| } | |||
| }) | |||
| .setPositiveButton ("OK", new DialogInterface.OnClickListener() | |||
| { | |||
| public void onClick (DialogInterface dialog, int id) | |||
| { | |||
| dialog.cancel(); | |||
| dialog.dismiss(); | |||
| InAppPurchase.this.alertDismissed (callback, 0); | |||
| } | |||
| }); | |||
| @@ -538,11 +545,18 @@ public class InAppPurchase extends Activity | |||
| builder.setTitle (title) | |||
| .setMessage (message) | |||
| .setCancelable (true) | |||
| .setOnCancelListener (new DialogInterface.OnCancelListener() | |||
| { | |||
| public void onCancel (DialogInterface dialog) | |||
| { | |||
| InAppPurchase.this.alertDismissed (callback, 0); | |||
| } | |||
| }) | |||
| .setPositiveButton (okButtonText.isEmpty() ? "OK" : okButtonText, new DialogInterface.OnClickListener() | |||
| { | |||
| public void onClick (DialogInterface dialog, int id) | |||
| { | |||
| dialog.cancel(); | |||
| dialog.dismiss(); | |||
| InAppPurchase.this.alertDismissed (callback, 1); | |||
| } | |||
| }) | |||
| @@ -550,7 +564,7 @@ public class InAppPurchase extends Activity | |||
| { | |||
| public void onClick (DialogInterface dialog, int id) | |||
| { | |||
| dialog.cancel(); | |||
| dialog.dismiss(); | |||
| InAppPurchase.this.alertDismissed (callback, 0); | |||
| } | |||
| }); | |||
| @@ -564,11 +578,18 @@ public class InAppPurchase extends Activity | |||
| builder.setTitle (title) | |||
| .setMessage (message) | |||
| .setCancelable (true) | |||
| .setOnCancelListener (new DialogInterface.OnCancelListener() | |||
| { | |||
| public void onCancel (DialogInterface dialog) | |||
| { | |||
| InAppPurchase.this.alertDismissed (callback, 0); | |||
| } | |||
| }) | |||
| .setPositiveButton ("Yes", new DialogInterface.OnClickListener() | |||
| { | |||
| public void onClick (DialogInterface dialog, int id) | |||
| { | |||
| dialog.cancel(); | |||
| dialog.dismiss(); | |||
| InAppPurchase.this.alertDismissed (callback, 1); | |||
| } | |||
| }) | |||
| @@ -576,7 +597,7 @@ public class InAppPurchase extends Activity | |||
| { | |||
| public void onClick (DialogInterface dialog, int id) | |||
| { | |||
| dialog.cancel(); | |||
| dialog.dismiss(); | |||
| InAppPurchase.this.alertDismissed (callback, 2); | |||
| } | |||
| }) | |||
| @@ -584,7 +605,7 @@ public class InAppPurchase extends Activity | |||
| { | |||
| public void onClick (DialogInterface dialog, int id) | |||
| { | |||
| dialog.cancel(); | |||
| dialog.dismiss(); | |||
| InAppPurchase.this.alertDismissed (callback, 0); | |||
| } | |||
| }); | |||
| @@ -982,13 +1003,75 @@ public class InAppPurchase extends Activity | |||
| //============================================================================== | |||
| public static class HTTPStream | |||
| { | |||
| public HTTPStream (HttpURLConnection connection_, | |||
| int[] statusCode_, | |||
| StringBuffer responseHeaders_) | |||
| public HTTPStream (String address, boolean isPostToUse, byte[] postDataToUse, | |||
| String headersToUse, int timeOutMsToUse, | |||
| int[] statusCodeToUse, StringBuffer responseHeadersToUse, | |||
| int numRedirectsToFollowToUse, String httpRequestCmdToUse) throws IOException | |||
| { | |||
| connection = connection_; | |||
| statusCode = statusCode_; | |||
| responseHeaders = responseHeaders_; | |||
| isPost = isPostToUse; | |||
| postData = postDataToUse; | |||
| headers = headersToUse; | |||
| timeOutMs = timeOutMsToUse; | |||
| statusCode = statusCodeToUse; | |||
| responseHeaders = responseHeadersToUse; | |||
| totalLength = -1; | |||
| numRedirectsToFollow = numRedirectsToFollowToUse; | |||
| httpRequestCmd = httpRequestCmdToUse; | |||
| connection = createConnection (address, isPost, postData, headers, timeOutMs, httpRequestCmd); | |||
| } | |||
| private final HttpURLConnection createConnection (String address, boolean isPost, byte[] postData, | |||
| String headers, int timeOutMs, String httpRequestCmdToUse) throws IOException | |||
| { | |||
| HttpURLConnection newConnection = (HttpURLConnection) (new URL(address).openConnection()); | |||
| try | |||
| { | |||
| newConnection.setInstanceFollowRedirects (false); | |||
| newConnection.setConnectTimeout (timeOutMs); | |||
| newConnection.setReadTimeout (timeOutMs); | |||
| // headers - if not empty, this string is appended onto the headers that are used for the request. It must therefore be a valid set of HTML header directives, separated by newlines. | |||
| // So convert headers string to an array, with an element for each line | |||
| String headerLines[] = headers.split("\\n"); | |||
| // Set request headers | |||
| for (int i = 0; i < headerLines.length; ++i) | |||
| { | |||
| int pos = headerLines[i].indexOf (":"); | |||
| if (pos > 0 && pos < headerLines[i].length()) | |||
| { | |||
| String field = headerLines[i].substring (0, pos); | |||
| String value = headerLines[i].substring (pos + 1); | |||
| if (value.length() > 0) | |||
| newConnection.setRequestProperty (field, value); | |||
| } | |||
| } | |||
| newConnection.setRequestMethod (httpRequestCmd); | |||
| if (isPost) | |||
| { | |||
| newConnection.setDoOutput (true); | |||
| if (postData != null) | |||
| { | |||
| OutputStream out = newConnection.getOutputStream(); | |||
| out.write(postData); | |||
| out.flush(); | |||
| } | |||
| } | |||
| return newConnection; | |||
| } | |||
| catch (Throwable e) | |||
| { | |||
| newConnection.disconnect(); | |||
| throw new IOException ("Connection error"); | |||
| } | |||
| } | |||
| private final InputStream getCancellableStream (final boolean isInput) throws ExecutionException | |||
| @@ -1011,19 +1094,12 @@ public class InAppPurchase extends Activity | |||
| try | |||
| { | |||
| if (connection.getConnectTimeout() > 0) | |||
| return streamFuture.get (connection.getConnectTimeout(), TimeUnit.MILLISECONDS); | |||
| else | |||
| return streamFuture.get(); | |||
| return streamFuture.get(); | |||
| } | |||
| catch (InterruptedException e) | |||
| { | |||
| return null; | |||
| } | |||
| catch (TimeoutException e) | |||
| { | |||
| return null; | |||
| } | |||
| catch (CancellationException e) | |||
| { | |||
| return null; | |||
| @@ -1031,6 +1107,89 @@ public class InAppPurchase extends Activity | |||
| } | |||
| public final boolean connect() | |||
| { | |||
| boolean result = false; | |||
| int numFollowedRedirects = 0; | |||
| while (true) | |||
| { | |||
| result = doConnect(); | |||
| if (! result) | |||
| return false; | |||
| if (++numFollowedRedirects > numRedirectsToFollow) | |||
| break; | |||
| int status = statusCode[0]; | |||
| if (status == 301 || status == 302 || status == 303 || status == 307) | |||
| { | |||
| // Assumes only one occurrence of "Location" | |||
| int pos1 = responseHeaders.indexOf ("Location:") + 10; | |||
| int pos2 = responseHeaders.indexOf ("\n", pos1); | |||
| if (pos2 > pos1) | |||
| { | |||
| String currentLocation = connection.getURL().toString(); | |||
| String newLocation = responseHeaders.substring (pos1, pos2); | |||
| try | |||
| { | |||
| // Handle newLocation whether it's absolute or relative | |||
| URL baseUrl = new URL (currentLocation); | |||
| URL newUrl = new URL (baseUrl, newLocation); | |||
| String transformedNewLocation = newUrl.toString(); | |||
| if (transformedNewLocation != currentLocation) | |||
| { | |||
| // Clear responseHeaders before next iteration | |||
| responseHeaders.delete (0, responseHeaders.length()); | |||
| synchronized (createStreamLock) | |||
| { | |||
| if (hasBeenCancelled.get()) | |||
| return false; | |||
| connection.disconnect(); | |||
| try | |||
| { | |||
| connection = createConnection (transformedNewLocation, isPost, | |||
| postData, headers, timeOutMs, | |||
| httpRequestCmd); | |||
| } | |||
| catch (Throwable e) | |||
| { | |||
| return false; | |||
| } | |||
| } | |||
| } | |||
| else | |||
| { | |||
| break; | |||
| } | |||
| } | |||
| catch (Throwable e) | |||
| { | |||
| return false; | |||
| } | |||
| } | |||
| else | |||
| { | |||
| break; | |||
| } | |||
| } | |||
| else | |||
| { | |||
| break; | |||
| } | |||
| } | |||
| return result; | |||
| } | |||
| private final boolean doConnect() | |||
| { | |||
| synchronized (createStreamLock) | |||
| { | |||
| @@ -1068,9 +1227,16 @@ public class InAppPurchase extends Activity | |||
| {} | |||
| for (java.util.Map.Entry<String, java.util.List<String>> entry : connection.getHeaderFields().entrySet()) | |||
| { | |||
| if (entry.getKey() != null && entry.getValue() != null) | |||
| responseHeaders.append (entry.getKey() + ": " | |||
| + android.text.TextUtils.join (",", entry.getValue()) + "\n"); | |||
| { | |||
| responseHeaders.append(entry.getKey() + ": " | |||
| + android.text.TextUtils.join(",", entry.getValue()) + "\n"); | |||
| if (entry.getKey().compareTo ("Content-Length") == 0) | |||
| totalLength = Integer.decode (entry.getValue().get (0)); | |||
| } | |||
| } | |||
| return true; | |||
| } | |||
| @@ -1173,13 +1339,20 @@ public class InAppPurchase extends Activity | |||
| } | |||
| public final long getPosition() { return position; } | |||
| public final long getTotalLength() { return -1; } | |||
| public final long getTotalLength() { return totalLength; } | |||
| public final boolean isExhausted() { return false; } | |||
| public final boolean setPosition (long newPos) { return false; } | |||
| private boolean isPost; | |||
| private byte[] postData; | |||
| private String headers; | |||
| private int timeOutMs; | |||
| String httpRequestCmd; | |||
| private HttpURLConnection connection; | |||
| private int[] statusCode; | |||
| private StringBuffer responseHeaders; | |||
| private int totalLength; | |||
| private int numRedirectsToFollow; | |||
| private InputStream inputStream; | |||
| private long position; | |||
| private final ReentrantLock createStreamLock = new ReentrantLock(); | |||
| @@ -1201,89 +1374,15 @@ public class InAppPurchase extends Activity | |||
| else if (timeOutMs == 0) | |||
| timeOutMs = 30000; | |||
| // headers - if not empty, this string is appended onto the headers that are used for the request. It must therefore be a valid set of HTML header directives, separated by newlines. | |||
| // So convert headers string to an array, with an element for each line | |||
| String headerLines[] = headers.split("\\n"); | |||
| for (;;) | |||
| { | |||
| try | |||
| { | |||
| HttpURLConnection connection = (HttpURLConnection) (new URL(address).openConnection()); | |||
| if (connection != null) | |||
| { | |||
| try | |||
| { | |||
| connection.setInstanceFollowRedirects (false); | |||
| connection.setConnectTimeout (timeOutMs); | |||
| connection.setReadTimeout (timeOutMs); | |||
| HTTPStream httpStream = new HTTPStream (address, isPost, postData, headers, | |||
| timeOutMs, statusCode, responseHeaders, | |||
| numRedirectsToFollow, httpRequestCmd); | |||
| // Set request headers | |||
| for (int i = 0; i < headerLines.length; ++i) | |||
| { | |||
| int pos = headerLines[i].indexOf (":"); | |||
| if (pos > 0 && pos < headerLines[i].length()) | |||
| { | |||
| String field = headerLines[i].substring (0, pos); | |||
| String value = headerLines[i].substring (pos + 1); | |||
| if (value.length() > 0) | |||
| connection.setRequestProperty (field, value); | |||
| } | |||
| } | |||
| connection.setRequestMethod (httpRequestCmd); | |||
| if (isPost) | |||
| { | |||
| connection.setDoOutput (true); | |||
| if (postData != null) | |||
| { | |||
| OutputStream out = connection.getOutputStream(); | |||
| out.write(postData); | |||
| out.flush(); | |||
| } | |||
| } | |||
| HTTPStream httpStream = new HTTPStream (connection, statusCode, responseHeaders); | |||
| // Process redirect & continue as necessary | |||
| int status = statusCode[0]; | |||
| if (--numRedirectsToFollow >= 0 | |||
| && (status == 301 || status == 302 || status == 303 || status == 307)) | |||
| { | |||
| // Assumes only one occurrence of "Location" | |||
| int pos1 = responseHeaders.indexOf ("Location:") + 10; | |||
| int pos2 = responseHeaders.indexOf ("\n", pos1); | |||
| if (pos2 > pos1) | |||
| { | |||
| String newLocation = responseHeaders.substring(pos1, pos2); | |||
| // Handle newLocation whether it's absolute or relative | |||
| URL baseUrl = new URL (address); | |||
| URL newUrl = new URL (baseUrl, newLocation); | |||
| String transformedNewLocation = newUrl.toString(); | |||
| if (transformedNewLocation != address) | |||
| { | |||
| address = transformedNewLocation; | |||
| // Clear responseHeaders before next iteration | |||
| responseHeaders.delete (0, responseHeaders.length()); | |||
| continue; | |||
| } | |||
| } | |||
| } | |||
| return httpStream; | |||
| } | |||
| catch (Throwable e) | |||
| { | |||
| connection.disconnect(); | |||
| } | |||
| } | |||
| return httpStream; | |||
| } | |||
| catch (Throwable e) {} | |||
| @@ -1309,7 +1408,14 @@ public class InAppPurchase extends Activity | |||
| return Environment.getExternalStoragePublicDirectory (type).getAbsolutePath(); | |||
| } | |||
| public static final String getDocumentsFolder() { return Environment.getDataDirectory().getAbsolutePath(); } | |||
| public static final String getDocumentsFolder() | |||
| { | |||
| if (getAndroidSDKVersion() >= 19) | |||
| return getFileLocation ("Documents"); | |||
| return Environment.getDataDirectory().getAbsolutePath(); | |||
| } | |||
| public static final String getPicturesFolder() { return getFileLocation (Environment.DIRECTORY_PICTURES); } | |||
| public static final String getMusicFolder() { return getFileLocation (Environment.DIRECTORY_MUSIC); } | |||
| public static final String getMoviesFolder() { return getFileLocation (Environment.DIRECTORY_MOVIES); } | |||
| @@ -1404,7 +1510,7 @@ public class InAppPurchase extends Activity | |||
| return null; | |||
| } | |||
| public final int getAndroidSDKVersion() | |||
| public static final int getAndroidSDKVersion() | |||
| { | |||
| return android.os.Build.VERSION.SDK_INT; | |||
| } | |||
| @@ -223,6 +223,7 @@ | |||
| GCC_PREPROCESSOR_DEFINITIONS = ( | |||
| "_DEBUG=1", | |||
| "DEBUG=1", | |||
| "JUCE_IN_APP_PURCHASES=1", | |||
| "JUCER_XCODE_IPHONE_5BC26AE3=1", | |||
| "JUCE_APP_VERSION=0.0.2", | |||
| "JUCE_APP_VERSION_HEX=0x2", | |||
| @@ -254,6 +255,7 @@ | |||
| GCC_PREPROCESSOR_DEFINITIONS = ( | |||
| "_NDEBUG=1", | |||
| "NDEBUG=1", | |||
| "JUCE_IN_APP_PURCHASES=1", | |||
| "JUCER_XCODE_IPHONE_5BC26AE3=1", | |||
| "JUCE_APP_VERSION=0.0.2", | |||
| "JUCE_APP_VERSION_HEX=0x2", | |||
| @@ -3,7 +3,7 @@ | |||
| <JUCERPROJECT id="bSx0Ct" name="InAppPurchase" displaySplashScreen="0" reportAppUsage="1" | |||
| splashScreenColour="Dark" projectType="guiapp" version="0.0.2" | |||
| bundleIdentifier="com.roli.juceInAppPurchaseSample" includeBinaryInAppConfig="1" | |||
| cppLanguageStandard="11" jucerVersion="5.1.1" companyName="ROLI Ltd." | |||
| cppLanguageStandard="11" jucerVersion="5.1.2" companyName="ROLI Ltd." | |||
| companyWebsite="www.juce.com" companyEmail="info@juce.com"> | |||
| <MAINGROUP id="y4sxBT" name="InAppPurchase"> | |||
| <GROUP id="{D5BB0A26-3258-B6DA-ED7A-A306325E64AE}" name="BinaryData"> | |||
| @@ -2,7 +2,7 @@ | |||
| <JUCERPROJECT id="IilE7R" name="MPETest" projectType="guiapp" version="1.0.0" | |||
| bundleIdentifier="com.roli.MPETest" includeBinaryInAppConfig="1" | |||
| jucerVersion="5.1.1" displaySplashScreen="0" reportAppUsage="0" | |||
| jucerVersion="5.1.2" displaySplashScreen="0" reportAppUsage="0" | |||
| splashScreenColour="Dark" companyName="ROLI Ltd." cppLanguageStandard="11"> | |||
| <MAINGROUP id="VCQQcn" name="MPETest"> | |||
| <GROUP id="{D43238F0-992F-BC5A-F1AA-31BBBD3D17B0}" name="Source"> | |||
| @@ -1448,11 +1448,18 @@ public class MidiTest extends Activity | |||
| builder.setTitle (title) | |||
| .setMessage (message) | |||
| .setCancelable (true) | |||
| .setOnCancelListener (new DialogInterface.OnCancelListener() | |||
| { | |||
| public void onCancel (DialogInterface dialog) | |||
| { | |||
| MidiTest.this.alertDismissed (callback, 0); | |||
| } | |||
| }) | |||
| .setPositiveButton ("OK", new DialogInterface.OnClickListener() | |||
| { | |||
| public void onClick (DialogInterface dialog, int id) | |||
| { | |||
| dialog.cancel(); | |||
| dialog.dismiss(); | |||
| MidiTest.this.alertDismissed (callback, 0); | |||
| } | |||
| }); | |||
| @@ -1467,11 +1474,18 @@ public class MidiTest extends Activity | |||
| builder.setTitle (title) | |||
| .setMessage (message) | |||
| .setCancelable (true) | |||
| .setOnCancelListener (new DialogInterface.OnCancelListener() | |||
| { | |||
| public void onCancel (DialogInterface dialog) | |||
| { | |||
| MidiTest.this.alertDismissed (callback, 0); | |||
| } | |||
| }) | |||
| .setPositiveButton (okButtonText.isEmpty() ? "OK" : okButtonText, new DialogInterface.OnClickListener() | |||
| { | |||
| public void onClick (DialogInterface dialog, int id) | |||
| { | |||
| dialog.cancel(); | |||
| dialog.dismiss(); | |||
| MidiTest.this.alertDismissed (callback, 1); | |||
| } | |||
| }) | |||
| @@ -1479,7 +1493,7 @@ public class MidiTest extends Activity | |||
| { | |||
| public void onClick (DialogInterface dialog, int id) | |||
| { | |||
| dialog.cancel(); | |||
| dialog.dismiss(); | |||
| MidiTest.this.alertDismissed (callback, 0); | |||
| } | |||
| }); | |||
| @@ -1493,11 +1507,18 @@ public class MidiTest extends Activity | |||
| builder.setTitle (title) | |||
| .setMessage (message) | |||
| .setCancelable (true) | |||
| .setOnCancelListener (new DialogInterface.OnCancelListener() | |||
| { | |||
| public void onCancel (DialogInterface dialog) | |||
| { | |||
| MidiTest.this.alertDismissed (callback, 0); | |||
| } | |||
| }) | |||
| .setPositiveButton ("Yes", new DialogInterface.OnClickListener() | |||
| { | |||
| public void onClick (DialogInterface dialog, int id) | |||
| { | |||
| dialog.cancel(); | |||
| dialog.dismiss(); | |||
| MidiTest.this.alertDismissed (callback, 1); | |||
| } | |||
| }) | |||
| @@ -1505,7 +1526,7 @@ public class MidiTest extends Activity | |||
| { | |||
| public void onClick (DialogInterface dialog, int id) | |||
| { | |||
| dialog.cancel(); | |||
| dialog.dismiss(); | |||
| MidiTest.this.alertDismissed (callback, 2); | |||
| } | |||
| }) | |||
| @@ -1513,7 +1534,7 @@ public class MidiTest extends Activity | |||
| { | |||
| public void onClick (DialogInterface dialog, int id) | |||
| { | |||
| dialog.cancel(); | |||
| dialog.dismiss(); | |||
| MidiTest.this.alertDismissed (callback, 0); | |||
| } | |||
| }); | |||
| @@ -1911,13 +1932,75 @@ public class MidiTest extends Activity | |||
| //============================================================================== | |||
| public static class HTTPStream | |||
| { | |||
| public HTTPStream (HttpURLConnection connection_, | |||
| int[] statusCode_, | |||
| StringBuffer responseHeaders_) | |||
| public HTTPStream (String address, boolean isPostToUse, byte[] postDataToUse, | |||
| String headersToUse, int timeOutMsToUse, | |||
| int[] statusCodeToUse, StringBuffer responseHeadersToUse, | |||
| int numRedirectsToFollowToUse, String httpRequestCmdToUse) throws IOException | |||
| { | |||
| connection = connection_; | |||
| statusCode = statusCode_; | |||
| responseHeaders = responseHeaders_; | |||
| isPost = isPostToUse; | |||
| postData = postDataToUse; | |||
| headers = headersToUse; | |||
| timeOutMs = timeOutMsToUse; | |||
| statusCode = statusCodeToUse; | |||
| responseHeaders = responseHeadersToUse; | |||
| totalLength = -1; | |||
| numRedirectsToFollow = numRedirectsToFollowToUse; | |||
| httpRequestCmd = httpRequestCmdToUse; | |||
| connection = createConnection (address, isPost, postData, headers, timeOutMs, httpRequestCmd); | |||
| } | |||
| private final HttpURLConnection createConnection (String address, boolean isPost, byte[] postData, | |||
| String headers, int timeOutMs, String httpRequestCmdToUse) throws IOException | |||
| { | |||
| HttpURLConnection newConnection = (HttpURLConnection) (new URL(address).openConnection()); | |||
| try | |||
| { | |||
| newConnection.setInstanceFollowRedirects (false); | |||
| newConnection.setConnectTimeout (timeOutMs); | |||
| newConnection.setReadTimeout (timeOutMs); | |||
| // headers - if not empty, this string is appended onto the headers that are used for the request. It must therefore be a valid set of HTML header directives, separated by newlines. | |||
| // So convert headers string to an array, with an element for each line | |||
| String headerLines[] = headers.split("\\n"); | |||
| // Set request headers | |||
| for (int i = 0; i < headerLines.length; ++i) | |||
| { | |||
| int pos = headerLines[i].indexOf (":"); | |||
| if (pos > 0 && pos < headerLines[i].length()) | |||
| { | |||
| String field = headerLines[i].substring (0, pos); | |||
| String value = headerLines[i].substring (pos + 1); | |||
| if (value.length() > 0) | |||
| newConnection.setRequestProperty (field, value); | |||
| } | |||
| } | |||
| newConnection.setRequestMethod (httpRequestCmd); | |||
| if (isPost) | |||
| { | |||
| newConnection.setDoOutput (true); | |||
| if (postData != null) | |||
| { | |||
| OutputStream out = newConnection.getOutputStream(); | |||
| out.write(postData); | |||
| out.flush(); | |||
| } | |||
| } | |||
| return newConnection; | |||
| } | |||
| catch (Throwable e) | |||
| { | |||
| newConnection.disconnect(); | |||
| throw new IOException ("Connection error"); | |||
| } | |||
| } | |||
| private final InputStream getCancellableStream (final boolean isInput) throws ExecutionException | |||
| @@ -1940,19 +2023,12 @@ public class MidiTest extends Activity | |||
| try | |||
| { | |||
| if (connection.getConnectTimeout() > 0) | |||
| return streamFuture.get (connection.getConnectTimeout(), TimeUnit.MILLISECONDS); | |||
| else | |||
| return streamFuture.get(); | |||
| return streamFuture.get(); | |||
| } | |||
| catch (InterruptedException e) | |||
| { | |||
| return null; | |||
| } | |||
| catch (TimeoutException e) | |||
| { | |||
| return null; | |||
| } | |||
| catch (CancellationException e) | |||
| { | |||
| return null; | |||
| @@ -1960,6 +2036,89 @@ public class MidiTest extends Activity | |||
| } | |||
| public final boolean connect() | |||
| { | |||
| boolean result = false; | |||
| int numFollowedRedirects = 0; | |||
| while (true) | |||
| { | |||
| result = doConnect(); | |||
| if (! result) | |||
| return false; | |||
| if (++numFollowedRedirects > numRedirectsToFollow) | |||
| break; | |||
| int status = statusCode[0]; | |||
| if (status == 301 || status == 302 || status == 303 || status == 307) | |||
| { | |||
| // Assumes only one occurrence of "Location" | |||
| int pos1 = responseHeaders.indexOf ("Location:") + 10; | |||
| int pos2 = responseHeaders.indexOf ("\n", pos1); | |||
| if (pos2 > pos1) | |||
| { | |||
| String currentLocation = connection.getURL().toString(); | |||
| String newLocation = responseHeaders.substring (pos1, pos2); | |||
| try | |||
| { | |||
| // Handle newLocation whether it's absolute or relative | |||
| URL baseUrl = new URL (currentLocation); | |||
| URL newUrl = new URL (baseUrl, newLocation); | |||
| String transformedNewLocation = newUrl.toString(); | |||
| if (transformedNewLocation != currentLocation) | |||
| { | |||
| // Clear responseHeaders before next iteration | |||
| responseHeaders.delete (0, responseHeaders.length()); | |||
| synchronized (createStreamLock) | |||
| { | |||
| if (hasBeenCancelled.get()) | |||
| return false; | |||
| connection.disconnect(); | |||
| try | |||
| { | |||
| connection = createConnection (transformedNewLocation, isPost, | |||
| postData, headers, timeOutMs, | |||
| httpRequestCmd); | |||
| } | |||
| catch (Throwable e) | |||
| { | |||
| return false; | |||
| } | |||
| } | |||
| } | |||
| else | |||
| { | |||
| break; | |||
| } | |||
| } | |||
| catch (Throwable e) | |||
| { | |||
| return false; | |||
| } | |||
| } | |||
| else | |||
| { | |||
| break; | |||
| } | |||
| } | |||
| else | |||
| { | |||
| break; | |||
| } | |||
| } | |||
| return result; | |||
| } | |||
| private final boolean doConnect() | |||
| { | |||
| synchronized (createStreamLock) | |||
| { | |||
| @@ -1997,9 +2156,16 @@ public class MidiTest extends Activity | |||
| {} | |||
| for (java.util.Map.Entry<String, java.util.List<String>> entry : connection.getHeaderFields().entrySet()) | |||
| { | |||
| if (entry.getKey() != null && entry.getValue() != null) | |||
| responseHeaders.append (entry.getKey() + ": " | |||
| + android.text.TextUtils.join (",", entry.getValue()) + "\n"); | |||
| { | |||
| responseHeaders.append(entry.getKey() + ": " | |||
| + android.text.TextUtils.join(",", entry.getValue()) + "\n"); | |||
| if (entry.getKey().compareTo ("Content-Length") == 0) | |||
| totalLength = Integer.decode (entry.getValue().get (0)); | |||
| } | |||
| } | |||
| return true; | |||
| } | |||
| @@ -2102,13 +2268,20 @@ public class MidiTest extends Activity | |||
| } | |||
| public final long getPosition() { return position; } | |||
| public final long getTotalLength() { return -1; } | |||
| public final long getTotalLength() { return totalLength; } | |||
| public final boolean isExhausted() { return false; } | |||
| public final boolean setPosition (long newPos) { return false; } | |||
| private boolean isPost; | |||
| private byte[] postData; | |||
| private String headers; | |||
| private int timeOutMs; | |||
| String httpRequestCmd; | |||
| private HttpURLConnection connection; | |||
| private int[] statusCode; | |||
| private StringBuffer responseHeaders; | |||
| private int totalLength; | |||
| private int numRedirectsToFollow; | |||
| private InputStream inputStream; | |||
| private long position; | |||
| private final ReentrantLock createStreamLock = new ReentrantLock(); | |||
| @@ -2130,89 +2303,15 @@ public class MidiTest extends Activity | |||
| else if (timeOutMs == 0) | |||
| timeOutMs = 30000; | |||
| // headers - if not empty, this string is appended onto the headers that are used for the request. It must therefore be a valid set of HTML header directives, separated by newlines. | |||
| // So convert headers string to an array, with an element for each line | |||
| String headerLines[] = headers.split("\\n"); | |||
| for (;;) | |||
| { | |||
| try | |||
| { | |||
| HttpURLConnection connection = (HttpURLConnection) (new URL(address).openConnection()); | |||
| HTTPStream httpStream = new HTTPStream (address, isPost, postData, headers, | |||
| timeOutMs, statusCode, responseHeaders, | |||
| numRedirectsToFollow, httpRequestCmd); | |||
| if (connection != null) | |||
| { | |||
| try | |||
| { | |||
| connection.setInstanceFollowRedirects (false); | |||
| connection.setConnectTimeout (timeOutMs); | |||
| connection.setReadTimeout (timeOutMs); | |||
| // Set request headers | |||
| for (int i = 0; i < headerLines.length; ++i) | |||
| { | |||
| int pos = headerLines[i].indexOf (":"); | |||
| if (pos > 0 && pos < headerLines[i].length()) | |||
| { | |||
| String field = headerLines[i].substring (0, pos); | |||
| String value = headerLines[i].substring (pos + 1); | |||
| if (value.length() > 0) | |||
| connection.setRequestProperty (field, value); | |||
| } | |||
| } | |||
| connection.setRequestMethod (httpRequestCmd); | |||
| if (isPost) | |||
| { | |||
| connection.setDoOutput (true); | |||
| if (postData != null) | |||
| { | |||
| OutputStream out = connection.getOutputStream(); | |||
| out.write(postData); | |||
| out.flush(); | |||
| } | |||
| } | |||
| HTTPStream httpStream = new HTTPStream (connection, statusCode, responseHeaders); | |||
| // Process redirect & continue as necessary | |||
| int status = statusCode[0]; | |||
| if (--numRedirectsToFollow >= 0 | |||
| && (status == 301 || status == 302 || status == 303 || status == 307)) | |||
| { | |||
| // Assumes only one occurrence of "Location" | |||
| int pos1 = responseHeaders.indexOf ("Location:") + 10; | |||
| int pos2 = responseHeaders.indexOf ("\n", pos1); | |||
| if (pos2 > pos1) | |||
| { | |||
| String newLocation = responseHeaders.substring(pos1, pos2); | |||
| // Handle newLocation whether it's absolute or relative | |||
| URL baseUrl = new URL (address); | |||
| URL newUrl = new URL (baseUrl, newLocation); | |||
| String transformedNewLocation = newUrl.toString(); | |||
| if (transformedNewLocation != address) | |||
| { | |||
| address = transformedNewLocation; | |||
| // Clear responseHeaders before next iteration | |||
| responseHeaders.delete (0, responseHeaders.length()); | |||
| continue; | |||
| } | |||
| } | |||
| } | |||
| return httpStream; | |||
| } | |||
| catch (Throwable e) | |||
| { | |||
| connection.disconnect(); | |||
| } | |||
| } | |||
| return httpStream; | |||
| } | |||
| catch (Throwable e) {} | |||
| @@ -2238,7 +2337,14 @@ public class MidiTest extends Activity | |||
| return Environment.getExternalStoragePublicDirectory (type).getAbsolutePath(); | |||
| } | |||
| public static final String getDocumentsFolder() { return Environment.getDataDirectory().getAbsolutePath(); } | |||
| public static final String getDocumentsFolder() | |||
| { | |||
| if (getAndroidSDKVersion() >= 19) | |||
| return getFileLocation ("Documents"); | |||
| return Environment.getDataDirectory().getAbsolutePath(); | |||
| } | |||
| public static final String getPicturesFolder() { return getFileLocation (Environment.DIRECTORY_PICTURES); } | |||
| public static final String getMusicFolder() { return getFileLocation (Environment.DIRECTORY_MUSIC); } | |||
| public static final String getMoviesFolder() { return getFileLocation (Environment.DIRECTORY_MOVIES); } | |||
| @@ -2333,7 +2439,7 @@ public class MidiTest extends Activity | |||
| return null; | |||
| } | |||
| public final int getAndroidSDKVersion() | |||
| public static final int getAndroidSDKVersion() | |||
| { | |||
| return android.os.Build.VERSION.SDK_INT; | |||
| } | |||
| @@ -2,7 +2,7 @@ | |||
| <JUCERPROJECT id="wHE0ay" name="MidiTest" projectType="guiapp" version="1.0.0" | |||
| bundleIdentifier="com.roli.MidiTest" includeBinaryInAppConfig="1" | |||
| jucerVersion="5.1.1" displaySplashScreen="0" reportAppUsage="0" | |||
| jucerVersion="5.1.2" displaySplashScreen="0" reportAppUsage="0" | |||
| splashScreenColour="Dark" companyName="ROLI Ltd." cppLanguageStandard="11"> | |||
| <MAINGROUP id="s3xxCh" name="MidiTest"> | |||
| <GROUP id="{7D29F5BC-1B05-AE8F-9202-5CF152AB1103}" name="Source"> | |||
| @@ -519,11 +519,18 @@ public class JUCENetworkGraphicsDemo extends Activity | |||
| builder.setTitle (title) | |||
| .setMessage (message) | |||
| .setCancelable (true) | |||
| .setOnCancelListener (new DialogInterface.OnCancelListener() | |||
| { | |||
| public void onCancel (DialogInterface dialog) | |||
| { | |||
| JUCENetworkGraphicsDemo.this.alertDismissed (callback, 0); | |||
| } | |||
| }) | |||
| .setPositiveButton ("OK", new DialogInterface.OnClickListener() | |||
| { | |||
| public void onClick (DialogInterface dialog, int id) | |||
| { | |||
| dialog.cancel(); | |||
| dialog.dismiss(); | |||
| JUCENetworkGraphicsDemo.this.alertDismissed (callback, 0); | |||
| } | |||
| }); | |||
| @@ -538,11 +545,18 @@ public class JUCENetworkGraphicsDemo extends Activity | |||
| builder.setTitle (title) | |||
| .setMessage (message) | |||
| .setCancelable (true) | |||
| .setOnCancelListener (new DialogInterface.OnCancelListener() | |||
| { | |||
| public void onCancel (DialogInterface dialog) | |||
| { | |||
| JUCENetworkGraphicsDemo.this.alertDismissed (callback, 0); | |||
| } | |||
| }) | |||
| .setPositiveButton (okButtonText.isEmpty() ? "OK" : okButtonText, new DialogInterface.OnClickListener() | |||
| { | |||
| public void onClick (DialogInterface dialog, int id) | |||
| { | |||
| dialog.cancel(); | |||
| dialog.dismiss(); | |||
| JUCENetworkGraphicsDemo.this.alertDismissed (callback, 1); | |||
| } | |||
| }) | |||
| @@ -550,7 +564,7 @@ public class JUCENetworkGraphicsDemo extends Activity | |||
| { | |||
| public void onClick (DialogInterface dialog, int id) | |||
| { | |||
| dialog.cancel(); | |||
| dialog.dismiss(); | |||
| JUCENetworkGraphicsDemo.this.alertDismissed (callback, 0); | |||
| } | |||
| }); | |||
| @@ -564,11 +578,18 @@ public class JUCENetworkGraphicsDemo extends Activity | |||
| builder.setTitle (title) | |||
| .setMessage (message) | |||
| .setCancelable (true) | |||
| .setOnCancelListener (new DialogInterface.OnCancelListener() | |||
| { | |||
| public void onCancel (DialogInterface dialog) | |||
| { | |||
| JUCENetworkGraphicsDemo.this.alertDismissed (callback, 0); | |||
| } | |||
| }) | |||
| .setPositiveButton ("Yes", new DialogInterface.OnClickListener() | |||
| { | |||
| public void onClick (DialogInterface dialog, int id) | |||
| { | |||
| dialog.cancel(); | |||
| dialog.dismiss(); | |||
| JUCENetworkGraphicsDemo.this.alertDismissed (callback, 1); | |||
| } | |||
| }) | |||
| @@ -576,7 +597,7 @@ public class JUCENetworkGraphicsDemo extends Activity | |||
| { | |||
| public void onClick (DialogInterface dialog, int id) | |||
| { | |||
| dialog.cancel(); | |||
| dialog.dismiss(); | |||
| JUCENetworkGraphicsDemo.this.alertDismissed (callback, 2); | |||
| } | |||
| }) | |||
| @@ -584,7 +605,7 @@ public class JUCENetworkGraphicsDemo extends Activity | |||
| { | |||
| public void onClick (DialogInterface dialog, int id) | |||
| { | |||
| dialog.cancel(); | |||
| dialog.dismiss(); | |||
| JUCENetworkGraphicsDemo.this.alertDismissed (callback, 0); | |||
| } | |||
| }); | |||
| @@ -982,13 +1003,75 @@ public class JUCENetworkGraphicsDemo extends Activity | |||
| //============================================================================== | |||
| public static class HTTPStream | |||
| { | |||
| public HTTPStream (HttpURLConnection connection_, | |||
| int[] statusCode_, | |||
| StringBuffer responseHeaders_) | |||
| public HTTPStream (String address, boolean isPostToUse, byte[] postDataToUse, | |||
| String headersToUse, int timeOutMsToUse, | |||
| int[] statusCodeToUse, StringBuffer responseHeadersToUse, | |||
| int numRedirectsToFollowToUse, String httpRequestCmdToUse) throws IOException | |||
| { | |||
| connection = connection_; | |||
| statusCode = statusCode_; | |||
| responseHeaders = responseHeaders_; | |||
| isPost = isPostToUse; | |||
| postData = postDataToUse; | |||
| headers = headersToUse; | |||
| timeOutMs = timeOutMsToUse; | |||
| statusCode = statusCodeToUse; | |||
| responseHeaders = responseHeadersToUse; | |||
| totalLength = -1; | |||
| numRedirectsToFollow = numRedirectsToFollowToUse; | |||
| httpRequestCmd = httpRequestCmdToUse; | |||
| connection = createConnection (address, isPost, postData, headers, timeOutMs, httpRequestCmd); | |||
| } | |||
| private final HttpURLConnection createConnection (String address, boolean isPost, byte[] postData, | |||
| String headers, int timeOutMs, String httpRequestCmdToUse) throws IOException | |||
| { | |||
| HttpURLConnection newConnection = (HttpURLConnection) (new URL(address).openConnection()); | |||
| try | |||
| { | |||
| newConnection.setInstanceFollowRedirects (false); | |||
| newConnection.setConnectTimeout (timeOutMs); | |||
| newConnection.setReadTimeout (timeOutMs); | |||
| // headers - if not empty, this string is appended onto the headers that are used for the request. It must therefore be a valid set of HTML header directives, separated by newlines. | |||
| // So convert headers string to an array, with an element for each line | |||
| String headerLines[] = headers.split("\\n"); | |||
| // Set request headers | |||
| for (int i = 0; i < headerLines.length; ++i) | |||
| { | |||
| int pos = headerLines[i].indexOf (":"); | |||
| if (pos > 0 && pos < headerLines[i].length()) | |||
| { | |||
| String field = headerLines[i].substring (0, pos); | |||
| String value = headerLines[i].substring (pos + 1); | |||
| if (value.length() > 0) | |||
| newConnection.setRequestProperty (field, value); | |||
| } | |||
| } | |||
| newConnection.setRequestMethod (httpRequestCmd); | |||
| if (isPost) | |||
| { | |||
| newConnection.setDoOutput (true); | |||
| if (postData != null) | |||
| { | |||
| OutputStream out = newConnection.getOutputStream(); | |||
| out.write(postData); | |||
| out.flush(); | |||
| } | |||
| } | |||
| return newConnection; | |||
| } | |||
| catch (Throwable e) | |||
| { | |||
| newConnection.disconnect(); | |||
| throw new IOException ("Connection error"); | |||
| } | |||
| } | |||
| private final InputStream getCancellableStream (final boolean isInput) throws ExecutionException | |||
| @@ -1011,19 +1094,12 @@ public class JUCENetworkGraphicsDemo extends Activity | |||
| try | |||
| { | |||
| if (connection.getConnectTimeout() > 0) | |||
| return streamFuture.get (connection.getConnectTimeout(), TimeUnit.MILLISECONDS); | |||
| else | |||
| return streamFuture.get(); | |||
| return streamFuture.get(); | |||
| } | |||
| catch (InterruptedException e) | |||
| { | |||
| return null; | |||
| } | |||
| catch (TimeoutException e) | |||
| { | |||
| return null; | |||
| } | |||
| catch (CancellationException e) | |||
| { | |||
| return null; | |||
| @@ -1031,6 +1107,89 @@ public class JUCENetworkGraphicsDemo extends Activity | |||
| } | |||
| public final boolean connect() | |||
| { | |||
| boolean result = false; | |||
| int numFollowedRedirects = 0; | |||
| while (true) | |||
| { | |||
| result = doConnect(); | |||
| if (! result) | |||
| return false; | |||
| if (++numFollowedRedirects > numRedirectsToFollow) | |||
| break; | |||
| int status = statusCode[0]; | |||
| if (status == 301 || status == 302 || status == 303 || status == 307) | |||
| { | |||
| // Assumes only one occurrence of "Location" | |||
| int pos1 = responseHeaders.indexOf ("Location:") + 10; | |||
| int pos2 = responseHeaders.indexOf ("\n", pos1); | |||
| if (pos2 > pos1) | |||
| { | |||
| String currentLocation = connection.getURL().toString(); | |||
| String newLocation = responseHeaders.substring (pos1, pos2); | |||
| try | |||
| { | |||
| // Handle newLocation whether it's absolute or relative | |||
| URL baseUrl = new URL (currentLocation); | |||
| URL newUrl = new URL (baseUrl, newLocation); | |||
| String transformedNewLocation = newUrl.toString(); | |||
| if (transformedNewLocation != currentLocation) | |||
| { | |||
| // Clear responseHeaders before next iteration | |||
| responseHeaders.delete (0, responseHeaders.length()); | |||
| synchronized (createStreamLock) | |||
| { | |||
| if (hasBeenCancelled.get()) | |||
| return false; | |||
| connection.disconnect(); | |||
| try | |||
| { | |||
| connection = createConnection (transformedNewLocation, isPost, | |||
| postData, headers, timeOutMs, | |||
| httpRequestCmd); | |||
| } | |||
| catch (Throwable e) | |||
| { | |||
| return false; | |||
| } | |||
| } | |||
| } | |||
| else | |||
| { | |||
| break; | |||
| } | |||
| } | |||
| catch (Throwable e) | |||
| { | |||
| return false; | |||
| } | |||
| } | |||
| else | |||
| { | |||
| break; | |||
| } | |||
| } | |||
| else | |||
| { | |||
| break; | |||
| } | |||
| } | |||
| return result; | |||
| } | |||
| private final boolean doConnect() | |||
| { | |||
| synchronized (createStreamLock) | |||
| { | |||
| @@ -1068,9 +1227,16 @@ public class JUCENetworkGraphicsDemo extends Activity | |||
| {} | |||
| for (java.util.Map.Entry<String, java.util.List<String>> entry : connection.getHeaderFields().entrySet()) | |||
| { | |||
| if (entry.getKey() != null && entry.getValue() != null) | |||
| responseHeaders.append (entry.getKey() + ": " | |||
| + android.text.TextUtils.join (",", entry.getValue()) + "\n"); | |||
| { | |||
| responseHeaders.append(entry.getKey() + ": " | |||
| + android.text.TextUtils.join(",", entry.getValue()) + "\n"); | |||
| if (entry.getKey().compareTo ("Content-Length") == 0) | |||
| totalLength = Integer.decode (entry.getValue().get (0)); | |||
| } | |||
| } | |||
| return true; | |||
| } | |||
| @@ -1173,13 +1339,20 @@ public class JUCENetworkGraphicsDemo extends Activity | |||
| } | |||
| public final long getPosition() { return position; } | |||
| public final long getTotalLength() { return -1; } | |||
| public final long getTotalLength() { return totalLength; } | |||
| public final boolean isExhausted() { return false; } | |||
| public final boolean setPosition (long newPos) { return false; } | |||
| private boolean isPost; | |||
| private byte[] postData; | |||
| private String headers; | |||
| private int timeOutMs; | |||
| String httpRequestCmd; | |||
| private HttpURLConnection connection; | |||
| private int[] statusCode; | |||
| private StringBuffer responseHeaders; | |||
| private int totalLength; | |||
| private int numRedirectsToFollow; | |||
| private InputStream inputStream; | |||
| private long position; | |||
| private final ReentrantLock createStreamLock = new ReentrantLock(); | |||
| @@ -1201,89 +1374,15 @@ public class JUCENetworkGraphicsDemo extends Activity | |||
| else if (timeOutMs == 0) | |||
| timeOutMs = 30000; | |||
| // headers - if not empty, this string is appended onto the headers that are used for the request. It must therefore be a valid set of HTML header directives, separated by newlines. | |||
| // So convert headers string to an array, with an element for each line | |||
| String headerLines[] = headers.split("\\n"); | |||
| for (;;) | |||
| { | |||
| try | |||
| { | |||
| HttpURLConnection connection = (HttpURLConnection) (new URL(address).openConnection()); | |||
| if (connection != null) | |||
| { | |||
| try | |||
| { | |||
| connection.setInstanceFollowRedirects (false); | |||
| connection.setConnectTimeout (timeOutMs); | |||
| connection.setReadTimeout (timeOutMs); | |||
| HTTPStream httpStream = new HTTPStream (address, isPost, postData, headers, | |||
| timeOutMs, statusCode, responseHeaders, | |||
| numRedirectsToFollow, httpRequestCmd); | |||
| // Set request headers | |||
| for (int i = 0; i < headerLines.length; ++i) | |||
| { | |||
| int pos = headerLines[i].indexOf (":"); | |||
| if (pos > 0 && pos < headerLines[i].length()) | |||
| { | |||
| String field = headerLines[i].substring (0, pos); | |||
| String value = headerLines[i].substring (pos + 1); | |||
| if (value.length() > 0) | |||
| connection.setRequestProperty (field, value); | |||
| } | |||
| } | |||
| connection.setRequestMethod (httpRequestCmd); | |||
| if (isPost) | |||
| { | |||
| connection.setDoOutput (true); | |||
| if (postData != null) | |||
| { | |||
| OutputStream out = connection.getOutputStream(); | |||
| out.write(postData); | |||
| out.flush(); | |||
| } | |||
| } | |||
| HTTPStream httpStream = new HTTPStream (connection, statusCode, responseHeaders); | |||
| // Process redirect & continue as necessary | |||
| int status = statusCode[0]; | |||
| if (--numRedirectsToFollow >= 0 | |||
| && (status == 301 || status == 302 || status == 303 || status == 307)) | |||
| { | |||
| // Assumes only one occurrence of "Location" | |||
| int pos1 = responseHeaders.indexOf ("Location:") + 10; | |||
| int pos2 = responseHeaders.indexOf ("\n", pos1); | |||
| if (pos2 > pos1) | |||
| { | |||
| String newLocation = responseHeaders.substring(pos1, pos2); | |||
| // Handle newLocation whether it's absolute or relative | |||
| URL baseUrl = new URL (address); | |||
| URL newUrl = new URL (baseUrl, newLocation); | |||
| String transformedNewLocation = newUrl.toString(); | |||
| if (transformedNewLocation != address) | |||
| { | |||
| address = transformedNewLocation; | |||
| // Clear responseHeaders before next iteration | |||
| responseHeaders.delete (0, responseHeaders.length()); | |||
| continue; | |||
| } | |||
| } | |||
| } | |||
| return httpStream; | |||
| } | |||
| catch (Throwable e) | |||
| { | |||
| connection.disconnect(); | |||
| } | |||
| } | |||
| return httpStream; | |||
| } | |||
| catch (Throwable e) {} | |||
| @@ -1309,7 +1408,14 @@ public class JUCENetworkGraphicsDemo extends Activity | |||
| return Environment.getExternalStoragePublicDirectory (type).getAbsolutePath(); | |||
| } | |||
| public static final String getDocumentsFolder() { return Environment.getDataDirectory().getAbsolutePath(); } | |||
| public static final String getDocumentsFolder() | |||
| { | |||
| if (getAndroidSDKVersion() >= 19) | |||
| return getFileLocation ("Documents"); | |||
| return Environment.getDataDirectory().getAbsolutePath(); | |||
| } | |||
| public static final String getPicturesFolder() { return getFileLocation (Environment.DIRECTORY_PICTURES); } | |||
| public static final String getMusicFolder() { return getFileLocation (Environment.DIRECTORY_MUSIC); } | |||
| public static final String getMoviesFolder() { return getFileLocation (Environment.DIRECTORY_MOVIES); } | |||
| @@ -1404,7 +1510,7 @@ public class JUCENetworkGraphicsDemo extends Activity | |||
| return null; | |||
| } | |||
| public final int getAndroidSDKVersion() | |||
| public static final int getAndroidSDKVersion() | |||
| { | |||
| return android.os.Build.VERSION.SDK_INT; | |||
| } | |||
| @@ -2,7 +2,7 @@ | |||
| <JUCERPROJECT id="gWI5Ir" name="JUCE Network Graphics Demo" projectType="guiapp" | |||
| version="1.0.0" bundleIdentifier="com.juce.NetworkGraphicsDemo" | |||
| includeBinaryInAppConfig="1" jucerVersion="5.1.1" displaySplashScreen="0" | |||
| includeBinaryInAppConfig="1" jucerVersion="5.1.2" displaySplashScreen="0" | |||
| reportAppUsage="0" splashScreenColour="Dark" companyName="ROLI Ltd." | |||
| cppLanguageStandard="11"> | |||
| <MAINGROUP id="OT9rJ2" name="JUCE Network Graphics Demo"> | |||
| @@ -2,7 +2,7 @@ | |||
| <JUCERPROJECT id="IhmIkj" name="OSCMonitor" projectType="guiapp" version="1.0.0" | |||
| bundleIdentifier="com.roli.OSCMonitor" includeBinaryInAppConfig="1" | |||
| jucerVersion="5.1.1" displaySplashScreen="0" reportAppUsage="0" | |||
| jucerVersion="5.1.2" displaySplashScreen="0" reportAppUsage="0" | |||
| splashScreenColour="Dark" companyName="ROLI Ltd." cppLanguageStandard="11"> | |||
| <MAINGROUP id="N9NMMk" name="OSCMonitor"> | |||
| <GROUP id="{2B92546C-6B49-72D9-ACD0-0F2FCE9AD0D5}" name="Source"> | |||
| @@ -128,13 +128,12 @@ public: | |||
| else if (arg.isBlob()) | |||
| { | |||
| typeAsString = "blob"; | |||
| const MemoryBlock& blob = arg.getBlob(); | |||
| valueAsString = String::fromUTF8( (const char*)blob.getData(), blob.getSize()); | |||
| auto& blob = arg.getBlob(); | |||
| valueAsString = String::fromUTF8 ((const char*) blob.getData(), (int) blob.getSize()); | |||
| } | |||
| else | |||
| { | |||
| typeAsString = "(unknown)"; | |||
| valueAsString = ""; | |||
| } | |||
| oscLogList.add (getIndentationString (level + 1) + "- " + typeAsString.paddedRight(' ', 12) + valueAsString); | |||
| @@ -519,11 +519,18 @@ public class OSCReceiver extends Activity | |||
| builder.setTitle (title) | |||
| .setMessage (message) | |||
| .setCancelable (true) | |||
| .setOnCancelListener (new DialogInterface.OnCancelListener() | |||
| { | |||
| public void onCancel (DialogInterface dialog) | |||
| { | |||
| OSCReceiver.this.alertDismissed (callback, 0); | |||
| } | |||
| }) | |||
| .setPositiveButton ("OK", new DialogInterface.OnClickListener() | |||
| { | |||
| public void onClick (DialogInterface dialog, int id) | |||
| { | |||
| dialog.cancel(); | |||
| dialog.dismiss(); | |||
| OSCReceiver.this.alertDismissed (callback, 0); | |||
| } | |||
| }); | |||
| @@ -538,11 +545,18 @@ public class OSCReceiver extends Activity | |||
| builder.setTitle (title) | |||
| .setMessage (message) | |||
| .setCancelable (true) | |||
| .setOnCancelListener (new DialogInterface.OnCancelListener() | |||
| { | |||
| public void onCancel (DialogInterface dialog) | |||
| { | |||
| OSCReceiver.this.alertDismissed (callback, 0); | |||
| } | |||
| }) | |||
| .setPositiveButton (okButtonText.isEmpty() ? "OK" : okButtonText, new DialogInterface.OnClickListener() | |||
| { | |||
| public void onClick (DialogInterface dialog, int id) | |||
| { | |||
| dialog.cancel(); | |||
| dialog.dismiss(); | |||
| OSCReceiver.this.alertDismissed (callback, 1); | |||
| } | |||
| }) | |||
| @@ -550,7 +564,7 @@ public class OSCReceiver extends Activity | |||
| { | |||
| public void onClick (DialogInterface dialog, int id) | |||
| { | |||
| dialog.cancel(); | |||
| dialog.dismiss(); | |||
| OSCReceiver.this.alertDismissed (callback, 0); | |||
| } | |||
| }); | |||
| @@ -564,11 +578,18 @@ public class OSCReceiver extends Activity | |||
| builder.setTitle (title) | |||
| .setMessage (message) | |||
| .setCancelable (true) | |||
| .setOnCancelListener (new DialogInterface.OnCancelListener() | |||
| { | |||
| public void onCancel (DialogInterface dialog) | |||
| { | |||
| OSCReceiver.this.alertDismissed (callback, 0); | |||
| } | |||
| }) | |||
| .setPositiveButton ("Yes", new DialogInterface.OnClickListener() | |||
| { | |||
| public void onClick (DialogInterface dialog, int id) | |||
| { | |||
| dialog.cancel(); | |||
| dialog.dismiss(); | |||
| OSCReceiver.this.alertDismissed (callback, 1); | |||
| } | |||
| }) | |||
| @@ -576,7 +597,7 @@ public class OSCReceiver extends Activity | |||
| { | |||
| public void onClick (DialogInterface dialog, int id) | |||
| { | |||
| dialog.cancel(); | |||
| dialog.dismiss(); | |||
| OSCReceiver.this.alertDismissed (callback, 2); | |||
| } | |||
| }) | |||
| @@ -584,7 +605,7 @@ public class OSCReceiver extends Activity | |||
| { | |||
| public void onClick (DialogInterface dialog, int id) | |||
| { | |||
| dialog.cancel(); | |||
| dialog.dismiss(); | |||
| OSCReceiver.this.alertDismissed (callback, 0); | |||
| } | |||
| }); | |||
| @@ -982,13 +1003,75 @@ public class OSCReceiver extends Activity | |||
| //============================================================================== | |||
| public static class HTTPStream | |||
| { | |||
| public HTTPStream (HttpURLConnection connection_, | |||
| int[] statusCode_, | |||
| StringBuffer responseHeaders_) | |||
| public HTTPStream (String address, boolean isPostToUse, byte[] postDataToUse, | |||
| String headersToUse, int timeOutMsToUse, | |||
| int[] statusCodeToUse, StringBuffer responseHeadersToUse, | |||
| int numRedirectsToFollowToUse, String httpRequestCmdToUse) throws IOException | |||
| { | |||
| connection = connection_; | |||
| statusCode = statusCode_; | |||
| responseHeaders = responseHeaders_; | |||
| isPost = isPostToUse; | |||
| postData = postDataToUse; | |||
| headers = headersToUse; | |||
| timeOutMs = timeOutMsToUse; | |||
| statusCode = statusCodeToUse; | |||
| responseHeaders = responseHeadersToUse; | |||
| totalLength = -1; | |||
| numRedirectsToFollow = numRedirectsToFollowToUse; | |||
| httpRequestCmd = httpRequestCmdToUse; | |||
| connection = createConnection (address, isPost, postData, headers, timeOutMs, httpRequestCmd); | |||
| } | |||
| private final HttpURLConnection createConnection (String address, boolean isPost, byte[] postData, | |||
| String headers, int timeOutMs, String httpRequestCmdToUse) throws IOException | |||
| { | |||
| HttpURLConnection newConnection = (HttpURLConnection) (new URL(address).openConnection()); | |||
| try | |||
| { | |||
| newConnection.setInstanceFollowRedirects (false); | |||
| newConnection.setConnectTimeout (timeOutMs); | |||
| newConnection.setReadTimeout (timeOutMs); | |||
| // headers - if not empty, this string is appended onto the headers that are used for the request. It must therefore be a valid set of HTML header directives, separated by newlines. | |||
| // So convert headers string to an array, with an element for each line | |||
| String headerLines[] = headers.split("\\n"); | |||
| // Set request headers | |||
| for (int i = 0; i < headerLines.length; ++i) | |||
| { | |||
| int pos = headerLines[i].indexOf (":"); | |||
| if (pos > 0 && pos < headerLines[i].length()) | |||
| { | |||
| String field = headerLines[i].substring (0, pos); | |||
| String value = headerLines[i].substring (pos + 1); | |||
| if (value.length() > 0) | |||
| newConnection.setRequestProperty (field, value); | |||
| } | |||
| } | |||
| newConnection.setRequestMethod (httpRequestCmd); | |||
| if (isPost) | |||
| { | |||
| newConnection.setDoOutput (true); | |||
| if (postData != null) | |||
| { | |||
| OutputStream out = newConnection.getOutputStream(); | |||
| out.write(postData); | |||
| out.flush(); | |||
| } | |||
| } | |||
| return newConnection; | |||
| } | |||
| catch (Throwable e) | |||
| { | |||
| newConnection.disconnect(); | |||
| throw new IOException ("Connection error"); | |||
| } | |||
| } | |||
| private final InputStream getCancellableStream (final boolean isInput) throws ExecutionException | |||
| @@ -1011,19 +1094,12 @@ public class OSCReceiver extends Activity | |||
| try | |||
| { | |||
| if (connection.getConnectTimeout() > 0) | |||
| return streamFuture.get (connection.getConnectTimeout(), TimeUnit.MILLISECONDS); | |||
| else | |||
| return streamFuture.get(); | |||
| return streamFuture.get(); | |||
| } | |||
| catch (InterruptedException e) | |||
| { | |||
| return null; | |||
| } | |||
| catch (TimeoutException e) | |||
| { | |||
| return null; | |||
| } | |||
| catch (CancellationException e) | |||
| { | |||
| return null; | |||
| @@ -1031,6 +1107,89 @@ public class OSCReceiver extends Activity | |||
| } | |||
| public final boolean connect() | |||
| { | |||
| boolean result = false; | |||
| int numFollowedRedirects = 0; | |||
| while (true) | |||
| { | |||
| result = doConnect(); | |||
| if (! result) | |||
| return false; | |||
| if (++numFollowedRedirects > numRedirectsToFollow) | |||
| break; | |||
| int status = statusCode[0]; | |||
| if (status == 301 || status == 302 || status == 303 || status == 307) | |||
| { | |||
| // Assumes only one occurrence of "Location" | |||
| int pos1 = responseHeaders.indexOf ("Location:") + 10; | |||
| int pos2 = responseHeaders.indexOf ("\n", pos1); | |||
| if (pos2 > pos1) | |||
| { | |||
| String currentLocation = connection.getURL().toString(); | |||
| String newLocation = responseHeaders.substring (pos1, pos2); | |||
| try | |||
| { | |||
| // Handle newLocation whether it's absolute or relative | |||
| URL baseUrl = new URL (currentLocation); | |||
| URL newUrl = new URL (baseUrl, newLocation); | |||
| String transformedNewLocation = newUrl.toString(); | |||
| if (transformedNewLocation != currentLocation) | |||
| { | |||
| // Clear responseHeaders before next iteration | |||
| responseHeaders.delete (0, responseHeaders.length()); | |||
| synchronized (createStreamLock) | |||
| { | |||
| if (hasBeenCancelled.get()) | |||
| return false; | |||
| connection.disconnect(); | |||
| try | |||
| { | |||
| connection = createConnection (transformedNewLocation, isPost, | |||
| postData, headers, timeOutMs, | |||
| httpRequestCmd); | |||
| } | |||
| catch (Throwable e) | |||
| { | |||
| return false; | |||
| } | |||
| } | |||
| } | |||
| else | |||
| { | |||
| break; | |||
| } | |||
| } | |||
| catch (Throwable e) | |||
| { | |||
| return false; | |||
| } | |||
| } | |||
| else | |||
| { | |||
| break; | |||
| } | |||
| } | |||
| else | |||
| { | |||
| break; | |||
| } | |||
| } | |||
| return result; | |||
| } | |||
| private final boolean doConnect() | |||
| { | |||
| synchronized (createStreamLock) | |||
| { | |||
| @@ -1068,9 +1227,16 @@ public class OSCReceiver extends Activity | |||
| {} | |||
| for (java.util.Map.Entry<String, java.util.List<String>> entry : connection.getHeaderFields().entrySet()) | |||
| { | |||
| if (entry.getKey() != null && entry.getValue() != null) | |||
| responseHeaders.append (entry.getKey() + ": " | |||
| + android.text.TextUtils.join (",", entry.getValue()) + "\n"); | |||
| { | |||
| responseHeaders.append(entry.getKey() + ": " | |||
| + android.text.TextUtils.join(",", entry.getValue()) + "\n"); | |||
| if (entry.getKey().compareTo ("Content-Length") == 0) | |||
| totalLength = Integer.decode (entry.getValue().get (0)); | |||
| } | |||
| } | |||
| return true; | |||
| } | |||
| @@ -1173,13 +1339,20 @@ public class OSCReceiver extends Activity | |||
| } | |||
| public final long getPosition() { return position; } | |||
| public final long getTotalLength() { return -1; } | |||
| public final long getTotalLength() { return totalLength; } | |||
| public final boolean isExhausted() { return false; } | |||
| public final boolean setPosition (long newPos) { return false; } | |||
| private boolean isPost; | |||
| private byte[] postData; | |||
| private String headers; | |||
| private int timeOutMs; | |||
| String httpRequestCmd; | |||
| private HttpURLConnection connection; | |||
| private int[] statusCode; | |||
| private StringBuffer responseHeaders; | |||
| private int totalLength; | |||
| private int numRedirectsToFollow; | |||
| private InputStream inputStream; | |||
| private long position; | |||
| private final ReentrantLock createStreamLock = new ReentrantLock(); | |||
| @@ -1201,89 +1374,15 @@ public class OSCReceiver extends Activity | |||
| else if (timeOutMs == 0) | |||
| timeOutMs = 30000; | |||
| // headers - if not empty, this string is appended onto the headers that are used for the request. It must therefore be a valid set of HTML header directives, separated by newlines. | |||
| // So convert headers string to an array, with an element for each line | |||
| String headerLines[] = headers.split("\\n"); | |||
| for (;;) | |||
| { | |||
| try | |||
| { | |||
| HttpURLConnection connection = (HttpURLConnection) (new URL(address).openConnection()); | |||
| if (connection != null) | |||
| { | |||
| try | |||
| { | |||
| connection.setInstanceFollowRedirects (false); | |||
| connection.setConnectTimeout (timeOutMs); | |||
| connection.setReadTimeout (timeOutMs); | |||
| HTTPStream httpStream = new HTTPStream (address, isPost, postData, headers, | |||
| timeOutMs, statusCode, responseHeaders, | |||
| numRedirectsToFollow, httpRequestCmd); | |||
| // Set request headers | |||
| for (int i = 0; i < headerLines.length; ++i) | |||
| { | |||
| int pos = headerLines[i].indexOf (":"); | |||
| if (pos > 0 && pos < headerLines[i].length()) | |||
| { | |||
| String field = headerLines[i].substring (0, pos); | |||
| String value = headerLines[i].substring (pos + 1); | |||
| if (value.length() > 0) | |||
| connection.setRequestProperty (field, value); | |||
| } | |||
| } | |||
| connection.setRequestMethod (httpRequestCmd); | |||
| if (isPost) | |||
| { | |||
| connection.setDoOutput (true); | |||
| if (postData != null) | |||
| { | |||
| OutputStream out = connection.getOutputStream(); | |||
| out.write(postData); | |||
| out.flush(); | |||
| } | |||
| } | |||
| HTTPStream httpStream = new HTTPStream (connection, statusCode, responseHeaders); | |||
| // Process redirect & continue as necessary | |||
| int status = statusCode[0]; | |||
| if (--numRedirectsToFollow >= 0 | |||
| && (status == 301 || status == 302 || status == 303 || status == 307)) | |||
| { | |||
| // Assumes only one occurrence of "Location" | |||
| int pos1 = responseHeaders.indexOf ("Location:") + 10; | |||
| int pos2 = responseHeaders.indexOf ("\n", pos1); | |||
| if (pos2 > pos1) | |||
| { | |||
| String newLocation = responseHeaders.substring(pos1, pos2); | |||
| // Handle newLocation whether it's absolute or relative | |||
| URL baseUrl = new URL (address); | |||
| URL newUrl = new URL (baseUrl, newLocation); | |||
| String transformedNewLocation = newUrl.toString(); | |||
| if (transformedNewLocation != address) | |||
| { | |||
| address = transformedNewLocation; | |||
| // Clear responseHeaders before next iteration | |||
| responseHeaders.delete (0, responseHeaders.length()); | |||
| continue; | |||
| } | |||
| } | |||
| } | |||
| return httpStream; | |||
| } | |||
| catch (Throwable e) | |||
| { | |||
| connection.disconnect(); | |||
| } | |||
| } | |||
| return httpStream; | |||
| } | |||
| catch (Throwable e) {} | |||
| @@ -1309,7 +1408,14 @@ public class OSCReceiver extends Activity | |||
| return Environment.getExternalStoragePublicDirectory (type).getAbsolutePath(); | |||
| } | |||
| public static final String getDocumentsFolder() { return Environment.getDataDirectory().getAbsolutePath(); } | |||
| public static final String getDocumentsFolder() | |||
| { | |||
| if (getAndroidSDKVersion() >= 19) | |||
| return getFileLocation ("Documents"); | |||
| return Environment.getDataDirectory().getAbsolutePath(); | |||
| } | |||
| public static final String getPicturesFolder() { return getFileLocation (Environment.DIRECTORY_PICTURES); } | |||
| public static final String getMusicFolder() { return getFileLocation (Environment.DIRECTORY_MUSIC); } | |||
| public static final String getMoviesFolder() { return getFileLocation (Environment.DIRECTORY_MOVIES); } | |||
| @@ -1404,7 +1510,7 @@ public class OSCReceiver extends Activity | |||
| return null; | |||
| } | |||
| public final int getAndroidSDKVersion() | |||
| public static final int getAndroidSDKVersion() | |||
| { | |||
| return android.os.Build.VERSION.SDK_INT; | |||
| } | |||
| @@ -2,7 +2,7 @@ | |||
| <JUCERPROJECT id="pdocPt" name="OSCReceiver" projectType="guiapp" version="1.0.0" | |||
| bundleIdentifier="com.roli.OSCReceiver" includeBinaryInAppConfig="1" | |||
| jucerVersion="5.1.1" displaySplashScreen="0" reportAppUsage="0" | |||
| jucerVersion="5.1.2" displaySplashScreen="0" reportAppUsage="0" | |||
| splashScreenColour="Dark" companyName="ROLI Ltd." cppLanguageStandard="11"> | |||
| <MAINGROUP id="Y6Q0O9" name="OSCReceiver"> | |||
| <GROUP id="{9F303ECD-83DE-CA91-97B4-C083DA897F49}" name="Source"> | |||
| @@ -519,11 +519,18 @@ public class OSCSender extends Activity | |||
| builder.setTitle (title) | |||
| .setMessage (message) | |||
| .setCancelable (true) | |||
| .setOnCancelListener (new DialogInterface.OnCancelListener() | |||
| { | |||
| public void onCancel (DialogInterface dialog) | |||
| { | |||
| OSCSender.this.alertDismissed (callback, 0); | |||
| } | |||
| }) | |||
| .setPositiveButton ("OK", new DialogInterface.OnClickListener() | |||
| { | |||
| public void onClick (DialogInterface dialog, int id) | |||
| { | |||
| dialog.cancel(); | |||
| dialog.dismiss(); | |||
| OSCSender.this.alertDismissed (callback, 0); | |||
| } | |||
| }); | |||
| @@ -538,11 +545,18 @@ public class OSCSender extends Activity | |||
| builder.setTitle (title) | |||
| .setMessage (message) | |||
| .setCancelable (true) | |||
| .setOnCancelListener (new DialogInterface.OnCancelListener() | |||
| { | |||
| public void onCancel (DialogInterface dialog) | |||
| { | |||
| OSCSender.this.alertDismissed (callback, 0); | |||
| } | |||
| }) | |||
| .setPositiveButton (okButtonText.isEmpty() ? "OK" : okButtonText, new DialogInterface.OnClickListener() | |||
| { | |||
| public void onClick (DialogInterface dialog, int id) | |||
| { | |||
| dialog.cancel(); | |||
| dialog.dismiss(); | |||
| OSCSender.this.alertDismissed (callback, 1); | |||
| } | |||
| }) | |||
| @@ -550,7 +564,7 @@ public class OSCSender extends Activity | |||
| { | |||
| public void onClick (DialogInterface dialog, int id) | |||
| { | |||
| dialog.cancel(); | |||
| dialog.dismiss(); | |||
| OSCSender.this.alertDismissed (callback, 0); | |||
| } | |||
| }); | |||
| @@ -564,11 +578,18 @@ public class OSCSender extends Activity | |||
| builder.setTitle (title) | |||
| .setMessage (message) | |||
| .setCancelable (true) | |||
| .setOnCancelListener (new DialogInterface.OnCancelListener() | |||
| { | |||
| public void onCancel (DialogInterface dialog) | |||
| { | |||
| OSCSender.this.alertDismissed (callback, 0); | |||
| } | |||
| }) | |||
| .setPositiveButton ("Yes", new DialogInterface.OnClickListener() | |||
| { | |||
| public void onClick (DialogInterface dialog, int id) | |||
| { | |||
| dialog.cancel(); | |||
| dialog.dismiss(); | |||
| OSCSender.this.alertDismissed (callback, 1); | |||
| } | |||
| }) | |||
| @@ -576,7 +597,7 @@ public class OSCSender extends Activity | |||
| { | |||
| public void onClick (DialogInterface dialog, int id) | |||
| { | |||
| dialog.cancel(); | |||
| dialog.dismiss(); | |||
| OSCSender.this.alertDismissed (callback, 2); | |||
| } | |||
| }) | |||
| @@ -584,7 +605,7 @@ public class OSCSender extends Activity | |||
| { | |||
| public void onClick (DialogInterface dialog, int id) | |||
| { | |||
| dialog.cancel(); | |||
| dialog.dismiss(); | |||
| OSCSender.this.alertDismissed (callback, 0); | |||
| } | |||
| }); | |||
| @@ -982,13 +1003,75 @@ public class OSCSender extends Activity | |||
| //============================================================================== | |||
| public static class HTTPStream | |||
| { | |||
| public HTTPStream (HttpURLConnection connection_, | |||
| int[] statusCode_, | |||
| StringBuffer responseHeaders_) | |||
| public HTTPStream (String address, boolean isPostToUse, byte[] postDataToUse, | |||
| String headersToUse, int timeOutMsToUse, | |||
| int[] statusCodeToUse, StringBuffer responseHeadersToUse, | |||
| int numRedirectsToFollowToUse, String httpRequestCmdToUse) throws IOException | |||
| { | |||
| connection = connection_; | |||
| statusCode = statusCode_; | |||
| responseHeaders = responseHeaders_; | |||
| isPost = isPostToUse; | |||
| postData = postDataToUse; | |||
| headers = headersToUse; | |||
| timeOutMs = timeOutMsToUse; | |||
| statusCode = statusCodeToUse; | |||
| responseHeaders = responseHeadersToUse; | |||
| totalLength = -1; | |||
| numRedirectsToFollow = numRedirectsToFollowToUse; | |||
| httpRequestCmd = httpRequestCmdToUse; | |||
| connection = createConnection (address, isPost, postData, headers, timeOutMs, httpRequestCmd); | |||
| } | |||
| private final HttpURLConnection createConnection (String address, boolean isPost, byte[] postData, | |||
| String headers, int timeOutMs, String httpRequestCmdToUse) throws IOException | |||
| { | |||
| HttpURLConnection newConnection = (HttpURLConnection) (new URL(address).openConnection()); | |||
| try | |||
| { | |||
| newConnection.setInstanceFollowRedirects (false); | |||
| newConnection.setConnectTimeout (timeOutMs); | |||
| newConnection.setReadTimeout (timeOutMs); | |||
| // headers - if not empty, this string is appended onto the headers that are used for the request. It must therefore be a valid set of HTML header directives, separated by newlines. | |||
| // So convert headers string to an array, with an element for each line | |||
| String headerLines[] = headers.split("\\n"); | |||
| // Set request headers | |||
| for (int i = 0; i < headerLines.length; ++i) | |||
| { | |||
| int pos = headerLines[i].indexOf (":"); | |||
| if (pos > 0 && pos < headerLines[i].length()) | |||
| { | |||
| String field = headerLines[i].substring (0, pos); | |||
| String value = headerLines[i].substring (pos + 1); | |||
| if (value.length() > 0) | |||
| newConnection.setRequestProperty (field, value); | |||
| } | |||
| } | |||
| newConnection.setRequestMethod (httpRequestCmd); | |||
| if (isPost) | |||
| { | |||
| newConnection.setDoOutput (true); | |||
| if (postData != null) | |||
| { | |||
| OutputStream out = newConnection.getOutputStream(); | |||
| out.write(postData); | |||
| out.flush(); | |||
| } | |||
| } | |||
| return newConnection; | |||
| } | |||
| catch (Throwable e) | |||
| { | |||
| newConnection.disconnect(); | |||
| throw new IOException ("Connection error"); | |||
| } | |||
| } | |||
| private final InputStream getCancellableStream (final boolean isInput) throws ExecutionException | |||
| @@ -1011,19 +1094,12 @@ public class OSCSender extends Activity | |||
| try | |||
| { | |||
| if (connection.getConnectTimeout() > 0) | |||
| return streamFuture.get (connection.getConnectTimeout(), TimeUnit.MILLISECONDS); | |||
| else | |||
| return streamFuture.get(); | |||
| return streamFuture.get(); | |||
| } | |||
| catch (InterruptedException e) | |||
| { | |||
| return null; | |||
| } | |||
| catch (TimeoutException e) | |||
| { | |||
| return null; | |||
| } | |||
| catch (CancellationException e) | |||
| { | |||
| return null; | |||
| @@ -1031,6 +1107,89 @@ public class OSCSender extends Activity | |||
| } | |||
| public final boolean connect() | |||
| { | |||
| boolean result = false; | |||
| int numFollowedRedirects = 0; | |||
| while (true) | |||
| { | |||
| result = doConnect(); | |||
| if (! result) | |||
| return false; | |||
| if (++numFollowedRedirects > numRedirectsToFollow) | |||
| break; | |||
| int status = statusCode[0]; | |||
| if (status == 301 || status == 302 || status == 303 || status == 307) | |||
| { | |||
| // Assumes only one occurrence of "Location" | |||
| int pos1 = responseHeaders.indexOf ("Location:") + 10; | |||
| int pos2 = responseHeaders.indexOf ("\n", pos1); | |||
| if (pos2 > pos1) | |||
| { | |||
| String currentLocation = connection.getURL().toString(); | |||
| String newLocation = responseHeaders.substring (pos1, pos2); | |||
| try | |||
| { | |||
| // Handle newLocation whether it's absolute or relative | |||
| URL baseUrl = new URL (currentLocation); | |||
| URL newUrl = new URL (baseUrl, newLocation); | |||
| String transformedNewLocation = newUrl.toString(); | |||
| if (transformedNewLocation != currentLocation) | |||
| { | |||
| // Clear responseHeaders before next iteration | |||
| responseHeaders.delete (0, responseHeaders.length()); | |||
| synchronized (createStreamLock) | |||
| { | |||
| if (hasBeenCancelled.get()) | |||
| return false; | |||
| connection.disconnect(); | |||
| try | |||
| { | |||
| connection = createConnection (transformedNewLocation, isPost, | |||
| postData, headers, timeOutMs, | |||
| httpRequestCmd); | |||
| } | |||
| catch (Throwable e) | |||
| { | |||
| return false; | |||
| } | |||
| } | |||
| } | |||
| else | |||
| { | |||
| break; | |||
| } | |||
| } | |||
| catch (Throwable e) | |||
| { | |||
| return false; | |||
| } | |||
| } | |||
| else | |||
| { | |||
| break; | |||
| } | |||
| } | |||
| else | |||
| { | |||
| break; | |||
| } | |||
| } | |||
| return result; | |||
| } | |||
| private final boolean doConnect() | |||
| { | |||
| synchronized (createStreamLock) | |||
| { | |||
| @@ -1068,9 +1227,16 @@ public class OSCSender extends Activity | |||
| {} | |||
| for (java.util.Map.Entry<String, java.util.List<String>> entry : connection.getHeaderFields().entrySet()) | |||
| { | |||
| if (entry.getKey() != null && entry.getValue() != null) | |||
| responseHeaders.append (entry.getKey() + ": " | |||
| + android.text.TextUtils.join (",", entry.getValue()) + "\n"); | |||
| { | |||
| responseHeaders.append(entry.getKey() + ": " | |||
| + android.text.TextUtils.join(",", entry.getValue()) + "\n"); | |||
| if (entry.getKey().compareTo ("Content-Length") == 0) | |||
| totalLength = Integer.decode (entry.getValue().get (0)); | |||
| } | |||
| } | |||
| return true; | |||
| } | |||
| @@ -1173,13 +1339,20 @@ public class OSCSender extends Activity | |||
| } | |||
| public final long getPosition() { return position; } | |||
| public final long getTotalLength() { return -1; } | |||
| public final long getTotalLength() { return totalLength; } | |||
| public final boolean isExhausted() { return false; } | |||
| public final boolean setPosition (long newPos) { return false; } | |||
| private boolean isPost; | |||
| private byte[] postData; | |||
| private String headers; | |||
| private int timeOutMs; | |||
| String httpRequestCmd; | |||
| private HttpURLConnection connection; | |||
| private int[] statusCode; | |||
| private StringBuffer responseHeaders; | |||
| private int totalLength; | |||
| private int numRedirectsToFollow; | |||
| private InputStream inputStream; | |||
| private long position; | |||
| private final ReentrantLock createStreamLock = new ReentrantLock(); | |||
| @@ -1201,89 +1374,15 @@ public class OSCSender extends Activity | |||
| else if (timeOutMs == 0) | |||
| timeOutMs = 30000; | |||
| // headers - if not empty, this string is appended onto the headers that are used for the request. It must therefore be a valid set of HTML header directives, separated by newlines. | |||
| // So convert headers string to an array, with an element for each line | |||
| String headerLines[] = headers.split("\\n"); | |||
| for (;;) | |||
| { | |||
| try | |||
| { | |||
| HttpURLConnection connection = (HttpURLConnection) (new URL(address).openConnection()); | |||
| if (connection != null) | |||
| { | |||
| try | |||
| { | |||
| connection.setInstanceFollowRedirects (false); | |||
| connection.setConnectTimeout (timeOutMs); | |||
| connection.setReadTimeout (timeOutMs); | |||
| HTTPStream httpStream = new HTTPStream (address, isPost, postData, headers, | |||
| timeOutMs, statusCode, responseHeaders, | |||
| numRedirectsToFollow, httpRequestCmd); | |||
| // Set request headers | |||
| for (int i = 0; i < headerLines.length; ++i) | |||
| { | |||
| int pos = headerLines[i].indexOf (":"); | |||
| if (pos > 0 && pos < headerLines[i].length()) | |||
| { | |||
| String field = headerLines[i].substring (0, pos); | |||
| String value = headerLines[i].substring (pos + 1); | |||
| if (value.length() > 0) | |||
| connection.setRequestProperty (field, value); | |||
| } | |||
| } | |||
| connection.setRequestMethod (httpRequestCmd); | |||
| if (isPost) | |||
| { | |||
| connection.setDoOutput (true); | |||
| if (postData != null) | |||
| { | |||
| OutputStream out = connection.getOutputStream(); | |||
| out.write(postData); | |||
| out.flush(); | |||
| } | |||
| } | |||
| HTTPStream httpStream = new HTTPStream (connection, statusCode, responseHeaders); | |||
| // Process redirect & continue as necessary | |||
| int status = statusCode[0]; | |||
| if (--numRedirectsToFollow >= 0 | |||
| && (status == 301 || status == 302 || status == 303 || status == 307)) | |||
| { | |||
| // Assumes only one occurrence of "Location" | |||
| int pos1 = responseHeaders.indexOf ("Location:") + 10; | |||
| int pos2 = responseHeaders.indexOf ("\n", pos1); | |||
| if (pos2 > pos1) | |||
| { | |||
| String newLocation = responseHeaders.substring(pos1, pos2); | |||
| // Handle newLocation whether it's absolute or relative | |||
| URL baseUrl = new URL (address); | |||
| URL newUrl = new URL (baseUrl, newLocation); | |||
| String transformedNewLocation = newUrl.toString(); | |||
| if (transformedNewLocation != address) | |||
| { | |||
| address = transformedNewLocation; | |||
| // Clear responseHeaders before next iteration | |||
| responseHeaders.delete (0, responseHeaders.length()); | |||
| continue; | |||
| } | |||
| } | |||
| } | |||
| return httpStream; | |||
| } | |||
| catch (Throwable e) | |||
| { | |||
| connection.disconnect(); | |||
| } | |||
| } | |||
| return httpStream; | |||
| } | |||
| catch (Throwable e) {} | |||
| @@ -1309,7 +1408,14 @@ public class OSCSender extends Activity | |||
| return Environment.getExternalStoragePublicDirectory (type).getAbsolutePath(); | |||
| } | |||
| public static final String getDocumentsFolder() { return Environment.getDataDirectory().getAbsolutePath(); } | |||
| public static final String getDocumentsFolder() | |||
| { | |||
| if (getAndroidSDKVersion() >= 19) | |||
| return getFileLocation ("Documents"); | |||
| return Environment.getDataDirectory().getAbsolutePath(); | |||
| } | |||
| public static final String getPicturesFolder() { return getFileLocation (Environment.DIRECTORY_PICTURES); } | |||
| public static final String getMusicFolder() { return getFileLocation (Environment.DIRECTORY_MUSIC); } | |||
| public static final String getMoviesFolder() { return getFileLocation (Environment.DIRECTORY_MOVIES); } | |||
| @@ -1404,7 +1510,7 @@ public class OSCSender extends Activity | |||
| return null; | |||
| } | |||
| public final int getAndroidSDKVersion() | |||
| public static final int getAndroidSDKVersion() | |||
| { | |||
| return android.os.Build.VERSION.SDK_INT; | |||
| } | |||
| @@ -2,7 +2,7 @@ | |||
| <JUCERPROJECT id="rysVAr" name="OSCSender" projectType="guiapp" version="1.0.0" | |||
| bundleIdentifier="com.roli.OSCSender" includeBinaryInAppConfig="1" | |||
| jucerVersion="5.1.1" displaySplashScreen="0" reportAppUsage="0" | |||
| jucerVersion="5.1.2" displaySplashScreen="0" reportAppUsage="0" | |||
| splashScreenColour="Dark" companyName="ROLI Ltd." cppLanguageStandard="11"> | |||
| <MAINGROUP id="knnZZJ" name="OSCSender"> | |||
| <GROUP id="{F2A0007A-4D24-4DD6-DEC8-6428F36CE45D}" name="Source"> | |||
| @@ -2,7 +2,7 @@ | |||
| <JUCERPROJECT id="c3KrlE" name="OpenGLAppExample" projectType="guiapp" version="1.0.0" | |||
| bundleIdentifier="com.roli.OpenGLAppExample" includeBinaryInAppConfig="1" | |||
| jucerVersion="5.1.1" displaySplashScreen="0" reportAppUsage="0" | |||
| jucerVersion="5.1.2" displaySplashScreen="0" reportAppUsage="0" | |||
| splashScreenColour="Dark" companyName="ROLI Ltd." cppLanguageStandard="11"> | |||
| <MAINGROUP id="amjEXL" name="OpenGLAppExample"> | |||
| <GROUP id="{28FE2D12-A88D-07E2-72CF-CD04014CF225}" name="Source"> | |||
| @@ -2,7 +2,7 @@ | |||
| <JUCERPROJECT id="jKHEJM" name="PluckedStringsDemo" projectType="guiapp" version="1.0.0" | |||
| bundleIdentifier="com.roli.PluckedStringsDemo" includeBinaryInAppConfig="1" | |||
| jucerVersion="5.1.1" displaySplashScreen="0" reportAppUsage="0" | |||
| jucerVersion="5.1.2" displaySplashScreen="0" reportAppUsage="0" | |||
| splashScreenColour="Dark" companyName="ROLI Ltd." cppLanguageStandard="11"> | |||
| <MAINGROUP id="IWdVf7" name="PluckedStringsDemo"> | |||
| <GROUP id="{8800CD50-6741-8B75-0305-FAF13427EF5D}" name="Source"> | |||
| @@ -9,7 +9,7 @@ | |||
| pluginIsMidiEffectPlugin="1" pluginSilenceInIsSilenceOut="0" | |||
| pluginEditorRequiresKeys="0" pluginAUExportPrefix="ArpeggiatorAU" | |||
| pluginRTASCategory="" aaxIdentifier="com.roli.Arpeggiator" pluginAAXCategory="AAX_EPlugInCategory_Effect" | |||
| jucerVersion="5.1.1" companyName="ROLI Ltd." companyWebsite="www.juce.com" | |||
| jucerVersion="5.1.2" companyName="ROLI Ltd." companyWebsite="www.juce.com" | |||
| companyEmail="info@juce.com" buildAUv3="0" buildStandalone="0" | |||
| enableIAA="0" displaySplashScreen="0" reportAppUsage="0" splashScreenColour="Dark" | |||
| cppLanguageStandard="11"> | |||
| @@ -103,6 +103,10 @@ | |||
| //#define JUCE_FORCE_USE_LEGACY_PARAM_IDS 1 | |||
| #endif | |||
| #ifndef JUCE_FORCE_LEGACY_PARAMETER_AUTOMATION_TYPE | |||
| //#define JUCE_FORCE_LEGACY_PARAMETER_AUTOMATION_TYPE 1 | |||
| #endif | |||
| #ifndef JUCE_USE_STUDIO_ONE_COMPATIBLE_PARAMETERS | |||
| //#define JUCE_USE_STUDIO_ONE_COMPATIBLE_PARAMETERS 1 | |||
| #endif | |||
| @@ -8,7 +8,7 @@ | |||
| pluginIsSynth="0" pluginWantsMidiIn="0" pluginProducesMidiOut="0" | |||
| pluginSilenceInIsSilenceOut="1" pluginEditorRequiresKeys="0" | |||
| pluginAUExportPrefix="GainPlugInAU" pluginRTASCategory="" aaxIdentifier="com.roli.GainPlugIn" | |||
| pluginAAXCategory="AAX_ePlugInCategory_Dynamics" jucerVersion="5.1.1" | |||
| pluginAAXCategory="AAX_ePlugInCategory_Dynamics" jucerVersion="5.1.2" | |||
| pluginIsMidiEffectPlugin="0" buildAUv3="0" buildStandalone="0" | |||
| enableIAA="0" displaySplashScreen="0" reportAppUsage="0" splashScreenColour="Dark" | |||
| companyName="ROLI Ltd." cppLanguageStandard="11"> | |||
| @@ -103,6 +103,10 @@ | |||
| //#define JUCE_FORCE_USE_LEGACY_PARAM_IDS 1 | |||
| #endif | |||
| #ifndef JUCE_FORCE_LEGACY_PARAMETER_AUTOMATION_TYPE | |||
| //#define JUCE_FORCE_LEGACY_PARAMETER_AUTOMATION_TYPE 1 | |||
| #endif | |||
| #ifndef JUCE_USE_STUDIO_ONE_COMPATIBLE_PARAMETERS | |||
| //#define JUCE_USE_STUDIO_ONE_COMPATIBLE_PARAMETERS 1 | |||
| #endif | |||
| @@ -9,7 +9,7 @@ | |||
| pluginIsSynth="0" pluginWantsMidiIn="0" pluginProducesMidiOut="0" | |||
| pluginIsMidiEffectPlugin="0" pluginEditorRequiresKeys="0" pluginAUExportPrefix="InterAppAudioEffectAU" | |||
| pluginRTASCategory="" aaxIdentifier="com.yourcompany.InterAppAudioEffect" | |||
| pluginAAXCategory="AAX_ePlugInCategory_Dynamics" jucerVersion="5.1.1" | |||
| pluginAAXCategory="AAX_ePlugInCategory_Dynamics" jucerVersion="5.1.2" | |||
| companyName="ROLI Ltd." buildStandalone="1" displaySplashScreen="0" | |||
| reportAppUsage="0" splashScreenColour="Dark" cppLanguageStandard="11"> | |||
| <MAINGROUP id="BhTVmm" name="InterAppAudioEffect"> | |||
| @@ -101,6 +101,10 @@ | |||
| //#define JUCE_FORCE_USE_LEGACY_PARAM_IDS 1 | |||
| #endif | |||
| #ifndef JUCE_FORCE_LEGACY_PARAMETER_AUTOMATION_TYPE | |||
| //#define JUCE_FORCE_LEGACY_PARAMETER_AUTOMATION_TYPE 1 | |||
| #endif | |||
| #ifndef JUCE_USE_STUDIO_ONE_COMPATIBLE_PARAMETERS | |||
| //#define JUCE_USE_STUDIO_ONE_COMPATIBLE_PARAMETERS 1 | |||
| #endif | |||
| @@ -103,6 +103,10 @@ | |||
| //#define JUCE_FORCE_USE_LEGACY_PARAM_IDS 1 | |||
| #endif | |||
| #ifndef JUCE_FORCE_LEGACY_PARAMETER_AUTOMATION_TYPE | |||
| //#define JUCE_FORCE_LEGACY_PARAMETER_AUTOMATION_TYPE 1 | |||
| #endif | |||
| #ifndef JUCE_USE_STUDIO_ONE_COMPATIBLE_PARAMETERS | |||
| //#define JUCE_USE_STUDIO_ONE_COMPATIBLE_PARAMETERS 1 | |||
| #endif | |||
| @@ -9,7 +9,7 @@ | |||
| pluginSilenceInIsSilenceOut="0" pluginEditorRequiresKeys="0" | |||
| pluginAUExportPrefix="MultiOutSynthAU" pluginRTASCategory="" | |||
| aaxIdentifier="com.roli.MultiOutSynth" pluginAAXCategory="AAX_ePlugInCategory_SWGenerators" | |||
| jucerVersion="5.1.1" companyName="ROLI Ltd." companyWebsite="www.roli.com" | |||
| jucerVersion="5.1.2" companyName="ROLI Ltd." companyWebsite="www.roli.com" | |||
| companyEmail="info@juce.com" pluginIsMidiEffectPlugin="0" buildAUv3="0" | |||
| buildStandalone="0" enableIAA="0" displaySplashScreen="0" reportAppUsage="0" | |||
| splashScreenColour="Dark" cppLanguageStandard="11"> | |||
| @@ -103,6 +103,10 @@ | |||
| //#define JUCE_FORCE_USE_LEGACY_PARAM_IDS 1 | |||
| #endif | |||
| #ifndef JUCE_FORCE_LEGACY_PARAMETER_AUTOMATION_TYPE | |||
| //#define JUCE_FORCE_LEGACY_PARAMETER_AUTOMATION_TYPE 1 | |||
| #endif | |||
| #ifndef JUCE_USE_STUDIO_ONE_COMPATIBLE_PARAMETERS | |||
| //#define JUCE_USE_STUDIO_ONE_COMPATIBLE_PARAMETERS 1 | |||
| #endif | |||
| @@ -8,7 +8,7 @@ | |||
| pluginIsSynth="0" pluginWantsMidiIn="0" pluginProducesMidiOut="0" | |||
| pluginSilenceInIsSilenceOut="0" pluginEditorRequiresKeys="0" | |||
| pluginAUExportPrefix="NoiseGateAU" pluginRTASCategory="" aaxIdentifier="com.roli.NoiseGate" | |||
| pluginAAXCategory="AAX_ePlugInCategory_Dynamics" jucerVersion="5.1.1" | |||
| pluginAAXCategory="AAX_ePlugInCategory_Dynamics" jucerVersion="5.1.2" | |||
| pluginIsMidiEffectPlugin="0" buildAUv3="0" buildStandalone="0" | |||
| enableIAA="0" displaySplashScreen="0" reportAppUsage="0" splashScreenColour="Dark" | |||
| companyName="ROLI Ltd." cppLanguageStandard="11"> | |||
| @@ -103,6 +103,10 @@ | |||
| //#define JUCE_FORCE_USE_LEGACY_PARAM_IDS 1 | |||
| #endif | |||
| #ifndef JUCE_FORCE_LEGACY_PARAMETER_AUTOMATION_TYPE | |||
| //#define JUCE_FORCE_LEGACY_PARAMETER_AUTOMATION_TYPE 1 | |||
| #endif | |||
| #ifndef JUCE_USE_STUDIO_ONE_COMPATIBLE_PARAMETERS | |||
| //#define JUCE_USE_STUDIO_ONE_COMPATIBLE_PARAMETERS 1 | |||
| #endif | |||
| @@ -9,7 +9,7 @@ | |||
| pluginIsMidiEffectPlugin="0" pluginSilenceInIsSilenceOut="0" | |||
| pluginEditorRequiresKeys="0" pluginAUExportPrefix="SurroundAU" | |||
| pluginRTASCategory="" aaxIdentifier="com.roli.Surround" pluginAAXCategory="AAX_ePlugInCategory_Dynamics" | |||
| jucerVersion="5.1.1" buildAUv3="0" buildStandalone="0" enableIAA="0" | |||
| jucerVersion="5.1.2" buildAUv3="0" buildStandalone="0" enableIAA="0" | |||
| displaySplashScreen="0" reportAppUsage="0" splashScreenColour="Dark" | |||
| companyName="ROLI Ltd." cppLanguageStandard="11"> | |||
| <MAINGROUP id="dEAH7t" name="Surround"> | |||
| @@ -174,6 +174,10 @@ | |||
| //#define JUCE_DSP_USE_STATIC_FFTW 1 | |||
| #endif | |||
| #ifndef JUCE_DSP_ENABLE_SNAP_TO_ZERO | |||
| //#define JUCE_DSP_ENABLE_SNAP_TO_ZERO 1 | |||
| #endif | |||
| //============================================================================== | |||
| // juce_events flags: | |||
| @@ -2,7 +2,7 @@ | |||
| <JUCERPROJECT id="cgcxPd" name="SimpleFFTExample" projectType="guiapp" version="1.0.0" | |||
| bundleIdentifier="com.roli.SimpleFFTExample" includeBinaryInAppConfig="1" | |||
| jucerVersion="5.1.1" displaySplashScreen="0" reportAppUsage="0" | |||
| jucerVersion="5.1.2" displaySplashScreen="0" reportAppUsage="0" | |||
| splashScreenColour="Dark" companyName="ROLI Ltd." cppLanguageStandard="14"> | |||
| <MAINGROUP id="rZCHr8" name="SimpleFFTExample"> | |||
| <GROUP id="{8DC23B3F-98AC-AB1C-B26A-E693AF2DF0D2}" name="Source"> | |||
| @@ -1448,11 +1448,18 @@ public class JuceDemoPlugin extends Activity | |||
| builder.setTitle (title) | |||
| .setMessage (message) | |||
| .setCancelable (true) | |||
| .setOnCancelListener (new DialogInterface.OnCancelListener() | |||
| { | |||
| public void onCancel (DialogInterface dialog) | |||
| { | |||
| JuceDemoPlugin.this.alertDismissed (callback, 0); | |||
| } | |||
| }) | |||
| .setPositiveButton ("OK", new DialogInterface.OnClickListener() | |||
| { | |||
| public void onClick (DialogInterface dialog, int id) | |||
| { | |||
| dialog.cancel(); | |||
| dialog.dismiss(); | |||
| JuceDemoPlugin.this.alertDismissed (callback, 0); | |||
| } | |||
| }); | |||
| @@ -1467,11 +1474,18 @@ public class JuceDemoPlugin extends Activity | |||
| builder.setTitle (title) | |||
| .setMessage (message) | |||
| .setCancelable (true) | |||
| .setOnCancelListener (new DialogInterface.OnCancelListener() | |||
| { | |||
| public void onCancel (DialogInterface dialog) | |||
| { | |||
| JuceDemoPlugin.this.alertDismissed (callback, 0); | |||
| } | |||
| }) | |||
| .setPositiveButton (okButtonText.isEmpty() ? "OK" : okButtonText, new DialogInterface.OnClickListener() | |||
| { | |||
| public void onClick (DialogInterface dialog, int id) | |||
| { | |||
| dialog.cancel(); | |||
| dialog.dismiss(); | |||
| JuceDemoPlugin.this.alertDismissed (callback, 1); | |||
| } | |||
| }) | |||
| @@ -1479,7 +1493,7 @@ public class JuceDemoPlugin extends Activity | |||
| { | |||
| public void onClick (DialogInterface dialog, int id) | |||
| { | |||
| dialog.cancel(); | |||
| dialog.dismiss(); | |||
| JuceDemoPlugin.this.alertDismissed (callback, 0); | |||
| } | |||
| }); | |||
| @@ -1493,11 +1507,18 @@ public class JuceDemoPlugin extends Activity | |||
| builder.setTitle (title) | |||
| .setMessage (message) | |||
| .setCancelable (true) | |||
| .setOnCancelListener (new DialogInterface.OnCancelListener() | |||
| { | |||
| public void onCancel (DialogInterface dialog) | |||
| { | |||
| JuceDemoPlugin.this.alertDismissed (callback, 0); | |||
| } | |||
| }) | |||
| .setPositiveButton ("Yes", new DialogInterface.OnClickListener() | |||
| { | |||
| public void onClick (DialogInterface dialog, int id) | |||
| { | |||
| dialog.cancel(); | |||
| dialog.dismiss(); | |||
| JuceDemoPlugin.this.alertDismissed (callback, 1); | |||
| } | |||
| }) | |||
| @@ -1505,7 +1526,7 @@ public class JuceDemoPlugin extends Activity | |||
| { | |||
| public void onClick (DialogInterface dialog, int id) | |||
| { | |||
| dialog.cancel(); | |||
| dialog.dismiss(); | |||
| JuceDemoPlugin.this.alertDismissed (callback, 2); | |||
| } | |||
| }) | |||
| @@ -1513,7 +1534,7 @@ public class JuceDemoPlugin extends Activity | |||
| { | |||
| public void onClick (DialogInterface dialog, int id) | |||
| { | |||
| dialog.cancel(); | |||
| dialog.dismiss(); | |||
| JuceDemoPlugin.this.alertDismissed (callback, 0); | |||
| } | |||
| }); | |||
| @@ -1911,13 +1932,75 @@ public class JuceDemoPlugin extends Activity | |||
| //============================================================================== | |||
| public static class HTTPStream | |||
| { | |||
| public HTTPStream (HttpURLConnection connection_, | |||
| int[] statusCode_, | |||
| StringBuffer responseHeaders_) | |||
| public HTTPStream (String address, boolean isPostToUse, byte[] postDataToUse, | |||
| String headersToUse, int timeOutMsToUse, | |||
| int[] statusCodeToUse, StringBuffer responseHeadersToUse, | |||
| int numRedirectsToFollowToUse, String httpRequestCmdToUse) throws IOException | |||
| { | |||
| connection = connection_; | |||
| statusCode = statusCode_; | |||
| responseHeaders = responseHeaders_; | |||
| isPost = isPostToUse; | |||
| postData = postDataToUse; | |||
| headers = headersToUse; | |||
| timeOutMs = timeOutMsToUse; | |||
| statusCode = statusCodeToUse; | |||
| responseHeaders = responseHeadersToUse; | |||
| totalLength = -1; | |||
| numRedirectsToFollow = numRedirectsToFollowToUse; | |||
| httpRequestCmd = httpRequestCmdToUse; | |||
| connection = createConnection (address, isPost, postData, headers, timeOutMs, httpRequestCmd); | |||
| } | |||
| private final HttpURLConnection createConnection (String address, boolean isPost, byte[] postData, | |||
| String headers, int timeOutMs, String httpRequestCmdToUse) throws IOException | |||
| { | |||
| HttpURLConnection newConnection = (HttpURLConnection) (new URL(address).openConnection()); | |||
| try | |||
| { | |||
| newConnection.setInstanceFollowRedirects (false); | |||
| newConnection.setConnectTimeout (timeOutMs); | |||
| newConnection.setReadTimeout (timeOutMs); | |||
| // headers - if not empty, this string is appended onto the headers that are used for the request. It must therefore be a valid set of HTML header directives, separated by newlines. | |||
| // So convert headers string to an array, with an element for each line | |||
| String headerLines[] = headers.split("\\n"); | |||
| // Set request headers | |||
| for (int i = 0; i < headerLines.length; ++i) | |||
| { | |||
| int pos = headerLines[i].indexOf (":"); | |||
| if (pos > 0 && pos < headerLines[i].length()) | |||
| { | |||
| String field = headerLines[i].substring (0, pos); | |||
| String value = headerLines[i].substring (pos + 1); | |||
| if (value.length() > 0) | |||
| newConnection.setRequestProperty (field, value); | |||
| } | |||
| } | |||
| newConnection.setRequestMethod (httpRequestCmd); | |||
| if (isPost) | |||
| { | |||
| newConnection.setDoOutput (true); | |||
| if (postData != null) | |||
| { | |||
| OutputStream out = newConnection.getOutputStream(); | |||
| out.write(postData); | |||
| out.flush(); | |||
| } | |||
| } | |||
| return newConnection; | |||
| } | |||
| catch (Throwable e) | |||
| { | |||
| newConnection.disconnect(); | |||
| throw new IOException ("Connection error"); | |||
| } | |||
| } | |||
| private final InputStream getCancellableStream (final boolean isInput) throws ExecutionException | |||
| @@ -1940,19 +2023,12 @@ public class JuceDemoPlugin extends Activity | |||
| try | |||
| { | |||
| if (connection.getConnectTimeout() > 0) | |||
| return streamFuture.get (connection.getConnectTimeout(), TimeUnit.MILLISECONDS); | |||
| else | |||
| return streamFuture.get(); | |||
| return streamFuture.get(); | |||
| } | |||
| catch (InterruptedException e) | |||
| { | |||
| return null; | |||
| } | |||
| catch (TimeoutException e) | |||
| { | |||
| return null; | |||
| } | |||
| catch (CancellationException e) | |||
| { | |||
| return null; | |||
| @@ -1960,6 +2036,89 @@ public class JuceDemoPlugin extends Activity | |||
| } | |||
| public final boolean connect() | |||
| { | |||
| boolean result = false; | |||
| int numFollowedRedirects = 0; | |||
| while (true) | |||
| { | |||
| result = doConnect(); | |||
| if (! result) | |||
| return false; | |||
| if (++numFollowedRedirects > numRedirectsToFollow) | |||
| break; | |||
| int status = statusCode[0]; | |||
| if (status == 301 || status == 302 || status == 303 || status == 307) | |||
| { | |||
| // Assumes only one occurrence of "Location" | |||
| int pos1 = responseHeaders.indexOf ("Location:") + 10; | |||
| int pos2 = responseHeaders.indexOf ("\n", pos1); | |||
| if (pos2 > pos1) | |||
| { | |||
| String currentLocation = connection.getURL().toString(); | |||
| String newLocation = responseHeaders.substring (pos1, pos2); | |||
| try | |||
| { | |||
| // Handle newLocation whether it's absolute or relative | |||
| URL baseUrl = new URL (currentLocation); | |||
| URL newUrl = new URL (baseUrl, newLocation); | |||
| String transformedNewLocation = newUrl.toString(); | |||
| if (transformedNewLocation != currentLocation) | |||
| { | |||
| // Clear responseHeaders before next iteration | |||
| responseHeaders.delete (0, responseHeaders.length()); | |||
| synchronized (createStreamLock) | |||
| { | |||
| if (hasBeenCancelled.get()) | |||
| return false; | |||
| connection.disconnect(); | |||
| try | |||
| { | |||
| connection = createConnection (transformedNewLocation, isPost, | |||
| postData, headers, timeOutMs, | |||
| httpRequestCmd); | |||
| } | |||
| catch (Throwable e) | |||
| { | |||
| return false; | |||
| } | |||
| } | |||
| } | |||
| else | |||
| { | |||
| break; | |||
| } | |||
| } | |||
| catch (Throwable e) | |||
| { | |||
| return false; | |||
| } | |||
| } | |||
| else | |||
| { | |||
| break; | |||
| } | |||
| } | |||
| else | |||
| { | |||
| break; | |||
| } | |||
| } | |||
| return result; | |||
| } | |||
| private final boolean doConnect() | |||
| { | |||
| synchronized (createStreamLock) | |||
| { | |||
| @@ -1997,9 +2156,16 @@ public class JuceDemoPlugin extends Activity | |||
| {} | |||
| for (java.util.Map.Entry<String, java.util.List<String>> entry : connection.getHeaderFields().entrySet()) | |||
| { | |||
| if (entry.getKey() != null && entry.getValue() != null) | |||
| responseHeaders.append (entry.getKey() + ": " | |||
| + android.text.TextUtils.join (",", entry.getValue()) + "\n"); | |||
| { | |||
| responseHeaders.append(entry.getKey() + ": " | |||
| + android.text.TextUtils.join(",", entry.getValue()) + "\n"); | |||
| if (entry.getKey().compareTo ("Content-Length") == 0) | |||
| totalLength = Integer.decode (entry.getValue().get (0)); | |||
| } | |||
| } | |||
| return true; | |||
| } | |||
| @@ -2102,13 +2268,20 @@ public class JuceDemoPlugin extends Activity | |||
| } | |||
| public final long getPosition() { return position; } | |||
| public final long getTotalLength() { return -1; } | |||
| public final long getTotalLength() { return totalLength; } | |||
| public final boolean isExhausted() { return false; } | |||
| public final boolean setPosition (long newPos) { return false; } | |||
| private boolean isPost; | |||
| private byte[] postData; | |||
| private String headers; | |||
| private int timeOutMs; | |||
| String httpRequestCmd; | |||
| private HttpURLConnection connection; | |||
| private int[] statusCode; | |||
| private StringBuffer responseHeaders; | |||
| private int totalLength; | |||
| private int numRedirectsToFollow; | |||
| private InputStream inputStream; | |||
| private long position; | |||
| private final ReentrantLock createStreamLock = new ReentrantLock(); | |||
| @@ -2130,89 +2303,15 @@ public class JuceDemoPlugin extends Activity | |||
| else if (timeOutMs == 0) | |||
| timeOutMs = 30000; | |||
| // headers - if not empty, this string is appended onto the headers that are used for the request. It must therefore be a valid set of HTML header directives, separated by newlines. | |||
| // So convert headers string to an array, with an element for each line | |||
| String headerLines[] = headers.split("\\n"); | |||
| for (;;) | |||
| { | |||
| try | |||
| { | |||
| HttpURLConnection connection = (HttpURLConnection) (new URL(address).openConnection()); | |||
| HTTPStream httpStream = new HTTPStream (address, isPost, postData, headers, | |||
| timeOutMs, statusCode, responseHeaders, | |||
| numRedirectsToFollow, httpRequestCmd); | |||
| if (connection != null) | |||
| { | |||
| try | |||
| { | |||
| connection.setInstanceFollowRedirects (false); | |||
| connection.setConnectTimeout (timeOutMs); | |||
| connection.setReadTimeout (timeOutMs); | |||
| // Set request headers | |||
| for (int i = 0; i < headerLines.length; ++i) | |||
| { | |||
| int pos = headerLines[i].indexOf (":"); | |||
| if (pos > 0 && pos < headerLines[i].length()) | |||
| { | |||
| String field = headerLines[i].substring (0, pos); | |||
| String value = headerLines[i].substring (pos + 1); | |||
| if (value.length() > 0) | |||
| connection.setRequestProperty (field, value); | |||
| } | |||
| } | |||
| connection.setRequestMethod (httpRequestCmd); | |||
| if (isPost) | |||
| { | |||
| connection.setDoOutput (true); | |||
| if (postData != null) | |||
| { | |||
| OutputStream out = connection.getOutputStream(); | |||
| out.write(postData); | |||
| out.flush(); | |||
| } | |||
| } | |||
| HTTPStream httpStream = new HTTPStream (connection, statusCode, responseHeaders); | |||
| // Process redirect & continue as necessary | |||
| int status = statusCode[0]; | |||
| if (--numRedirectsToFollow >= 0 | |||
| && (status == 301 || status == 302 || status == 303 || status == 307)) | |||
| { | |||
| // Assumes only one occurrence of "Location" | |||
| int pos1 = responseHeaders.indexOf ("Location:") + 10; | |||
| int pos2 = responseHeaders.indexOf ("\n", pos1); | |||
| if (pos2 > pos1) | |||
| { | |||
| String newLocation = responseHeaders.substring(pos1, pos2); | |||
| // Handle newLocation whether it's absolute or relative | |||
| URL baseUrl = new URL (address); | |||
| URL newUrl = new URL (baseUrl, newLocation); | |||
| String transformedNewLocation = newUrl.toString(); | |||
| if (transformedNewLocation != address) | |||
| { | |||
| address = transformedNewLocation; | |||
| // Clear responseHeaders before next iteration | |||
| responseHeaders.delete (0, responseHeaders.length()); | |||
| continue; | |||
| } | |||
| } | |||
| } | |||
| return httpStream; | |||
| } | |||
| catch (Throwable e) | |||
| { | |||
| connection.disconnect(); | |||
| } | |||
| } | |||
| return httpStream; | |||
| } | |||
| catch (Throwable e) {} | |||
| @@ -2238,7 +2337,14 @@ public class JuceDemoPlugin extends Activity | |||
| return Environment.getExternalStoragePublicDirectory (type).getAbsolutePath(); | |||
| } | |||
| public static final String getDocumentsFolder() { return Environment.getDataDirectory().getAbsolutePath(); } | |||
| public static final String getDocumentsFolder() | |||
| { | |||
| if (getAndroidSDKVersion() >= 19) | |||
| return getFileLocation ("Documents"); | |||
| return Environment.getDataDirectory().getAbsolutePath(); | |||
| } | |||
| public static final String getPicturesFolder() { return getFileLocation (Environment.DIRECTORY_PICTURES); } | |||
| public static final String getMusicFolder() { return getFileLocation (Environment.DIRECTORY_MUSIC); } | |||
| public static final String getMoviesFolder() { return getFileLocation (Environment.DIRECTORY_MOVIES); } | |||
| @@ -2333,7 +2439,7 @@ public class JuceDemoPlugin extends Activity | |||
| return null; | |||
| } | |||
| public final int getAndroidSDKVersion() | |||
| public static final int getAndroidSDKVersion() | |||
| { | |||
| return android.os.Build.VERSION.SDK_INT; | |||
| } | |||
| @@ -5,6 +5,8 @@ | |||
| <dict> | |||
| <key>LSRequiresIPhoneOS</key> | |||
| <true/> | |||
| <key>NSMicrophoneUsageDescription</key> | |||
| <string>This app requires microphone input.</string> | |||
| <key>CFBundleExecutable</key> | |||
| <string>${EXECUTABLE_NAME}</string> | |||
| <key>CFBundleIdentifier</key> | |||
| @@ -5,6 +5,8 @@ | |||
| <dict> | |||
| <key>LSRequiresIPhoneOS</key> | |||
| <true/> | |||
| <key>NSMicrophoneUsageDescription</key> | |||
| <string>This app requires microphone input.</string> | |||
| <key>UIViewControllerBasedStatusBarAppearance</key> | |||
| <false/> | |||
| <key>CFBundleExecutable</key> | |||
| @@ -8,7 +8,7 @@ | |||
| pluginProducesMidiOut="1" pluginSilenceInIsSilenceOut="0" pluginTailLength="0" | |||
| pluginEditorRequiresKeys="1" pluginAUExportPrefix="JuceDemoProjectAU" | |||
| pluginAUViewClass="JuceDemoProjectAU_V1" pluginRTASCategory="" | |||
| bundleIdentifier="com.juce.JuceDemoPlugin" jucerVersion="5.1.1" | |||
| bundleIdentifier="com.juce.JuceDemoPlugin" jucerVersion="5.1.2" | |||
| companyName="ROLI Ltd." aaxIdentifier="com.yourcompany.JuceDemoPlugin" | |||
| buildAAX="0" pluginAAXCategory="AAX_ePlugInCategory_Dynamics" | |||
| includeBinaryInAppConfig="1" buildVST3="0" pluginManufacturerEmail="support@yourcompany.com" | |||
| @@ -107,7 +107,7 @@ | |||
| </MODULEPATHS> | |||
| </LINUX_MAKE> | |||
| <XCODE_IPHONE targetFolder="Builds/iOS" iosScreenOrientation="landscape" iosBackgroundAudio="1" | |||
| iosBackgroundBle="1"> | |||
| iosBackgroundBle="1" microphonePermissionNeeded="1"> | |||
| <CONFIGURATIONS> | |||
| <CONFIGURATION name="Debug" isDebug="1" optimisation="1" targetName="JuceDemoPlugin"/> | |||
| <CONFIGURATION name="Release" isDebug="0" optimisation="3" targetName="JuceDemoPlugin"/> | |||
| @@ -101,6 +101,10 @@ | |||
| //#define JUCE_FORCE_USE_LEGACY_PARAM_IDS 1 | |||
| #endif | |||
| #ifndef JUCE_FORCE_LEGACY_PARAMETER_AUTOMATION_TYPE | |||
| //#define JUCE_FORCE_LEGACY_PARAMETER_AUTOMATION_TYPE 1 | |||
| #endif | |||
| #ifndef JUCE_USE_STUDIO_ONE_COMPATIBLE_PARAMETERS | |||
| //#define JUCE_USE_STUDIO_ONE_COMPATIBLE_PARAMETERS 1 | |||
| #endif | |||
| @@ -42,13 +42,7 @@ public: | |||
| updateSliderPos(); | |||
| } | |||
| void valueChanged() override | |||
| { | |||
| if (isMouseButtonDown()) | |||
| param.setValueNotifyingHost ((float) Slider::getValue()); | |||
| else | |||
| param.setValue ((float) Slider::getValue()); | |||
| } | |||
| void valueChanged() override { param.setValueNotifyingHost ((float) Slider::getValue()); } | |||
| void timerCallback() override { updateSliderPos(); } | |||
| @@ -63,7 +57,7 @@ public: | |||
| const float newValue = param.getValue(); | |||
| if (newValue != (float) Slider::getValue() && ! isMouseButtonDown()) | |||
| Slider::setValue (newValue); | |||
| Slider::setValue (newValue, NotificationType::dontSendNotification); | |||
| } | |||
| AudioProcessorParameter& param; | |||
| @@ -26,137 +26,11 @@ | |||
| #include "PluginProcessor.h" | |||
| #include "PluginEditor.h" | |||
| #include "SinewaveSynth.h" | |||
| AudioProcessor* JUCE_CALLTYPE createPluginFilter(); | |||
| //============================================================================== | |||
| /** A demo synth sound that's just a basic sine wave.. */ | |||
| class SineWaveSound : public SynthesiserSound | |||
| { | |||
| public: | |||
| SineWaveSound() {} | |||
| bool appliesToNote (int /*midiNoteNumber*/) override { return true; } | |||
| bool appliesToChannel (int /*midiChannel*/) override { return true; } | |||
| }; | |||
| //============================================================================== | |||
| /** A simple demo synth voice that just plays a sine wave.. */ | |||
| class SineWaveVoice : public SynthesiserVoice | |||
| { | |||
| public: | |||
| SineWaveVoice() | |||
| { | |||
| } | |||
| bool canPlaySound (SynthesiserSound* sound) override | |||
| { | |||
| return dynamic_cast<SineWaveSound*> (sound) != nullptr; | |||
| } | |||
| void startNote (int midiNoteNumber, float velocity, | |||
| SynthesiserSound* /*sound*/, | |||
| int /*currentPitchWheelPosition*/) override | |||
| { | |||
| currentAngle = 0.0; | |||
| level = velocity * 0.15; | |||
| tailOff = 0.0; | |||
| double cyclesPerSecond = MidiMessage::getMidiNoteInHertz (midiNoteNumber); | |||
| double cyclesPerSample = cyclesPerSecond / getSampleRate(); | |||
| angleDelta = cyclesPerSample * 2.0 * double_Pi; | |||
| } | |||
| void stopNote (float /*velocity*/, bool allowTailOff) override | |||
| { | |||
| if (allowTailOff) | |||
| { | |||
| // start a tail-off by setting this flag. The render callback will pick up on | |||
| // this and do a fade out, calling clearCurrentNote() when it's finished. | |||
| if (tailOff == 0.0) // we only need to begin a tail-off if it's not already doing so - the | |||
| // stopNote method could be called more than once. | |||
| tailOff = 1.0; | |||
| } | |||
| else | |||
| { | |||
| // we're being told to stop playing immediately, so reset everything.. | |||
| clearCurrentNote(); | |||
| angleDelta = 0.0; | |||
| } | |||
| } | |||
| void pitchWheelMoved (int /*newValue*/) override | |||
| { | |||
| // can't be bothered implementing this for the demo! | |||
| } | |||
| void controllerMoved (int /*controllerNumber*/, int /*newValue*/) override | |||
| { | |||
| // not interested in controllers in this case. | |||
| } | |||
| void renderNextBlock (AudioBuffer<float>& outputBuffer, int startSample, int numSamples) override | |||
| { | |||
| processBlock (outputBuffer, startSample, numSamples); | |||
| } | |||
| void renderNextBlock (AudioBuffer<double>& outputBuffer, int startSample, int numSamples) override | |||
| { | |||
| processBlock (outputBuffer, startSample, numSamples); | |||
| } | |||
| private: | |||
| template <typename FloatType> | |||
| void processBlock (AudioBuffer<FloatType>& outputBuffer, int startSample, int numSamples) | |||
| { | |||
| if (angleDelta != 0.0) | |||
| { | |||
| if (tailOff > 0) | |||
| { | |||
| while (--numSamples >= 0) | |||
| { | |||
| auto currentSample = static_cast<FloatType> (std::sin (currentAngle) * level * tailOff); | |||
| for (int i = outputBuffer.getNumChannels(); --i >= 0;) | |||
| outputBuffer.addSample (i, startSample, currentSample); | |||
| currentAngle += angleDelta; | |||
| ++startSample; | |||
| tailOff *= 0.99; | |||
| if (tailOff <= 0.005) | |||
| { | |||
| clearCurrentNote(); | |||
| angleDelta = 0.0; | |||
| break; | |||
| } | |||
| } | |||
| } | |||
| else | |||
| { | |||
| while (--numSamples >= 0) | |||
| { | |||
| auto currentSample = static_cast<FloatType> (std::sin (currentAngle) * level); | |||
| for (int i = outputBuffer.getNumChannels(); --i >= 0;) | |||
| outputBuffer.addSample (i, startSample, currentSample); | |||
| currentAngle += angleDelta; | |||
| ++startSample; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| double currentAngle = 0, angleDelta = 0, level = 0, tailOff = 0; | |||
| }; | |||
| //============================================================================== | |||
| JuceDemoPluginAudioProcessor::JuceDemoPluginAudioProcessor() | |||
| : AudioProcessor (getBusesProperties()) | |||
| @@ -2,7 +2,7 @@ | |||
| <JUCERPROJECT id="NTe0XB0ij" name="Plugin Host" projectType="guiapp" version="1.0.0" | |||
| juceFolder="../../../juce" vstFolderMac="~/SDKs/vstsdk2.4" vstFolderPC="c:\SDKs\vstsdk2.4" | |||
| bundleIdentifier="com.roli.pluginhost" jucerVersion="5.1.1" companyName="ROLI Ltd." | |||
| bundleIdentifier="com.roli.pluginhost" jucerVersion="5.1.2" companyName="ROLI Ltd." | |||
| includeBinaryInAppConfig="1" displaySplashScreen="0" reportAppUsage="0" | |||
| splashScreenColour="Dark" cppLanguageStandard="11"> | |||
| <EXPORTFORMATS> | |||
| @@ -213,7 +213,12 @@ void MainHostWindow::changeListenerCallback (ChangeBroadcaster* changed) | |||
| StringArray MainHostWindow::getMenuBarNames() | |||
| { | |||
| return { "File", "Plugins", "Options", "Windows" }; | |||
| StringArray names; | |||
| names.add ("File"); | |||
| names.add ("Plugins"); | |||
| names.add ("Options"); | |||
| names.add ("Windows"); | |||
| return names; | |||
| } | |||
| PopupMenu MainHostWindow::getMenuForIndex (int topLevelMenuIndex, const String& /*menuName*/) | |||
| @@ -2,7 +2,7 @@ | |||
| <JUCERPROJECT id="AKfc5m" name="AudioPerformanceTest" projectType="guiapp" | |||
| version="1.0.0" bundleIdentifier="com.juce.AudioPerformanceTest" | |||
| includeBinaryInAppConfig="1" jucerVersion="5.1.1" displaySplashScreen="0" | |||
| includeBinaryInAppConfig="1" jucerVersion="5.1.2" displaySplashScreen="0" | |||
| reportAppUsage="0" splashScreenColour="Dark" companyName="ROLI Ltd." | |||
| cppLanguageStandard="11"> | |||
| <MAINGROUP id="b1eVTe" name="AudioPerformanceTest"> | |||
| @@ -1448,11 +1448,18 @@ public class AudioPerformanceTest extends Activity | |||
| builder.setTitle (title) | |||
| .setMessage (message) | |||
| .setCancelable (true) | |||
| .setOnCancelListener (new DialogInterface.OnCancelListener() | |||
| { | |||
| public void onCancel (DialogInterface dialog) | |||
| { | |||
| AudioPerformanceTest.this.alertDismissed (callback, 0); | |||
| } | |||
| }) | |||
| .setPositiveButton ("OK", new DialogInterface.OnClickListener() | |||
| { | |||
| public void onClick (DialogInterface dialog, int id) | |||
| { | |||
| dialog.cancel(); | |||
| dialog.dismiss(); | |||
| AudioPerformanceTest.this.alertDismissed (callback, 0); | |||
| } | |||
| }); | |||
| @@ -1467,11 +1474,18 @@ public class AudioPerformanceTest extends Activity | |||
| builder.setTitle (title) | |||
| .setMessage (message) | |||
| .setCancelable (true) | |||
| .setOnCancelListener (new DialogInterface.OnCancelListener() | |||
| { | |||
| public void onCancel (DialogInterface dialog) | |||
| { | |||
| AudioPerformanceTest.this.alertDismissed (callback, 0); | |||
| } | |||
| }) | |||
| .setPositiveButton (okButtonText.isEmpty() ? "OK" : okButtonText, new DialogInterface.OnClickListener() | |||
| { | |||
| public void onClick (DialogInterface dialog, int id) | |||
| { | |||
| dialog.cancel(); | |||
| dialog.dismiss(); | |||
| AudioPerformanceTest.this.alertDismissed (callback, 1); | |||
| } | |||
| }) | |||
| @@ -1479,7 +1493,7 @@ public class AudioPerformanceTest extends Activity | |||
| { | |||
| public void onClick (DialogInterface dialog, int id) | |||
| { | |||
| dialog.cancel(); | |||
| dialog.dismiss(); | |||
| AudioPerformanceTest.this.alertDismissed (callback, 0); | |||
| } | |||
| }); | |||
| @@ -1493,11 +1507,18 @@ public class AudioPerformanceTest extends Activity | |||
| builder.setTitle (title) | |||
| .setMessage (message) | |||
| .setCancelable (true) | |||
| .setOnCancelListener (new DialogInterface.OnCancelListener() | |||
| { | |||
| public void onCancel (DialogInterface dialog) | |||
| { | |||
| AudioPerformanceTest.this.alertDismissed (callback, 0); | |||
| } | |||
| }) | |||
| .setPositiveButton ("Yes", new DialogInterface.OnClickListener() | |||
| { | |||
| public void onClick (DialogInterface dialog, int id) | |||
| { | |||
| dialog.cancel(); | |||
| dialog.dismiss(); | |||
| AudioPerformanceTest.this.alertDismissed (callback, 1); | |||
| } | |||
| }) | |||
| @@ -1505,7 +1526,7 @@ public class AudioPerformanceTest extends Activity | |||
| { | |||
| public void onClick (DialogInterface dialog, int id) | |||
| { | |||
| dialog.cancel(); | |||
| dialog.dismiss(); | |||
| AudioPerformanceTest.this.alertDismissed (callback, 2); | |||
| } | |||
| }) | |||
| @@ -1513,7 +1534,7 @@ public class AudioPerformanceTest extends Activity | |||
| { | |||
| public void onClick (DialogInterface dialog, int id) | |||
| { | |||
| dialog.cancel(); | |||
| dialog.dismiss(); | |||
| AudioPerformanceTest.this.alertDismissed (callback, 0); | |||
| } | |||
| }); | |||
| @@ -1911,13 +1932,75 @@ public class AudioPerformanceTest extends Activity | |||
| //============================================================================== | |||
| public static class HTTPStream | |||
| { | |||
| public HTTPStream (HttpURLConnection connection_, | |||
| int[] statusCode_, | |||
| StringBuffer responseHeaders_) | |||
| public HTTPStream (String address, boolean isPostToUse, byte[] postDataToUse, | |||
| String headersToUse, int timeOutMsToUse, | |||
| int[] statusCodeToUse, StringBuffer responseHeadersToUse, | |||
| int numRedirectsToFollowToUse, String httpRequestCmdToUse) throws IOException | |||
| { | |||
| connection = connection_; | |||
| statusCode = statusCode_; | |||
| responseHeaders = responseHeaders_; | |||
| isPost = isPostToUse; | |||
| postData = postDataToUse; | |||
| headers = headersToUse; | |||
| timeOutMs = timeOutMsToUse; | |||
| statusCode = statusCodeToUse; | |||
| responseHeaders = responseHeadersToUse; | |||
| totalLength = -1; | |||
| numRedirectsToFollow = numRedirectsToFollowToUse; | |||
| httpRequestCmd = httpRequestCmdToUse; | |||
| connection = createConnection (address, isPost, postData, headers, timeOutMs, httpRequestCmd); | |||
| } | |||
| private final HttpURLConnection createConnection (String address, boolean isPost, byte[] postData, | |||
| String headers, int timeOutMs, String httpRequestCmdToUse) throws IOException | |||
| { | |||
| HttpURLConnection newConnection = (HttpURLConnection) (new URL(address).openConnection()); | |||
| try | |||
| { | |||
| newConnection.setInstanceFollowRedirects (false); | |||
| newConnection.setConnectTimeout (timeOutMs); | |||
| newConnection.setReadTimeout (timeOutMs); | |||
| // headers - if not empty, this string is appended onto the headers that are used for the request. It must therefore be a valid set of HTML header directives, separated by newlines. | |||
| // So convert headers string to an array, with an element for each line | |||
| String headerLines[] = headers.split("\\n"); | |||
| // Set request headers | |||
| for (int i = 0; i < headerLines.length; ++i) | |||
| { | |||
| int pos = headerLines[i].indexOf (":"); | |||
| if (pos > 0 && pos < headerLines[i].length()) | |||
| { | |||
| String field = headerLines[i].substring (0, pos); | |||
| String value = headerLines[i].substring (pos + 1); | |||
| if (value.length() > 0) | |||
| newConnection.setRequestProperty (field, value); | |||
| } | |||
| } | |||
| newConnection.setRequestMethod (httpRequestCmd); | |||
| if (isPost) | |||
| { | |||
| newConnection.setDoOutput (true); | |||
| if (postData != null) | |||
| { | |||
| OutputStream out = newConnection.getOutputStream(); | |||
| out.write(postData); | |||
| out.flush(); | |||
| } | |||
| } | |||
| return newConnection; | |||
| } | |||
| catch (Throwable e) | |||
| { | |||
| newConnection.disconnect(); | |||
| throw new IOException ("Connection error"); | |||
| } | |||
| } | |||
| private final InputStream getCancellableStream (final boolean isInput) throws ExecutionException | |||
| @@ -1940,19 +2023,12 @@ public class AudioPerformanceTest extends Activity | |||
| try | |||
| { | |||
| if (connection.getConnectTimeout() > 0) | |||
| return streamFuture.get (connection.getConnectTimeout(), TimeUnit.MILLISECONDS); | |||
| else | |||
| return streamFuture.get(); | |||
| return streamFuture.get(); | |||
| } | |||
| catch (InterruptedException e) | |||
| { | |||
| return null; | |||
| } | |||
| catch (TimeoutException e) | |||
| { | |||
| return null; | |||
| } | |||
| catch (CancellationException e) | |||
| { | |||
| return null; | |||
| @@ -1960,6 +2036,89 @@ public class AudioPerformanceTest extends Activity | |||
| } | |||
| public final boolean connect() | |||
| { | |||
| boolean result = false; | |||
| int numFollowedRedirects = 0; | |||
| while (true) | |||
| { | |||
| result = doConnect(); | |||
| if (! result) | |||
| return false; | |||
| if (++numFollowedRedirects > numRedirectsToFollow) | |||
| break; | |||
| int status = statusCode[0]; | |||
| if (status == 301 || status == 302 || status == 303 || status == 307) | |||
| { | |||
| // Assumes only one occurrence of "Location" | |||
| int pos1 = responseHeaders.indexOf ("Location:") + 10; | |||
| int pos2 = responseHeaders.indexOf ("\n", pos1); | |||
| if (pos2 > pos1) | |||
| { | |||
| String currentLocation = connection.getURL().toString(); | |||
| String newLocation = responseHeaders.substring (pos1, pos2); | |||
| try | |||
| { | |||
| // Handle newLocation whether it's absolute or relative | |||
| URL baseUrl = new URL (currentLocation); | |||
| URL newUrl = new URL (baseUrl, newLocation); | |||
| String transformedNewLocation = newUrl.toString(); | |||
| if (transformedNewLocation != currentLocation) | |||
| { | |||
| // Clear responseHeaders before next iteration | |||
| responseHeaders.delete (0, responseHeaders.length()); | |||
| synchronized (createStreamLock) | |||
| { | |||
| if (hasBeenCancelled.get()) | |||
| return false; | |||
| connection.disconnect(); | |||
| try | |||
| { | |||
| connection = createConnection (transformedNewLocation, isPost, | |||
| postData, headers, timeOutMs, | |||
| httpRequestCmd); | |||
| } | |||
| catch (Throwable e) | |||
| { | |||
| return false; | |||
| } | |||
| } | |||
| } | |||
| else | |||
| { | |||
| break; | |||
| } | |||
| } | |||
| catch (Throwable e) | |||
| { | |||
| return false; | |||
| } | |||
| } | |||
| else | |||
| { | |||
| break; | |||
| } | |||
| } | |||
| else | |||
| { | |||
| break; | |||
| } | |||
| } | |||
| return result; | |||
| } | |||
| private final boolean doConnect() | |||
| { | |||
| synchronized (createStreamLock) | |||
| { | |||
| @@ -1997,9 +2156,16 @@ public class AudioPerformanceTest extends Activity | |||
| {} | |||
| for (java.util.Map.Entry<String, java.util.List<String>> entry : connection.getHeaderFields().entrySet()) | |||
| { | |||
| if (entry.getKey() != null && entry.getValue() != null) | |||
| responseHeaders.append (entry.getKey() + ": " | |||
| + android.text.TextUtils.join (",", entry.getValue()) + "\n"); | |||
| { | |||
| responseHeaders.append(entry.getKey() + ": " | |||
| + android.text.TextUtils.join(",", entry.getValue()) + "\n"); | |||
| if (entry.getKey().compareTo ("Content-Length") == 0) | |||
| totalLength = Integer.decode (entry.getValue().get (0)); | |||
| } | |||
| } | |||
| return true; | |||
| } | |||
| @@ -2102,13 +2268,20 @@ public class AudioPerformanceTest extends Activity | |||
| } | |||
| public final long getPosition() { return position; } | |||
| public final long getTotalLength() { return -1; } | |||
| public final long getTotalLength() { return totalLength; } | |||
| public final boolean isExhausted() { return false; } | |||
| public final boolean setPosition (long newPos) { return false; } | |||
| private boolean isPost; | |||
| private byte[] postData; | |||
| private String headers; | |||
| private int timeOutMs; | |||
| String httpRequestCmd; | |||
| private HttpURLConnection connection; | |||
| private int[] statusCode; | |||
| private StringBuffer responseHeaders; | |||
| private int totalLength; | |||
| private int numRedirectsToFollow; | |||
| private InputStream inputStream; | |||
| private long position; | |||
| private final ReentrantLock createStreamLock = new ReentrantLock(); | |||
| @@ -2130,89 +2303,15 @@ public class AudioPerformanceTest extends Activity | |||
| else if (timeOutMs == 0) | |||
| timeOutMs = 30000; | |||
| // headers - if not empty, this string is appended onto the headers that are used for the request. It must therefore be a valid set of HTML header directives, separated by newlines. | |||
| // So convert headers string to an array, with an element for each line | |||
| String headerLines[] = headers.split("\\n"); | |||
| for (;;) | |||
| { | |||
| try | |||
| { | |||
| HttpURLConnection connection = (HttpURLConnection) (new URL(address).openConnection()); | |||
| HTTPStream httpStream = new HTTPStream (address, isPost, postData, headers, | |||
| timeOutMs, statusCode, responseHeaders, | |||
| numRedirectsToFollow, httpRequestCmd); | |||
| if (connection != null) | |||
| { | |||
| try | |||
| { | |||
| connection.setInstanceFollowRedirects (false); | |||
| connection.setConnectTimeout (timeOutMs); | |||
| connection.setReadTimeout (timeOutMs); | |||
| // Set request headers | |||
| for (int i = 0; i < headerLines.length; ++i) | |||
| { | |||
| int pos = headerLines[i].indexOf (":"); | |||
| if (pos > 0 && pos < headerLines[i].length()) | |||
| { | |||
| String field = headerLines[i].substring (0, pos); | |||
| String value = headerLines[i].substring (pos + 1); | |||
| if (value.length() > 0) | |||
| connection.setRequestProperty (field, value); | |||
| } | |||
| } | |||
| connection.setRequestMethod (httpRequestCmd); | |||
| if (isPost) | |||
| { | |||
| connection.setDoOutput (true); | |||
| if (postData != null) | |||
| { | |||
| OutputStream out = connection.getOutputStream(); | |||
| out.write(postData); | |||
| out.flush(); | |||
| } | |||
| } | |||
| HTTPStream httpStream = new HTTPStream (connection, statusCode, responseHeaders); | |||
| // Process redirect & continue as necessary | |||
| int status = statusCode[0]; | |||
| if (--numRedirectsToFollow >= 0 | |||
| && (status == 301 || status == 302 || status == 303 || status == 307)) | |||
| { | |||
| // Assumes only one occurrence of "Location" | |||
| int pos1 = responseHeaders.indexOf ("Location:") + 10; | |||
| int pos2 = responseHeaders.indexOf ("\n", pos1); | |||
| if (pos2 > pos1) | |||
| { | |||
| String newLocation = responseHeaders.substring(pos1, pos2); | |||
| // Handle newLocation whether it's absolute or relative | |||
| URL baseUrl = new URL (address); | |||
| URL newUrl = new URL (baseUrl, newLocation); | |||
| String transformedNewLocation = newUrl.toString(); | |||
| if (transformedNewLocation != address) | |||
| { | |||
| address = transformedNewLocation; | |||
| // Clear responseHeaders before next iteration | |||
| responseHeaders.delete (0, responseHeaders.length()); | |||
| continue; | |||
| } | |||
| } | |||
| } | |||
| return httpStream; | |||
| } | |||
| catch (Throwable e) | |||
| { | |||
| connection.disconnect(); | |||
| } | |||
| } | |||
| return httpStream; | |||
| } | |||
| catch (Throwable e) {} | |||
| @@ -2238,7 +2337,14 @@ public class AudioPerformanceTest extends Activity | |||
| return Environment.getExternalStoragePublicDirectory (type).getAbsolutePath(); | |||
| } | |||
| public static final String getDocumentsFolder() { return Environment.getDataDirectory().getAbsolutePath(); } | |||
| public static final String getDocumentsFolder() | |||
| { | |||
| if (getAndroidSDKVersion() >= 19) | |||
| return getFileLocation ("Documents"); | |||
| return Environment.getDataDirectory().getAbsolutePath(); | |||
| } | |||
| public static final String getPicturesFolder() { return getFileLocation (Environment.DIRECTORY_PICTURES); } | |||
| public static final String getMusicFolder() { return getFileLocation (Environment.DIRECTORY_MUSIC); } | |||
| public static final String getMoviesFolder() { return getFileLocation (Environment.DIRECTORY_MOVIES); } | |||
| @@ -2333,7 +2439,7 @@ public class AudioPerformanceTest extends Activity | |||
| return null; | |||
| } | |||
| public final int getAndroidSDKVersion() | |||
| public static final int getAndroidSDKVersion() | |||
| { | |||
| return android.os.Build.VERSION.SDK_INT; | |||
| } | |||
| @@ -33,7 +33,7 @@ ifeq ($(CONFIG),Debug) | |||
| TARGET_ARCH := -march=native | |||
| endif | |||
| JUCE_CPPFLAGS := $(DEPFLAGS) -DLINUX=1 -DDEBUG=1 -D_DEBUG=1 -DJUCER_LINUX_MAKE_6D53C8B4=1 -DJUCE_APP_VERSION=5.1.1 -DJUCE_APP_VERSION_HEX=0x50101 $(shell pkg-config --cflags freetype2 libcurl x11 xext xinerama webkit2gtk-4.0 gtk+-x11-3.0) -pthread -I../../JuceLibraryCode -I../../../../modules $(CPPFLAGS) | |||
| JUCE_CPPFLAGS := $(DEPFLAGS) -DLINUX=1 -DDEBUG=1 -D_DEBUG=1 -DJUCER_LINUX_MAKE_6D53C8B4=1 -DJUCE_APP_VERSION=5.1.2 -DJUCE_APP_VERSION_HEX=0x50102 $(shell pkg-config --cflags freetype2 libcurl x11 xext xinerama webkit2gtk-4.0 gtk+-x11-3.0) -pthread -I../../JuceLibraryCode -I../../../../modules $(CPPFLAGS) | |||
| JUCE_CPPFLAGS_APP := -DJucePlugin_Build_VST=0 -DJucePlugin_Build_VST3=0 -DJucePlugin_Build_AU=0 -DJucePlugin_Build_AUv3=0 -DJucePlugin_Build_RTAS=0 -DJucePlugin_Build_AAX=0 -DJucePlugin_Build_Standalone=0 | |||
| JUCE_TARGET_APP := Projucer | |||
| @@ -54,7 +54,7 @@ ifeq ($(CONFIG),Release) | |||
| TARGET_ARCH := -march=native | |||
| endif | |||
| JUCE_CPPFLAGS := $(DEPFLAGS) -DLINUX=1 -DNDEBUG=1 -DJUCER_LINUX_MAKE_6D53C8B4=1 -DJUCE_APP_VERSION=5.1.1 -DJUCE_APP_VERSION_HEX=0x50101 $(shell pkg-config --cflags freetype2 libcurl x11 xext xinerama webkit2gtk-4.0 gtk+-x11-3.0) -pthread -I../../JuceLibraryCode -I../../../../modules $(CPPFLAGS) | |||
| JUCE_CPPFLAGS := $(DEPFLAGS) -DLINUX=1 -DNDEBUG=1 -DJUCER_LINUX_MAKE_6D53C8B4=1 -DJUCE_APP_VERSION=5.1.2 -DJUCE_APP_VERSION_HEX=0x50102 $(shell pkg-config --cflags freetype2 libcurl x11 xext xinerama webkit2gtk-4.0 gtk+-x11-3.0) -pthread -I../../JuceLibraryCode -I../../../../modules $(CPPFLAGS) | |||
| JUCE_CPPFLAGS_APP := -DJucePlugin_Build_VST=0 -DJucePlugin_Build_VST3=0 -DJucePlugin_Build_AU=0 -DJucePlugin_Build_AUv3=0 -DJucePlugin_Build_RTAS=0 -DJucePlugin_Build_AAX=0 -DJucePlugin_Build_Standalone=0 | |||
| JUCE_TARGET_APP := Projucer | |||
| @@ -33,9 +33,9 @@ | |||
| <key>CFBundleSignature</key> | |||
| <string>????</string> | |||
| <key>CFBundleShortVersionString</key> | |||
| <string>5.1.1</string> | |||
| <string>5.1.2</string> | |||
| <key>CFBundleVersion</key> | |||
| <string>5.1.1</string> | |||
| <string>5.1.2</string> | |||
| <key>NSHumanReadableCopyright</key> | |||
| <string>ROLI Ltd.</string> | |||
| <key>NSHighResolutionCapable</key> | |||
| @@ -738,8 +738,8 @@ | |||
| "_DEBUG=1", | |||
| "DEBUG=1", | |||
| "JUCER_XCODE_MAC_F6D2F4CF=1", | |||
| "JUCE_APP_VERSION=5.1.1", | |||
| "JUCE_APP_VERSION_HEX=0x50101", | |||
| "JUCE_APP_VERSION=5.1.2", | |||
| "JUCE_APP_VERSION_HEX=0x50102", | |||
| "JucePlugin_Build_VST=0", | |||
| "JucePlugin_Build_VST3=0", | |||
| "JucePlugin_Build_AU=0", | |||
| @@ -771,8 +771,8 @@ | |||
| "_NDEBUG=1", | |||
| "NDEBUG=1", | |||
| "JUCER_XCODE_MAC_F6D2F4CF=1", | |||
| "JUCE_APP_VERSION=5.1.1", | |||
| "JUCE_APP_VERSION_HEX=0x50101", | |||
| "JUCE_APP_VERSION=5.1.2", | |||
| "JUCE_APP_VERSION_HEX=0x50102", | |||
| "JucePlugin_Build_VST=0", | |||
| "JucePlugin_Build_VST3=0", | |||
| "JucePlugin_Build_AU=0", | |||
| @@ -73,7 +73,7 @@ | |||
| <Optimization>Disabled</Optimization> | |||
| <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> | |||
| <AdditionalIncludeDirectories>..\..\JuceLibraryCode;..\..\..\..\modules;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> | |||
| <PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32;_WINDOWS;DEBUG;_DEBUG;JUCER_VS2013_78A5020=1;JUCE_APP_VERSION=5.1.1;JUCE_APP_VERSION_HEX=0x50101;JucePlugin_Build_VST=0;JucePlugin_Build_VST3=0;JucePlugin_Build_AU=0;JucePlugin_Build_AUv3=0;JucePlugin_Build_RTAS=0;JucePlugin_Build_AAX=0;JucePlugin_Build_Standalone=0;%(PreprocessorDefinitions)</PreprocessorDefinitions> | |||
| <PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32;_WINDOWS;DEBUG;_DEBUG;JUCER_VS2013_78A5020=1;JUCE_APP_VERSION=5.1.2;JUCE_APP_VERSION_HEX=0x50102;JucePlugin_Build_VST=0;JucePlugin_Build_VST3=0;JucePlugin_Build_AU=0;JucePlugin_Build_AUv3=0;JucePlugin_Build_RTAS=0;JucePlugin_Build_AAX=0;JucePlugin_Build_Standalone=0;%(PreprocessorDefinitions)</PreprocessorDefinitions> | |||
| <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> | |||
| <RuntimeTypeInfo>true</RuntimeTypeInfo> | |||
| <PrecompiledHeader/> | |||
| @@ -113,7 +113,7 @@ | |||
| <ClCompile> | |||
| <Optimization>Full</Optimization> | |||
| <AdditionalIncludeDirectories>..\..\JuceLibraryCode;..\..\..\..\modules;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> | |||
| <PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32;_WINDOWS;NDEBUG;JUCER_VS2013_78A5020=1;JUCE_APP_VERSION=5.1.1;JUCE_APP_VERSION_HEX=0x50101;JucePlugin_Build_VST=0;JucePlugin_Build_VST3=0;JucePlugin_Build_AU=0;JucePlugin_Build_AUv3=0;JucePlugin_Build_RTAS=0;JucePlugin_Build_AAX=0;JucePlugin_Build_Standalone=0;%(PreprocessorDefinitions)</PreprocessorDefinitions> | |||
| <PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32;_WINDOWS;NDEBUG;JUCER_VS2013_78A5020=1;JUCE_APP_VERSION=5.1.2;JUCE_APP_VERSION_HEX=0x50102;JucePlugin_Build_VST=0;JucePlugin_Build_VST3=0;JucePlugin_Build_AU=0;JucePlugin_Build_AUv3=0;JucePlugin_Build_RTAS=0;JucePlugin_Build_AAX=0;JucePlugin_Build_Standalone=0;%(PreprocessorDefinitions)</PreprocessorDefinitions> | |||
| <RuntimeLibrary>MultiThreaded</RuntimeLibrary> | |||
| <RuntimeTypeInfo>true</RuntimeTypeInfo> | |||
| <PrecompiledHeader/> | |||
| @@ -7,7 +7,7 @@ | |||
| #include <windows.h> | |||
| VS_VERSION_INFO VERSIONINFO | |||
| FILEVERSION 5,1,1,0 | |||
| FILEVERSION 5,1,2,0 | |||
| BEGIN | |||
| BLOCK "StringFileInfo" | |||
| BEGIN | |||
| @@ -15,9 +15,9 @@ BEGIN | |||
| BEGIN | |||
| VALUE "CompanyName", "ROLI Ltd.\0" | |||
| VALUE "FileDescription", "Projucer\0" | |||
| VALUE "FileVersion", "5.1.1\0" | |||
| VALUE "FileVersion", "5.1.2\0" | |||
| VALUE "ProductName", "Projucer\0" | |||
| VALUE "ProductVersion", "5.1.1\0" | |||
| VALUE "ProductVersion", "5.1.2\0" | |||
| END | |||
| END | |||
| @@ -73,7 +73,7 @@ | |||
| <Optimization>Disabled</Optimization> | |||
| <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> | |||
| <AdditionalIncludeDirectories>..\..\JuceLibraryCode;..\..\..\..\modules;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> | |||
| <PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32;_WINDOWS;DEBUG;_DEBUG;JUCER_VS2015_78A5022=1;JUCE_APP_VERSION=5.1.1;JUCE_APP_VERSION_HEX=0x50101;JucePlugin_Build_VST=0;JucePlugin_Build_VST3=0;JucePlugin_Build_AU=0;JucePlugin_Build_AUv3=0;JucePlugin_Build_RTAS=0;JucePlugin_Build_AAX=0;JucePlugin_Build_Standalone=0;%(PreprocessorDefinitions)</PreprocessorDefinitions> | |||
| <PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32;_WINDOWS;DEBUG;_DEBUG;JUCER_VS2015_78A5022=1;JUCE_APP_VERSION=5.1.2;JUCE_APP_VERSION_HEX=0x50102;JucePlugin_Build_VST=0;JucePlugin_Build_VST3=0;JucePlugin_Build_AU=0;JucePlugin_Build_AUv3=0;JucePlugin_Build_RTAS=0;JucePlugin_Build_AAX=0;JucePlugin_Build_Standalone=0;%(PreprocessorDefinitions)</PreprocessorDefinitions> | |||
| <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> | |||
| <RuntimeTypeInfo>true</RuntimeTypeInfo> | |||
| <PrecompiledHeader/> | |||
| @@ -113,7 +113,7 @@ | |||
| <ClCompile> | |||
| <Optimization>Full</Optimization> | |||
| <AdditionalIncludeDirectories>..\..\JuceLibraryCode;..\..\..\..\modules;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> | |||
| <PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32;_WINDOWS;NDEBUG;JUCER_VS2015_78A5022=1;JUCE_APP_VERSION=5.1.1;JUCE_APP_VERSION_HEX=0x50101;JucePlugin_Build_VST=0;JucePlugin_Build_VST3=0;JucePlugin_Build_AU=0;JucePlugin_Build_AUv3=0;JucePlugin_Build_RTAS=0;JucePlugin_Build_AAX=0;JucePlugin_Build_Standalone=0;%(PreprocessorDefinitions)</PreprocessorDefinitions> | |||
| <PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32;_WINDOWS;NDEBUG;JUCER_VS2015_78A5022=1;JUCE_APP_VERSION=5.1.2;JUCE_APP_VERSION_HEX=0x50102;JucePlugin_Build_VST=0;JucePlugin_Build_VST3=0;JucePlugin_Build_AU=0;JucePlugin_Build_AUv3=0;JucePlugin_Build_RTAS=0;JucePlugin_Build_AAX=0;JucePlugin_Build_Standalone=0;%(PreprocessorDefinitions)</PreprocessorDefinitions> | |||
| <RuntimeLibrary>MultiThreaded</RuntimeLibrary> | |||
| <RuntimeTypeInfo>true</RuntimeTypeInfo> | |||
| <PrecompiledHeader/> | |||
| @@ -7,7 +7,7 @@ | |||
| #include <windows.h> | |||
| VS_VERSION_INFO VERSIONINFO | |||
| FILEVERSION 5,1,1,0 | |||
| FILEVERSION 5,1,2,0 | |||
| BEGIN | |||
| BLOCK "StringFileInfo" | |||
| BEGIN | |||
| @@ -15,9 +15,9 @@ BEGIN | |||
| BEGIN | |||
| VALUE "CompanyName", "ROLI Ltd.\0" | |||
| VALUE "FileDescription", "Projucer\0" | |||
| VALUE "FileVersion", "5.1.1\0" | |||
| VALUE "FileVersion", "5.1.2\0" | |||
| VALUE "ProductName", "Projucer\0" | |||
| VALUE "ProductVersion", "5.1.1\0" | |||
| VALUE "ProductVersion", "5.1.2\0" | |||
| END | |||
| END | |||
| @@ -73,7 +73,7 @@ | |||
| <Optimization>Disabled</Optimization> | |||
| <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> | |||
| <AdditionalIncludeDirectories>..\..\JuceLibraryCode;..\..\..\..\modules;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> | |||
| <PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32;_WINDOWS;DEBUG;_DEBUG;JUCER_VS2017_78A5024=1;JUCE_APP_VERSION=5.1.1;JUCE_APP_VERSION_HEX=0x50101;JucePlugin_Build_VST=0;JucePlugin_Build_VST3=0;JucePlugin_Build_AU=0;JucePlugin_Build_AUv3=0;JucePlugin_Build_RTAS=0;JucePlugin_Build_AAX=0;JucePlugin_Build_Standalone=0;%(PreprocessorDefinitions)</PreprocessorDefinitions> | |||
| <PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32;_WINDOWS;DEBUG;_DEBUG;JUCER_VS2017_78A5024=1;JUCE_APP_VERSION=5.1.2;JUCE_APP_VERSION_HEX=0x50102;JucePlugin_Build_VST=0;JucePlugin_Build_VST3=0;JucePlugin_Build_AU=0;JucePlugin_Build_AUv3=0;JucePlugin_Build_RTAS=0;JucePlugin_Build_AAX=0;JucePlugin_Build_Standalone=0;%(PreprocessorDefinitions)</PreprocessorDefinitions> | |||
| <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> | |||
| <RuntimeTypeInfo>true</RuntimeTypeInfo> | |||
| <PrecompiledHeader/> | |||
| @@ -113,7 +113,7 @@ | |||
| <ClCompile> | |||
| <Optimization>Full</Optimization> | |||
| <AdditionalIncludeDirectories>..\..\JuceLibraryCode;..\..\..\..\modules;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> | |||
| <PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32;_WINDOWS;NDEBUG;JUCER_VS2017_78A5024=1;JUCE_APP_VERSION=5.1.1;JUCE_APP_VERSION_HEX=0x50101;JucePlugin_Build_VST=0;JucePlugin_Build_VST3=0;JucePlugin_Build_AU=0;JucePlugin_Build_AUv3=0;JucePlugin_Build_RTAS=0;JucePlugin_Build_AAX=0;JucePlugin_Build_Standalone=0;%(PreprocessorDefinitions)</PreprocessorDefinitions> | |||
| <PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;WIN32;_WINDOWS;NDEBUG;JUCER_VS2017_78A5024=1;JUCE_APP_VERSION=5.1.2;JUCE_APP_VERSION_HEX=0x50102;JucePlugin_Build_VST=0;JucePlugin_Build_VST3=0;JucePlugin_Build_AU=0;JucePlugin_Build_AUv3=0;JucePlugin_Build_RTAS=0;JucePlugin_Build_AAX=0;JucePlugin_Build_Standalone=0;%(PreprocessorDefinitions)</PreprocessorDefinitions> | |||
| <RuntimeLibrary>MultiThreaded</RuntimeLibrary> | |||
| <RuntimeTypeInfo>true</RuntimeTypeInfo> | |||
| <PrecompiledHeader/> | |||
| @@ -7,7 +7,7 @@ | |||
| #include <windows.h> | |||
| VS_VERSION_INFO VERSIONINFO | |||
| FILEVERSION 5,1,1,0 | |||
| FILEVERSION 5,1,2,0 | |||
| BEGIN | |||
| BLOCK "StringFileInfo" | |||
| BEGIN | |||
| @@ -15,9 +15,9 @@ BEGIN | |||
| BEGIN | |||
| VALUE "CompanyName", "ROLI Ltd.\0" | |||
| VALUE "FileDescription", "Projucer\0" | |||
| VALUE "FileVersion", "5.1.1\0" | |||
| VALUE "FileVersion", "5.1.2\0" | |||
| VALUE "ProductName", "Projucer\0" | |||
| VALUE "ProductVersion", "5.1.1\0" | |||
| VALUE "ProductVersion", "5.1.2\0" | |||
| END | |||
| END | |||
| @@ -1540,9 +1540,9 @@ static const unsigned char temp_binary_data_8[] = | |||
| " #endif\r\n" | |||
| "}\r\n" | |||
| "\r\n" | |||
| "bool FILTERCLASSNAME::isMidiEffect () const\r\n" | |||
| "bool FILTERCLASSNAME::isMidiEffect() const\r\n" | |||
| "{\r\n" | |||
| " #ifdef JucePlugin_IsMidiEffect\r\n" | |||
| " #if JucePlugin_IsMidiEffect\r\n" | |||
| " return true;\r\n" | |||
| " #else\r\n" | |||
| " return false;\r\n" | |||
| @@ -1617,6 +1617,7 @@ static const unsigned char temp_binary_data_8[] = | |||
| "\r\n" | |||
| "void FILTERCLASSNAME::processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages)\r\n" | |||
| "{\r\n" | |||
| " ScopedNoDenormals noDenormals;\r\n" | |||
| " const int totalNumInputChannels = getTotalNumInputChannels();\r\n" | |||
| " const int totalNumOutputChannels = getTotalNumOutputChannels();\r\n" | |||
| "\r\n" | |||
| @@ -7028,7 +7029,7 @@ const char* getNamedResource (const char* resourceNameUTF8, int& numBytes) throw | |||
| case 0xafccbd3f: numBytes = 3141; return jucer_AudioComponentTemplate_cpp; | |||
| case 0x27c5a93a: numBytes = 1310; return jucer_AudioPluginEditorTemplate_cpp; | |||
| case 0x4d0721bf: numBytes = 938; return jucer_AudioPluginEditorTemplate_h; | |||
| case 0x51b49ac5: numBytes = 5615; return jucer_AudioPluginFilterTemplate_cpp; | |||
| case 0x51b49ac5: numBytes = 5647; return jucer_AudioPluginFilterTemplate_cpp; | |||
| case 0x488afa0a: numBytes = 2245; return jucer_AudioPluginFilterTemplate_h; | |||
| case 0xabad7041: numBytes = 2151; return jucer_ComponentTemplate_cpp; | |||
| case 0xfc72fe86: numBytes = 2064; return jucer_ComponentTemplate_h; | |||
| @@ -33,7 +33,7 @@ namespace BinaryData | |||
| const int jucer_AudioPluginEditorTemplate_hSize = 938; | |||
| extern const char* jucer_AudioPluginFilterTemplate_cpp; | |||
| const int jucer_AudioPluginFilterTemplate_cppSize = 5615; | |||
| const int jucer_AudioPluginFilterTemplate_cppSize = 5647; | |||
| extern const char* jucer_AudioPluginFilterTemplate_h; | |||
| const int jucer_AudioPluginFilterTemplate_hSize = 2245; | |||
| @@ -34,7 +34,7 @@ | |||
| namespace ProjectInfo | |||
| { | |||
| const char* const projectName = "Projucer"; | |||
| const char* const versionString = "5.1.1"; | |||
| const int versionNumber = 0x50101; | |||
| const char* const versionString = "5.1.2"; | |||
| const int versionNumber = 0x50102; | |||
| } | |||
| #endif | |||
| @@ -1,7 +1,7 @@ | |||
| <?xml version="1.0" encoding="UTF-8"?> | |||
| <JUCERPROJECT id="M70qfTRRk" name="Projucer" projectType="guiapp" juceFolder="../../juce" | |||
| jucerVersion="5.1.1" version="5.1.1" bundleIdentifier="com.juce.theprojucer" | |||
| jucerVersion="5.1.2" version="5.1.2" bundleIdentifier="com.juce.theprojucer" | |||
| defines="" includeBinaryInAppConfig="1" splashScreenColour="Dark" | |||
| displaySplashScreen="0" reportAppUsage="0" companyName="ROLI Ltd." | |||
| cppLanguageStandard="11"> | |||
| @@ -54,9 +54,9 @@ bool FILTERCLASSNAME::producesMidi() const | |||
| #endif | |||
| } | |||
| bool FILTERCLASSNAME::isMidiEffect () const | |||
| bool FILTERCLASSNAME::isMidiEffect() const | |||
| { | |||
| #ifdef JucePlugin_IsMidiEffect | |||
| #if JucePlugin_IsMidiEffect | |||
| return true; | |||
| #else | |||
| return false; | |||
| @@ -131,6 +131,7 @@ bool FILTERCLASSNAME::isBusesLayoutSupported (const BusesLayout& layouts) const | |||
| void FILTERCLASSNAME::processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages) | |||
| { | |||
| ScopedNoDenormals noDenormals; | |||
| const int totalNumInputChannels = getTotalNumInputChannels(); | |||
| const int totalNumOutputChannels = getTotalNumOutputChannels(); | |||
| @@ -1236,6 +1236,9 @@ private: | |||
| defines.set ("JUCE_ANDROID_ACTIVITY_CLASSNAME", getJNIActivityClassName().replaceCharacter ('/', '_')); | |||
| defines.set ("JUCE_ANDROID_ACTIVITY_CLASSPATH", "\"" + getJNIActivityClassName() + "\""); | |||
| if (androidInAppBillingPermission.get()) | |||
| defines.set ("JUCE_IN_APP_PURCHASES", "1"); | |||
| if (supportsGLv3()) | |||
| defines.set ("JUCE_ANDROID_GL_ES_VERSION_3_0", "1"); | |||
| @@ -80,12 +80,10 @@ public: | |||
| void addWindowsTargetPlatformProperties (PropertyListBuilder& props) | |||
| { | |||
| static const char* targetPlatformNames[] = { "(default)", "8.1", "10.0.10240.0", "10.0.10586.0", "10.0.14393.0", "10.0.15063.0" }; | |||
| static const var targetPlatforms[] = { var(), "8.1", "10.0.10240.0", "10.0.10586.0", "10.0.14393.0", "10.0.15063.0" }; | |||
| if (getWindowsTargetPlatformVersionValue() == Value()) | |||
| getWindowsTargetPlatformVersionValue() = getDefaultWindowsTargetPlatformVersion(); | |||
| props.add (new ChoicePropertyComponent (getWindowsTargetPlatformVersionValue(), "Windows Target Platform", | |||
| StringArray (targetPlatformNames, numElementsInArray (targetPlatformNames)), | |||
| Array<var> (targetPlatforms, numElementsInArray (targetPlatforms))), | |||
| props.add (new TextPropertyComponent (getWindowsTargetPlatformVersionValue(), "Windows Target Platform", 20, false), | |||
| "Specifies the version of the Windows SDK that will be used when building this project. " | |||
| "The default value for this exporter is " + getDefaultWindowsTargetPlatformVersion()); | |||
| } | |||
| @@ -1040,6 +1040,9 @@ public: | |||
| s.add ("SEPARATE_STRIP = YES"); | |||
| } | |||
| if (owner.iOS && owner.isInAppPurchasesEnabled()) | |||
| defines.set ("JUCE_IN_APP_PURCHASES", "1"); | |||
| defines = mergePreprocessorDefs (defines, owner.getAllPreprocessorDefs (config, type)); | |||
| StringArray defsList; | |||
| @@ -180,6 +180,10 @@ | |||
| //#define JUCE_DSP_USE_STATIC_FFTW 1 | |||
| #endif | |||
| #ifndef JUCE_DSP_ENABLE_SNAP_TO_ZERO | |||
| //#define JUCE_DSP_ENABLE_SNAP_TO_ZERO 1 | |||
| #endif | |||
| //============================================================================== | |||
| // juce_events flags: | |||
| @@ -2,7 +2,7 @@ | |||
| <JUCERPROJECT id="Z2Xzcp" name="UnitTestRunner" projectType="consoleapp" version="1.0.0" | |||
| bundleIdentifier="com.roli.UnitTestRunner" includeBinaryInAppConfig="1" | |||
| jucerVersion="5.1.1" defines="JUCE_UNIT_TESTS=1" displaySplashScreen="0" | |||
| jucerVersion="5.1.2" defines="JUCE_UNIT_TESTS=1" displaySplashScreen="0" | |||
| reportAppUsage="0" splashScreenColour="Dark" companyName="ROLI Ltd." | |||
| cppLanguageStandard="14"> | |||
| <MAINGROUP id="GZdWCU" name="UnitTestRunner"> | |||
| @@ -1,7 +1,7 @@ | |||
| <?xml version="1.0" encoding="UTF-8"?> | |||
| <JUCERPROJECT id="3t6YqETY1" name="BinaryBuilder" projectType="consoleapp" | |||
| juceFolder="../../../juce" jucerVersion="5.1.1" bundleIdentifier="com.roli.binarybuilder" | |||
| juceFolder="../../../juce" jucerVersion="5.1.2" bundleIdentifier="com.roli.binarybuilder" | |||
| includeBinaryInAppConfig="1" version="1.0.0" displaySplashScreen="0" | |||
| reportAppUsage="0" splashScreenColour="Dark" companyName="ROLI Ltd." | |||
| cppLanguageStandard="11"> | |||
| @@ -1,7 +1,7 @@ | |||
| <?xml version="1.0" encoding="UTF-8"?> | |||
| <JUCERPROJECT id="IvabE4" name="juce_dll" projectType="library" version="1.0.0" | |||
| juceLinkage="none" bundleIdentifier="com.roli.jucedll" jucerVersion="5.1.1" | |||
| juceLinkage="none" bundleIdentifier="com.roli.jucedll" jucerVersion="5.1.2" | |||
| defines="JUCE_DLL_BUILD=1" includeBinaryInAppConfig="1" displaySplashScreen="0" | |||
| reportAppUsage="0" splashScreenColour="Dark" companyName="ROLI Ltd." | |||
| cppLanguageStandard="11"> | |||
| @@ -20,8 +20,8 @@ | |||
| ============================================================================== | |||
| */ | |||
| #pragma once | |||
| namespace juce | |||
| { | |||
| //============================================================================== | |||
| /** | |||
| @@ -46,14 +46,15 @@ public: | |||
| /** Frame rate types. */ | |||
| enum FrameRateType | |||
| { | |||
| fps24 = 0, | |||
| fps25 = 1, | |||
| fps2997 = 2, | |||
| fps30 = 3, | |||
| fps2997drop = 4, | |||
| fps30drop = 5, | |||
| fps60 = 6, | |||
| fps60drop = 7, | |||
| fps23976 = 0, | |||
| fps24 = 1, | |||
| fps25 = 2, | |||
| fps2997 = 3, | |||
| fps30 = 4, | |||
| fps2997drop = 5, | |||
| fps30drop = 6, | |||
| fps60 = 7, | |||
| fps60drop = 8, | |||
| fpsUnknown = 99 | |||
| }; | |||
| @@ -70,12 +71,12 @@ public: | |||
| /** Time signature denominator, e.g. the 4 of a 3/4 time sig */ | |||
| int timeSigDenominator; | |||
| /** The current play position, in samples from the start of the edit. */ | |||
| /** The current play position, in samples from the start of the timeline. */ | |||
| int64 timeInSamples; | |||
| /** The current play position, in seconds from the start of the edit. */ | |||
| /** The current play position, in seconds from the start of the timeline. */ | |||
| double timeInSeconds; | |||
| /** For timecode, the position of the start of the edit, in seconds from 00:00:00:00. */ | |||
| /** For timecode, the position of the start of the timeline, in seconds from 00:00:00:00. */ | |||
| double editOriginTime; | |||
| /** The current play position, in pulses-per-quarter-note. */ | |||
| @@ -83,7 +84,7 @@ public: | |||
| /** The position of the start of the last bar, in pulses-per-quarter-note. | |||
| This is the time from the start of the edit to the start of the current | |||
| This is the time from the start of the timeline to the start of the current | |||
| bar, in ppq units. | |||
| Note - this value may be unavailable on some hosts, e.g. Pro-Tools. If | |||
| @@ -150,3 +151,5 @@ public: | |||
| /** Rewinds the audio. */ | |||
| virtual void transportRewind() {} | |||
| }; | |||
| } // namespace juce | |||
| @@ -20,6 +20,9 @@ | |||
| ============================================================================== | |||
| */ | |||
| namespace juce | |||
| { | |||
| AudioChannelSet::AudioChannelSet (uint32 c) : channels (c) {} | |||
| AudioChannelSet::AudioChannelSet (const Array<ChannelType>& c) | |||
| { | |||
| @@ -414,3 +417,5 @@ int32 AudioChannelSet::getWaveChannelMask() const noexcept | |||
| return (channels.toInteger() >> 1); | |||
| } | |||
| } // namespace juce | |||
| @@ -20,8 +20,8 @@ | |||
| ============================================================================== | |||
| */ | |||
| #pragma once | |||
| namespace juce | |||
| { | |||
| //============================================================================== | |||
| /** | |||
| @@ -388,3 +388,5 @@ private: | |||
| explicit AudioChannelSet (uint32); | |||
| explicit AudioChannelSet (const Array<ChannelType>&); | |||
| }; | |||
| } // namespace juce | |||
| @@ -20,6 +20,9 @@ | |||
| ============================================================================== | |||
| */ | |||
| namespace juce | |||
| { | |||
| void AudioDataConverters::convertFloatToInt16LE (const float* source, void* dest, int numSamples, const int destBytesPerSample) | |||
| { | |||
| const double maxVal = (double) 0x7fff; | |||
| @@ -309,7 +312,7 @@ void AudioDataConverters::convertInt24BEToFloat (const void* const source, float | |||
| void AudioDataConverters::convertInt32LEToFloat (const void* const source, float* const dest, int numSamples, const int srcBytesPerSample) | |||
| { | |||
| const float scale = 1.0f / 0x7fffffff; | |||
| const auto scale = 1.0f / (float) 0x7fffffff; | |||
| const char* intData = static_cast<const char*> (source); | |||
| if (source != (void*) dest || srcBytesPerSample >= 4) | |||
| @@ -334,7 +337,7 @@ void AudioDataConverters::convertInt32LEToFloat (const void* const source, float | |||
| void AudioDataConverters::convertInt32BEToFloat (const void* const source, float* const dest, int numSamples, const int srcBytesPerSample) | |||
| { | |||
| const float scale = 1.0f / 0x7fffffff; | |||
| const auto scale = 1.0f / (float) 0x7fffffff; | |||
| const char* intData = static_cast<const char*> (source); | |||
| if (source != (void*) dest || srcBytesPerSample >= 4) | |||
| @@ -596,3 +599,5 @@ public: | |||
| static AudioConversionTests audioConversionUnitTests; | |||
| #endif | |||
| } // namespace juce | |||
| @@ -20,8 +20,8 @@ | |||
| ============================================================================== | |||
| */ | |||
| #pragma once | |||
| namespace juce | |||
| { | |||
| //============================================================================== | |||
| /** | |||
| @@ -708,3 +708,5 @@ private: | |||
| AudioDataConverters(); | |||
| JUCE_DECLARE_NON_COPYABLE (AudioDataConverters) | |||
| }; | |||
| } // namespace juce | |||
| @@ -20,8 +20,8 @@ | |||
| ============================================================================== | |||
| */ | |||
| #pragma once | |||
| namespace juce | |||
| { | |||
| //============================================================================== | |||
| /** | |||
| @@ -184,11 +184,19 @@ public: | |||
| : numChannels (other.numChannels), | |||
| size (other.size), | |||
| allocatedBytes (other.allocatedBytes), | |||
| channels (other.channels), | |||
| allocatedData (static_cast<HeapBlock<char, true>&&> (other.allocatedData)), | |||
| isClear (other.isClear) | |||
| { | |||
| memcpy (preallocatedChannelSpace, other.preallocatedChannelSpace, sizeof (preallocatedChannelSpace)); | |||
| if (numChannels < (int) numElementsInArray (preallocatedChannelSpace)) | |||
| { | |||
| channels = preallocatedChannelSpace; | |||
| memcpy (preallocatedChannelSpace, other.channels, sizeof (preallocatedChannelSpace)); | |||
| } | |||
| else | |||
| { | |||
| channels = other.channels; | |||
| } | |||
| other.numChannels = 0; | |||
| other.size = 0; | |||
| other.allocatedBytes = 0; | |||
| @@ -200,10 +208,19 @@ public: | |||
| numChannels = other.numChannels; | |||
| size = other.size; | |||
| allocatedBytes = other.allocatedBytes; | |||
| channels = other.channels; | |||
| allocatedData = static_cast<HeapBlock<char, true>&&> (other.allocatedData); | |||
| isClear = other.isClear; | |||
| memcpy (preallocatedChannelSpace, other.preallocatedChannelSpace, sizeof (preallocatedChannelSpace)); | |||
| if (numChannels < (int) numElementsInArray (preallocatedChannelSpace)) | |||
| { | |||
| channels = preallocatedChannelSpace; | |||
| memcpy (preallocatedChannelSpace, other.channels, sizeof (preallocatedChannelSpace)); | |||
| } | |||
| else | |||
| { | |||
| channels = other.channels; | |||
| } | |||
| other.numChannels = 0; | |||
| other.size = 0; | |||
| other.allocatedBytes = 0; | |||
| @@ -332,7 +349,7 @@ public: | |||
| auto numSamplesToCopy = (size_t) jmin (newNumSamples, size); | |||
| auto newChannels = reinterpret_cast<Type**> (newData.getData()); | |||
| auto newChannels = reinterpret_cast<Type**> (newData.get()); | |||
| auto newChan = reinterpret_cast<Type*> (newData + channelListSize); | |||
| for (int j = 0; j < newNumChannels; ++j) | |||
| @@ -364,7 +381,7 @@ public: | |||
| { | |||
| allocatedBytes = newTotalBytes; | |||
| allocatedData.allocate (newTotalBytes, clearExtraSpace || isClear); | |||
| channels = reinterpret_cast<Type**> (allocatedData.getData()); | |||
| channels = reinterpret_cast<Type**> (allocatedData.get()); | |||
| } | |||
| auto* chan = reinterpret_cast<Type*> (allocatedData + channelListSize); | |||
| @@ -629,7 +646,7 @@ public: | |||
| jassert (isPositiveAndBelow (channel, numChannels)); | |||
| jassert (startSample >= 0 && numSamples >= 0 && startSample + numSamples <= size); | |||
| const auto increment = (endGain - startGain) / numSamples; | |||
| const auto increment = (endGain - startGain) / (float) numSamples; | |||
| auto* d = channels[channel] + startSample; | |||
| while (--numSamples >= 0) | |||
| @@ -1051,7 +1068,7 @@ private: | |||
| auto channelListSize = sizeof (Type*) * (size_t) (numChannels + 1); | |||
| allocatedBytes = (size_t) numChannels * (size_t) size * sizeof (Type) + channelListSize + 32; | |||
| allocatedData.malloc (allocatedBytes); | |||
| channels = reinterpret_cast<Type**> (allocatedData.getData()); | |||
| channels = reinterpret_cast<Type**> (allocatedData.get()); | |||
| auto* chan = (Type*) (allocatedData + channelListSize); | |||
| for (int i = 0; i < numChannels; ++i) | |||
| @@ -1076,7 +1093,7 @@ private: | |||
| else | |||
| { | |||
| allocatedData.malloc ((size_t) numChannels + 1, sizeof (Type*)); | |||
| channels = reinterpret_cast<Type**> (allocatedData.getData()); | |||
| channels = reinterpret_cast<Type**> (allocatedData.get()); | |||
| } | |||
| for (int i = 0; i < numChannels; ++i) | |||
| @@ -1105,3 +1122,5 @@ private: | |||
| @see AudioBuffer | |||
| */ | |||
| typedef AudioBuffer<float> AudioSampleBuffer; | |||
| } // namespace juce | |||
| @@ -20,6 +20,9 @@ | |||
| ============================================================================== | |||
| */ | |||
| namespace juce | |||
| { | |||
| namespace FloatVectorHelpers | |||
| { | |||
| #define JUCE_INCREMENT_SRC_DEST dest += (16 / sizeof (*dest)); src += (16 / sizeof (*dest)); | |||
| @@ -874,7 +877,7 @@ void JUCE_CALLTYPE FloatVectorOperations::convertFixedToFloat (float* dest, cons | |||
| vmulq_n_f32 (vcvtq_f32_s32 (vld1q_s32 (src)), multiplier), | |||
| JUCE_LOAD_NONE, JUCE_INCREMENT_SRC_DEST, ) | |||
| #else | |||
| JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = src[i] * multiplier, | |||
| JUCE_PERFORM_VEC_OP_SRC_DEST (dest[i] = (float) src[i] * multiplier, | |||
| Mode::mul (mult, _mm_cvtepi32_ps (_mm_loadu_si128 ((const __m128i*) src))), | |||
| JUCE_LOAD_NONE, JUCE_INCREMENT_SRC_DEST, | |||
| const Mode::ParallelType mult = Mode::load1 (multiplier);) | |||
| @@ -1068,9 +1071,9 @@ public: | |||
| #else | |||
| // These tests deliberately operate on misaligned memory and will be flagged up by | |||
| // checks for undefined behavior! | |||
| ValueType* const data1 = addBytesToPointer (buffer1.getData(), random.nextInt (16)); | |||
| ValueType* const data2 = addBytesToPointer (buffer2.getData(), random.nextInt (16)); | |||
| int* const int1 = addBytesToPointer (buffer3.getData(), random.nextInt (16)); | |||
| ValueType* const data1 = addBytesToPointer (buffer1.get(), random.nextInt (16)); | |||
| ValueType* const data2 = addBytesToPointer (buffer2.get(), random.nextInt (16)); | |||
| int* const int1 = addBytesToPointer (buffer3.get(), random.nextInt (16)); | |||
| #endif | |||
| fillRandomly (random, data1, num); | |||
| @@ -1158,7 +1161,7 @@ public: | |||
| static void convertFixed (float* d, const int* s, ValueType multiplier, int num) | |||
| { | |||
| while (--num >= 0) | |||
| *d++ = *s++ * multiplier; | |||
| *d++ = (float) *s++ * multiplier; | |||
| } | |||
| static bool areAllValuesEqual (const ValueType* d, int num, ValueType target) | |||
| @@ -1200,3 +1203,5 @@ public: | |||
| static FloatVectorOperationsTests vectorOpTests; | |||
| #endif | |||
| } // namespace juce | |||
| @@ -20,7 +20,8 @@ | |||
| ============================================================================== | |||
| */ | |||
| #pragma once | |||
| namespace juce | |||
| { | |||
| #if JUCE_INTEL | |||
| #define JUCE_SNAP_TO_ZERO(n) if (! (n < -1.0e-8f || n > 1.0e-8f)) n = 0; | |||
| @@ -219,3 +220,35 @@ public: | |||
| */ | |||
| static void JUCE_CALLTYPE disableDenormalisedNumberSupport() noexcept; | |||
| }; | |||
| //============================================================================== | |||
| /** | |||
| Helper class providing an RAII-based mechanism for temporarily disabling | |||
| denormals on your CPU. | |||
| */ | |||
| class ScopedNoDenormals | |||
| { | |||
| public: | |||
| inline ScopedNoDenormals() noexcept | |||
| { | |||
| #if JUCE_USE_SSE_INTRINSICS | |||
| mxcsr = _mm_getcsr(); | |||
| _mm_setcsr (mxcsr | 0x8040); // add the DAZ and FZ bits | |||
| #endif | |||
| } | |||
| inline ~ScopedNoDenormals() noexcept | |||
| { | |||
| #if JUCE_USE_SSE_INTRINSICS | |||
| _mm_setcsr (mxcsr); | |||
| #endif | |||
| } | |||
| private: | |||
| #if JUCE_USE_SSE_INTRINSICS | |||
| unsigned int mxcsr; | |||
| #endif | |||
| }; | |||
| } // namespace juce | |||